summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.cpp10
-rw-r--r--cmds/hid/jni/com_android_commands_hid_Device.h2
-rw-r--r--cmds/hid/src/com/android/commands/hid/Device.java7
-rw-r--r--core/api/current.txt66
-rw-r--r--core/api/module-lib-current.txt2
-rw-r--r--core/api/system-current.txt36
-rw-r--r--core/api/test-current.txt10
-rw-r--r--core/java/android/app/ActivityManager.java99
-rw-r--r--core/java/android/app/SystemServiceRegistry.java3
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl12
-rw-r--r--core/java/android/hardware/input/InputDeviceLightsManager.java139
-rw-r--r--core/java/android/hardware/input/InputManager.java98
-rw-r--r--core/java/android/hardware/lights/Light.java60
-rw-r--r--core/java/android/hardware/lights/LightState.java73
-rw-r--r--core/java/android/hardware/lights/LightsManager.java142
-rw-r--r--core/java/android/hardware/lights/LightsRequest.java32
-rw-r--r--core/java/android/hardware/lights/SystemLightsManager.java181
-rw-r--r--core/java/android/net/NetworkPolicyManager.java50
-rw-r--r--core/java/android/view/InputDevice.java19
-rw-r--r--core/java/android/view/View.java19
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/widget/ImageView.java1
-rw-r--r--core/java/android/widget/ProgressBar.java2
-rw-r--r--core/java/android/widget/RemoteViews.java41
-rw-r--r--core/java/android/widget/TextView.java4
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java28
-rw-r--r--core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java227
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java148
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java56
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt48
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java20
-rw-r--r--media/java/android/media/session/MediaSessionManager.java104
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java9
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java20
-rw-r--r--packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java4
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java77
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java161
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyLogger.java18
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java109
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java8
-rw-r--r--services/core/java/com/android/server/pm/DumpState.java19
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java675
-rw-r--r--services/core/java/com/android/server/pm/Settings.java2
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp113
-rw-r--r--services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java32
53 files changed, 2272 insertions, 784 deletions
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 437a87e8df54..422c2be44251 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -113,9 +113,10 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) {
checkAndClearException(env, "onDeviceGetReport");
}
-void DeviceCallback::onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data) {
+void DeviceCallback::onDeviceOutput(uint8_t eventId, uint8_t rType,
+ const std::vector<uint8_t>& data) {
JNIEnv* env = getJNIEnv();
- env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType,
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, eventId, rType,
toJbyteArray(env, data).get());
checkAndClearException(env, "onDeviceOutput");
}
@@ -261,6 +262,7 @@ int Device::handleEvents(int events) {
ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
set_report.rnum, toString(data).c_str());
}
+ mDeviceCallback->onDeviceOutput(UHID_SET_REPORT, set_report.rtype, data);
break;
}
case UHID_OUTPUT: {
@@ -269,7 +271,7 @@ int Device::handleEvents(int events) {
if (DEBUG_OUTPUT) {
ALOGD("UHID_OUTPUT rtype=%" PRIu8 " data=%s", output.rtype, toString(data).c_str());
}
- mDeviceCallback->onDeviceOutput(output.rtype, data);
+ mDeviceCallback->onDeviceOutput(UHID_OUTPUT, output.rtype, data);
break;
}
default: {
@@ -365,7 +367,7 @@ int register_com_android_commands_hid_Device(JNIEnv* env) {
uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
uhid::gDeviceCallbackClassInfo.onDeviceOutput =
- env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V");
+ env->GetMethodID(clazz, "onDeviceOutput", "(BB[B)V");
uhid::gDeviceCallbackClassInfo.onDeviceError =
env->GetMethodID(clazz, "onDeviceError", "()V");
if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 5483b40831a0..bb73132cc20d 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -31,7 +31,7 @@ public:
void onDeviceOpen();
void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
- void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data);
+ void onDeviceOutput(uint8_t eventId, uint8_t rType, const std::vector<uint8_t>& data);
void onDeviceError();
private:
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 20b4bd86baec..37d0b1c6bfa1 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -46,7 +46,8 @@ public class Device {
// Sync with linux uhid_event_type::UHID_OUTPUT
private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6;
-
+ // Sync with linux uhid_event_type::UHID_SET_REPORT
+ private static final byte UHID_EVENT_TYPE_SET_REPORT = 13;
private final int mId;
private final HandlerThread mThread;
private final DeviceHandler mHandler;
@@ -199,10 +200,10 @@ public class Device {
}
// native callback
- public void onDeviceOutput(byte rtype, byte[] data) {
+ public void onDeviceOutput(byte eventId, byte rtype, byte[] data) {
JSONObject json = new JSONObject();
try {
- json.put("eventId", UHID_EVENT_TYPE_UHID_OUTPUT);
+ json.put("eventId", eventId);
json.put("deviceId", mId);
json.put("reportType", rtype);
JSONArray dataArray = new JSONArray();
diff --git a/core/api/current.txt b/core/api/current.txt
index b0375b69553d..db5d143662e4 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18659,6 +18659,58 @@ package android.hardware.input {
}
+package android.hardware.lights {
+
+ public final class Light implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getId();
+ method @NonNull public String getName();
+ method public int getOrdinal();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
+ field public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10; // 0xa
+ field public static final int LIGHT_TYPE_INPUT_RGB = 11; // 0xb
+ field public static final int LIGHT_TYPE_INPUT_SINGLE = 9; // 0x9
+ field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
+ }
+
+ public final class LightState implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public static android.hardware.lights.LightState forColor(@ColorInt int);
+ method @NonNull public static android.hardware.lights.LightState forPlayerId(int);
+ method @ColorInt public int getColor();
+ method public int getPlayerId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
+ }
+
+ public abstract class LightsManager {
+ method @NonNull public abstract android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light);
+ method @NonNull public abstract java.util.List<android.hardware.lights.Light> getLights();
+ method @NonNull public abstract android.hardware.lights.LightsManager.LightsSession openSession();
+ }
+
+ public abstract static class LightsManager.LightsSession implements java.lang.AutoCloseable {
+ ctor public LightsManager.LightsSession();
+ method public abstract void close();
+ method public abstract void requestLights(@NonNull android.hardware.lights.LightsRequest);
+ }
+
+ public final class LightsRequest {
+ method @NonNull public java.util.List<android.hardware.lights.LightState> getLightStates();
+ method @NonNull public java.util.List<java.lang.Integer> getLights();
+ }
+
+ public static final class LightsRequest.Builder {
+ ctor public LightsRequest.Builder();
+ method @NonNull public android.hardware.lights.LightsRequest.Builder addLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+ method @NonNull public android.hardware.lights.LightsRequest build();
+ method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
+ }
+
+}
+
package android.hardware.usb {
public class UsbAccessory implements android.os.Parcelable {
@@ -46898,6 +46950,7 @@ package android.view {
method public int getId();
method public android.view.KeyCharacterMap getKeyCharacterMap();
method public int getKeyboardType();
+ method @NonNull public android.hardware.lights.LightsManager getLightsManager();
method public android.view.InputDevice.MotionRange getMotionRange(int);
method public android.view.InputDevice.MotionRange getMotionRange(int, int);
method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges();
@@ -55071,6 +55124,7 @@ package android.widget {
method public void setChronometerCountDown(@IdRes int, boolean);
method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
method public void setColorInt(@IdRes int, @NonNull String, @ColorInt int, @ColorInt int);
+ method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList);
method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList, @Nullable android.content.res.ColorStateList);
method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
method public void setCompoundButtonChecked(@IdRes int, boolean);
@@ -55113,6 +55167,12 @@ package android.widget {
method public void setTextViewText(@IdRes int, CharSequence);
method public void setTextViewTextSize(@IdRes int, int, float);
method public void setUri(@IdRes int, String, android.net.Uri);
+ method public void setViewLayoutHeight(@IdRes int, float, int);
+ method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int);
+ method public void setViewLayoutMargin(@IdRes int, int, float, int);
+ method public void setViewLayoutMarginDimen(@IdRes int, int, @DimenRes int);
+ method public void setViewLayoutWidth(@IdRes int, float, int);
+ method public void setViewLayoutWidthDimen(@IdRes int, @DimenRes int);
method public void setViewOutlinePreferredRadius(@IdRes int, float, int);
method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int);
method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int);
@@ -55123,6 +55183,12 @@ package android.widget {
field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR;
field public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED";
field public static final String EXTRA_SHARED_ELEMENT_BOUNDS = "android.widget.extra.SHARED_ELEMENT_BOUNDS";
+ field public static final int MARGIN_BOTTOM = 3; // 0x3
+ field public static final int MARGIN_END = 5; // 0x5
+ field public static final int MARGIN_LEFT = 0; // 0x0
+ field public static final int MARGIN_RIGHT = 2; // 0x2
+ field public static final int MARGIN_START = 4; // 0x4
+ field public static final int MARGIN_TOP = 1; // 0x1
}
public static class RemoteViews.ActionException extends java.lang.RuntimeException {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index de02d0bb7e98..5dc1cd7a3173 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -135,7 +135,7 @@ package android.media.session {
}
public final class MediaSessionManager {
- method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.os.Handler);
+ method public void addOnActiveSessionsChangedListener(@Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent);
method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean);
method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d79c11db9d09..a5d40a63a795 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3398,42 +3398,12 @@ package android.hardware.hdmi {
package android.hardware.lights {
- public final class Light implements android.os.Parcelable {
- method public int describeContents();
- method public int getId();
- method public int getOrdinal();
- method public int getType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
- }
-
public final class LightState implements android.os.Parcelable {
- ctor public LightState(@ColorInt int);
- method public int describeContents();
- method @ColorInt public int getColor();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR;
- }
-
- public final class LightsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights();
- method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession();
- field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
- }
-
- public final class LightsManager.LightsSession implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close();
- method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest);
- }
-
- public final class LightsRequest {
+ ctor @Deprecated public LightState(@ColorInt int);
}
- public static final class LightsRequest.Builder {
- ctor public LightsRequest.Builder();
- method @NonNull public android.hardware.lights.LightsRequest build();
- method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light);
- method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState);
+ public abstract class LightsManager {
+ field @Deprecated public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d7b43c0bc48e..fc1edf115625 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -100,7 +100,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
field public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L; // 0xa6929b8L
field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL
- field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
+ field public static final int PROCESS_CAPABILITY_ALL = 15; // 0xf
field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2
@@ -1001,14 +1001,6 @@ package android.hardware.input {
}
-package android.hardware.lights {
-
- public final class LightsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light);
- }
-
-}
-
package android.hardware.soundtrigger {
public class KeyphraseEnrollmentInfo {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e2426d116319..220c332647a9 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -616,11 +616,15 @@ public class ActivityManager {
@TestApi
public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2;
+ /** @hide Process can access network despite any power saving resrictions */
+ public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3;
+
/** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/
@TestApi
public static final int PROCESS_CAPABILITY_ALL = PROCESS_CAPABILITY_FOREGROUND_LOCATION
| PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
+ | PROCESS_CAPABILITY_NETWORK;
/**
* All explicit capabilities. These are capabilities that need to be specified from manifest
* file.
@@ -646,6 +650,15 @@ public class ActivityManager {
pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-');
pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-');
pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-');
+ pw.print((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-');
+ }
+
+ /** @hide */
+ public static void printCapabilitiesSummary(StringBuilder sb, @ProcessCapability int caps) {
+ sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-');
}
/**
@@ -656,13 +669,21 @@ public class ActivityManager {
printCapabilitiesSummary(pw, caps);
final int remain = caps & ~(PROCESS_CAPABILITY_FOREGROUND_LOCATION
| PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE);
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
+ | PROCESS_CAPABILITY_NETWORK);
if (remain != 0) {
pw.print('+');
pw.print(remain);
}
}
+ /** @hide */
+ public static String getCapabilitiesSummary(@ProcessCapability int caps) {
+ final StringBuilder sb = new StringBuilder();
+ printCapabilitiesSummary(sb, caps);
+ return sb.toString();
+ }
+
// NOTE: If PROCESS_STATEs are added, then new fields must be added
// to frameworks/base/core/proto/android/app/enums.proto and the following method must
// be updated to correctly map between them.
@@ -4485,6 +4506,80 @@ public class ActivityManager {
}
}
+ /** @hide */
+ public static String procStateToString(int procState) {
+ final String procStateStr;
+ switch (procState) {
+ case ActivityManager.PROCESS_STATE_PERSISTENT:
+ procStateStr = "PER ";
+ break;
+ case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
+ procStateStr = "PERU";
+ break;
+ case ActivityManager.PROCESS_STATE_TOP:
+ procStateStr = "TOP ";
+ break;
+ case ActivityManager.PROCESS_STATE_BOUND_TOP:
+ procStateStr = "BTOP";
+ break;
+ case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
+ procStateStr = "FGS ";
+ break;
+ case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
+ procStateStr = "BFGS";
+ break;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+ procStateStr = "IMPF";
+ break;
+ case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+ procStateStr = "IMPB";
+ break;
+ case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
+ procStateStr = "TRNB";
+ break;
+ case ActivityManager.PROCESS_STATE_BACKUP:
+ procStateStr = "BKUP";
+ break;
+ case ActivityManager.PROCESS_STATE_SERVICE:
+ procStateStr = "SVC ";
+ break;
+ case ActivityManager.PROCESS_STATE_RECEIVER:
+ procStateStr = "RCVR";
+ break;
+ case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
+ procStateStr = "TPSL";
+ break;
+ case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
+ procStateStr = "HVY ";
+ break;
+ case ActivityManager.PROCESS_STATE_HOME:
+ procStateStr = "HOME";
+ break;
+ case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
+ procStateStr = "LAST";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+ procStateStr = "CAC ";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+ procStateStr = "CACC";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_RECENT:
+ procStateStr = "CRE ";
+ break;
+ case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+ procStateStr = "CEM ";
+ break;
+ case ActivityManager.PROCESS_STATE_NONEXISTENT:
+ procStateStr = "NONE";
+ break;
+ default:
+ procStateStr = "??";
+ break;
+ }
+ return procStateStr;
+ }
+
/**
* The AppTask allows you to manage your own application's tasks.
* See {@link android.app.ActivityManager#getAppTasks()}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index ffaaa578cdd2..dd5a9a8a28af 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -99,6 +99,7 @@ import android.hardware.input.InputManager;
import android.hardware.iris.IIrisService;
import android.hardware.iris.IrisManager;
import android.hardware.lights.LightsManager;
+import android.hardware.lights.SystemLightsManager;
import android.hardware.location.ContextHubManager;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
@@ -1344,7 +1345,7 @@ public final class SystemServiceRegistry {
@Override
public LightsManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- return new LightsManager(ctx);
+ return new SystemLightsManager(ctx);
}});
registerService(Context.INCREMENTAL_SERVICE, IncrementalManager.class,
new CachedServiceFetcher<IncrementalManager>() {
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index eaa38f3e862c..4743fee3257b 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -25,6 +25,8 @@ import android.hardware.input.TouchCalibration;
import android.os.CombinedVibrationEffect;
import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.InputSensorInfo;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
import android.os.IBinder;
import android.os.IVibratorStateListener;
import android.os.VibrationEffect;
@@ -127,4 +129,14 @@ interface IInputManager {
void disableSensor(int deviceId, int sensorType);
boolean flushSensor(int deviceId, int sensorType);
+
+ List<Light> getLights(int deviceId);
+
+ LightState getLightState(int deviceId, int lightId);
+
+ void setLightStates(int deviceId, in int[] lightIds, in LightState[] states, in IBinder token);
+
+ void openLightSession(int deviceId, String opPkg, in IBinder token);
+
+ void closeLightSession(int deviceId, in IBinder token);
}
diff --git a/core/java/android/hardware/input/InputDeviceLightsManager.java b/core/java/android/hardware/input/InputDeviceLightsManager.java
new file mode 100644
index 000000000000..a3b91a99fdb7
--- /dev/null
+++ b/core/java/android/hardware/input/InputDeviceLightsManager.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
+import android.util.CloseGuard;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.Reference;
+import java.util.List;
+
+/**
+ * LightsManager manages an input device's lights {@link android.hardware.input.Light}.
+ */
+class InputDeviceLightsManager extends LightsManager {
+ private static final String TAG = "InputDeviceLightsManager";
+ private static final boolean DEBUG = false;
+
+ private final InputManager mInputManager;
+
+ // The input device ID.
+ private final int mDeviceId;
+ // Package name
+ private final String mPackageName;
+
+ InputDeviceLightsManager(InputManager inputManager, int deviceId) {
+ super(ActivityThread.currentActivityThread().getSystemContext());
+ mInputManager = inputManager;
+ mDeviceId = deviceId;
+ mPackageName = ActivityThread.currentPackageName();
+ }
+
+ /**
+ * Returns the lights available on the device.
+ *
+ * @return A list of available lights
+ */
+ @Override
+ public @NonNull List<Light> getLights() {
+ return mInputManager.getLights(mDeviceId);
+ }
+
+ /**
+ * Returns the state of a specified light.
+ *
+ * @hide
+ */
+ @Override
+ public @NonNull LightState getLightState(@NonNull Light light) {
+ Preconditions.checkNotNull(light);
+ return mInputManager.getLightState(mDeviceId, light);
+ }
+
+ /**
+ * Creates a new LightsSession that can be used to control the device lights.
+ */
+ @Override
+ public @NonNull LightsSession openSession() {
+ final LightsSession session = new InputDeviceLightsSession();
+ mInputManager.openLightSession(mDeviceId, mPackageName, session.getToken());
+ return session;
+ }
+
+ /**
+ * Encapsulates a session that can be used to control device lights and represents the lifetime
+ * of the requests.
+ */
+ public final class InputDeviceLightsSession extends LightsManager.LightsSession
+ implements AutoCloseable {
+
+ private final CloseGuard mCloseGuard = new CloseGuard();
+ private boolean mClosed = false;
+
+ /**
+ * Instantiated by {@link LightsManager#openSession()}.
+ */
+ private InputDeviceLightsSession() {
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Sends a request to modify the states of multiple lights.
+ *
+ * @param request the settings for lights that should change
+ */
+ @Override
+ public void requestLights(@NonNull LightsRequest request) {
+ Preconditions.checkNotNull(request);
+ Preconditions.checkArgument(!mClosed);
+
+ mInputManager.requestLights(mDeviceId, request, getToken());
+ }
+
+ /**
+ * Closes the session, reverting all changes made through it.
+ */
+ @Override
+ public void close() {
+ if (!mClosed) {
+ mInputManager.closeLightSession(mDeviceId, getToken());
+ mClosed = true;
+ mCloseGuard.close();
+ }
+ Reference.reachabilityFence(this);
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 8a01c660ebd0..e15d6298d63d 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -30,6 +30,10 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.SensorManager;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
import android.os.BlockUntrustedTouchesMode;
import android.os.Build;
import android.os.CombinedVibrationEffect;
@@ -1409,7 +1413,7 @@ public final class InputManager {
}
/**
- * Gets a vibrator service associated with an input device, always create a new instance.
+ * Gets a vibrator service associated with an input device, always creates a new instance.
* @return The vibrator, never null.
* @hide
*/
@@ -1418,7 +1422,7 @@ public final class InputManager {
}
/**
- * Gets a vibrator manager service associated with an input device, always create a new
+ * Gets a vibrator manager service associated with an input device, always creates a new
* instance.
* @return The vibrator manager, never null.
* @hide
@@ -1486,10 +1490,8 @@ public final class InputManager {
/**
* Register input device vibrator state listener
- *
- * @hide
*/
- public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.registerVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
@@ -1499,10 +1501,8 @@ public final class InputManager {
/**
* Unregister input device vibrator state listener
- *
- * @hide
*/
- public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.unregisterVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
@@ -1511,7 +1511,7 @@ public final class InputManager {
}
/**
- * Gets a sensor manager service associated with an input device, always create a new instance.
+ * Gets a sensor manager service associated with an input device, always creates a new instance.
* @return The sensor manager, never null.
* @hide
*/
@@ -1533,6 +1533,86 @@ public final class InputManager {
}
/**
+ * Gets a lights manager associated with an input device, always creates a new instance.
+ * @return The lights manager, never null.
+ * @hide
+ */
+ @NonNull
+ public LightsManager getInputDeviceLightsManager(int deviceId) {
+ return new InputDeviceLightsManager(InputManager.this, deviceId);
+ }
+
+ /**
+ * Gets a list of light objects associated with an input device.
+ * @return The list of lights, never null.
+ */
+ @NonNull List<Light> getLights(int deviceId) {
+ try {
+ return mIm.getLights(deviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the state of an input device light.
+ * @return the light state
+ */
+ @NonNull LightState getLightState(int deviceId, @NonNull Light light) {
+ try {
+ return mIm.getLightState(deviceId, light.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to modify the states of multiple lights.
+ *
+ * @param request the settings for lights that should change
+ */
+ void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) {
+ try {
+ List<Integer> lightIdList = request.getLights();
+ int[] lightIds = new int[lightIdList.size()];
+ for (int i = 0; i < lightIds.length; i++) {
+ lightIds[i] = lightIdList.get(i);
+ }
+ List<LightState> lightStateList = request.getLightStates();
+ mIm.setLightStates(deviceId, lightIds,
+ lightStateList.toArray(new LightState[lightStateList.size()]),
+ token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Open light session for input device manager
+ *
+ * @param token The token for the light session
+ */
+ void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) {
+ try {
+ mIm.openLightSession(deviceId, opPkg, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Close light session
+ *
+ */
+ void closeLightSession(int deviceId, @NonNull IBinder token) {
+ try {
+ mIm.closeLightSession(deviceId, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Listens for changes in input devices.
*/
public interface InputDeviceListener {
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index da270182052d..7bfff5d3af97 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -16,22 +16,56 @@
package android.hardware.lights;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents a logical light on the device.
*
- * @hide
*/
-@SystemApi
public final class Light implements Parcelable {
+ // These enum values copy the values from {@link com.android.server.lights.LightsManager}
+ // and the light HAL. Since 0-7 are lights reserved for system use, 8 for microphone light is
+ // defined in {@link android.hardware.lights.LightsManager}, following types are available
+ // through this API.
+ /** Type for lights that indicate microphone usage */
+ public static final int LIGHT_TYPE_MICROPHONE = 8;
+
+ /**
+ * Type for lights that indicate a monochrome color LED light.
+ */
+ public static final int LIGHT_TYPE_INPUT_SINGLE = 9;
+
+ /**
+ * Type for lights that indicate a group of LED lights representing player ID.
+ */
+ public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10;
+
+ /**
+ * Type for lights that indicate a color LED light.
+ */
+ public static final int LIGHT_TYPE_INPUT_RGB = 11;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"LIGHT_TYPE_"},
+ value = {
+ LIGHT_TYPE_INPUT_PLAYER_ID,
+ LIGHT_TYPE_INPUT_SINGLE,
+ LIGHT_TYPE_INPUT_RGB,
+ })
+ public @interface LightType {}
+
private final int mId;
private final int mOrdinal;
private final int mType;
+ private final String mName;
/**
* Creates a new light with the given data.
@@ -39,15 +73,26 @@ public final class Light implements Parcelable {
* @hide
*/
public Light(int id, int ordinal, int type) {
+ this(id, ordinal, type, "Light");
+ }
+
+ /**
+ * Creates a new light with the given data.
+ *
+ * @hide
+ */
+ public Light(int id, int ordinal, int type, String name) {
mId = id;
mOrdinal = ordinal;
mType = type;
+ mName = name;
}
private Light(@NonNull Parcel in) {
mId = in.readInt();
mOrdinal = in.readInt();
mType = in.readInt();
+ mName = in.readString();
}
/** Implement the Parcelable interface */
@@ -56,6 +101,7 @@ public final class Light implements Parcelable {
dest.writeInt(mId);
dest.writeInt(mOrdinal);
dest.writeInt(mType);
+ dest.writeString(mName);
}
/** Implement the Parcelable interface */
@@ -100,6 +146,14 @@ public final class Light implements Parcelable {
}
/**
+ * Returns the name of the light.
+ */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
* Returns the ordinal of the light.
*
* <p>This is a sort key that represents the physical order of lights on the device with the
diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java
index cd39e6df91a9..650b383eeb0f 100644
--- a/core/java/android/hardware/lights/LightState.java
+++ b/core/java/android/hardware/lights/LightState.java
@@ -32,36 +32,93 @@ import android.os.Parcelable;
* will be converted to only a brightness value and that will be used for the light's single
* channel.
*
- * @hide
*/
-@SystemApi
public final class LightState implements Parcelable {
private final int mColor;
+ private final int mPlayerId;
/**
- * Creates a new LightState with the desired color and intensity.
+ * Creates a new LightState with the desired color and intensity, for a light type
+ * of RBG color or monochrome color.
*
* @param color the desired color and intensity in ARGB format.
+ * @deprecated this has been replaced with {@link android.hardware.lights.LightState#forColor }
+ * @hide
*/
+ @Deprecated
+ @SystemApi
public LightState(@ColorInt int color) {
+ this(color, 0);
+ }
+
+ /**
+ * Creates a new LightState with the desired color and intensity, and the player Id.
+ * Player Id will only be applied on Light type
+ * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}
+ *
+ * @param color the desired color and intensity in ARGB format.
+ * @hide
+ */
+ public LightState(@ColorInt int color, int playerId) {
mColor = color;
+ mPlayerId = playerId;
+ }
+
+ /**
+ * Creates a new LightState with the desired color and intensity, for a light type
+ * of RBG color or single monochrome color.
+ *
+ * @param color the desired color and intensity in ARGB format.
+ * @return The LightState object contains the color.
+ */
+ @NonNull
+ public static LightState forColor(@ColorInt int color) {
+ return new LightState(color, 0);
+ }
+
+ /**
+ * Creates a new LightState with the desired player id, for a light of type
+ * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}.
+ *
+ * @param playerId the desired player id.
+ * @return The LightState object contains the player id.
+ */
+ @NonNull
+ public static LightState forPlayerId(int playerId) {
+ return new LightState(0, playerId);
}
+ /**
+ * Creates a new LightState from a parcel object.
+ */
private LightState(@NonNull Parcel in) {
mColor = in.readInt();
+ mPlayerId = in.readInt();
}
/**
- * Return the color and intensity associated with this LightState.
- * @return the color and intensity in ARGB format. The A channel is ignored.
+ * Returns the color and intensity associated with this LightState.
+ * @return the color and intensity in ARGB format. The A channel is ignored. return 0 when
+ * calling LightsManager.getLightState with LIGHT_TYPE_INPUT_PLAYER_ID.
*/
public @ColorInt int getColor() {
return mColor;
}
+ /**
+ * Returns the player ID associated with this LightState for Light type
+ * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID},
+ * or 0 for other types.
+ * @return the player ID.
+ */
+ public int getPlayerId() {
+ return mPlayerId;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
+ dest.writeInt(mPlayerId);
}
@Override
@@ -69,6 +126,12 @@ public final class LightState implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ return "LightState{Color=0x" + Integer.toHexString(mColor) + ", PlayerId="
+ + mPlayerId + "}";
+ }
+
public static final @NonNull Parcelable.Creator<LightState> CREATOR =
new Parcelable.Creator<LightState>() {
public LightState createFromParcel(Parcel in) {
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
index 33e5fcaf2abb..8fd56db33c4b 100644
--- a/core/java/android/hardware/lights/LightsManager.java
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -16,43 +16,38 @@
package android.hardware.lights;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.CloseGuard;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.Reference;
import java.util.List;
/**
* The LightsManager class allows control over device lights.
*
- * @hide
*/
-@SystemApi
@SystemService(Context.LIGHTS_SERVICE)
-public final class LightsManager {
+public abstract class LightsManager {
private static final String TAG = "LightsManager";
+ @NonNull private final Context mContext;
// These enum values copy the values from {@link com.android.server.lights.LightsManager}
// and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light
- // is available through this API.
- /** Type for lights that indicate microphone usage */
+ // and following types are available through this API.
+ /** Type for lights that indicate microphone usage
+ * @deprecated this has been moved to {@link android.hardware.lights.Light }
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
public static final int LIGHT_TYPE_MICROPHONE = 8;
/** @hide */
@@ -63,28 +58,11 @@ public final class LightsManager {
})
public @interface LightType {}
- @NonNull private final Context mContext;
- @NonNull private final ILightsManager mService;
-
/**
- * Creates a LightsManager.
- *
- * @hide
+ * @hide to prevent subclassing from outside of the framework
*/
- public LightsManager(@NonNull Context context) throws ServiceNotFoundException {
- this(context, ILightsManager.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
- }
-
- /**
- * Creates a LightsManager with a provided service implementation.
- *
- * @hide
- */
- @VisibleForTesting
- public LightsManager(@NonNull Context context, @NonNull ILightsManager service) {
+ public LightsManager(Context context) {
mContext = Preconditions.checkNotNull(context);
- mService = Preconditions.checkNotNull(service);
}
/**
@@ -92,112 +70,44 @@ public final class LightsManager {
*
* @return A list of available lights
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- public @NonNull List<Light> getLights() {
- try {
- return mService.getLights();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ public @NonNull abstract List<Light> getLights();
/**
* Returns the state of a specified light.
*
- * @hide
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- @TestApi
- public @NonNull LightState getLightState(@NonNull Light light) {
- Preconditions.checkNotNull(light);
- try {
- return mService.getLightState(light.getId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ public abstract @NonNull LightState getLightState(@NonNull Light light);
/**
* Creates a new LightsSession that can be used to control the device lights.
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- public @NonNull LightsSession openSession() {
- try {
- final LightsSession session = new LightsSession();
- mService.openSession(session.mToken);
- return session;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ public abstract @NonNull LightsSession openSession();
/**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
*/
- public final class LightsSession implements AutoCloseable {
-
+ public abstract static class LightsSession implements AutoCloseable {
private final IBinder mToken = new Binder();
-
- private final CloseGuard mCloseGuard = new CloseGuard();
- private boolean mClosed = false;
-
- /**
- * Instantiated by {@link LightsManager#openSession()}.
- */
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- private LightsSession() {
- mCloseGuard.open("close");
- }
-
/**
* Sends a request to modify the states of multiple lights.
*
- * <p>This method only controls lights that aren't overridden by higher-priority sessions.
- * Additionally, lights not controlled by this session can be controlled by lower-priority
- * sessions.
- *
* @param request the settings for lights that should change
*/
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
- public void requestLights(@NonNull LightsRequest request) {
- Preconditions.checkNotNull(request);
- if (!mClosed) {
- try {
- mService.setLightStates(mToken, request.mLightIds, request.mLightStates);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
+ public abstract void requestLights(@NonNull LightsRequest request);
- /**
- * Closes the session, reverting all changes made through it.
- */
- @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@Override
- public void close() {
- if (!mClosed) {
- try {
- mService.closeSession(mToken);
- mClosed = true;
- mCloseGuard.close();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- Reference.reachabilityFence(this);
- }
+ public abstract void close();
- /** @hide */
- @Override
- protected void finalize() throws Throwable {
- try {
- mCloseGuard.warnIfOpen();
- close();
- } finally {
- super.finalize();
- }
+ /**
+ * Get the token of a light session.
+ *
+ * @return Binder token of the light session.
+ * @hide
+ */
+ public @NonNull IBinder getToken() {
+ return mToken;
}
}
+
}
diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java
index a318992c35ee..2626a461aaf5 100644
--- a/core/java/android/hardware/lights/LightsRequest.java
+++ b/core/java/android/hardware/lights/LightsRequest.java
@@ -17,17 +17,17 @@
package android.hardware.lights;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
/**
* Encapsulates a request to modify the state of multiple lights.
*
- * @hide
*/
-@SystemApi
public final class LightsRequest {
/** Visible to {@link LightsManager.Session}. */
@@ -50,6 +50,30 @@ public final class LightsRequest {
}
/**
+ * Get a list of Light as ids. The ids will returned in same order as the lights passed
+ * in Builder.
+ *
+ * @return List of light ids
+ */
+ public @NonNull List<Integer> getLights() {
+ List<Integer> lightList = new ArrayList<Integer>(mLightIds.length);
+ for (int i = 0; i < mLightIds.length; i++) {
+ lightList.add(mLightIds[i]);
+ }
+ return lightList;
+ }
+
+ /**
+ * Get a list of LightState. The states will be returned in same order as the light states
+ * passed in Builder.
+ *
+ * @return List of light states
+ */
+ public @NonNull List<LightState> getLightStates() {
+ return Arrays.asList(mLightStates);
+ }
+
+ /**
* Builder for creating device light change requests.
*/
public static final class Builder {
@@ -62,7 +86,7 @@ public final class LightsRequest {
* @param light the light to modify
* @param state the desired color and intensity of the light
*/
- public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) {
+ public @NonNull Builder addLight(@NonNull Light light, @NonNull LightState state) {
Preconditions.checkNotNull(light);
Preconditions.checkNotNull(state);
mChanges.put(light.getId(), state);
diff --git a/core/java/android/hardware/lights/SystemLightsManager.java b/core/java/android/hardware/lights/SystemLightsManager.java
new file mode 100644
index 000000000000..726a61359c01
--- /dev/null
+++ b/core/java/android/hardware/lights/SystemLightsManager.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.lights;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.hardware.lights.LightsManager.LightsSession;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.CloseGuard;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.ref.Reference;
+import java.util.List;
+
+/**
+ * The LightsManager class allows control over device lights.
+ *
+ * @hide
+ */
+public final class SystemLightsManager extends LightsManager {
+ private static final String TAG = "LightsManager";
+
+ @NonNull private final ILightsManager mService;
+
+ /**
+ * Creates a SystemLightsManager.
+ *
+ * @hide
+ */
+ public SystemLightsManager(@NonNull Context context) throws ServiceNotFoundException {
+ this(context, ILightsManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
+ }
+
+ /**
+ * Creates a SystemLightsManager with a provided service implementation.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public SystemLightsManager(@NonNull Context context, @NonNull ILightsManager service) {
+ super(context);
+ mService = Preconditions.checkNotNull(service);
+ }
+
+ /**
+ * Returns the lights available on the device.
+ *
+ * @return A list of available lights
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public @NonNull List<Light> getLights() {
+ try {
+ return mService.getLights();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the state of a specified light.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public @NonNull LightState getLightState(@NonNull Light light) {
+ Preconditions.checkNotNull(light);
+ try {
+ return mService.getLightState(light.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a new LightsSession that can be used to control the device lights.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public @NonNull LightsSession openSession() {
+ try {
+ final LightsSession session = new SystemLightsSession();
+ mService.openSession(session.getToken());
+ return session;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Encapsulates a session that can be used to control device lights and represents the lifetime
+ * of the requests.
+ */
+ public final class SystemLightsSession extends LightsManager.LightsSession
+ implements AutoCloseable {
+
+ private final CloseGuard mCloseGuard = new CloseGuard();
+ private boolean mClosed = false;
+
+ /**
+ * Instantiated by {@link LightsManager#openSession()}.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ private SystemLightsSession() {
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * Sends a request to modify the states of multiple lights.
+ *
+ * <p>This method only controls lights that aren't overridden by higher-priority sessions.
+ * Additionally, lights not controlled by this session can be controlled by lower-priority
+ * sessions.
+ *
+ * @param request the settings for lights that should change
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public void requestLights(@NonNull LightsRequest request) {
+ Preconditions.checkNotNull(request);
+ if (!mClosed) {
+ try {
+ mService.setLightStates(getToken(), request.mLightIds, request.mLightStates);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Closes the session, reverting all changes made through it.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
+ @Override
+ public void close() {
+ if (!mClosed) {
+ try {
+ mService.closeSession(getToken());
+ mClosed = true;
+ mCloseGuard.close();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ Reference.reachabilityFence(this);
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 6353a25e745f..664120698971 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -16,14 +16,17 @@
package android.net;
+import static android.app.ActivityManager.procStateToString;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessCapability;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -617,8 +620,18 @@ public class NetworkPolicyManager {
* to access network when the device is idle or in battery saver mode. Otherwise, false.
* @hide
*/
- public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
- return procState <= FOREGROUND_THRESHOLD_STATE;
+ public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(@Nullable UidState uidState) {
+ if (uidState == null) {
+ return false;
+ }
+ return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState.procState, uidState.capability);
+ }
+
+ /** @hide */
+ public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(
+ int procState, @ProcessCapability int capability) {
+ return procState <= FOREGROUND_THRESHOLD_STATE
+ || (capability & ActivityManager.PROCESS_CAPABILITY_NETWORK) != 0;
}
/**
@@ -626,11 +639,44 @@ public class NetworkPolicyManager {
* to access network when the device is in data saver mode. Otherwise, false.
* @hide
*/
+ public static boolean isProcStateAllowedWhileOnRestrictBackground(@Nullable UidState uidState) {
+ if (uidState == null) {
+ return false;
+ }
+ return isProcStateAllowedWhileOnRestrictBackground(uidState.procState);
+ }
+
+ /** @hide */
public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
+ // Data saver and bg policy restrictions will only take procstate into account.
return procState <= FOREGROUND_THRESHOLD_STATE;
}
/** @hide */
+ public static final class UidState {
+ public int uid;
+ public int procState;
+ public int capability;
+
+ public UidState(int uid, int procState, int capability) {
+ this.uid = uid;
+ this.procState = procState;
+ this.capability = capability;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{procState=");
+ sb.append(procStateToString(procState));
+ sb.append(",cap=");
+ ActivityManager.printCapabilitiesSummary(sb, capability);
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ /** @hide */
@TestApi
@NonNull
public static String resolveNetworkId(@NonNull WifiConfiguration config) {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 59e493191711..fafb885c58e0 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -26,6 +26,7 @@ import android.hardware.Battery;
import android.hardware.SensorManager;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
+import android.hardware.lights.LightsManager;
import android.os.Build;
import android.os.NullVibrator;
import android.os.Parcel;
@@ -89,6 +90,9 @@ public final class InputDevice implements Parcelable {
@GuardedBy("mMotionRanges")
private Battery mBattery;
+ @GuardedBy("mMotionRanges")
+ private LightsManager mLightsManager;
+
/**
* A mask for input source classes.
*
@@ -859,6 +863,21 @@ public final class InputDevice implements Parcelable {
}
/**
+ * Gets the lights manager associated with the device, if there is one.
+ * Even if the device does not have lights, the result is never null.
+ * Use {@link LightsManager#getLights} to determine whether any lights is
+ * present.
+ *
+ * @return The lights manager associated with the device, never null.
+ */
+ public @NonNull LightsManager getLightsManager() {
+ if (mLightsManager == null) {
+ mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId);
+ }
+ return mLightsManager;
+ }
+
+ /**
* Gets the sensor manager service associated with the input device.
* Even if the device does not have a sensor, the result is never null.
* Use {@link SensorManager#getSensorList} to get a full list of all supported sensors.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c46fbc7fb4c4..3789324a038c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6996,6 +6996,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #getScrollIndicators()
* @attr ref android.R.styleable#View_scrollIndicators
*/
+ @RemotableViewMethod
public void setScrollIndicators(@ScrollIndicators int indicators) {
setScrollIndicators(indicators,
SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT);
@@ -11881,6 +11882,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setFocusable(int)
* @attr ref android.R.styleable#View_focusable
*/
+ @RemotableViewMethod
public void setFocusable(boolean focusable) {
setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE);
}
@@ -11899,6 +11901,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setFocusableInTouchMode(boolean)
* @attr ref android.R.styleable#View_focusable
*/
+ @RemotableViewMethod
public void setFocusable(@Focusable int focusable) {
if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) {
setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
@@ -11917,6 +11920,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #setFocusable(boolean)
* @attr ref android.R.styleable#View_focusableInTouchMode
*/
+ @RemotableViewMethod
public void setFocusableInTouchMode(boolean focusableInTouchMode) {
// Focusable in touch mode should always be set before the focusable flag
// otherwise, setting the focusable flag will trigger a focusableViewAvailable()
@@ -12871,6 +12875,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_focusedByDefault
*/
+ @RemotableViewMethod
public void setFocusedByDefault(boolean isFocusedByDefault) {
if (isFocusedByDefault == ((mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0)) {
return;
@@ -16850,6 +16855,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_rotation
*/
+ @RemotableViewMethod
public void setRotation(float rotation) {
if (rotation != getRotation()) {
// Double-invalidation is necessary to capture view's old and new areas
@@ -16896,6 +16902,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_rotationY
*/
+ @RemotableViewMethod
public void setRotationY(float rotationY) {
if (rotationY != getRotationY()) {
invalidateViewProperty(true, false);
@@ -16941,6 +16948,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_rotationX
*/
+ @RemotableViewMethod
public void setRotationX(float rotationX) {
if (rotationX != getRotationX()) {
invalidateViewProperty(true, false);
@@ -16978,6 +16986,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_scaleX
*/
+ @RemotableViewMethod
public void setScaleX(float scaleX) {
if (scaleX != getScaleX()) {
scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX");
@@ -17016,6 +17025,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_scaleY
*/
+ @RemotableViewMethod
public void setScaleY(float scaleY) {
if (scaleY != getScaleY()) {
scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY");
@@ -17061,6 +17071,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_transformPivotX
*/
+ @RemotableViewMethod
public void setPivotX(float pivotX) {
if (!mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) {
invalidateViewProperty(true, false);
@@ -17103,6 +17114,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_transformPivotY
*/
+ @RemotableViewMethod
public void setPivotY(float pivotY) {
if (!mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) {
invalidateViewProperty(true, false);
@@ -17243,6 +17255,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_alpha
*/
+ @RemotableViewMethod
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
@@ -17732,6 +17745,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_elevation
*/
+ @RemotableViewMethod
public void setElevation(float elevation) {
if (elevation != getElevation()) {
elevation = sanitizeFloatPropertyValue(elevation, "elevation");
@@ -17766,6 +17780,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_translationX
*/
+ @RemotableViewMethod
public void setTranslationX(float translationX) {
if (translationX != getTranslationX()) {
invalidateViewProperty(true, false);
@@ -17801,6 +17816,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_translationY
*/
+ @RemotableViewMethod
public void setTranslationY(float translationY) {
if (translationY != getTranslationY()) {
invalidateViewProperty(true, false);
@@ -17828,6 +17844,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @attr ref android.R.styleable#View_translationZ
*/
+ @RemotableViewMethod
public void setTranslationZ(float translationZ) {
if (translationZ != getTranslationZ()) {
translationZ = sanitizeFloatPropertyValue(translationZ, "translationZ");
@@ -23989,6 +24006,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #getBackgroundTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setBackgroundTintList(@Nullable ColorStateList tint) {
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
@@ -24248,6 +24266,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #getForegroundTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setForegroundTintList(@Nullable ColorStateList tint) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f1f6786aa43e..144691d3eaa0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1677,7 +1677,8 @@ public final class ViewRootImpl implements ViewParent,
// See comment for View.sForceLayoutWhenInsetsChanged
if (View.sForceLayoutWhenInsetsChanged && mView != null
- && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) {
+ && (mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST)
+ == SOFT_INPUT_ADJUST_RESIZE) {
forceLayout(mView);
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 0a08ccd34f02..8aa557bab4e3 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -648,6 +648,7 @@ public class ImageView extends View {
* @see #getImageTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @android.view.RemotableViewMethod
public void setImageTintList(@Nullable ColorStateList tint) {
mDrawableTintList = tint;
mHasDrawableTint = true;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index a44808eb3120..1b76ebf7c8c6 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1308,6 +1308,7 @@ public class ProgressBar extends View {
* @see #getSecondaryProgressTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setSecondaryProgressTintList(@Nullable ColorStateList tint) {
if (mProgressTintInfo == null) {
mProgressTintInfo = new ProgressTintInfo();
@@ -1619,6 +1620,7 @@ public class ProgressBar extends View {
* @param stateDescription The state description.
*/
@Override
+ @RemotableViewMethod
public void setStateDescription(@Nullable CharSequence stateDescription) {
mCustomStateDescription = stateDescription;
if (stateDescription == null) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 30bf546eb83e..514471783c6a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -224,35 +224,17 @@ public class RemoteViews implements Parcelable, Filter {
})
@Retention(RetentionPolicy.SOURCE)
public @interface MarginType {}
- /**
- * The value will apply to the marginLeft.
- * @hide
- */
+ /** The value will apply to the marginLeft. */
public static final int MARGIN_LEFT = 0;
- /**
- * The value will apply to the marginTop.
- * @hide
- */
+ /** The value will apply to the marginTop. */
public static final int MARGIN_TOP = 1;
- /**
- * The value will apply to the marginRight.
- * @hide
- */
+ /** The value will apply to the marginRight. */
public static final int MARGIN_RIGHT = 2;
- /**
- * The value will apply to the marginBottom.
- * @hide
- */
+ /** The value will apply to the marginBottom. */
public static final int MARGIN_BOTTOM = 3;
- /**
- * The value will apply to the marginStart.
- * @hide
- */
+ /** The value will apply to the marginStart. */
public static final int MARGIN_START = 4;
- /**
- * The value will apply to the marginEnd.
- * @hide
- */
+ /** The value will apply to the marginEnd. */
public static final int MARGIN_END = 5;
/** @hide **/
@@ -4002,7 +3984,6 @@ public class RemoteViews implements Parcelable, Filter {
* @param viewId The id of the view to change
* @param type The margin being set e.g. {@link #MARGIN_END}
* @param dimen a dimension resource to apply to the margin, or 0 to clear the margin.
- * @hide
*/
public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type,
@DimenRes int dimen) {
@@ -4021,7 +4002,6 @@ public class RemoteViews implements Parcelable, Filter {
* @param type The margin being set e.g. {@link #MARGIN_END}
* @param value a value for the margin the given units.
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
- * @hide
*/
public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value,
@ComplexDimensionUnit int units) {
@@ -4039,7 +4019,6 @@ public class RemoteViews implements Parcelable, Filter {
*
* @param width Width of the view in the given units
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
- * @hide
*/
public void setViewLayoutWidth(@IdRes int viewId, float width,
@ComplexDimensionUnit int units) {
@@ -4051,7 +4030,6 @@ public class RemoteViews implements Parcelable, Filter {
* the result of {@link Resources#getDimensionPixelSize(int)}.
*
* @param widthDimen the dimension resource for the view's width
- * @hide
*/
public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen));
@@ -4068,7 +4046,6 @@ public class RemoteViews implements Parcelable, Filter {
*
* @param height height of the view in the given units
* @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
- * @hide
*/
public void setViewLayoutHeight(@IdRes int viewId, float height,
@ComplexDimensionUnit int units) {
@@ -4080,7 +4057,6 @@ public class RemoteViews implements Parcelable, Filter {
* the result of {@link Resources#getDimensionPixelSize(int)}.
*
* @param heightDimen a dimen resource to read the height from.
- * @hide
*/
public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) {
addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen));
@@ -4231,10 +4207,9 @@ public class RemoteViews implements Parcelable, Filter {
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
- *
- * @hide
*/
- public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) {
+ public void setColorStateList(@IdRes int viewId, @NonNull String methodName,
+ @Nullable ColorStateList value) {
addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST,
value));
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0f2089a5463f..ca0747fadf14 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4756,6 +4756,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @see #getJustificationMode()
*/
@Layout.JustificationMode
+ @android.view.RemotableViewMethod
public void setJustificationMode(@Layout.JustificationMode int justificationMode) {
mJustificationMode = justificationMode;
if (mLayout != null) {
@@ -5232,6 +5233,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @see android.view.Gravity
* @attr ref android.R.styleable#TextView_gravity
*/
+ @android.view.RemotableViewMethod
public void setGravity(int gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.START;
@@ -5826,6 +5828,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* @attr ref android.R.styleable#TextView_lineHeight
*/
+ @android.view.RemotableViewMethod
public void setLineHeight(@Px @IntRange(from = 0) int lineHeight) {
Preconditions.checkArgumentNonnegative(lineHeight);
@@ -10277,6 +10280,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @see #setTransformationMethod(TransformationMethod)
* @attr ref android.R.styleable#TextView_textAllCaps
*/
+ @android.view.RemotableViewMethod
public void setAllCaps(boolean allCaps) {
if (allCaps) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 56b25b2060ea..93ba0372df08 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -180,6 +180,7 @@ public class TransitionAnimation {
"android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
}
+ /** Load animation by resource Id from specific LayoutParams. */
@Nullable
private Animation loadAnimationRes(LayoutParams lp, int resId) {
Context context = mContext;
@@ -193,6 +194,7 @@ public class TransitionAnimation {
return null;
}
+ /** Load animation by resource Id from specific package. */
@Nullable
private Animation loadAnimationRes(String packageName, int resId) {
if (ResourceId.isValid(resId)) {
@@ -204,6 +206,13 @@ public class TransitionAnimation {
return null;
}
+ /** Load animation by resource Id from android package. */
+ @Nullable
+ public Animation loadDefaultAnimationRes(int resId) {
+ return loadAnimationRes("android", resId);
+ }
+
+ /** Load animation by attribute Id from specific LayoutParams */
@Nullable
public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
int resId = Resources.ID_NULL;
@@ -222,6 +231,25 @@ public class TransitionAnimation {
return null;
}
+ /** Load animation by attribute Id from android package. */
+ @Nullable
+ public Animation loadDefaultAnimationAttr(int animAttr) {
+ int resId = Resources.ID_NULL;
+ Context context = mContext;
+ if (animAttr >= 0) {
+ AttributeCache.Entry ent = getCachedAnimations("android",
+ mDefaultWindowAnimationStyleResId);
+ if (ent != null) {
+ context = ent.context;
+ resId = ent.array.getResourceId(animAttr, 0);
+ }
+ }
+ if (ResourceId.isValid(resId)) {
+ return loadAnimationSafely(context, resId, mTag);
+ }
+ return null;
+ }
+
@Nullable
private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (mDebug) {
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
new file mode 100644
index 000000000000..412b36713fa2
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import static android.hardware.lights.LightsRequest.Builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.view.InputDevice;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link InputDeviceLightsManager}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:InputDeviceLightsManagerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class InputDeviceLightsManagerTest {
+ private static final String TAG = "InputDeviceLightsManagerTest";
+
+ private static final int DEVICE_ID = 1000;
+ private static final int PLAYER_ID = 3;
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private InputManager mInputManager;
+
+ @Mock private IInputManager mIInputManagerMock;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+
+ when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ createInputDevice(DEVICE_ID));
+
+ mInputManager = InputManager.resetInstance(mIInputManagerMock);
+
+ ArrayMap<Integer, LightState> lightStatesById = new ArrayMap<>();
+ doAnswer(invocation -> {
+ final int[] lightIds = (int[]) invocation.getArguments()[1];
+ final LightState[] lightStates =
+ (LightState[]) invocation.getArguments()[2];
+ for (int i = 0; i < lightIds.length; i++) {
+ lightStatesById.put(lightIds[i], lightStates[i]);
+ }
+ return null;
+ }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID),
+ any(int[].class), any(LightState[].class), any(IBinder.class));
+
+ doAnswer(invocation -> {
+ int lightId = (int) invocation.getArguments()[1];
+ if (lightStatesById.containsKey(lightId)) {
+ return lightStatesById.get(lightId);
+ }
+ return new LightState(0);
+ }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt());
+ }
+
+ @After
+ public void tearDown() {
+ InputManager.clearInstance();
+ }
+
+ private InputDevice createInputDevice(int id) {
+ return new InputDevice(id, 0 /* generation */, 0 /* controllerNumber */, "name",
+ 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
+ 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */,
+ false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
+ false /* hasSensor */, false /* hasBattery */);
+ }
+
+ private void mockLights(Light[] lights) throws Exception {
+ // Mock the Lights returned form InputManagerService
+ when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn(
+ new ArrayList(Arrays.asList(lights)));
+ }
+
+ @Test
+ public void testGetInputDeviceLights() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE),
+ new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID)
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lights = lightsManager.getLights();
+ verify(mIInputManagerMock).getLights(eq(DEVICE_ID));
+ assertEquals(lights, Arrays.asList(mockedLights));
+ }
+
+ @Test
+ public void testControlMultipleLights() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ new Light(4 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB)
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lightList = lightsManager.getLights();
+ LightState[] states = new LightState[]{new LightState(0xf1), new LightState(0xf2),
+ new LightState(0xf3)};
+ // Open a session to request turn 3/4 lights on:
+ LightsManager.LightsSession session = lightsManager.openSession();
+ session.requestLights(new Builder()
+ .addLight(lightsManager.getLights().get(0), states[0])
+ .addLight(lightsManager.getLights().get(1), states[1])
+ .addLight(lightsManager.getLights().get(2), states[2])
+ .build());
+ IBinder token = session.getToken();
+
+ verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ any(String.class), eq(token));
+ verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
+ eq(states), eq(token));
+
+ // Then all 3 should turn on.
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
+ .isEqualTo(0xf1);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(1)).getColor())
+ .isEqualTo(0xf2);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(2)).getColor())
+ .isEqualTo(0xf3);
+
+ // And the 4th should remain off.
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(3)).getColor())
+ .isEqualTo(0x00);
+
+ // close session
+ session.close();
+ verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ }
+
+ @Test
+ public void testControlPlayerIdLight() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID),
+ new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE),
+ new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB),
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lightList = lightsManager.getLights();
+ LightState[] states = new LightState[]{new LightState(0xf1, PLAYER_ID)};
+ // Open a session to request set Player ID light:
+ LightsManager.LightsSession session = lightsManager.openSession();
+ session.requestLights(new Builder()
+ .addLight(lightsManager.getLights().get(0), states[0])
+ .build());
+ IBinder token = session.getToken();
+
+ verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ any(String.class), eq(token));
+ verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
+ eq(states), eq(token));
+
+ // Verify the light state
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
+ .isEqualTo(0xf1);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getPlayerId())
+ .isEqualTo(PLAYER_ID);
+
+ // close session
+ session.close();
+ verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 0ee1f0642352..8697be9db3fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -124,6 +124,7 @@ public class Bubble implements BubbleViewProvider {
private int mDesiredHeight;
@DimenRes
private int mDesiredHeightResId;
+ private int mTaskId;
/** for logging **/
@Nullable
@@ -162,7 +163,7 @@ public class Bubble implements BubbleViewProvider {
*/
Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
- Executor mainExecutor) {
+ int taskId, Executor mainExecutor) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
mMetadataShortcutId = shortcutInfo.getId();
@@ -178,6 +179,7 @@ public class Bubble implements BubbleViewProvider {
mTitle = title;
mShowBubbleUpdateDot = false;
mMainExecutor = mainExecutor;
+ mTaskId = taskId;
}
@VisibleForTesting(visibility = PRIVATE)
@@ -197,6 +199,7 @@ public class Bubble implements BubbleViewProvider {
});
};
mMainExecutor = mainExecutor;
+ mTaskId = INVALID_TASK_ID;
setEntry(entry);
}
@@ -520,7 +523,7 @@ public class Bubble implements BubbleViewProvider {
*/
@Override
public int getTaskId() {
- return mExpandedView != null ? mExpandedView.getTaskId() : INVALID_TASK_ID;
+ return mExpandedView != null ? mExpandedView.getTaskId() : mTaskId;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index 3108b02cc010..241755227af0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -28,7 +28,6 @@ import com.android.wm.shell.bubbles.storage.BubbleEntity
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.common.annotations.ExternalThread
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -36,8 +35,11 @@ import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
-internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps,
- private val mainExecutor : ShellExecutor) {
+internal class BubbleDataRepository(
+ context: Context,
+ private val launcherApps: LauncherApps,
+ private val mainExecutor: ShellExecutor
+) {
private val volatileRepository = BubbleVolatileRepository(launcherApps)
private val persistentRepository = BubblePersistentRepository(context)
@@ -78,7 +80,8 @@ internal class BubbleDataRepository(context: Context, private val launcherApps:
b.key,
b.rawDesiredHeight,
b.rawDesiredHeightResId,
- b.title
+ b.title,
+ b.taskId
)
}
}
@@ -168,6 +171,7 @@ internal class BubbleDataRepository(context: Context, private val launcherApps:
entity.desiredHeight,
entity.desiredHeightResId,
entity.title,
+ entity.taskId,
mainExecutor
) }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
index aeba302bf487..d5cab5af42e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt
@@ -25,5 +25,6 @@ data class BubbleEntity(
val key: String,
val desiredHeight: Int,
@DimenRes val desiredHeightResId: Int,
- val title: String? = null
+ val title: String? = null,
+ val taskId: Int
)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
index fe72bd301e04..470011b136fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.util.Xml
import com.android.internal.util.FastXmlSerializer
import com.android.internal.util.XmlUtils
@@ -38,6 +39,7 @@ private const val ATTR_KEY = "key"
private const val ATTR_DESIRED_HEIGHT = "h"
private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid"
private const val ATTR_TITLE = "t"
+private const val ATTR_TASK_ID = "tid"
/**
* Writes the bubbles in xml format into given output stream.
@@ -70,6 +72,7 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleEntity) {
serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString())
serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString())
bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) }
+ serializer.attribute(null, ATTR_TASK_ID, bubble.taskId.toString())
serializer.endTag(null, TAG_BUBBLE)
} catch (e: IOException) {
throw RuntimeException(e)
@@ -103,7 +106,8 @@ private fun readXmlEntry(parser: XmlPullParser): BubbleEntity? {
parser.getAttributeWithName(ATTR_KEY) ?: return null,
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null,
parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null,
- parser.getAttributeWithName(ATTR_TITLE)
+ parser.getAttributeWithName(ATTR_TITLE),
+ parser.getAttributeWithName(ATTR_TASK_ID)?.toInt() ?: INVALID_TASK_ID
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 1a4616c5f591..6afcc06aa1ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -32,6 +32,7 @@ import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.UserHandle;
import androidx.annotation.Nullable;
@@ -76,6 +77,7 @@ public class PipMediaController {
private final Context mContext;
private final Handler mMainHandler;
+ private final HandlerExecutor mHandlerExecutor;
private final MediaSessionManager mMediaSessionManager;
private MediaController mMediaController;
@@ -123,6 +125,7 @@ public class PipMediaController {
public PipMediaController(Context context, Handler mainHandler) {
mContext = context;
mMainHandler = mainHandler;
+ mHandlerExecutor = new HandlerExecutor(mMainHandler);
IntentFilter mediaControlFilter = new IntentFilter();
mediaControlFilter.addAction(ACTION_PLAY);
mediaControlFilter.addAction(ACTION_PAUSE);
@@ -247,8 +250,8 @@ public class PipMediaController {
*/
public void registerSessionListenerForCurrentUser() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
- mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null,
- UserHandle.CURRENT, mMainHandler);
+ mMediaSessionManager.addOnActiveSessionsChangedListener(null, UserHandle.CURRENT,
+ mHandlerExecutor, mSessionsChangedListener);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 59f8c1df1213..2182ee5590e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -16,55 +16,78 @@
package com.android.wm.shell.transition;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArrayMap;
+import android.view.Choreographer;
import android.view.SurfaceControl;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import com.android.internal.R;
+import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.TransitionAnimation;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
+ private static final int MAX_ANIMATION_DURATION = 3000;
+
private final TransactionPool mTransactionPool;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ private final TransitionAnimation mTransitionAnimation;
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
- DefaultTransitionHandler(@NonNull TransactionPool transactionPool,
+ private float mTransitionAnimationScaleSetting = 1.0f;
+
+ DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
mTransactionPool = transactionPool;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
+
+ AttributeCache.init(context);
}
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "start default transition animation, info = %s", info);
if (mAnimations.containsKey(transition)) {
throw new IllegalStateException("Got a duplicate startAnimation call for "
+ transition);
}
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
- final boolean isOpening = Transitions.isOpeningType(info.getType());
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
@@ -77,19 +100,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
// Don't animate anything with an animating parent
if (change.getParent() != null) continue;
- final int mode = change.getMode();
- if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
- if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- continue;
- }
- // fade in
- startExampleAnimation(
- animations, change.getLeash(), true /* show */, onAnimFinish);
- } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
- // fade out
- startExampleAnimation(
- animations, change.getLeash(), false /* show */, onAnimFinish);
+ Animation a = loadAnimation(info.getType(), change);
+ if (a != null) {
+ startAnimInternal(animations, a, change.getLeash(), onAnimFinish);
}
}
t.apply();
@@ -105,32 +118,93 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return null;
}
- // TODO(shell-transitions): real animations
- private void startExampleAnimation(@NonNull ArrayList<Animator> animations,
- @NonNull SurfaceControl leash, boolean show, @NonNull Runnable finishCallback) {
- final float end = show ? 1.f : 0.f;
- final float start = 1.f - end;
+ @Override
+ public void setAnimScaleSetting(float scale) {
+ mTransitionAnimationScaleSetting = scale;
+ }
+
+ @Nullable
+ private Animation loadAnimation(int type, TransitionInfo.Change change) {
+ // TODO(b/178678389): It should handle more type animation here
+ Animation a = null;
+
+ final boolean isOpening = Transitions.isOpeningType(type);
+ final int mode = change.getMode();
+ final int flags = change.getFlags();
+
+ if (mode == TRANSIT_OPEN && isOpening) {
+ if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ // This received a transferred starting window, so don't animate
+ return null;
+ }
+
+ if (change.getTaskInfo() != null) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskOpenEnterAnimation);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0
+ ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
+ }
+ } else if (mode == TRANSIT_TO_FRONT && isOpening) {
+ if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ // This received a transferred starting window, so don't animate
+ return null;
+ }
+
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskToFrontEnterAnimation);
+ } else if (mode == TRANSIT_CLOSE && !isOpening) {
+ if (change.getTaskInfo() != null) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskCloseExitAnimation);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0
+ ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit);
+ }
+ } else if (mode == TRANSIT_TO_BACK && !isOpening) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_taskToBackExitAnimation);
+ } else if (mode == TRANSIT_CHANGE) {
+ // In the absence of a specific adapter, we just want to keep everything stationary.
+ a = new AlphaAnimation(1.f, 1.f);
+ a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+ }
+
+ if (a != null) {
+ Rect start = change.getStartAbsBounds();
+ Rect end = change.getEndAbsBounds();
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.initialize(end.width(), end.height(), start.width(), start.height());
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ }
+ return a;
+ }
+
+ private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim,
+ @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final ValueAnimator va = ValueAnimator.ofFloat(start, end);
- va.setDuration(500);
+ final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ final Transformation transformation = new Transformation();
+ final float[] matrix = new float[9];
+ // Animation length is already expected to be scaled.
+ va.overrideDurationScale(1.0f);
+ va.setDuration(anim.computeDurationHint());
va.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
- transaction.apply();
+ final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
+
+ applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix);
});
+
final Runnable finisher = () -> {
- transaction.setAlpha(leash, end);
- transaction.apply();
+ applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix);
+
mTransactionPool.release(transaction);
mMainExecutor.execute(() -> {
animations.remove(va);
finishCallback.run();
});
};
- va.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) { }
-
+ va.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finisher.run();
@@ -140,11 +214,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
public void onAnimationCancel(Animator animation) {
finisher.run();
}
-
- @Override
- public void onAnimationRepeat(Animator animation) { }
});
animations.add(va);
mAnimExecutor.execute(va::start);
}
+
+ private static void applyTransformation(long time, SurfaceControl.Transaction t,
+ SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) {
+ anim.getTransformation(time, transformation);
+ t.setMatrix(leash, transformation.getMatrix(), matrix);
+ t.setAlpha(leash, transformation.getAlpha());
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ t.apply();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 10344892e766..89eee67bf5af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -25,9 +25,13 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControl;
@@ -43,6 +47,7 @@ import android.window.WindowOrganizer;
import androidx.annotation.BinderThread;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -63,6 +68,7 @@ public class Transitions {
SystemProperties.getBoolean("persist.debug.shell_transit", false);
private final WindowOrganizer mOrganizer;
+ private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
@@ -72,6 +78,8 @@ public class Transitions {
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
+ private float mTransitionAnimationScaleSetting = 1.0f;
+
private static final class ActiveTransition {
TransitionHandler mFirstHandler = null;
}
@@ -84,26 +92,46 @@ public class Transitions {
}
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
- @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ @NonNull Context context, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
+ mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
mPlayerImpl = new TransitionPlayerImpl();
// The very last handler (0 in the list) should be the default one.
- mHandlers.add(new DefaultTransitionHandler(pool, mainExecutor, animExecutor));
+ mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor));
// Next lowest priority is remote transitions.
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
mHandlers.add(mRemoteTransitionHandler);
+
+ ContentResolver resolver = context.getContentResolver();
+ mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ context.getResources().getFloat(
+ R.dimen.config_appTransitionAnimationDurationScaleDefault));
+ dispatchAnimScaleSetting(mTransitionAnimationScaleSetting);
+
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
+ new SettingsObserver());
}
private Transitions() {
mOrganizer = null;
+ mContext = null;
mMainExecutor = null;
mAnimExecutor = null;
mPlayerImpl = null;
mRemoteTransitionHandler = null;
}
+ private void dispatchAnimScaleSetting(float scale) {
+ for (int i = mHandlers.size() - 1; i >= 0; --i) {
+ mHandlers.get(i).setAnimScaleSetting(scale);
+ }
+ }
+
/** Create an empty/non-registering transitions object for system-ui tests. */
@VisibleForTesting
public static RemoteTransitions createEmptyForTesting() {
@@ -368,6 +396,13 @@ public class Transitions {
@Nullable
WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request);
+
+ /**
+ * Sets transition animation scale settings value to handler.
+ *
+ * @param scale The setting value of transition animation scale.
+ */
+ default void setAnimScaleSetting(float scale) {}
}
@BinderThread
@@ -404,4 +439,21 @@ public class Transitions {
});
}
}
+
+ private class SettingsObserver extends ContentObserver {
+
+ SettingsObserver() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ mTransitionAnimationScaleSetting = Settings.Global.getFloat(
+ mContext.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mTransitionAnimationScaleSetting);
+
+ mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting));
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
index 416028088294..bdf75fcd8816 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
@@ -31,9 +32,10 @@ import org.junit.runner.RunWith
class BubblePersistentRepositoryTest : ShellTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0),
- BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0, null, 1),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title", 2),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0, null,
+ INVALID_TASK_ID)
)
private lateinit var repository: BubblePersistentRepository
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index dd1a6a5a281e..05795fde7d6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.pm.LauncherApps
import android.os.UserHandle
import android.testing.AndroidTestingRunner
@@ -37,10 +38,12 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
private val user0 = UserHandle.of(0)
private val user10 = UserHandle.of(10)
- private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0)
+ private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0,
+ null, 1)
private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob",
- "key-2", 0, 16537428, "title")
- private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0)
+ "key-2", 0, 16537428, "title", 2)
+ private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0,
+ null, INVALID_TASK_ID)
private val bubbles = listOf(bubble1, bubble2, bubble3)
@@ -105,13 +108,13 @@ class BubbleVolatileRepositoryTest : ShellTestCase() {
@Test
fun testAddBubbleMatchesByKey() {
- val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title")
+ val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title", 1)
repository.addBubbles(listOf(bubble))
assertEquals(bubble, repository.bubbles.get(0))
// Same key as first bubble but different entry
val bubbleModified = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0,
- "different title")
+ "different title", 2)
repository.addBubbles(listOf(bubbleModified))
assertEquals(bubbleModified, repository.bubbles.get(0))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
index e0891a95c6a6..839b873d0c23 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.storage
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
@@ -31,17 +32,18 @@ import java.io.ByteArrayOutputStream
class BubbleXmlHelperTest : ShellTestCase() {
private val bubbles = listOf(
- BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0),
- BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title"),
- BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0)
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null, 1),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title", 2),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null,
+ INVALID_TASK_ID)
)
@Test
fun testWriteXml() {
val expectedEntries = """
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" />
""".trimIndent()
ByteArrayOutputStream().use {
writeXml(it, bubbles)
@@ -56,9 +58,9 @@ class BubbleXmlHelperTest : ShellTestCase() {
val src = """
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<bs v="1">
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
-<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
-<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" />
</bs>
""".trimIndent()
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
@@ -79,4 +81,32 @@ class BubbleXmlHelperTest : ShellTestCase() {
val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
assertEquals("failed parsing bubbles from xml\n$src", emptyList<BubbleEntity>(), actual)
}
+
+ /**
+ * In S we changed the XML to include a taskId, version didn't increase because we can set a
+ * reasonable default for taskId (INVALID_TASK_ID) if it wasn't in the XML previously, this
+ * tests that that works.
+ */
+ @Test
+ fun testReadXMLWithoutTaskId() {
+ val expectedBubbles = listOf(
+ BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null,
+ INVALID_TASK_ID),
+ BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title",
+ INVALID_TASK_ID),
+ BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null,
+ INVALID_TASK_ID)
+ )
+
+ val src = """
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<bs v="1">
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" />
+<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" />
+<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" />
+</bs>
+ """.trimIndent()
+ val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8)))
+ assertEquals("failed parsing bubbles from xml\n$src", expectedBubbles, actual)
+ }
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 5eca3e75a7b8..926108c41e5e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -41,6 +41,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -58,6 +59,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
@@ -78,6 +80,8 @@ public class ShellTransitionTests {
private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
@@ -90,8 +94,8 @@ public class ShellTransitionTests {
@Test
public void testBasicTransitionFlow() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
IBinder transitToken = new Binder();
@@ -109,8 +113,8 @@ public class ShellTransitionTests {
@Test
public void testNonDefaultHandler() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
@@ -188,8 +192,8 @@ public class ShellTransitionTests {
@Test
public void testRequestRemoteTransition() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
@@ -255,8 +259,8 @@ public class ShellTransitionTests {
@Test
public void testRegisteredRemoteTransition() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor,
- mAnimExecutor);
+ Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
+ mMainExecutor, mAnimExecutor);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8de5e42e93b2..aa0f7fdd70d5 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -34,6 +34,7 @@ import android.media.Session2Token;
import android.media.VolumeProvider;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
@@ -321,7 +322,7 @@ public final class MediaSessionManager {
@NonNull OnActiveSessionsChangedListener sessionListener,
@Nullable ComponentName notificationListener, @Nullable Handler handler) {
addOnActiveSessionsChangedListener(sessionListener, notificationListener,
- UserHandle.myUserId(), handler);
+ UserHandle.myUserId(), handler == null ? null : new HandlerExecutor(handler));
}
/**
@@ -337,38 +338,40 @@ public final class MediaSessionManager {
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to
* add listeners for user ids that do not belong to current process.
*
- * @param sessionListener The listener to add.
* @param notificationListener The enabled notification listener component. May be null.
* @param userHandle The user handle to listen for changes on.
- * @param handler The handler to post updates on.
+ * @param executor The executor on which the listener should be invoked
+ * @param sessionListener The listener to add.
* @hide
*/
- @SuppressLint({"ExecutorRegistration", "SamShouldBeLast", "UserHandle"})
+ @SuppressLint("UserHandle")
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void addOnActiveSessionsChangedListener(
- @NonNull OnActiveSessionsChangedListener sessionListener,
- @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle,
- @Nullable Handler handler) {
+ @Nullable ComponentName notificationListener,
+ @NonNull UserHandle userHandle, @NonNull Executor executor,
+ @NonNull OnActiveSessionsChangedListener sessionListener) {
Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
+ Objects.requireNonNull(executor, "executor shouldn't be null");
addOnActiveSessionsChangedListener(sessionListener, notificationListener,
- userHandle.getIdentifier(), handler);
+ userHandle.getIdentifier(), executor);
}
private void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
@Nullable ComponentName notificationListener, int userId,
- @Nullable Handler handler) {
+ @Nullable Executor executor) {
Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null");
- if (handler == null) {
- handler = new Handler();
+ if (executor == null) {
+ executor = new HandlerExecutor(new Handler());
}
+
synchronized (mLock) {
if (mListeners.get(sessionListener) != null) {
Log.w(TAG, "Attempted to add session listener twice, ignoring.");
return;
}
SessionsChangedWrapper wrapper = new SessionsChangedWrapper(mContext, sessionListener,
- handler);
+ executor);
try {
mService.addSessionsListener(wrapper.mStub, notificationListener, userId);
mListeners.put(sessionListener, wrapper);
@@ -412,7 +415,8 @@ public final class MediaSessionManager {
*/
public void addOnSession2TokensChangedListener(
@NonNull OnSession2TokensChangedListener listener) {
- addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, new Handler());
+ addOnSession2TokensChangedListener(UserHandle.myUserId(), listener,
+ new HandlerExecutor(new Handler()));
}
/**
@@ -428,7 +432,9 @@ public final class MediaSessionManager {
*/
public void addOnSession2TokensChangedListener(
@NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) {
- addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler);
+ Objects.requireNonNull(handler, "handler shouldn't be null");
+ addOnSession2TokensChangedListener(UserHandle.myUserId(), listener,
+ new HandlerExecutor(handler));
}
/**
@@ -445,20 +451,19 @@ public final class MediaSessionManager {
*
* @param userHandle The userHandle to listen for changes on
* @param listener The listener to add
- * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
- * be used.
+ * @param executor The executor on which the listener should be invoked
* @hide
*/
@SuppressLint("UserHandle")
public void addOnSession2TokensChangedListener(@NonNull UserHandle userHandle,
- @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) {
+ @NonNull OnSession2TokensChangedListener listener, @NonNull Executor executor) {
Objects.requireNonNull(userHandle, "userHandle shouldn't be null");
- addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, handler);
+ Objects.requireNonNull(executor, "executor shouldn't be null");
+ addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, executor);
}
private void addOnSession2TokensChangedListener(int userId,
- OnSession2TokensChangedListener listener, Handler handler) {
- Objects.requireNonNull(handler, "handler shouldn't be null");
+ OnSession2TokensChangedListener listener, Executor executor) {
Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
if (mSession2TokensListeners.get(listener) != null) {
@@ -466,7 +471,7 @@ public final class MediaSessionManager {
return;
}
Session2TokensChangedWrapper wrapper =
- new Session2TokensChangedWrapper(listener, handler);
+ new Session2TokensChangedWrapper(listener, executor);
try {
mService.addSession2TokensListener(wrapper.getStub(), userId);
mSession2TokensListeners.put(listener, wrapper);
@@ -847,7 +852,7 @@ public final class MediaSessionManager {
/**
* Add a {@link OnMediaKeyEventDispatchedListener}.
*
- * @param executor The executor on which the callback should be invoked
+ * @param executor The executor on which the listener should be invoked
* @param listener A {@link OnMediaKeyEventDispatchedListener}.
* @hide
*/
@@ -898,7 +903,7 @@ public final class MediaSessionManager {
/**
* Add a {@link OnMediaKeyEventDispatchedListener}.
*
- * @param executor The executor on which the callback should be invoked
+ * @param executor The executor on which the listener should be invoked
* @param listener A {@link OnMediaKeyEventSessionChangedListener}.
* @hide
*/
@@ -1257,62 +1262,61 @@ public final class MediaSessionManager {
private static final class SessionsChangedWrapper {
private Context mContext;
private OnActiveSessionsChangedListener mListener;
- private Handler mHandler;
+ private Executor mExecutor;
public SessionsChangedWrapper(Context context, OnActiveSessionsChangedListener listener,
- Handler handler) {
+ Executor executor) {
mContext = context;
mListener = listener;
- mHandler = handler;
+ mExecutor = executor;
}
private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
@Override
public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) {
- final Handler handler = mHandler;
- if (handler != null) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- final Context context = mContext;
- if (context != null) {
- ArrayList<MediaController> controllers = new ArrayList<>();
- int size = tokens.size();
- for (int i = 0; i < size; i++) {
- controllers.add(new MediaController(context, tokens.get(i)));
- }
- final OnActiveSessionsChangedListener listener = mListener;
- if (listener != null) {
- listener.onActiveSessionsChanged(controllers);
- }
- }
- }
- });
+ if (mExecutor != null) {
+ final Executor executor = mExecutor;
+ executor.execute(() -> callOnActiveSessionsChangedListener(tokens));
}
}
};
+ private void callOnActiveSessionsChangedListener(final List<MediaSession.Token> tokens) {
+ final Context context = mContext;
+ if (context != null) {
+ ArrayList<MediaController> controllers = new ArrayList<>();
+ int size = tokens.size();
+ for (int i = 0; i < size; i++) {
+ controllers.add(new MediaController(context, tokens.get(i)));
+ }
+ final OnActiveSessionsChangedListener listener = mListener;
+ if (listener != null) {
+ listener.onActiveSessionsChanged(controllers);
+ }
+ }
+ }
+
private void release() {
mListener = null;
mContext = null;
- mHandler = null;
+ mExecutor = null;
}
}
private static final class Session2TokensChangedWrapper {
private final OnSession2TokensChangedListener mListener;
- private final Handler mHandler;
+ private final Executor mExecutor;
private final ISession2TokensListener.Stub mStub =
new ISession2TokensListener.Stub() {
@Override
public void onSession2TokensChanged(final List<Session2Token> tokens) {
- mHandler.post(() -> mListener.onSession2TokensChanged(tokens));
+ mExecutor.execute(() -> mListener.onSession2TokensChanged(tokens));
}
};
- Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) {
+ Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Executor executor) {
mListener = listener;
- mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper());
+ mExecutor = executor;
}
public ISession2TokensListener.Stub getStub() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4c80b91f300d..5e2d21b2e188 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -851,11 +851,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) {
// The pairing dialog now warns of phone-book access for paired devices.
// No separate prompt is displayed after pairing.
+ final BluetoothClass bluetoothClass = mDevice.getBluetoothClass();
if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
- if (mDevice.getBluetoothClass().getDeviceClass()
- == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE ||
- mDevice.getBluetoothClass().getDeviceClass()
- == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) {
+ if (bluetoothClass != null && (bluetoothClass.getDeviceClass()
+ == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE
+ || bluetoothClass.getDeviceClass()
+ == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
EventLog.writeEvent(0x534e4554, "138529441", -1, "");
}
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 53ff1a10b6ff..53a99ab8cbe4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -27,12 +27,14 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
import com.android.settingslib.R;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Test;
@@ -41,8 +43,11 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
public class CachedBluetoothDeviceTest {
private static final String DEVICE_NAME = "TestName";
private static final String DEVICE_ALIAS = "TestAlias";
@@ -72,12 +77,14 @@ public class CachedBluetoothDeviceTest {
private AudioManager mAudioManager;
private Context mContext;
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mAudioManager = mContext.getSystemService(AudioManager.class);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -937,4 +944,17 @@ public class CachedBluetoothDeviceTest {
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
mContext.getString(R.string.profile_connect_timeout_subtext));
}
+
+ @Test
+ public void onUuidChanged_bluetoothClassIsNull_shouldNotCrash() {
+ mShadowBluetoothAdapter.setUuids(PbapServerProfile.PBAB_CLIENT_UUIDS);
+ when(mDevice.getUuids()).thenReturn(PbapServerProfile.PBAB_CLIENT_UUIDS);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice.getPhonebookAccessPermission()).thenReturn(BluetoothDevice.ACCESS_UNKNOWN);
+ when(mDevice.getBluetoothClass()).thenReturn(null);
+
+ mCachedDevice.onUuidChanged();
+
+ // Should not crash
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index b265d46058be..3b7fbc73522f 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -24,6 +24,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.os.ParcelUuid;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@@ -36,6 +37,7 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto
private List<Integer> mSupportedProfiles;
private List<BluetoothDevice> mMostRecentlyConnectedDevices;
private BluetoothProfile.ServiceListener mServiceListener;
+ private ParcelUuid[] mParcelUuids;
@Implementation
protected boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
@@ -87,4 +89,13 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto
}
return true;
}
+
+ @Implementation
+ protected ParcelUuid[] getUuids() {
+ return mParcelUuids;
+ }
+
+ public void setUuids(ParcelUuid[] uuids) {
+ mParcelUuids = uuids;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index fba0b0079012..5d9465904e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -380,9 +380,9 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
- @ShellMainThread ShellExecutor mainExecutor,
+ Context context, @ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor) {
- return new Transitions(organizer, pool, mainExecutor, animExecutor);
+ return new Transitions(organizer, pool, context, mainExecutor, animExecutor);
}
//
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 172f7073852a..194736fbf911 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1054,76 +1054,7 @@ public final class ProcessList {
}
public static String makeProcStateString(int curProcState) {
- String procState;
- switch (curProcState) {
- case ActivityManager.PROCESS_STATE_PERSISTENT:
- procState = "PER ";
- break;
- case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
- procState = "PERU";
- break;
- case ActivityManager.PROCESS_STATE_TOP:
- procState = "TOP ";
- break;
- case ActivityManager.PROCESS_STATE_BOUND_TOP:
- procState = "BTOP";
- break;
- case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
- procState = "FGS ";
- break;
- case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
- procState = "BFGS";
- break;
- case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
- procState = "IMPF";
- break;
- case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
- procState = "IMPB";
- break;
- case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
- procState = "TRNB";
- break;
- case ActivityManager.PROCESS_STATE_BACKUP:
- procState = "BKUP";
- break;
- case ActivityManager.PROCESS_STATE_SERVICE:
- procState = "SVC ";
- break;
- case ActivityManager.PROCESS_STATE_RECEIVER:
- procState = "RCVR";
- break;
- case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
- procState = "TPSL";
- break;
- case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
- procState = "HVY ";
- break;
- case ActivityManager.PROCESS_STATE_HOME:
- procState = "HOME";
- break;
- case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
- procState = "LAST";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
- procState = "CAC ";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
- procState = "CACC";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_RECENT:
- procState = "CRE ";
- break;
- case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
- procState = "CEM ";
- break;
- case ActivityManager.PROCESS_STATE_NONEXISTENT:
- procState = "NONE";
- break;
- default:
- procState = "??";
- break;
- }
- return procState;
+ return ActivityManager.procStateToString(curProcState);
}
public static int makeProcStateProtoEnum(int curProcState) {
@@ -4768,11 +4699,13 @@ public final class ProcessList {
int getBlockStateForUid(UidRecord uidRec) {
// Denotes whether uid's process state is currently allowed network access.
final boolean isAllowed =
- isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState())
+ isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState(),
+ uidRec.getCurCapability())
|| isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
// Denotes whether uid's process state was previously allowed network access.
final boolean wasAllowed =
- isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState())
+ isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState(),
+ uidRec.getSetCapability())
|| isProcStateAllowedWhileOnRestrictBackground(uidRec.getSetProcState());
// When the uid is coming to foreground, AMS should inform the app thread that it should
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index c5be20e39864..f6a79ba1abfb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.input;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -51,6 +52,8 @@ import android.hardware.input.InputManagerInternal.LidSwitchCallback;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Bundle;
@@ -237,6 +240,13 @@ public class InputManagerService extends IInputManager.Stub
// List of vibrator states by device id.
@GuardedBy("mVibratorLock")
private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
+ private Object mLightLock = new Object();
+ // State for light tokens. A light token marks a lights manager session, it is generated
+ // by light session open() and deleted in session close().
+ // When lights session requests light states, the token will be used to find the light session.
+ @GuardedBy("mLightLock")
+ private final ArrayMap<IBinder, LightSession> mLightSessions =
+ new ArrayMap<IBinder, LightSession>();
// State for lid switch
private final Object mLidSwitchLock = new Object();
@@ -303,6 +313,12 @@ public class InputManagerService extends IInputManager.Stub
private static native int[] nativeGetVibratorIds(long ptr, int deviceId);
private static native int nativeGetBatteryCapacity(long ptr, int deviceId);
private static native int nativeGetBatteryStatus(long ptr, int deviceId);
+ private static native List<Light> nativeGetLights(long ptr, int deviceId);
+ private static native int nativeGetLightPlayerId(long ptr, int deviceId, int lightId);
+ private static native int nativeGetLightColor(long ptr, int deviceId, int lightId);
+ private static native void nativeSetLightPlayerId(long ptr, int deviceId, int lightId,
+ int playerId);
+ private static native void nativeSetLightColor(long ptr, int deviceId, int lightId, int color);
private static native void nativeReloadKeyboardLayouts(long ptr);
private static native void nativeReloadDeviceAliases(long ptr);
private static native String nativeDump(long ptr);
@@ -2294,6 +2310,151 @@ public class InputManagerService extends IInputManager.Stub
}
}
+ /**
+ * LightSession represents a light session for lights manager.
+ */
+ private final class LightSession implements DeathRecipient {
+ private final int mDeviceId;
+ private final IBinder mToken;
+ private final String mOpPkg;
+ // The light ids and states that are requested by the light seesion
+ private int[] mLightIds;
+ private LightState[] mLightStates;
+
+ LightSession(int deviceId, String opPkg, IBinder token) {
+ mDeviceId = deviceId;
+ mOpPkg = opPkg;
+ mToken = token;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Slog.d(TAG, "Light token died.");
+ }
+ synchronized (mLightLock) {
+ closeLightSession(mDeviceId, mToken);
+ mLightSessions.remove(mToken);
+ }
+ }
+ }
+
+ /**
+ * Returns the lights available for apps to control on the specified input device.
+ * Only lights that aren't reserved for system use are available to apps.
+ */
+ @Override // Binder call
+ public List<Light> getLights(int deviceId) {
+ return nativeGetLights(mPtr, deviceId);
+ }
+
+ /**
+ * Set specified light state with for a specific input device.
+ */
+ private void setLightStateInternal(int deviceId, Light light, LightState lightState) {
+ Preconditions.checkNotNull(light, "light does not exist");
+ if (DEBUG) {
+ Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light
+ + "lightState " + lightState);
+ }
+ if (light.getType() == Light.LIGHT_TYPE_INPUT_PLAYER_ID) {
+ nativeSetLightPlayerId(mPtr, deviceId, light.getId(), lightState.getPlayerId());
+ } else if (light.getType() == Light.LIGHT_TYPE_INPUT_SINGLE
+ || light.getType() == Light.LIGHT_TYPE_INPUT_RGB) {
+ // Set ARGB format color to input device light
+ // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color
+ nativeSetLightColor(mPtr, deviceId, light.getId(), lightState.getColor());
+ } else {
+ Slog.e(TAG, "setLightStates for unsupported light type " + light.getType());
+ }
+ }
+
+ /**
+ * Set multiple light states with multiple light ids for a specific input device.
+ */
+ private void setLightStatesInternal(int deviceId, int[] lightIds, LightState[] lightStates) {
+ final List<Light> lights = nativeGetLights(mPtr, deviceId);
+ SparseArray<Light> lightArray = new SparseArray<>();
+ for (int i = 0; i < lights.size(); i++) {
+ lightArray.put(lights.get(i).getId(), lights.get(i));
+ }
+ for (int i = 0; i < lightIds.length; i++) {
+ if (lightArray.contains(lightIds[i])) {
+ setLightStateInternal(deviceId, lightArray.get(lightIds[i]), lightStates[i]);
+ }
+ }
+ }
+
+ /**
+ * Set states for multiple lights for an opened light session.
+ */
+ @Override
+ public void setLightStates(int deviceId, int[] lightIds, LightState[] lightStates,
+ IBinder token) {
+ Preconditions.checkArgument(lightIds.length == lightStates.length,
+ "lights and light states are not same length");
+ synchronized (mLightLock) {
+ LightSession lightSession = mLightSessions.get(token);
+ Preconditions.checkArgument(lightSession != null, "not registered");
+ Preconditions.checkState(lightSession.mDeviceId == deviceId, "Incorrect device ID");
+ lightSession.mLightIds = lightIds.clone();
+ lightSession.mLightStates = lightStates.clone();
+ if (DEBUG) {
+ Slog.d(TAG, "setLightStates for " + lightSession.mOpPkg + " device " + deviceId);
+ }
+ }
+ setLightStatesInternal(deviceId, lightIds, lightStates);
+ }
+
+ @Override
+ public @Nullable LightState getLightState(int deviceId, int lightId) {
+ synchronized (mLightLock) {
+ int color = nativeGetLightColor(mPtr, deviceId, lightId);
+ int playerId = nativeGetLightPlayerId(mPtr, deviceId, lightId);
+
+ return new LightState(color, playerId);
+ }
+ }
+
+ @Override
+ public void openLightSession(int deviceId, String opPkg, IBinder token) {
+ Preconditions.checkNotNull(token);
+ synchronized (mLightLock) {
+ Preconditions.checkState(mLightSessions.get(token) == null, "already registered");
+ LightSession lightSession = new LightSession(deviceId, opPkg, token);
+ try {
+ token.linkToDeath(lightSession, 0);
+ } catch (RemoteException ex) {
+ // give up
+ ex.rethrowAsRuntimeException();
+ }
+ mLightSessions.put(token, lightSession);
+ if (DEBUG) {
+ Slog.d(TAG, "Open light session for " + opPkg + " device " + deviceId);
+ }
+ }
+ }
+
+ @Override
+ public void closeLightSession(int deviceId, IBinder token) {
+ Preconditions.checkNotNull(token);
+ synchronized (mLightLock) {
+ LightSession lightSession = mLightSessions.get(token);
+ Preconditions.checkState(lightSession != null, "not registered");
+ // Turn off the lights that were previously requested by the session to be closed.
+ Arrays.fill(lightSession.mLightStates, new LightState(0));
+ setLightStatesInternal(deviceId, lightSession.mLightIds,
+ lightSession.mLightStates);
+ mLightSessions.remove(token);
+ // If any other session is still pending with light request, apply the first session's
+ // request.
+ if (!mLightSessions.isEmpty()) {
+ LightSession nextSession = mLightSessions.valueAt(0);
+ setLightStatesInternal(deviceId, nextSession.mLightIds, nextSession.mLightStates);
+ }
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 676f4218f745..4b4146683a09 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -29,6 +29,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.os.Process.INVALID_UID;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessCapability;
import android.net.NetworkPolicyManager;
import android.os.UserHandle;
import android.util.Log;
@@ -97,13 +98,15 @@ public class NetworkPolicyLogger {
}
}
- void uidStateChanged(int uid, int procState, long procStateSeq) {
+ void uidStateChanged(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
synchronized (mLock) {
if (LOGV || uid == mDebugUid) {
Slog.v(TAG, uid + " state changed to "
- + ProcessList.makeProcStateString(procState) + " with seq=" + procStateSeq);
+ + ProcessList.makeProcStateString(procState) + ",seq=" + procStateSeq
+ + ",cap=" + ActivityManager.getCapabilitiesSummary(capability));
}
- mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq);
+ mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq, capability);
}
}
@@ -373,7 +376,8 @@ public class NetworkPolicyLogger {
super(Data.class, capacity);
}
- public void uidStateChanged(int uid, int procState, long procStateSeq) {
+ public void uidStateChanged(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
final Data data = getNextSlot();
if (data == null) return;
@@ -381,6 +385,7 @@ public class NetworkPolicyLogger {
data.type = EVENT_UID_STATE_CHANGED;
data.ifield1 = uid;
data.ifield2 = procState;
+ data.ifield3 = capability;
data.lfield1 = procStateSeq;
data.timeStamp = System.currentTimeMillis();
}
@@ -546,8 +551,9 @@ public class NetworkPolicyLogger {
case EVENT_NETWORK_BLOCKED:
return data.ifield1 + "-" + getBlockedReason(data.ifield2);
case EVENT_UID_STATE_CHANGED:
- return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2)
- + "-" + data.lfield1;
+ return data.ifield1 + ":" + ProcessList.makeProcStateString(data.ifield2)
+ + ":" + ActivityManager.getCapabilitiesSummary(data.ifield3)
+ + ":" + data.lfield1;
case EVENT_POLICIES_CHANGED:
return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3);
case EVENT_METEREDNESS_CHANGED:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index aa7da54b2e1d..ecf4438d8aca 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -131,6 +131,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -166,6 +167,7 @@ import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
+import android.net.NetworkPolicyManager.UidState;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
@@ -557,7 +559,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Foreground at UID granularity. */
@GuardedBy("mUidRulesFirstLock")
- final SparseIntArray mUidState = new SparseIntArray();
+ final SparseArray<UidState> mUidState = new SparseArray<UidState>();
/** Map from network ID to last observed meteredness state */
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -988,9 +990,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
- int capability) {
+ @ProcessCapability int capability) {
+ // TODO: Avoid creating a new UidStateCallbackInfo object every time
+ // we get a callback for an uid
mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED,
- uid, procState, procStateSeq).sendToTarget();
+ new UidStateCallbackInfo(uid, procState, procStateSeq, capability))
+ .sendToTarget();
}
@Override public void onUidGone(int uid, boolean disabled) {
@@ -1007,6 +1012,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
};
+ private static final class UidStateCallbackInfo {
+ public final int uid;
+ public final int procState;
+ public final long procStateSeq;
+ @ProcessCapability
+ public final int capability;
+
+ UidStateCallbackInfo(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
+ this.uid = uid;
+ this.procState = procState;
+ this.procStateSeq = procStateSeq;
+ this.capability = capability;
+ }
+ }
+
final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -3718,14 +3739,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
fout.print("UID=");
fout.print(uid);
- final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- fout.print(" state=");
- fout.print(state);
- if (state <= ActivityManager.PROCESS_STATE_TOP) {
- fout.print(" (fg)");
+ final UidState uidState = mUidState.get(uid);
+ if (uidState == null) {
+ fout.print(" state={null}");
} else {
- fout.print(state <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- ? " (fg svc)" : " (bg)");
+ fout.print(" state=");
+ fout.print(uidState.toString());
}
final int uidRules = mUidRules.get(uid, RULE_NONE);
@@ -3783,26 +3802,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@VisibleForTesting
boolean isUidForeground(int uid) {
synchronized (mUidRulesFirstLock) {
- return isUidStateForeground(
- mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
+ return isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid));
}
}
@GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictBackgroundUL(int uid) {
- final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- return isProcStateAllowedWhileOnRestrictBackground(procState);
+ final UidState uidState = mUidState.get(uid);
+ return isProcStateAllowedWhileOnRestrictBackground(uidState);
}
@GuardedBy("mUidRulesFirstLock")
private boolean isUidForegroundOnRestrictPowerUL(int uid) {
- final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- return isProcStateAllowedWhileIdleOrPowerSaveMode(procState);
- }
-
- private boolean isUidStateForeground(int state) {
- // only really in foreground when screen is also on
- return state <= NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
+ final UidState uidState = mUidState.get(uid);
+ return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState);
}
/**
@@ -3811,16 +3824,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated.
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean updateUidStateUL(int uid, int uidState) {
+ private boolean updateUidStateUL(int uid, int procState, @ProcessCapability int capability) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
try {
- final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
- if (oldUidState != uidState) {
+ final UidState oldUidState = mUidState.get(uid);
+ if (oldUidState == null || oldUidState.procState != procState
+ || oldUidState.capability != capability) {
+ final UidState newUidState = new UidState(uid, procState, capability);
// state changed, push updated rules
- mUidState.put(uid, uidState);
- updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, uidState);
+ mUidState.put(uid, newUidState);
+ updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState);
if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
- != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
+ != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState)) {
updateRuleForAppIdleUL(uid);
if (mDeviceIdleMode) {
updateRuleForDeviceIdleUL(uid);
@@ -3842,11 +3857,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private boolean removeUidStateUL(int uid) {
final int index = mUidState.indexOfKey(uid);
if (index >= 0) {
- final int oldUidState = mUidState.valueAt(index);
+ final UidState oldUidState = mUidState.valueAt(index);
mUidState.removeAt(index);
- if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState,
- ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ if (oldUidState != null) {
+ updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, null);
if (mDeviceIdleMode) {
updateRuleForDeviceIdleUL(uid);
}
@@ -3873,8 +3887,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
- private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, int oldUidState,
- int newUidState) {
+ private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid,
+ @Nullable UidState oldUidState, @Nullable UidState newUidState) {
final boolean oldForeground =
isProcStateAllowedWhileOnRestrictBackground(oldUidState);
final boolean newForeground =
@@ -4979,11 +4993,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public boolean handleMessage(Message msg) {
switch (msg.what) {
case UID_MSG_STATE_CHANGED: {
- final int uid = msg.arg1;
- final int procState = msg.arg2;
- final long procStateSeq = (Long) msg.obj;
-
- handleUidChanged(uid, procState, procStateSeq);
+ final UidStateCallbackInfo uidStateCallbackInfo =
+ (UidStateCallbackInfo) msg.obj;
+ final int uid = uidStateCallbackInfo.uid;
+ final int procState = uidStateCallbackInfo.procState;
+ final long procStateSeq = uidStateCallbackInfo.procStateSeq;
+ final int capability = uidStateCallbackInfo.capability;
+
+ handleUidChanged(uid, procState, procStateSeq, capability);
return true;
}
case UID_MSG_GONE: {
@@ -4998,23 +5015,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
};
- void handleUidChanged(int uid, int procState, long procStateSeq) {
+ void handleUidChanged(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
try {
boolean updated;
synchronized (mUidRulesFirstLock) {
// We received a uid state change callback, add it to the history so that it
// will be useful for debugging.
- mLogger.uidStateChanged(uid, procState, procStateSeq);
+ mLogger.uidStateChanged(uid, procState, procStateSeq, capability);
// Now update the network policy rules as per the updated uid state.
- updated = updateUidStateUL(uid, procState);
+ updated = updateUidStateUL(uid, procState, capability);
// Updating the network rules is done, so notify AMS about this.
mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq);
}
// Do this without the lock held. handleUidChanged() and handleUidGone() are
// called from the handler, so there's no multi-threading issue.
if (updated) {
- updateNetworkStats(uid, isUidStateForeground(procState));
+ updateNetworkStats(uid, isProcStateAllowedWhileOnRestrictBackground(procState));
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -5349,6 +5367,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ private static void collectKeys(SparseArray<UidState> source, SparseBooleanArray target) {
+ final int size = source.size();
+ for (int i = 0; i < size; i++) {
+ target.put(source.keyAt(i), true);
+ }
+ }
+
@Override
public void factoryReset(String subscriber) {
mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 5d7c41c7b08f..1acbabda9e19 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -69,6 +69,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.Executor;
+import java.util.function.Function;
/**
* The entity responsible for filtering visibility between apps based on declarations in their
@@ -1354,14 +1355,13 @@ public class AppsFilter implements Watchable, Snappable {
}
public void dumpQueries(
- PrintWriter pw, PackageManagerService pms, @Nullable Integer filteringAppId,
- DumpState dumpState,
- int[] users) {
+ PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, int[] users,
+ Function<Integer, String[]> getPackagesForUid) {
final SparseArray<String> cache = new SparseArray<>();
ToString<Integer> expandPackages = input -> {
String cachedValue = cache.get(input);
if (cachedValue == null) {
- final String[] packagesForUid = pms.getPackagesForUid(input);
+ final String[] packagesForUid = getPackagesForUid.apply(input);
if (packagesForUid == null) {
cachedValue = "[unknown app id " + input + "]";
} else {
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 2a1fc87fc29f..380cdb10569b 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -55,6 +55,9 @@ public final class DumpState {
private int mOptions;
private boolean mTitlePrinted;
+ private boolean mFullPreferred;
+
+ private String mTargetPackageName;
private SharedUserSetting mSharedUser;
@@ -99,4 +102,20 @@ public final class DumpState {
public void setSharedUser(SharedUserSetting user) {
mSharedUser = user;
}
+
+ public String getTargetPackageName() {
+ return mTargetPackageName;
+ }
+
+ public void setTargetPackageName(String packageName) {
+ mTargetPackageName = packageName;
+ }
+
+ public boolean isFullPreferred() {
+ return mFullPreferred;
+ }
+
+ public void setFullPreferred(boolean fullPreferred) {
+ mFullPreferred = fullPreferred;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2a3feb123dd9..b0037f423563 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1994,6 +1994,7 @@ public class PackageManagerService extends IPackageManager.Stub
SigningDetails getSigningDetails(int uid);
boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId);
boolean filterAppAccess(String packageName, int callingUid, int userId);
+ void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
}
/**
@@ -2037,6 +2038,9 @@ public class PackageManagerService extends IPackageManager.Stub
private final InstantAppResolverConnection mInstantAppResolverConnection;
private final DefaultAppProvider mDefaultAppProvider;
private final DomainVerificationManagerInternal mDomainVerificationManager;
+ private final PackageDexOptimizer mPackageDexOptimizer;
+ private final DexManager mDexManager;
+ private final CompilerStats mCompilerStats;
// PackageManagerService attributes that are primitives are referenced through the
// pms object directly. Primitives are the only attributes so referenced.
@@ -2083,6 +2087,9 @@ public class PackageManagerService extends IPackageManager.Stub
mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
mDefaultAppProvider = args.service.mDefaultAppProvider;
mDomainVerificationManager = args.service.mDomainVerificationManager;
+ mPackageDexOptimizer = args.service.mPackageDexOptimizer;
+ mDexManager = args.service.mDexManager;
+ mCompilerStats = args.service.mCompilerStats;
// Used to reference PMS attributes that are primitives and which are not
// updated under control of the PMS lock.
@@ -4393,6 +4400,143 @@ public class PackageManagerService extends IPackageManager.Stub
userId);
}
+ public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+ final String packageName = dumpState.getTargetPackageName();
+
+ switch (type) {
+ case DumpState.DUMP_VERSION:
+ {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Database versions:");
+ mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " "));
+ break;
+ }
+
+ case DumpState.DUMP_PREFERRED_XML:
+ {
+ pw.flush();
+ FileOutputStream fout = new FileOutputStream(fd);
+ BufferedOutputStream str = new BufferedOutputStream(fout);
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ try {
+ serializer.setOutput(str, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ mSettings.writePreferredActivitiesLPr(serializer, 0,
+ dumpState.isFullPreferred());
+ serializer.endDocument();
+ serializer.flush();
+ } catch (IllegalArgumentException e) {
+ pw.println("Failed writing: " + e);
+ } catch (IllegalStateException e) {
+ pw.println("Failed writing: " + e);
+ } catch (IOException e) {
+ pw.println("Failed writing: " + e);
+ }
+ break;
+ }
+
+ case DumpState.DUMP_QUERIES:
+ {
+ final PackageSetting setting = mSettings.getPackageLPr(packageName);
+ Integer filteringAppId = setting == null ? null : setting.appId;
+ mAppsFilter.dumpQueries(
+ pw, filteringAppId, dumpState, mUserManager.getUserIds(),
+ this::getPackagesForUid);
+ break;
+ }
+
+ case DumpState.DUMP_DOMAIN_PREFERRED:
+ {
+ final android.util.IndentingPrintWriter writer =
+ new android.util.IndentingPrintWriter(pw);
+ if (dumpState.onTitlePrinted()) pw.println();
+
+ writer.println("Domain verification status:");
+ writer.increaseIndent();
+ try {
+ mDomainVerificationManager.printState(writer, packageName,
+ UserHandle.USER_ALL, mSettings::getPackageLPr);
+ } catch (PackageManager.NameNotFoundException e) {
+ pw.println("Failure printing domain verification information");
+ Slog.e(TAG, "Failure printing domain verification information", e);
+ }
+ writer.decreaseIndent();
+ break;
+ }
+
+ case DumpState.DUMP_DEXOPT:
+ {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println();
+ ipw.println("Dexopt state:");
+ ipw.increaseIndent();
+ Collection<PackageSetting> pkgSettings;
+ if (packageName != null) {
+ PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName);
+ if (targetPkgSetting != null) {
+ pkgSettings = Collections.singletonList(targetPkgSetting);
+ } else {
+ ipw.println("Unable to find package: " + packageName);
+ return;
+ }
+ } else {
+ pkgSettings = mSettings.getPackagesLocked().values();
+ }
+
+ for (PackageSetting pkgSetting : pkgSettings) {
+ final AndroidPackage pkg = pkgSetting.getPkg();
+ if (pkg == null) {
+ continue;
+ }
+ ipw.println("[" + pkgSetting.name + "]");
+ ipw.increaseIndent();
+ mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
+ mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName()));
+ ipw.decreaseIndent();
+ }
+ break;
+ }
+
+ case DumpState.DUMP_COMPILER_STATS:
+ {
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println();
+ ipw.println("Compiler stats:");
+ ipw.increaseIndent();
+ Collection<AndroidPackage> packages;
+ if (packageName != null) {
+ AndroidPackage targetPackage = mPackages.get(packageName);
+ if (targetPackage != null) {
+ packages = Collections.singletonList(targetPackage);
+ } else {
+ ipw.println("Unable to find package: " + packageName);
+ return;
+ }
+ } else {
+ packages = mPackages.values();
+ }
+
+ for (AndroidPackage pkg : packages) {
+ final String pkgName = pkg.getPackageName();
+ ipw.println("[" + pkgName + "]");
+ ipw.increaseIndent();
+
+ CompilerStats.PackageStats stats = mCompilerStats.getPackageStats(pkgName);
+ if (stats == null) {
+ ipw.println("(No recorded stats)");
+ } else {
+ stats.dump(ipw);
+ }
+ ipw.decreaseIndent();
+ }
+ break;
+ }
+ } // switch
+ }
}
/**
@@ -23554,10 +23698,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
DumpState dumpState = new DumpState();
- boolean fullPreferred = false;
boolean checkin = false;
- String packageName = null;
ArraySet<String> permissionNames = null;
int opti = 0;
@@ -23626,7 +23768,7 @@ public class PackageManagerService extends IPackageManager.Stub
opti++;
// Is this a package name?
if ("android".equals(cmd) || cmd.contains(".")) {
- packageName = cmd;
+ dumpState.setTargetPackageName(cmd);
// When dumping a single package, we always dump all of its
// filter information since the amount of data will be reasonable.
dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
@@ -23707,7 +23849,7 @@ public class PackageManagerService extends IPackageManager.Stub
} else if ("preferred-xml".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PREFERRED_XML);
if (opti < args.length && "--full".equals(args[opti])) {
- fullPreferred = true;
+ dumpState.setFullPreferred(true);
opti++;
}
} else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
@@ -23760,257 +23902,208 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ final String packageName = dumpState.getTargetPackageName();
if (checkin) {
pw.println("vers,1");
}
// reader
- synchronized (mLock) {
- if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
- if (!checkin) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Database versions:");
- mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " "));
- }
+ if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
+ if (!checkin) {
+ dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
}
+ }
- if (!checkin
- && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES)
- && packageName == null) {
- if (dumpState.onTitlePrinted()) {
- pw.println();
- }
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
- ipw.println("Known Packages:");
+ if (!checkin
+ && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES)
+ && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println("Known Packages:");
+ ipw.increaseIndent();
+ for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
+ final String knownPackage = mPmInternal.knownPackageToString(i);
+ ipw.print(knownPackage);
+ ipw.println(":");
+ final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
+ UserHandle.USER_SYSTEM);
ipw.increaseIndent();
- for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
- final String knownPackage = mPmInternal.knownPackageToString(i);
- ipw.print(knownPackage);
- ipw.println(":");
- final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
- UserHandle.USER_SYSTEM);
- ipw.increaseIndent();
- if (ArrayUtils.isEmpty(pkgNames)) {
- ipw.println("none");
- } else {
- for (String name : pkgNames) {
- ipw.println(name);
- }
+ if (ArrayUtils.isEmpty(pkgNames)) {
+ ipw.println("none");
+ } else {
+ for (String name : pkgNames) {
+ ipw.println(name);
}
- ipw.decreaseIndent();
}
ipw.decreaseIndent();
}
+ ipw.decreaseIndent();
+ }
- if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ final String requiredVerifierPackage = mRequiredVerifierPackage;
+ if (!checkin) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Verifiers:");
+ pw.print(" Required: ");
+ pw.print(requiredVerifierPackage);
+ pw.print(" (uid=");
+ pw.print(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ pw.println(")");
+ } else if (requiredVerifierPackage != null) {
+ pw.print("vrfy,"); pw.print(requiredVerifierPackage);
+ pw.print(",");
+ pw.println(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ UserHandle.USER_SYSTEM));
+ }
+ }
+
+ if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
+ final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
+ final ComponentName verifierComponent = proxy.getComponentName();
+ if (verifierComponent != null) {
+ String verifierPackageName = verifierComponent.getPackageName();
if (!checkin) {
if (dumpState.onTitlePrinted())
pw.println();
- pw.println("Verifiers:");
- pw.print(" Required: ");
- pw.print(mRequiredVerifierPackage);
+ pw.println("Domain Verifier:");
+ pw.print(" Using: ");
+ pw.print(verifierPackageName);
pw.print(" (uid=");
- pw.print(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
pw.println(")");
- } else if (mRequiredVerifierPackage != null) {
- pw.print("vrfy,"); pw.print(mRequiredVerifierPackage);
+ } else if (verifierPackageName != null) {
+ pw.print("dv,"); pw.print(verifierPackageName);
pw.print(",");
- pw.println(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.USER_SYSTEM));
}
+ } else {
+ pw.println();
+ pw.println("No Domain Verifier available!");
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) &&
- packageName == null) {
- DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
- ComponentName verifierComponent = proxy.getComponentName();
- if (verifierComponent != null) {
- String verifierPackageName = verifierComponent.getPackageName();
- if (!checkin) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Domain Verifier:");
- pw.print(" Using: ");
- pw.print(verifierPackageName);
- pw.print(" (uid=");
- pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
- pw.println(")");
- } else if (verifierPackageName != null) {
- pw.print("dv,"); pw.print(verifierPackageName);
- pw.print(",");
- pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING,
- UserHandle.USER_SYSTEM));
- }
- } else {
- pw.println();
- pw.println("No Domain Verifier available!");
- }
+ if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
+ // TODO: Move it to ComputerEngine once LongSparseArray<SharedLibraryInfo> is copied
+ // in snapshot.
+ synchronized (mLock) {
+ dumpSharedLibrariesLPr(pw, dumpState, checkin);
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
- boolean printedHeader = false;
- final int numSharedLibraries = mSharedLibraries.size();
- for (int index = 0; index < numSharedLibraries; index++) {
- final String libName = mSharedLibraries.keyAt(index);
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib
- = mSharedLibraries.get(libName);
- if (versionedLib == null) {
- continue;
- }
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
- if (!checkin) {
- if (!printedHeader) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Libraries:");
- printedHeader = true;
- }
- pw.print(" ");
- } else {
- pw.print("lib,");
- }
- pw.print(libraryInfo.getName());
- if (libraryInfo.isStatic()) {
- pw.print(" version=" + libraryInfo.getLongVersion());
- }
- if (!checkin) {
- pw.print(" -> ");
- }
- if (libraryInfo.getPath() != null) {
- if (libraryInfo.isNative()) {
- pw.print(" (so) ");
- } else {
- pw.print(" (jar) ");
- }
- pw.print(libraryInfo.getPath());
- } else {
- pw.print(" (apk) ");
- pw.print(libraryInfo.getPackageName());
- }
- pw.println();
- }
- }
+ if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ if (!checkin) {
+ pw.println("Features:");
}
- if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
- if (dumpState.onTitlePrinted())
- pw.println();
- if (!checkin) {
- pw.println("Features:");
- }
-
- synchronized (mAvailableFeatures) {
- for (FeatureInfo feat : mAvailableFeatures.values()) {
- if (checkin) {
- pw.print("feat,");
- pw.print(feat.name);
- pw.print(",");
- pw.println(feat.version);
- } else {
- pw.print(" ");
- pw.print(feat.name);
- if (feat.version > 0) {
- pw.print(" version=");
- pw.print(feat.version);
- }
- pw.println();
+ synchronized (mAvailableFeatures) {
+ for (FeatureInfo feat : mAvailableFeatures.values()) {
+ if (checkin) {
+ pw.print("feat,");
+ pw.print(feat.name);
+ pw.print(",");
+ pw.println(feat.version);
+ } else {
+ pw.print(" ");
+ pw.print(feat.name);
+ if (feat.version > 0) {
+ pw.print(" version=");
+ pw.print(feat.version);
}
+ pw.println();
}
}
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ // TODO: This cannot be moved to ComputerEngine since some variables with collections
+ // types in IntentResolver such as mTypeToFilter do not have a copy of `F[]`.
+ synchronized (mLock) {
mSettings.dumpPreferred(pw, dumpState, packageName);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
- pw.flush();
- FileOutputStream fout = new FileOutputStream(fd);
- BufferedOutputStream str = new BufferedOutputStream(fout);
- TypedXmlSerializer serializer = Xml.newFastSerializer();
- try {
- serializer.setOutput(str, StandardCharsets.UTF_8.name());
- serializer.startDocument(null, true);
- serializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
- mSettings.writePreferredActivitiesLPr(serializer, 0, fullPreferred);
- serializer.endDocument();
- serializer.flush();
- } catch (IllegalArgumentException e) {
- pw.println("Failed writing: " + e);
- } catch (IllegalStateException e) {
- pw.println("Failed writing: " + e);
- } catch (IOException e) {
- pw.println("Failed writing: " + e);
- }
- }
-
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
- android.util.IndentingPrintWriter writer =
- new android.util.IndentingPrintWriter(pw);
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
+ dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
+ }
- writer.println("Domain verification status:");
- writer.increaseIndent();
- try {
- mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL,
- mSettings::getPackageLPr);
- } catch (PackageManager.NameNotFoundException e) {
- pw.println("Failure printing domain verification information");
- Slog.e(TAG, "Failure printing domain verification information", e);
- }
- writer.decreaseIndent();
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+ dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
- mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ synchronized (mLock) {
mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ synchronized (mLock) {
mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
+ if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
+ // This cannot be moved to ComputerEngine since some variables of the collections
+ // in PackageUserState such as suspendParams, disabledComponents and enabledComponents
+ // do not have a copy.
+ synchronized (mLock) {
mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
- final PackageSetting setting = mSettings.getPackageLPr(packageName);
- Integer filteringAppId = setting == null ? null : setting.appId;
- mAppsFilter.dumpQueries(
- pw, this, filteringAppId, dumpState,
- mUserManager.getUserIds());
- }
+ if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
+ dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
+ }
- if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ // This cannot be moved to ComputerEngine since the set of packages in the
+ // SharedUserSetting do not have a copy.
+ synchronized (mLock) {
mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
}
+ }
- if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
- if (dumpState.onTitlePrinted()) pw.println();
- pw.println("Package Changes:");
+ if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ pw.println("Package Changes:");
+ synchronized (mLock) {
pw.print(" Sequence number="); pw.println(mChangedPackagesSequenceNumber);
final int K = mChangedPackages.size();
for (int i = 0; i < K; i++) {
@@ -24032,16 +24125,18 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
- // XXX should handle packageName != null by dumping only install data that
- // the given package is involved with.
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
+ // XXX should handle packageName != null by dumping only install data that
+ // the given package is involved with.
+ if (dumpState.onTitlePrinted()) pw.println();
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
- ipw.println();
- ipw.println("Frozen packages:");
- ipw.increaseIndent();
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println();
+ ipw.println("Frozen packages:");
+ ipw.increaseIndent();
+ synchronized (mLock) {
if (mFrozenPackages.size() == 0) {
ipw.println("(none)");
} else {
@@ -24049,16 +24144,18 @@ public class PackageManagerService extends IPackageManager.Stub
ipw.println(mFrozenPackages.valueAt(i));
}
}
- ipw.decreaseIndent();
}
+ ipw.decreaseIndent();
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
+ if (dumpState.onTitlePrinted()) pw.println();
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
- ipw.println();
- ipw.println("Loaded volumes:");
- ipw.increaseIndent();
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
+ ipw.println();
+ ipw.println("Loaded volumes:");
+ ipw.increaseIndent();
+ synchronized (mLoadedVolumes) {
if (mLoadedVolumes.size() == 0) {
ipw.println("(none)");
} else {
@@ -24066,36 +24163,39 @@ public class PackageManagerService extends IPackageManager.Stub
ipw.println(mLoadedVolumes.valueAt(i));
}
}
- ipw.decreaseIndent();
}
+ ipw.decreaseIndent();
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
- && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+ && packageName == null) {
+ synchronized (mLock) {
mComponentResolver.dumpServicePermissions(pw, dumpState);
}
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
- if (dumpState.onTitlePrinted()) pw.println();
- dumpDexoptStateLPr(pw, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
- if (dumpState.onTitlePrinted()) pw.println();
- dumpCompilerStatsLPr(pw, packageName);
- }
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
+ }
- if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
- if (dumpState.onTitlePrinted()) pw.println();
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+ if (dumpState.onTitlePrinted()) pw.println();
+ synchronized (mLock) {
mSettings.dumpReadMessagesLPr(pw, dumpState);
-
- pw.println();
- pw.println("Package warning messages:");
- dumpCriticalInfo(pw, null);
}
+ pw.println();
+ pw.println("Package warning messages:");
+ dumpCriticalInfo(pw, null);
+ }
- if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
- dumpCriticalInfo(pw, "msg,");
- }
+ if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
+ dumpCriticalInfo(pw, "msg,");
}
// PackageInstaller should be called outside of mPackages lock
@@ -24130,6 +24230,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ /**
+ * Dump package manager states to the file according to a given dumping type of
+ * {@link DumpState}.
+ */
+ private void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+ snapshotComputer().dump(type, fd, pw, dumpState);
+ }
+
//TODO: b/111402650
private void disableSkuSpecificApps() {
String apkList[] = mContext.getResources().getStringArray(
@@ -24228,69 +24336,50 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- @GuardedBy("mLock")
- @SuppressWarnings("resource")
- private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println();
- ipw.println("Dexopt state:");
- ipw.increaseIndent();
- Collection<PackageSetting> pkgSettings;
- if (packageName != null) {
- PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName);
- if (targetPkgSetting != null) {
- pkgSettings = Collections.singletonList(targetPkgSetting);
- } else {
- ipw.println("Unable to find package: " + packageName);
- return;
- }
- } else {
- pkgSettings = mSettings.getPackagesLocked().values();
- }
-
- for (PackageSetting pkgSetting : pkgSettings) {
- if (pkgSetting.pkg == null) {
+ private void dumpSharedLibrariesLPr(PrintWriter pw, DumpState dumpState, boolean checkin) {
+ boolean printedHeader = false;
+ final int numSharedLibraries = mSharedLibraries.size();
+ for (int index = 0; index < numSharedLibraries; index++) {
+ final String libName = mSharedLibraries.keyAt(index);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
+ if (versionedLib == null) {
continue;
}
- ipw.println("[" + pkgSetting.name + "]");
- ipw.increaseIndent();
- mPackageDexOptimizer.dumpDexoptState(ipw, pkgSetting.pkg, pkgSetting,
- mDexManager.getPackageUseInfoOrDefault(pkgSetting.pkg.getPackageName()));
- ipw.decreaseIndent();
- }
- }
-
- @GuardedBy("mLock")
- @SuppressWarnings("resource")
- private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
- final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println();
- ipw.println("Compiler stats:");
- ipw.increaseIndent();
- Collection<AndroidPackage> packages;
- if (packageName != null) {
- AndroidPackage targetPackage = mPackages.get(packageName);
- if (targetPackage != null) {
- packages = Collections.singletonList(targetPackage);
- } else {
- ipw.println("Unable to find package: " + packageName);
- return;
- }
- } else {
- packages = mPackages.values();
- }
-
- for (AndroidPackage pkg : packages) {
- ipw.println("[" + pkg.getPackageName() + "]");
- ipw.increaseIndent();
-
- CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.getPackageName());
- if (stats == null) {
- ipw.println("(No recorded stats)");
- } else {
- stats.dump(ipw);
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
+ if (!checkin) {
+ if (!printedHeader) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Libraries:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ } else {
+ pw.print("lib,");
+ }
+ pw.print(libraryInfo.getName());
+ if (libraryInfo.isStatic()) {
+ pw.print(" version=" + libraryInfo.getLongVersion());
+ }
+ if (!checkin) {
+ pw.print(" -> ");
+ }
+ if (libraryInfo.getPath() != null) {
+ if (libraryInfo.isNative()) {
+ pw.print(" (so) ");
+ } else {
+ pw.print(" (jar) ");
+ }
+ pw.print(libraryInfo.getPath());
+ } else {
+ pw.print(" (apk) ");
+ pw.print(libraryInfo.getPackageName());
+ }
+ pw.println();
}
- ipw.decreaseIndent();
}
}
@@ -24452,7 +24541,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
sendResourcesChangedBroadcast(true, false, loaded, null);
- mLoadedVolumes.add(vol.getId());
+ synchronized (mLoadedVolumes) {
+ mLoadedVolumes.add(vol.getId());
+ }
}
private void unloadPrivatePackages(final VolumeInfo vol) {
@@ -24500,7 +24591,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
sendResourcesChangedBroadcast(false, false, unloaded, null);
- mLoadedVolumes.remove(vol.getId());
+ synchronized (mLoadedVolumes) {
+ mLoadedVolumes.remove(vol.getId());
+ }
// Try very hard to release any references to this path so we don't risk
// the system server being killed due to open FDs
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2112247650a5..f1ffdaf7f111 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4877,7 +4877,7 @@ public final class Settings implements Watchable, Snappable {
}
}
- void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
+ void dumpPermissions(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
DumpState dumpState) {
LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames,
mPermissionDataProvider.getLegacyPermissions(),
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 643503d18bed..21d57d8c189f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -158,6 +158,20 @@ static struct {
static struct {
jclass clazz;
jmethodID constructor;
+ jfieldID lightTypeSingle;
+ jfieldID lightTypePlayerId;
+ jfieldID lightTypeRgb;
+} gLightClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID constructor;
+ jmethodID add;
+} gArrayListClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID constructor;
jmethodID keyAt;
jmethodID valueAt;
jmethodID size;
@@ -1923,6 +1937,79 @@ static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint
return vibIdArray;
}
+static jobject nativeGetLights(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ jobject jLights = env->NewObject(gArrayListClassInfo.clazz, gArrayListClassInfo.constructor);
+
+ std::vector<int> lightIds = im->getInputManager()->getReader()->getLightIds(deviceId);
+
+ for (size_t i = 0; i < lightIds.size(); i++) {
+ const InputDeviceLightInfo* lightInfo =
+ im->getInputManager()->getReader()->getLightInfo(deviceId, lightIds[i]);
+ if (lightInfo == nullptr) {
+ ALOGW("Failed to get input device %d light info for id %d", deviceId, lightIds[i]);
+ continue;
+ }
+
+ jint jTypeId = 0;
+ if (lightInfo->type == InputDeviceLightType::SINGLE) {
+ jTypeId =
+ env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeSingle);
+ } else if (lightInfo->type == InputDeviceLightType::PLAYER_ID) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightTypePlayerId);
+ } else if (lightInfo->type == InputDeviceLightType::RGB ||
+ lightInfo->type == InputDeviceLightType::MULTI_COLOR) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeRgb);
+ } else {
+ ALOGW("Unknown light type %d", lightInfo->type);
+ continue;
+ }
+ ScopedLocalRef<jobject>
+ lightObj(env,
+ env->NewObject(gLightClassInfo.clazz, gLightClassInfo.constructor,
+ (jint)lightInfo->id, (jint)lightInfo->ordinal, jTypeId,
+ env->NewStringUTF(lightInfo->name.c_str())));
+ // Add light object to list
+ env->CallBooleanMethod(jLights, gArrayListClassInfo.add, lightObj.get());
+ }
+
+ return jLights;
+}
+
+static jint nativeGetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret =
+ im->getInputManager()->getReader()->getLightPlayerId(deviceId, lightId);
+
+ return static_cast<jint>(ret.value_or(0));
+}
+
+static jint nativeGetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ std::optional<int32_t> ret =
+ im->getInputManager()->getReader()->getLightColor(deviceId, lightId);
+ return static_cast<jint>(ret.value_or(0));
+}
+
+static void nativeSetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId, jint playerId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->setLightPlayerId(deviceId, lightId, playerId);
+}
+
+static void nativeSetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
+ jint lightId, jint color) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getReader()->setLightColor(deviceId, lightId, color);
+}
+
static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -2192,6 +2279,11 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
{"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating},
{"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds},
+ {"nativeGetLights", "(JI)Ljava/util/List;", (void*)nativeGetLights},
+ {"nativeGetLightPlayerId", "(JII)I", (void*)nativeGetLightPlayerId},
+ {"nativeGetLightColor", "(JII)I", (void*)nativeGetLightColor},
+ {"nativeSetLightPlayerId", "(JIII)V", (void*)nativeSetLightPlayerId},
+ {"nativeSetLightColor", "(JIII)V", (void*)nativeSetLightColor},
{"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity},
{"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus},
{"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
@@ -2386,6 +2478,27 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz,
"getAffineTransform", "()[F");
+ // Light
+ FIND_CLASS(gLightClassInfo.clazz, "android/hardware/lights/Light");
+ gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz));
+ GET_METHOD_ID(gLightClassInfo.constructor, gLightClassInfo.clazz, "<init>",
+ "(IIILjava/lang/String;)V");
+
+ gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz));
+ gLightClassInfo.lightTypeSingle =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_SINGLE", "I");
+ gLightClassInfo.lightTypePlayerId =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_PLAYER_ID", "I");
+ gLightClassInfo.lightTypeRgb =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_RGB", "I");
+
+ // ArrayList
+ FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList");
+ gArrayListClassInfo.clazz = jclass(env->NewGlobalRef(gArrayListClassInfo.clazz));
+ GET_METHOD_ID(gArrayListClassInfo.constructor, gArrayListClassInfo.clazz, "<init>", "()V");
+ GET_METHOD_ID(gArrayListClassInfo.add, gArrayListClassInfo.clazz, "add",
+ "(Ljava/lang/Object;)Z");
+
// SparseArray
FIND_CLASS(gSparseArrayClassInfo.clazz, "android/util/SparseArray");
gSparseArrayClassInfo.clazz = jclass(env->NewGlobalRef(gSparseArrayClassInfo.clazz));
diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
index 3e9709d55268..f0a9a0089ec9 100644
--- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java
@@ -31,6 +31,7 @@ import android.hardware.light.ILights;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
+import android.hardware.lights.SystemLightsManager;
import android.os.Looper;
import androidx.test.filters.SmallTest;
@@ -93,7 +94,7 @@ public class LightsServiceTest {
@Test
public void testGetLights_filtersSystemLights() {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
// When lights are listed, only the 4 MICROPHONE lights should be visible.
assertThat(manager.getLights().size()).isEqualTo(4);
@@ -102,14 +103,14 @@ public class LightsServiceTest {
@Test
public void testControlMultipleLights() {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
// When the session requests to turn 3/4 lights on:
LightsManager.LightsSession session = manager.openSession();
session.requestLights(new Builder()
- .setLight(manager.getLights().get(0), new LightState(0xf1))
- .setLight(manager.getLights().get(1), new LightState(0xf2))
- .setLight(manager.getLights().get(2), new LightState(0xf3))
+ .addLight(manager.getLights().get(0), new LightState(0xf1))
+ .addLight(manager.getLights().get(1), new LightState(0xf2))
+ .addLight(manager.getLights().get(2), new LightState(0xf3))
.build());
// Then all 3 should turn on.
@@ -122,9 +123,9 @@ public class LightsServiceTest {
}
@Test
- public void testControlLights_onlyEffectiveForLifetimeOfClient() {
+ public void testControlLights_onlyEffectiveForLifetimeOfClient() throws Exception {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
// The light should begin by being off.
@@ -132,38 +133,41 @@ public class LightsServiceTest {
// When a session commits changes:
LightsManager.LightsSession session = manager.openSession();
- session.requestLights(new Builder().setLight(micLight, new LightState(GREEN)).build());
+ session.requestLights(new Builder().addLight(micLight, new LightState(GREEN)).build());
// Then the light should turn on.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN);
// When the session goes away:
session.close();
+
// Then the light should turn off.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT);
}
@Test
- public void testControlLights_firstCallerWinsContention() {
+ public void testControlLights_firstCallerWinsContention() throws Exception {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
LightsManager.LightsSession session1 = manager.openSession();
LightsManager.LightsSession session2 = manager.openSession();
// When session1 and session2 both request the same light:
- session1.requestLights(new Builder().setLight(micLight, new LightState(BLUE)).build());
- session2.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build());
+ session1.requestLights(new Builder().addLight(micLight, new LightState(BLUE)).build());
+ session2.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build());
// Then session1 should win because it was created first.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE);
// When session1 goes away:
session1.close();
+
// Then session2 should have its request go into effect.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE);
// When session2 goes away:
session2.close();
+
// Then the light should turn off because there are no more sessions.
assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0);
}
@@ -171,12 +175,12 @@ public class LightsServiceTest {
@Test
public void testClearLight() {
LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper());
- LightsManager manager = new LightsManager(mContext, service.mManagerService);
+ LightsManager manager = new SystemLightsManager(mContext, service.mManagerService);
Light micLight = manager.getLights().get(0);
// When the session turns a light on:
LightsManager.LightsSession session = manager.openSession();
- session.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build());
+ session.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build());
// And then the session clears it again:
session.requestLights(new Builder().clearLight(micLight).build());