Merge "Fix CommunalManagerUpdaterTest."
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c4aec0e..ddaa96d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -997,8 +997,12 @@
field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
+ field public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION = "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
+ field public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+ field public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE = "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
field public static final String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
+ field public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_ICON_URI";
field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
@@ -1021,6 +1025,10 @@
field public static final String REQUIRED_APP_MANAGED_DEVICE = "android.app.REQUIRED_APP_MANAGED_DEVICE";
field public static final String REQUIRED_APP_MANAGED_PROFILE = "android.app.REQUIRED_APP_MANAGED_PROFILE";
field public static final String REQUIRED_APP_MANAGED_USER = "android.app.REQUIRED_APP_MANAGED_USER";
+ field public static final int RESULT_DEVICE_OWNER_SET = 123; // 0x7b
+ field public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR = 1; // 0x1
+ field public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2; // 0x2
+ field public static final int RESULT_WORK_PROFILE_CREATED = 122; // 0x7a
field public static final int STATE_USER_PROFILE_COMPLETE = 4; // 0x4
field public static final int STATE_USER_PROFILE_FINALIZED = 5; // 0x5
field public static final int STATE_USER_SETUP_COMPLETE = 2; // 0x2
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cf95ffe..ee74bb8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -470,6 +470,105 @@
= "android.app.action.PROVISION_FINALIZATION";
/**
+ * Activity action: starts the managed profile provisioning flow inside the device management
+ * role holder.
+ *
+ * <p>During the managed profile provisioning flow, the platform-provided provisioning handler
+ * will delegate provisioning to the device management role holder, by firing this intent.
+ * Third-party mobile device management applications attempting to fire this intent will
+ * receive a {@link SecurityException}.
+ *
+ * <p>Device management role holders are required to have a handler for this intent action.
+ *
+ * <p>A result code of {@link Activity#RESULT_OK} implies that managed profile provisioning
+ * finished successfully. If it did not, a result code of {@link Activity#RESULT_CANCELED}
+ * is used instead.
+ *
+ * @see #ACTION_PROVISION_MANAGED_PROFILE
+ *
+ * @hide
+ */
+ // TODO(b/208628038): Uncomment when the permission is in place
+ // @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE =
+ "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE";
+
+ /**
+ * Result code that can be returned by the {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} or {@link
+ * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent handlers if a work
+ * profile has been created.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_WORK_PROFILE_CREATED = 122;
+
+ /**
+ * Result code that can be returned by the {@link
+ * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} or {@link
+ * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent handlers if the
+ * device owner was set.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_DEVICE_OWNER_SET = 123;
+
+ /**
+ * Activity action: starts the trusted source provisioning flow inside the device management
+ * role holder.
+ *
+ * <p>During the trusted source provisioning flow, the platform-provided provisioning handler
+ * will delegate provisioning to the device management role holder, by firing this intent.
+ * Third-party mobile device management applications attempting to fire this intent will
+ * receive a {@link SecurityException}.
+ *
+ * <p>Device management role holders are required to have a handler for this intent action.
+ *
+ * <p>The result codes can be either {@link #RESULT_WORK_PROFILE_CREATED}, {@link
+ * #RESULT_DEVICE_OWNER_SET} or {@link Activity#RESULT_CANCELED} if provisioning failed.
+ *
+ * @see #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE
+ *
+ * @hide
+ */
+ // TODO(b/208628038): Uncomment when the permission is in place
+ // @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE =
+ "android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
+
+ /**
+ * Activity action: starts the provisioning finalization flow inside the device management
+ * role holder.
+ *
+ * <p>During the provisioning finalization flow, the platform-provided provisioning handler
+ * will delegate provisioning to the device management role holder, by firing this intent.
+ * Third-party mobile device management applications attempting to fire this intent will
+ * receive a {@link SecurityException}.
+ *
+ * <p>Device management role holders are required to have a handler for this intent action.
+ *
+ * <p>This handler forwards the result from the admin app's {@link
+ * #ACTION_ADMIN_POLICY_COMPLIANCE} handler. Result code {@link Activity#RESULT_CANCELED}
+ * implies the provisioning finalization flow has failed.
+ *
+ * @see #ACTION_PROVISION_FINALIZATION
+ *
+ * @hide
+ */
+ // TODO(b/208628038): Uncomment when the permission is in place
+ // @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_ROLE_HOLDER_PROVISION_FINALIZATION =
+ "android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION";
+
+ /**
* Action: Bugreport sharing with device owner has been accepted by the user.
*
* @hide
@@ -2821,6 +2920,43 @@
"android.app.action.ADMIN_POLICY_COMPLIANCE";
/**
+ * Activity action: Starts the device management role holder updater.
+ *
+ * <p>The activity must handle the device management role holder update and set the intent
+ * result to either {@link Activity#RESULT_OK} if the update was successful, {@link
+ * #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR} if it encounters a problem
+ * that may be solved by relaunching it again, or {@link
+ * #RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if it encounters a problem
+ * that will not be solved by relaunching it again.
+ *
+ * @hide
+ */
+ // TODO(b/208628038): Uncomment when the permission is in place
+ // @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_ROLE_HOLDER)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER =
+ "android.app.action.UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER";
+
+ /**
+ * Result code that can be returned by the {@link #ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER}
+ * handler if it encounters a problem that may be solved by relaunching it again.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR = 1;
+
+ /**
+ * Result code that can be returned by the {@link #ACTION_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER}
+ * handler if it encounters a problem that will not be solved by relaunching it again.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2;
+
+ /**
* Maximum supported password length. Kind-of arbitrary.
* @hide
*/
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index ceab02f..aac23d8 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -103,6 +103,14 @@
String SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK = "enable_telephony_fallback";
/**
+ * A shell command that dumps a {@link
+ * com.android.server.timezonedetector.MetricsTimeZoneDetectorState} object to stdout for
+ * debugging.
+ * @hide
+ */
+ String SHELL_COMMAND_DUMP_METRICS = "dump_metrics";
+
+ /**
* A shared utility method to create a {@link ManualTimeZoneSuggestion}.
*
* @hide
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 0cf9f9f..4f2dba75 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -436,6 +436,8 @@
return "OPP";
case HEARING_AID:
return "HEARING_AID";
+ case LE_AUDIO:
+ return "LE_AUDIO";
default:
return "UNKNOWN_PROFILE";
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e267db4..7bb290d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11427,22 +11427,38 @@
"night_display_forced_auto_mode_available";
/**
- * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment
- * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been
- * exceeded.
- * @hide
- */
+ * If UTC time between two NITZ signals is greater than this value then the second signal
+ * cannot be ignored.
+ *
+ * <p>This value is in milliseconds. It is used for telephony-based time and time zone
+ * detection.
+ * @hide
+ */
@Readable
public static final String NITZ_UPDATE_DIFF = "nitz_update_diff";
/**
- * The length of time in milli-seconds that automatic small adjustments to
- * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
- * @hide
- */
+ * If the elapsed realtime between two NITZ signals is greater than this value then the
+ * second signal cannot be ignored.
+ *
+ * <p>This value is in milliseconds. It is used for telephony-based time and time zone
+ * detection.
+ * @hide
+ */
@Readable
public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing";
+ /**
+ * If the device connects to a telephony network and was disconnected from a telephony
+ * network for less than this time, a previously received NITZ signal can be restored.
+ *
+ * <p>This value is in milliseconds. It is used for telephony-based time and time zone
+ * detection.
+ * @hide
+ */
+ public static final String NITZ_NETWORK_DISCONNECT_RETENTION =
+ "nitz_network_disconnect_retention";
+
/** Preferred NTP server. {@hide} */
@Readable
public static final String NTP_SERVER = "ntp_server";
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a45a91e..414a7f1 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3681,14 +3681,14 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidateTopGlow();
+ invalidateEdgeEffects();
} else if (incrementalDeltaY < 0) {
mEdgeGlowBottom.onPullDistance((float) overscroll / getHeight(),
1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
}
}
@@ -3728,7 +3728,7 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidateTopGlow();
+ invalidateEdgeEffects();
} else if (rawDeltaY < 0) {
mEdgeGlowBottom.onPullDistance(
(float) -overScrollDistance / getHeight(),
@@ -3736,7 +3736,7 @@
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
}
}
@@ -3782,17 +3782,21 @@
// First allow releasing existing overscroll effect:
float consumed = 0;
if (mEdgeGlowTop.getDistance() != 0) {
- consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(),
- (float) x / getWidth());
- if (consumed != 0f) {
- invalidateTopGlow();
+ if (canScrollUp()) {
+ mEdgeGlowTop.onRelease();
+ } else {
+ consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(),
+ (float) x / getWidth());
}
+ invalidateEdgeEffects();
} else if (mEdgeGlowBottom.getDistance() != 0) {
- consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(),
- 1f - (float) x / getWidth());
- if (consumed != 0f) {
- invalidateBottomGlow();
+ if (canScrollDown()) {
+ mEdgeGlowBottom.onRelease();
+ } else {
+ consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(),
+ 1f - (float) x / getWidth());
}
+ invalidateEdgeEffects();
}
int pixelsConsumed = Math.round(consumed * getHeight());
return deltaY - pixelsConsumed;
@@ -3802,30 +3806,16 @@
* @return <code>true</code> if either the top or bottom edge glow is currently active or
* <code>false</code> if it has no value to release.
*/
- private boolean isGlowActive() {
- return mEdgeGlowBottom.getDistance() != 0 || mEdgeGlowTop.getDistance() != 0;
+ private boolean doesTouchStopStretch() {
+ return (mEdgeGlowBottom.getDistance() != 0 && !canScrollDown())
+ || (mEdgeGlowTop.getDistance() != 0 && !canScrollUp());
}
- private void invalidateTopGlow() {
+ private void invalidateEdgeEffects() {
if (!shouldDisplayEdgeEffects()) {
return;
}
- final boolean clipToPadding = getClipToPadding();
- final int top = clipToPadding ? mPaddingTop : 0;
- final int left = clipToPadding ? mPaddingLeft : 0;
- final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
- invalidate(left, top, right, top + mEdgeGlowTop.getMaxHeight());
- }
-
- private void invalidateBottomGlow() {
- if (!shouldDisplayEdgeEffects()) {
- return;
- }
- final boolean clipToPadding = getClipToPadding();
- final int bottom = clipToPadding ? getHeight() - mPaddingBottom : getHeight();
- final int left = clipToPadding ? mPaddingLeft : 0;
- final int right = clipToPadding ? getWidth() - mPaddingRight : getWidth();
- invalidate(left, bottom - mEdgeGlowBottom.getMaxHeight(), right, bottom);
+ invalidate();
}
@Override
@@ -4468,7 +4458,7 @@
final int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess) + translateY;
canvas.translate(translateX, edgeY);
if (mEdgeGlowTop.draw(canvas)) {
- invalidateTopGlow();
+ invalidateEdgeEffects();
}
canvas.restoreToCount(restoreCount);
}
@@ -4482,7 +4472,7 @@
canvas.translate(edgeX, edgeY);
canvas.rotate(180, width, 0);
if (mEdgeGlowBottom.draw(canvas)) {
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
canvas.restoreToCount(restoreCount);
}
@@ -4572,7 +4562,7 @@
mActivePointerId = ev.getPointerId(0);
int motionPosition = findMotionRow(y);
- if (isGlowActive()) {
+ if (doesTouchStopStretch()) {
// Pressed during edge effect, so this is considered the same as a fling catch.
touchMode = mTouchMode = TOUCH_MODE_FLING;
} else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
@@ -6579,7 +6569,7 @@
*/
public void setBottomEdgeEffectColor(@ColorInt int color) {
mEdgeGlowBottom.setColor(color);
- invalidateBottomGlow();
+ invalidateEdgeEffects();
}
/**
@@ -6593,7 +6583,7 @@
*/
public void setTopEdgeEffectColor(@ColorInt int color) {
mEdgeGlowTop.setColor(color);
- invalidateTopGlow();
+ invalidateEdgeEffects();
}
/**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index a8bf50e..62585c1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1848,7 +1848,7 @@
if (mHasPendingRestartInputForSetText) {
final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
- imm.invalidateInput(mTextView);
+ imm.restartInput(mTextView);
}
mHasPendingRestartInputForSetText = false;
}
diff --git a/core/proto/android/app/time_zone_detector.proto b/core/proto/android/app/time_zone_detector.proto
index b33ca1d..b52aa82 100644
--- a/core/proto/android/app/time_zone_detector.proto
+++ b/core/proto/android/app/time_zone_detector.proto
@@ -32,16 +32,8 @@
}
/*
- * An obfuscated and simplified time zone suggestion for metrics use.
- *
- * The suggestion's time zone IDs (which relate to location) are obfuscated by
- * mapping them to an ordinal. When the ordinal is assigned consistently across
- * several objects (i.e. so the same time zone ID is always mapped to the same
- * ordinal), this allows comparisons between those objects. For example, we can
- * answer "did these two suggestions agree?", "does the suggestion match the
- * device's current time zone?", without leaking knowledge of location. Ordinals
- * are also significantly more compact than full IANA TZDB IDs, albeit highly
- * unstable and of limited use.
+ * A generic-form time zone suggestion for metrics use. Required to be a superset of the
+ * MetricsTimeZoneSuggestion proto defined in atoms.proto to ensure binary compatibility.
*/
message MetricsTimeZoneSuggestion {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -55,5 +47,24 @@
// The ordinals for time zone(s) in the suggestion. Always empty for
// UNCERTAIN, and can be empty for CERTAIN, for example when the device is in
// a disputed area / on an ocean.
- repeated uint32 time_zone_ordinals = 2;
+ //
+ // The suggestion's time zone IDs (which relate to location) are obfuscated by
+ // mapping them to an ordinal. When the ordinal is assigned consistently across
+ // several objects (i.e. so the same time zone ID is always mapped to the same
+ // ordinal), this allows comparisons between those objects. For example, we can
+ // answer "did these two suggestions agree?", "does the suggestion match the
+ // device's current time zone?", without leaking knowledge of location. Ordinals
+ // are also significantly more compact than full IANA TZDB IDs, albeit unstable
+ // and of limited use.
+ repeated int32 time_zone_ordinals = 2;
+
+ // The actual time zone ID(s) in the suggestion. Similar to time_zone_ordinals
+ // but contains the actual string IDs.
+ //
+ // This information is only captured / reported for some devices based on the
+ // value of a server side flag, i.e. it could be enabled for internal testers.
+ // Therefore the list can be empty even when time_zone_ordinals is populated.
+ //
+ // When enabled, see time_zone_ordinals for the expected number of values.
+ repeated string time_zone_ids = 3;
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index db5d49d..b406578 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -672,18 +672,31 @@
optional SettingProto new_contact_aggregator = 79 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto night_display_forced_auto_mode_available = 80 [ (android.privacy).dest = DEST_AUTOMATIC ];
- message NitzUpdate {
+ message Nitz {
option (android.msg_privacy).dest = DEST_EXPLICIT;
- // If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment to
- // SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been
- // exceeded.
- optional SettingProto diff = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
- // The length of time in milli-seconds that automatic small adjustments to
- // SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded.
- optional SettingProto spacing = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ // If UTC time between two NITZ signals is greater than this value then the second signal
+ // cannot be ignored.
+ //
+ // This value is in milliseconds. It is used for telephony-based time and time zone
+ // detection.
+ optional SettingProto update_diff = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ // If the elapsed realtime between two NITZ signals is greater than this value then the
+ // second signal cannot be ignored.
+ //
+ // This value is in milliseconds. It is used for telephony-based time and time zone
+ // detection.
+ optional SettingProto update_spacing = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ // If the device connects to a telephony network and was disconnected from a telephony
+ // network for less than this time, a previously received NITZ signal can be restored.
+ //
+ // This value is in milliseconds. It is used for telephony-based time and time zone
+ // detection.
+ optional SettingProto network_disconnect_retention = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
- optional NitzUpdate nitz_update = 81;
+ optional Nitz nitz = 81;
message Notification {
option (android.msg_privacy).dest = DEST_EXPLICIT;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 0c3a6b2..5161092 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -95,9 +95,8 @@
// Update bitmap
val fg = InsetDrawable(overflowBtn?.drawable, overflowIconInset)
- bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(
- ColorDrawable(colorAccent), fg),
- null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+ bitmap = iconFactory.createBadgedIconBitmap(
+ AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon
// Update dot path
dotPath = PathParser.createPathFromPathData(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 932f879..91aff3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -189,9 +189,7 @@
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
- info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */).icon;
+ info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
// Dot color & placement
Path iconPath = PathParser.createPathFromPathData(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index a163f37..413627d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -390,8 +390,7 @@
final ShapeIconFactory factory = new ShapeIconFactory(
SplashscreenContentDrawer.this.mContext,
scaledIconDpi, mFinalIconSize);
- final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(
- iconDrawable, true /* shrinkNonAdaptiveIcons */);
+ final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(iconDrawable);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
createIconDrawable(new BitmapDrawable(bitmap), true);
}
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java b/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java
index eac767f..02475dd 100644
--- a/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java
+++ b/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java
@@ -26,12 +26,10 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.LocalLog;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import com.android.server.power.ShutdownThread;
-import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -40,19 +38,19 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.LinkedList;
+import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.LinkedList;
-import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Generic connector class for interfacing with a native daemon which uses the
* {@code libsysutils} FrameworkListener protocol.
*/
-final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
+final class NativeDaemonConnector implements Runnable, Handler.Callback {
private final static boolean VDBG = false;
private final String TAG;
@@ -107,7 +105,7 @@
/**
* Enable Set debugging mode, which causes messages to also be written to both
- * {@link Slog} in addition to internal log.
+ * {@link Log} in addition to internal log.
*/
public void setDebug(boolean debug) {
mDebug = debug;
@@ -126,7 +124,9 @@
* calls while holding a lock on the given object.
*/
public void setWarnIfHeld(Object warnIfHeld) {
- Preconditions.checkState(mWarnIfHeld == null);
+ if (mWarnIfHeld != null) {
+ throw new IllegalStateException("warnIfHeld is already set.");
+ }
mWarnIfHeld = Objects.requireNonNull(warnIfHeld);
}
@@ -183,7 +183,7 @@
// In order to ensure that unprivileged apps aren't able to impersonate native daemons on
// production devices, even if said native daemons ill-advisedly pick a socket name that
// starts with __test__, only allow this on debug builds.
- if (mSocket.startsWith("__test__") && Build.IS_DEBUGGABLE) {
+ if (mSocket.startsWith("__test__") && Build.isDebuggable()) {
return new LocalSocketAddress(mSocket);
} else {
return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED);
@@ -375,7 +375,7 @@
try {
latch.await();
} catch (InterruptedException e) {
- Slog.wtf(TAG, "Interrupted while waiting for unsolicited response handling", e);
+ Log.wtf(TAG, "Interrupted while waiting for unsolicited response handling", e);
}
}
@@ -462,13 +462,13 @@
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
- Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ Log.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
}
final long startTime = SystemClock.elapsedRealtime();
- final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
+ final ArrayList<NativeDaemonEvent> events = new ArrayList<>();
final StringBuilder rawBuilder = new StringBuilder();
final StringBuilder logBuilder = new StringBuilder();
@@ -571,7 +571,7 @@
*/
public static class Command {
private String mCmd;
- private ArrayList<Object> mArguments = Lists.newArrayList();
+ private ArrayList<Object> mArguments = new ArrayList<>();
public Command(String cmd, Object... args) {
mCmd = cmd;
@@ -586,11 +586,6 @@
}
}
- /** {@inheritDoc} */
- public void monitor() {
- synchronized (mDaemonLock) { }
- }
-
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mLocalLog.dump(fd, pw, args);
pw.println();
@@ -598,12 +593,12 @@
}
private void log(String logstring) {
- if (mDebug) Slog.d(TAG, logstring);
+ if (mDebug) Log.d(TAG, logstring);
mLocalLog.log(logstring);
}
private void loge(String logstring) {
- Slog.e(TAG, logstring);
+ Log.e(TAG, logstring);
mLocalLog.log(logstring);
}
@@ -659,12 +654,12 @@
if (found == null) {
// didn't find it - make sure our queue isn't too big before adding
while (mPendingCmds.size() >= mMaxCount) {
- Slog.e("NativeDaemonConnector.ResponseQueue",
+ Log.e("NativeDaemonConnector.ResponseQueue",
"more buffered than allowed: " + mPendingCmds.size() +
" >= " + mMaxCount);
// let any waiter timeout waiting for this
PendingCmd pendingCmd = mPendingCmds.remove();
- Slog.e("NativeDaemonConnector.ResponseQueue",
+ Log.e("NativeDaemonConnector.ResponseQueue",
"Removing request: " + pendingCmd.logCmd + " (" +
pendingCmd.cmdNum + ")");
}
@@ -706,7 +701,7 @@
result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {}
if (result == null) {
- Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response");
+ Log.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response");
}
return result;
}
diff --git a/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java b/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java
index e6feda3..5683694 100644
--- a/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java
+++ b/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java
@@ -16,8 +16,7 @@
package com.android.server;
-import android.util.Slog;
-import com.google.android.collect.Lists;
+import android.util.Log;
import java.io.FileDescriptor;
import java.util.ArrayList;
@@ -179,7 +178,7 @@
* {@link #getMessage()} for any events matching the requested code.
*/
public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) {
- final ArrayList<String> result = Lists.newArrayList();
+ final ArrayList<String> result = new ArrayList<>();
for (NativeDaemonEvent event : events) {
if (event.getCode() == matchCode) {
result.add(event.getMessage());
@@ -212,7 +211,7 @@
int wordEnd = -1;
boolean quoted = false;
- if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'");
+ if (DEBUG_ROUTINE) Log.e(LOGTAG, "parsing '" + rawEvent + "'");
if (rawEvent.charAt(current) == '\"') {
quoted = true;
current++;
@@ -240,14 +239,14 @@
word = word.replace("\\\\", "\\");
word = word.replace("\\\"", "\"");
- if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'");
+ if (DEBUG_ROUTINE) Log.e(LOGTAG, "found '" + word + "'");
parsed.add(word);
// find the beginning of the next word - either of these options
int nextSpace = rawEvent.indexOf(' ', current);
int nextQuote = rawEvent.indexOf(" \"", current);
if (DEBUG_ROUTINE) {
- Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
+ Log.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote);
}
if (nextQuote > -1 && nextQuote <= nextSpace) {
quoted = true;
@@ -259,8 +258,8 @@
}
} // else we just start the next word after the current and read til the end
if (DEBUG_ROUTINE) {
- Slog.e(LOGTAG, "next loop - current=" + current +
- ", length=" + length + ", quoted=" + quoted);
+ Log.e(LOGTAG, "next loop - current=" + current
+ + ", length=" + length + ", quoted=" + quoted);
}
}
return parsed.toArray(new String[parsed.size()]);
diff --git a/packages/Nsd/service/src/com/android/server/NsdService.java b/packages/Nsd/service/src/com/android/server/NsdService.java
index 3e02084..76ecea7 100644
--- a/packages/Nsd/service/src/com/android/server/NsdService.java
+++ b/packages/Nsd/service/src/com/android/server/NsdService.java
@@ -19,6 +19,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.Uri;
import android.net.nsd.INsdManager;
@@ -36,12 +37,10 @@
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.net.module.util.DnsSdTxtRecord;
@@ -228,7 +227,7 @@
break;
case NsdManager.NATIVE_DAEMON_EVENT:
default:
- Slog.e(TAG, "Unhandled " + msg);
+ Log.e(TAG, "Unhandled " + msg);
return NOT_HANDLED;
}
return HANDLED;
@@ -274,7 +273,7 @@
private boolean requestLimitReached(ClientInfo clientInfo) {
if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) {
- if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo);
+ if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo);
return true;
}
return false;
@@ -307,7 +306,7 @@
transitionTo(mDisabledState);
break;
case NsdManager.DISCOVER_SERVICES:
- if (DBG) Slog.d(TAG, "Discover services");
+ if (DBG) Log.d(TAG, "Discover services");
args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
@@ -321,8 +320,8 @@
id = getUniqueId();
if (discoverServices(id, args.serviceInfo.getServiceType())) {
if (DBG) {
- Slog.d(TAG, "Discover " + msg.arg2 + " " + id +
- args.serviceInfo.getServiceType());
+ Log.d(TAG, "Discover " + msg.arg2 + " " + id
+ + args.serviceInfo.getServiceType());
}
storeRequestMap(clientId, id, clientInfo, msg.what);
clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo);
@@ -333,7 +332,7 @@
}
break;
case NsdManager.STOP_DISCOVERY:
- if (DBG) Slog.d(TAG, "Stop service discovery");
+ if (DBG) Log.d(TAG, "Stop service discovery");
args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
@@ -353,7 +352,7 @@
}
break;
case NsdManager.REGISTER_SERVICE:
- if (DBG) Slog.d(TAG, "Register service");
+ if (DBG) Log.d(TAG, "Register service");
args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
if (requestLimitReached(clientInfo)) {
@@ -365,7 +364,7 @@
maybeStartDaemon();
id = getUniqueId();
if (registerService(id, args.serviceInfo)) {
- if (DBG) Slog.d(TAG, "Register " + clientId + " " + id);
+ if (DBG) Log.d(TAG, "Register " + clientId + " " + id);
storeRequestMap(clientId, id, clientInfo, msg.what);
// Return success after mDns reports success
} else {
@@ -375,11 +374,11 @@
}
break;
case NsdManager.UNREGISTER_SERVICE:
- if (DBG) Slog.d(TAG, "unregister service");
+ if (DBG) Log.d(TAG, "unregister service");
args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
if (clientInfo == null) {
- Slog.e(TAG, "Unknown connector in unregistration");
+ Log.e(TAG, "Unknown connector in unregistration");
break;
}
id = clientInfo.mClientIds.get(clientId);
@@ -392,7 +391,7 @@
}
break;
case NsdManager.RESOLVE_SERVICE:
- if (DBG) Slog.d(TAG, "Resolve service");
+ if (DBG) Log.d(TAG, "Resolve service");
args = (ListenerArgs) msg.obj;
clientInfo = mClients.get(args.connector);
@@ -430,7 +429,7 @@
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
if (clientInfo == null) {
String name = NativeResponseCode.nameOf(code);
- Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
+ Log.e(TAG, String.format("id %d for %s has no client mapping", id, name));
return false;
}
@@ -441,14 +440,14 @@
// SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
// and we may get in this situation.
String name = NativeResponseCode.nameOf(code);
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"Notification %s for listener id %d that is no longer active",
name, id));
return false;
}
if (DBG) {
String name = NativeResponseCode.nameOf(code);
- Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
+ Log.d(TAG, String.format("Native daemon message %s: %s", name, raw));
}
switch (code) {
case NativeResponseCode.SERVICE_FOUND:
@@ -492,7 +491,7 @@
++index;
}
if (index >= cooked[2].length()) {
- Slog.e(TAG, "Invalid service found " + raw);
+ Log.e(TAG, "Invalid service found " + raw);
break;
}
String name = cooked[2].substring(0, index);
@@ -562,13 +561,13 @@
char c = s.charAt(i);
if (c == '\\') {
if (++i >= s.length()) {
- Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
+ Log.e(TAG, "Unexpected end of escape sequence in: " + s);
break;
}
c = s.charAt(i);
if (c != '.' && c != '\\') {
if (i + 2 >= s.length()) {
- Slog.e(TAG, "Unexpected end of escape sequence in: " + s);
+ Log.e(TAG, "Unexpected end of escape sequence in: " + s);
break;
}
c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0'));
@@ -685,7 +684,7 @@
private boolean isNsdEnabled() {
boolean ret = mNsdSettings.isEnabled();
if (DBG) {
- Slog.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled"));
+ Log.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled"));
}
return ret;
}
@@ -795,12 +794,12 @@
*/
public boolean execute(Object... args) {
if (DBG) {
- Slog.d(TAG, "mdnssd " + Arrays.toString(args));
+ Log.d(TAG, "mdnssd " + Arrays.toString(args));
}
try {
mNativeConnector.execute("mdnssd", args);
} catch (NativeDaemonConnectorException e) {
- Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
+ Log.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
return false;
}
return true;
@@ -831,7 +830,7 @@
private boolean registerService(int regId, NsdServiceInfo service) {
if (DBG) {
- Slog.d(TAG, "registerService: " + regId + " " + service);
+ Log.d(TAG, "registerService: " + regId + " " + service);
}
String name = service.getServiceName();
String type = service.getServiceType();
@@ -880,7 +879,12 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump " + TAG
+ + " due to missing android.permission.DUMP permission");
+ return;
+ }
for (ClientInfo client : mClients.values()) {
pw.println("Client Info");
@@ -909,7 +913,7 @@
private ClientInfo(INsdManagerCallback cb) {
mCb = cb;
- if (DBG) Slog.d(TAG, "New client");
+ if (DBG) Log.d(TAG, "New client");
}
@Override
@@ -943,8 +947,10 @@
clientId = mClientIds.keyAt(i);
globalId = mClientIds.valueAt(i);
mIdToClientInfoMap.remove(globalId);
- if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId +
- " global-ID " + globalId + " type " + mClientRequests.get(clientId));
+ if (DBG) {
+ Log.d(TAG, "Terminating client-ID " + clientId
+ + " global-ID " + globalId + " type " + mClientRequests.get(clientId));
+ }
switch (mClientRequests.get(clientId)) {
case NsdManager.DISCOVER_SERVICES:
stopServiceDiscovery(globalId);
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f637ff8..d73e45e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -19,7 +19,6 @@
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -44,6 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.UserIcons;
+import com.android.launcher3.icons.BaseIconFactory.IconOptions;
import com.android.launcher3.icons.IconFactory;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.fuelgauge.BatteryStatus;
@@ -525,9 +525,9 @@
/** Get the corresponding adaptive icon drawable. */
public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) {
try (IconFactory iconFactory = IconFactory.obtain(context)) {
- final Bitmap iconBmp = iconFactory.createBadgedIconBitmap(icon, user,
- true /* shrinkNonAdaptiveIcons */).icon;
- return new BitmapDrawable(context.getResources(), iconBmp);
+ return iconFactory
+ .createBadgedIconBitmap(icon, new IconOptions().setUser(user))
+ .newIcon(context);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
index 1d8f71e..78ec58b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompat.java
@@ -18,11 +18,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.icu.text.ListFormatter;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
@@ -155,9 +157,41 @@
return set;
}
- public static void saveInputMethodSubtypeList(PreferenceFragmentCompat context,
+ /**
+ * Save the enabled/disabled input methods and selected subtype states into system settings.
+ *
+ * @param fragment The preference fragment user interact with.
+ * @param resolver The {@link ContentResolver} used to access the database.
+ * @param inputMethodInfos The list of {@link InputMethodInfo} to be checked.
+ * @param hasHardKeyboard {@code true} if the device has the hardware keyboard.
+ */
+ public static void saveInputMethodSubtypeList(PreferenceFragmentCompat fragment,
ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
boolean hasHardKeyboard) {
+ saveInputMethodSubtypeListForUserInternal(
+ fragment, resolver, inputMethodInfos, hasHardKeyboard, UserHandle.myUserId());
+ }
+
+ /**
+ * Save the enabled/disabled input methods and selected subtype states into system settings as
+ * given userId.
+ *
+ * @param fragment The preference fragment user interact with.
+ * @param resolver The {@link ContentResolver} used to access the database.
+ * @param inputMethodInfos The list of {@link InputMethodInfo} to be checked.
+ * @param hasHardKeyboard {@code true} if the device has the hardware keyboard.
+ * @param userId The given userId
+ */
+ public static void saveInputMethodSubtypeListForUser(PreferenceFragmentCompat fragment,
+ ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
+ boolean hasHardKeyboard, @UserIdInt int userId) {
+ saveInputMethodSubtypeListForUserInternal(
+ fragment, resolver, inputMethodInfos, hasHardKeyboard, userId);
+ }
+
+ private static void saveInputMethodSubtypeListForUserInternal(PreferenceFragmentCompat fragment,
+ ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
+ boolean hasHardKeyboard, @UserIdInt int userId) {
String currentInputMethodId = Settings.Secure.getString(resolver,
Settings.Secure.DEFAULT_INPUT_METHOD);
final int selectedInputMethodSubtype = getInputMethodSubtypeSelected(resolver);
@@ -168,7 +202,7 @@
boolean needsToResetSelectedSubtype = false;
for (final InputMethodInfo imi : inputMethodInfos) {
final String imiId = imi.getId();
- final Preference pref = context.findPreference(imiId);
+ final Preference pref = fragment.findPreference(imiId);
if (pref == null) {
continue;
}
@@ -184,8 +218,11 @@
}
final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
final boolean systemIme = imi.isSystem();
+ // Create context as given userId
+ final Context wrapperContext = userId == UserHandle.myUserId() ? fragment.getActivity()
+ : fragment.getActivity().createContextAsUser(UserHandle.of(userId), 0);
if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
- context.getActivity()).isAlwaysCheckedIme(imi))
+ wrapperContext).isAlwaysCheckedIme(imi))
|| isImeChecked) {
if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
// imiId has just been enabled
@@ -198,7 +235,7 @@
for (int i = 0; i < subtypeCount; ++i) {
final InputMethodSubtype subtype = imi.getSubtypeAt(i);
final String subtypeHashCodeStr = String.valueOf(subtype.hashCode());
- final TwoStatePreference subtypePref = (TwoStatePreference) context
+ final TwoStatePreference subtypePref = (TwoStatePreference) fragment
.findPreference(imiId + subtypeHashCodeStr);
// In the Configure input method screen which does not have subtype preferences.
if (subtypePref == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 94a0c00..c1ab706 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -18,6 +18,7 @@
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import android.annotation.UserIdInt;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -75,30 +76,34 @@
private final OnSavePreferenceListener mOnSaveListener;
private final InputMethodSettingValuesWrapper mInputMethodSettingValues;
private final boolean mIsAllowedByOrganization;
+ @UserIdInt
+ private final int mUserId;
private AlertDialog mDialog = null;
/**
* A preference entry of an input method.
*
- * @param context The Context this is associated with.
+ * @param prefContext The Context this preference is associated with.
* @param imi The {@link InputMethodInfo} of this preference.
* @param isAllowedByOrganization false if the IME has been disabled by a device or profile
* owner.
* @param onSaveListener The listener called when this preference has been changed and needs
* to save the state to shared preference.
+ * @param userId The userId to specify the corresponding user for this preference.
*/
- public InputMethodPreference(final Context context, final InputMethodInfo imi,
- final boolean isAllowedByOrganization, final OnSavePreferenceListener onSaveListener) {
- this(context, imi, imi.loadLabel(context.getPackageManager()), isAllowedByOrganization,
- onSaveListener);
+ public InputMethodPreference(final Context prefContext, final InputMethodInfo imi,
+ final boolean isAllowedByOrganization, final OnSavePreferenceListener onSaveListener,
+ final @UserIdInt int userId) {
+ this(prefContext, imi, imi.loadLabel(prefContext.getPackageManager()),
+ isAllowedByOrganization, onSaveListener, userId);
}
@VisibleForTesting
- InputMethodPreference(final Context context, final InputMethodInfo imi,
+ InputMethodPreference(final Context prefContext, final InputMethodInfo imi,
final CharSequence title, final boolean isAllowedByOrganization,
- final OnSavePreferenceListener onSaveListener) {
- super(context);
+ final OnSavePreferenceListener onSaveListener, final @UserIdInt int userId) {
+ super(prefContext);
setPersistent(false);
mImi = imi;
mIsAllowedByOrganization = isAllowedByOrganization;
@@ -114,7 +119,12 @@
intent.setClassName(imi.getPackageName(), settingsActivity);
setIntent(intent);
}
- mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context);
+ // Handle the context by given userId because {@link InputMethodSettingValuesWrapper} is
+ // per-user instance.
+ final Context userAwareContext = userId == UserHandle.myUserId() ? prefContext :
+ getContext().createContextAsUser(UserHandle.of(userId), 0);
+ mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(userAwareContext);
+ mUserId = userId;
mHasPriorityInSorting = imi.isSystem()
&& InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(imi);
setOnPreferenceClickListener(this);
@@ -130,17 +140,15 @@
super.onBindViewHolder(holder);
final Switch switchWidget = getSwitch();
if (switchWidget != null) {
+ // Avoid default behavior in {@link PrimarySwitchPreference#onBindViewHolder}.
switchWidget.setOnClickListener(v -> {
- // no-op, avoid default behavior in {@link PrimarySwitchPreference#onBindViewHolder}
- });
- switchWidget.setOnCheckedChangeListener((buttonView, isChecked) -> {
- // Avoid the invocation after we call {@link PrimarySwitchPreference#setChecked()}
- // in {@link setCheckedInternal}
- if (isChecked != isChecked()) {
- // Keep switch to previous state because we have to show the dialog first
- buttonView.setChecked(!isChecked);
- callChangeListener(isChecked());
+ if (!switchWidget.isEnabled()) {
+ return;
}
+ final boolean newValue = !isChecked();
+ // Keep switch to previous state because we have to show the dialog first.
+ switchWidget.setChecked(isChecked());
+ callChangeListener(newValue);
});
}
final ImageView icon = holder.itemView.findViewById(android.R.id.icon);
@@ -187,7 +195,7 @@
final Intent intent = getIntent();
if (intent != null) {
// Invoke a settings activity of an input method.
- context.startActivity(intent);
+ context.startActivityAsUser(intent, UserHandle.of(mUserId));
}
} catch (final ActivityNotFoundException e) {
Log.d(TAG, "IME's Settings Activity Not Found", e);
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
index 13c1b82..39e6dce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
@@ -16,13 +16,18 @@
package com.android.settingslib.inputmethod;
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
import android.annotation.UiThread;
import android.content.ContentResolver;
import android.content.Context;
import android.util.Log;
+import android.util.SparseArray;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -39,20 +44,39 @@
public class InputMethodSettingValuesWrapper {
private static final String TAG = InputMethodSettingValuesWrapper.class.getSimpleName();
- private static volatile InputMethodSettingValuesWrapper sInstance;
+ private static final Object sInstanceMapLock = new Object();
+ /**
+ * Manages mapping between user ID and corresponding singleton
+ * {@link InputMethodSettingValuesWrapper} object.
+ */
+ @GuardedBy("sInstanceMapLock")
+ private static SparseArray<InputMethodSettingValuesWrapper> sInstanceMap = new SparseArray<>();
private final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
private final ContentResolver mContentResolver;
private final InputMethodManager mImm;
- public static InputMethodSettingValuesWrapper getInstance(Context context) {
- if (sInstance == null) {
- synchronized (TAG) {
- if (sInstance == null) {
- sInstance = new InputMethodSettingValuesWrapper(context);
- }
+ @AnyThread
+ @NonNull
+ public static InputMethodSettingValuesWrapper getInstance(@NonNull Context context) {
+ final int requestUserId = context.getUserId();
+ InputMethodSettingValuesWrapper valuesWrapper;
+ // First time to create the wrapper.
+ synchronized (sInstanceMapLock) {
+ if (sInstanceMap.size() == 0) {
+ valuesWrapper = new InputMethodSettingValuesWrapper(context);
+ sInstanceMap.put(requestUserId, valuesWrapper);
+ return valuesWrapper;
}
+ // We have same user context as request.
+ if (sInstanceMap.indexOfKey(requestUserId) >= 0) {
+ return sInstanceMap.get(requestUserId);
+ }
+ // Request by a new user context.
+ valuesWrapper = new InputMethodSettingValuesWrapper(context);
+ sInstanceMap.put(context.getUserId(), valuesWrapper);
}
- return sInstance;
+
+ return valuesWrapper;
}
// Ensure singleton
@@ -64,7 +88,7 @@
public void refreshAllInputMethodAndSubtypes() {
mMethodList.clear();
- mMethodList.addAll(mImm.getInputMethodList());
+ mMethodList.addAll(mImm.getInputMethodListAsUser(mContentResolver.getUserId()));
}
public List<InputMethodInfo> getInputMethodList() {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
index 9962e1c..1e75014 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodPreferenceTest.java
@@ -20,6 +20,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.os.UserHandle;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -112,7 +113,8 @@
createInputMethodInfo(systemIme, name),
title,
true /* isAllowedByOrganization */,
- p -> {} /* onSavePreferenceListener */);
+ p -> {} /* onSavePreferenceListener */,
+ UserHandle.myUserId());
}
private static InputMethodInfo createInputMethodInfo(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a67b565..6072f68 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1067,14 +1067,17 @@
Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE,
GlobalSettingsProto.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE);
- final long nitzUpdateToken = p.start(GlobalSettingsProto.NITZ_UPDATE);
+ final long nitzToken = p.start(GlobalSettingsProto.NITZ);
dumpSetting(s, p,
Settings.Global.NITZ_UPDATE_DIFF,
- GlobalSettingsProto.NitzUpdate.DIFF);
+ GlobalSettingsProto.Nitz.UPDATE_DIFF);
dumpSetting(s, p,
Settings.Global.NITZ_UPDATE_SPACING,
- GlobalSettingsProto.NitzUpdate.SPACING);
- p.end(nitzUpdateToken);
+ GlobalSettingsProto.Nitz.UPDATE_SPACING);
+ dumpSetting(s, p,
+ Settings.Global.NITZ_NETWORK_DISCONNECT_RETENTION,
+ GlobalSettingsProto.Nitz.NETWORK_DISCONNECT_RETENTION);
+ p.end(nitzToken);
final long notificationToken = p.start(GlobalSettingsProto.NOTIFICATION);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c9c93c4..19ed021 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -384,6 +384,7 @@
Settings.Global.NETWORK_WATCHLIST_ENABLED,
Settings.Global.NEW_CONTACT_AGGREGATOR,
Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE,
+ Settings.Global.NITZ_NETWORK_DISCONNECT_RETENTION,
Settings.Global.NITZ_UPDATE_DIFF,
Settings.Global.NITZ_UPDATE_SPACING,
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4790412..43254aa 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2164,6 +2164,15 @@
<!-- Content description for media cotnrols progress bar [CHAR_LIMIT=NONE] -->
<string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1:30">%1$s</xliff:g> of <xliff:g id="total_time" example="3:00">%2$s</xliff:g></string>
+ <!-- Description for button in media controls. Pressing button starts playback [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_button_play">Play</string>
+ <!-- Description for button in media controls. Pressing button pauses playback [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_button_pause">Pause</string>
+ <!-- Description for button in media controls. Pressing button goes to previous track [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_button_prev">Previous track</string>
+ <!-- Description for button in media controls. Pressing button goes to next track [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_button_next">Next track</string>
+
<!-- Title for Smartspace recommendation card within media controls. The "Play" means the action to play a media [CHAR_LIMIT=10] -->
<string name="controls_media_smartspace_rec_title">Play</string>
<!-- Description for Smartspace recommendation card within media controls [CHAR_LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index 9574101..b611c96 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -16,28 +16,31 @@
package com.android.systemui.flags
+import android.annotation.BoolRes
+import android.annotation.IntegerRes
+import android.annotation.StringRes
import android.os.Parcel
import android.os.Parcelable
-interface Flag<T> : Parcelable {
+interface Flag<T> {
val id: Int
+}
+
+interface ParcelableFlag<T> : Flag<T>, Parcelable {
val default: T
- val resourceOverride: Int
-
override fun describeContents() = 0
+}
- fun hasResourceOverride(): Boolean {
- return resourceOverride != -1
- }
+interface ResourceFlag<T> : Flag<T> {
+ val resourceId: Int
}
// Consider using the "parcelize" kotlin library.
data class BooleanFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Boolean = false,
- override val resourceOverride: Int = -1
-) : Flag<Boolean> {
+ override val default: Boolean = false
+) : ParcelableFlag<Boolean> {
companion object {
@JvmField
@@ -58,11 +61,15 @@
}
}
+data class ResourceBooleanFlag constructor(
+ override val id: Int,
+ @BoolRes override val resourceId: Int
+) : ResourceFlag<Boolean>
+
data class StringFlag @JvmOverloads constructor(
override val id: Int,
- override val default: String = "",
- override val resourceOverride: Int = -1
-) : Flag<String> {
+ override val default: String = ""
+) : ParcelableFlag<String> {
companion object {
@JvmField
val CREATOR = object : Parcelable.Creator<StringFlag> {
@@ -82,11 +89,15 @@
}
}
+data class ResourceStringFlag constructor(
+ override val id: Int,
+ @StringRes override val resourceId: Int
+) : ResourceFlag<String>
+
data class IntFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Int = 0,
- override val resourceOverride: Int = -1
-) : Flag<Int> {
+ override val default: Int = 0
+) : ParcelableFlag<Int> {
companion object {
@JvmField
@@ -107,11 +118,15 @@
}
}
+data class ResourceIntFlag constructor(
+ override val id: Int,
+ @IntegerRes override val resourceId: Int
+) : ResourceFlag<Int>
+
data class LongFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Long = 0,
- override val resourceOverride: Int = -1
-) : Flag<Long> {
+ override val default: Long = 0
+) : ParcelableFlag<Long> {
companion object {
@JvmField
@@ -134,9 +149,8 @@
data class FloatFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Float = 0f,
- override val resourceOverride: Int = -1
-) : Flag<Float> {
+ override val default: Float = 0f
+) : ParcelableFlag<Float> {
companion object {
@JvmField
@@ -157,11 +171,15 @@
}
}
+data class ResourceFloatFlag constructor(
+ override val id: Int,
+ override val resourceId: Int
+) : ResourceFlag<Int>
+
data class DoubleFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Double = 0.0,
- override val resourceOverride: Int = -1
-) : Flag<Double> {
+ override val default: Double = 0.0
+) : ParcelableFlag<Double> {
companion object {
@JvmField
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index e61cb5c..b2ca2d7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -60,7 +60,7 @@
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val extras: Bundle? = getResultExtras(false)
- val listOfFlags: java.util.ArrayList<Flag<*>>? =
+ val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? =
extras?.getParcelableArrayList(FIELD_FLAGS)
if (listOfFlags != null) {
completer.set(listOfFlags)
@@ -108,6 +108,10 @@
}
}
+ override fun isEnabled(flag: ResourceBooleanFlag): Boolean {
+ throw RuntimeException("Not implemented in FlagManager")
+ }
+
override fun addListener(listener: FlagReader.Listener) {
synchronized(listeners) {
val registerNeeded = listeners.isEmpty()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
index 91a3912..26c6a4b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
@@ -24,6 +24,8 @@
return flag.default
}
+ fun isEnabled(flag: ResourceBooleanFlag): Boolean
+
/** Returns a boolean value for the given flag. */
fun isEnabled(id: Int, def: Boolean): Boolean {
return def
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index a319b40..3cd090e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -270,7 +270,7 @@
mOpeningLeashes.add(openingTasks.get(i).getLeash());
// We are receiving new opening tasks, so convert to onTasksAppeared.
final RemoteAnimationTargetCompat target = new RemoteAnimationTargetCompat(
- openingTasks.get(i), layer, mInfo, t);
+ openingTasks.get(i), layer, info, t);
mLeashMap.put(mOpeningLeashes.get(i), target.leash.mSurfaceControl);
t.reparent(target.leash.mSurfaceControl, mInfo.getRootLeash());
t.setLayer(target.leash.mSurfaceControl, layer);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index b6be6ed..e46b6f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -26,6 +26,7 @@
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
@@ -62,7 +63,7 @@
mainExecutor
)
- return if (config.isHingeAngleEnabled) {
+ val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
PhysicsBasedUnfoldTransitionProgressProvider(
mainHandler,
foldStateProvider
@@ -70,6 +71,10 @@
} else {
FixedTimingTransitionProgressProvider(foldStateProvider)
}
+ return ScaleAwareTransitionProgressProvider(
+ unfoldTransitionProgressProvider,
+ context.contentResolver
+ )
}
fun createConfig(context: Context): UnfoldTransitionConfig =
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
index e072d41..58d7dfb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -2,7 +2,6 @@
import android.content.Context
import android.os.RemoteException
-import android.util.Log
import android.view.IRotationWatcher
import android.view.IWindowManager
import android.view.Surface
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
new file mode 100644
index 0000000..df9078a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.unfold.util
+
+import android.animation.ValueAnimator
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+
+/** Wraps [UnfoldTransitionProgressProvider] to disable transitions when animations are disabled. */
+class ScaleAwareTransitionProgressProvider(
+ unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ private val contentResolver: ContentResolver
+) : UnfoldTransitionProgressProvider {
+
+ private val scopedUnfoldTransitionProgressProvider =
+ ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+
+ private val animatorDurationScaleObserver = object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ onAnimatorScaleChanged()
+ }
+ }
+
+ init {
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ /* notifyForDescendants= */ false,
+ animatorDurationScaleObserver)
+ onAnimatorScaleChanged()
+ }
+
+ private fun onAnimatorScaleChanged() {
+ val animationsEnabled = ValueAnimator.areAnimatorsEnabled()
+ scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(animationsEnabled)
+ }
+
+ override fun addCallback(listener: TransitionProgressListener) {
+ scopedUnfoldTransitionProgressProvider.addCallback(listener)
+ }
+
+ override fun removeCallback(listener: TransitionProgressListener) {
+ scopedUnfoldTransitionProgressProvider.removeCallback(listener)
+ }
+
+ override fun destroy() {
+ contentResolver.unregisterContentObserver(animatorDurationScaleObserver)
+ scopedUnfoldTransitionProgressProvider.destroy()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index 3ebfb51..9b72655 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -22,12 +22,11 @@
import com.android.systemui.communal.conditions.CommunalCondition;
import com.android.systemui.communal.conditions.CommunalSettingCondition;
-import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
import com.android.systemui.idle.AmbientLightModeMonitor;
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -73,9 +72,7 @@
@ElementsIntoSet
@Named(COMMUNAL_CONDITIONS)
static Set<CommunalCondition> provideCommunalConditions(
- CommunalSettingCondition communalSettingCondition,
- CommunalTrustedNetworkCondition communalTrustedNetworkCondition) {
- return new HashSet<>(
- Arrays.asList(communalSettingCondition, communalTrustedNetworkCondition));
+ CommunalSettingCondition communalSettingCondition) {
+ return new HashSet<>(Collections.singletonList(communalSettingCondition));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index 0ee47a7..3f00b87 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -22,6 +22,7 @@
import static com.android.systemui.flags.FlagManager.FIELD_ID;
import static com.android.systemui.flags.FlagManager.FIELD_VALUE;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -30,7 +31,6 @@
import android.os.Bundle;
import android.util.Log;
-import androidx.annotation.BoolRes;
import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
@@ -89,16 +89,18 @@
public boolean isEnabled(BooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- boolean def = flag.getDefault();
- if (flag.hasResourceOverride()) {
- try {
- def = isEnabledInOverlay(flag.getResourceOverride());
- } catch (Resources.NotFoundException e) {
- // no-op
- }
- }
+ mBooleanFlagCache.put(id, isEnabled(id, flag.getDefault()));
+ }
- mBooleanFlagCache.put(id, isEnabled(id, def));
+ return mBooleanFlagCache.get(id);
+ }
+
+ @Override
+ public boolean isEnabled(ResourceBooleanFlag flag) {
+ int id = flag.getId();
+ if (!mBooleanFlagCache.containsKey(id)) {
+ mBooleanFlagCache.put(
+ id, isEnabled(id, mResources.getBoolean(flag.getResourceId())));
}
return mBooleanFlagCache.get(id);
@@ -111,6 +113,7 @@
return result == null ? defaultValue : result;
}
+
/** Returns the stored value or null if not set. */
private Boolean isEnabledInternal(int id) {
try {
@@ -121,10 +124,6 @@
return null;
}
- private boolean isEnabledInOverlay(@BoolRes int resId) {
- return mResources.getBoolean(resId);
- }
-
/** Set whether a given {@link BooleanFlag} is enabled or not. */
public void setEnabled(int id, boolean value) {
Boolean currentValue = isEnabledInternal(id);
@@ -185,9 +184,19 @@
} else if (ACTION_GET_FLAGS.equals(action)) {
Map<Integer, Flag<?>> knownFlagMap = Flags.collectFlags();
ArrayList<Flag<?>> flags = new ArrayList<>(knownFlagMap.values());
+
+ // Convert all flags to parcelable flags.
+ ArrayList<ParcelableFlag<?>> pFlags = new ArrayList<>();
+ for (Flag<?> f : flags) {
+ ParcelableFlag<?> pf = toParcelableFlag(f);
+ if (pf != null) {
+ pFlags.add(pf);
+ }
+ }
+
Bundle extras = getResultExtras(true);
if (extras != null) {
- extras.putParcelableArrayList(FIELD_FLAGS, flags);
+ extras.putParcelableArrayList(FIELD_FLAGS, pFlags);
}
}
}
@@ -215,6 +224,25 @@
setEnabled(id, extras.getBoolean(FIELD_VALUE));
}
}
+
+ /**
+ * Ensures that the data we send to the app reflects the current state of the flags.
+ *
+ * Also converts an non-parcelable versions of the flags to their parcelable versions.
+ */
+ @Nullable
+ private ParcelableFlag<?> toParcelableFlag(Flag<?> f) {
+ if (f instanceof BooleanFlag) {
+ return new BooleanFlag(f.getId(), isEnabled((BooleanFlag) f));
+ }
+ if (f instanceof ResourceBooleanFlag) {
+ return new BooleanFlag(f.getId(), isEnabled((ResourceBooleanFlag) f));
+ }
+
+ // TODO: add support for other flag types.
+ Log.w(TAG, "Unsupported Flag Type. Please file a bug.");
+ return null;
+ }
};
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index bd6cb66..5b6404f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -16,12 +16,14 @@
package com.android.systemui.flags;
+import android.content.res.Resources;
import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import java.io.FileDescriptor;
@@ -37,9 +39,11 @@
*/
@SysUISingleton
public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
- SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
+ private final Resources mResources;
+ SparseBooleanArray mFlagCache = new SparseBooleanArray();
@Inject
- public FeatureFlagsRelease(DumpManager dumpManager) {
+ public FeatureFlagsRelease(@Main Resources resources, DumpManager dumpManager) {
+ mResources = resources;
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -55,18 +59,28 @@
}
@Override
+ public boolean isEnabled(ResourceBooleanFlag flag) {
+ int cacheIndex = mFlagCache.indexOfKey(flag.getId());
+ if (cacheIndex < 0) {
+ return isEnabled(flag.getId(), mResources.getBoolean(flag.getResourceId()));
+ }
+
+ return mFlagCache.valueAt(cacheIndex);
+ }
+
+ @Override
public boolean isEnabled(int key, boolean defaultValue) {
- mAccessedFlags.append(key, defaultValue);
+ mFlagCache.append(key, defaultValue);
return defaultValue;
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: false");
- int size = mAccessedFlags.size();
+ int size = mFlagCache.size();
for (int i = 0; i < size; i++) {
- pw.println(" sysui_flag_" + mAccessedFlags.keyAt(i)
- + ": " + mAccessedFlags.valueAt(i));
+ pw.println(" sysui_flag_" + mFlagCache.keyAt(i)
+ + ": " + mFlagCache.valueAt(i));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 458cdc1f..97533c9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -61,8 +61,8 @@
public static final BooleanFlag NEW_UNLOCK_SWIPE_ANIMATION =
new BooleanFlag(202, true);
- public static final BooleanFlag CHARGING_RIPPLE =
- new BooleanFlag(203, false, R.bool.flag_charging_ripple);
+ public static final ResourceBooleanFlag CHARGING_RIPPLE =
+ new ResourceBooleanFlag(203, R.bool.flag_charging_ripple);
/***************************************/
// 300 - power menu
@@ -77,8 +77,8 @@
public static final BooleanFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
new BooleanFlag(401, false);
- public static final BooleanFlag SMARTSPACE =
- new BooleanFlag(402, false, R.bool.flag_smartspace);
+ public static final ResourceBooleanFlag SMARTSPACE =
+ new ResourceBooleanFlag(402, R.bool.flag_smartspace);
/***************************************/
// 500 - quick settings
@@ -88,11 +88,11 @@
public static final BooleanFlag COMBINED_QS_HEADERS =
new BooleanFlag(501, false);
- public static final BooleanFlag PEOPLE_TILE =
- new BooleanFlag(502, false, R.bool.flag_conversations);
+ public static final ResourceBooleanFlag PEOPLE_TILE =
+ new ResourceBooleanFlag(502, R.bool.flag_conversations);
- public static final BooleanFlag QS_USER_DETAIL_SHORTCUT =
- new BooleanFlag(503, false, R.bool.flag_lockscreen_qs_user_detail_shortcut);
+ public static final ResourceBooleanFlag QS_USER_DETAIL_SHORTCUT =
+ new ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut);
/***************************************/
// 600- status bar
@@ -115,12 +115,13 @@
/***************************************/
// 800 - general visual/theme
- public static final BooleanFlag MONET =
- new BooleanFlag(800, true, R.bool.flag_monet);
+ public static final ResourceBooleanFlag MONET =
+ new ResourceBooleanFlag(800, R.bool.flag_monet);
/***************************************/
// 900 - media
public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, false);
+ public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index b85f1072..e921ad29c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -862,8 +862,23 @@
@VisibleForTesting
internal object MediaPlayerData {
- private val EMPTY = MediaData(-1, false, 0, null, null, null, null, null,
- emptyList(), emptyList(), "INVALID", null, null, null, true, null)
+ private val EMPTY = MediaData(
+ userId = -1,
+ initialized = false,
+ backgroundColor = 0,
+ app = null,
+ appIcon = null,
+ artist = null,
+ song = null,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = "INVALID",
+ token = null,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null)
// Whether should prioritize Smartspace card.
internal var shouldPrioritizeSs: Boolean = false
private set
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index f66eb5b..63555bb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -62,6 +62,7 @@
import com.android.systemui.util.time.SystemClock;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -122,6 +123,8 @@
private MediaCarouselController mMediaCarouselController;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
private final FalsingManager mFalsingManager;
+ private final MediaFlags mMediaFlags;
+
// Used for swipe-to-dismiss logging.
protected boolean mIsImpressed = false;
private SystemClock mSystemClock;
@@ -138,7 +141,7 @@
SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
mediaOutputDialogFactory, MediaCarouselController mediaCarouselController,
- FalsingManager falsingManager, SystemClock systemClock) {
+ FalsingManager falsingManager, MediaFlags mediaFlags, SystemClock systemClock) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter;
@@ -149,8 +152,8 @@
mMediaOutputDialogFactory = mediaOutputDialogFactory;
mMediaCarouselController = mediaCarouselController;
mFalsingManager = falsingManager;
+ mMediaFlags = mediaFlags;
mSystemClock = systemClock;
-
loadDimens();
mSeekBarViewModel.setLogSmartspaceClick(() -> {
@@ -426,33 +429,61 @@
deviceName.setText(deviceString);
seamlessView.setContentDescription(deviceString);
- List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
- // Media controls
- int i = 0;
+ // Media action buttons
List<MediaAction> actionIcons = data.getActions();
+ List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact();
+
+ if (mMediaFlags.areMediaSessionActionsEnabled() && data.getSemanticActions() != null) {
+ // Use PlaybackState actions instead
+ MediaButton semanticActions = data.getSemanticActions();
+
+ actionIcons = new ArrayList<MediaAction>();
+ actionIcons.add(semanticActions.getStartCustom());
+ actionIcons.add(semanticActions.getPrevOrCustom());
+ actionIcons.add(semanticActions.getPlayOrPause());
+ actionIcons.add(semanticActions.getNextOrCustom());
+ actionIcons.add(semanticActions.getEndCustom());
+
+ actionsWhenCollapsed = new ArrayList<Integer>();
+ actionsWhenCollapsed.add(1);
+ actionsWhenCollapsed.add(2);
+ actionsWhenCollapsed.add(3);
+ }
+
+ int i = 0;
for (; i < actionIcons.size() && i < ACTION_IDS.length; i++) {
int actionId = ACTION_IDS[i];
+ boolean visibleInCompat = actionsWhenCollapsed.contains(i);
final ImageButton button = mPlayerViewHolder.getAction(actionId);
MediaAction mediaAction = actionIcons.get(i);
- button.setImageIcon(mediaAction.getIcon());
- button.setContentDescription(mediaAction.getContentDescription());
- Runnable action = mediaAction.getAction();
+ if (mediaAction != null) {
+ button.setImageIcon(mediaAction.getIcon());
+ button.setContentDescription(mediaAction.getContentDescription());
+ Runnable action = mediaAction.getAction();
- if (action == null) {
- button.setEnabled(false);
+ if (action == null) {
+ button.setEnabled(false);
+ } else {
+ button.setEnabled(true);
+ button.setOnClickListener(v -> {
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
+ /* isRecommendationCard */ false);
+ action.run();
+ }
+ });
+ }
+ setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
+ setVisibleAndAlpha(expandedSet, actionId, true /*visible */);
} else {
- button.setEnabled(true);
- button.setOnClickListener(v -> {
- if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- /* isRecommendationCard */ false);
- action.run();
- }
- });
+ button.setImageIcon(null);
+ button.setContentDescription(null);
+ button.setEnabled(false);
+ setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
+ // for expanded layout, set as INVISIBLE so that we still reserve space in the UI
+ expandedSet.setVisibility(actionId, ConstraintSet.INVISIBLE);
+ expandedSet.setAlpha(actionId, 0.0f);
}
- boolean visibleInCompat = actionsWhenCollapsed.contains(i);
- setVisibleAndAlpha(collapsedSet, actionId, visibleInCompat);
- setVisibleAndAlpha(expandedSet, actionId, true /*visible */);
}
// Hide any unused buttons
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index bda07f4..4b8dfde 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -47,7 +47,7 @@
*/
val artwork: Icon?,
/**
- * List of actions that can be performed on the player: prev, next, play, pause, etc.
+ * List of generic action buttons for the media player, based on notification actions
*/
val actions: List<MediaAction>,
/**
@@ -55,6 +55,11 @@
*/
val actionsToShowInCompact: List<Int>,
/**
+ * Semantic actions buttons, based on the PlaybackState of the media session.
+ * If present, these actions will be preferred in the UI over [actions]
+ */
+ val semanticActions: MediaButton? = null,
+ /**
* Package name of the app that's posting the media.
*/
val packageName: String,
@@ -125,6 +130,32 @@
}
}
+/**
+ * Contains [MediaAction] objects which represent specific buttons in the UI
+ */
+data class MediaButton(
+ /**
+ * Play/pause button
+ */
+ var playOrPause: MediaAction? = null,
+ /**
+ * Next button, or custom action
+ */
+ var nextOrCustom: MediaAction? = null,
+ /**
+ * Previous button, or custom action
+ */
+ var prevOrCustom: MediaAction? = null,
+ /**
+ * First custom action space
+ */
+ var startCustom: MediaAction? = null,
+ /**
+ * Last custom action space
+ */
+ var endCustom: MediaAction? = null
+)
+
/** State of a media action. */
data class MediaAction(
val icon: Icon?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 7c0f7fc..49a63c3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -38,6 +38,7 @@
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
+import android.media.session.PlaybackState
import android.net.Uri
import android.os.Parcelable
import android.os.UserHandle
@@ -45,6 +46,7 @@
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
+import androidx.media.utils.MediaConstants
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.R
@@ -80,8 +82,24 @@
private const val DEBUG = true
private const val EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY = "dismiss_intent"
-private val LOADING = MediaData(-1, false, 0, null, null, null, null, null,
- emptyList(), emptyList(), "INVALID", null, null, null, true, null)
+private val LOADING = MediaData(
+ userId = -1,
+ initialized = false,
+ backgroundColor = 0,
+ app = null,
+ appIcon = null,
+ artist = null,
+ song = null,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = "INVALID",
+ token = null,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null)
+
@VisibleForTesting
internal val EMPTY_SMARTSPACE_MEDIA_DATA = SmartspaceMediaData("INVALID", false, false,
"INVALID", null, emptyList(), null, 0, 0)
@@ -112,7 +130,8 @@
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean,
private val systemClock: SystemClock,
- private val tunerService: TunerService
+ private val tunerService: TunerService,
+ private val mediaFlags: MediaFlags,
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -127,6 +146,10 @@
// Maximum number of actions allowed in compact view
@JvmField
val MAX_COMPACT_ACTIONS = 3
+
+ /** Maximum number of [PlaybackState.CustomAction] buttons supported */
+ @JvmField
+ val MAX_CUSTOM_ACTIONS = 4
}
private val themeText = com.android.settingslib.Utils.getColorAttr(context,
@@ -182,12 +205,13 @@
activityStarter: ActivityStarter,
smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
clock: SystemClock,
- tunerService: TunerService
+ tunerService: TunerService,
+ mediaFlags: MediaFlags
) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
- Utils.useQsMediaPlayer(context), clock, tunerService)
+ Utils.useQsMediaPlayer(context), clock, tunerService, mediaFlags)
private val appChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -522,7 +546,7 @@
foregroundExecutor.execute {
onMediaDataLoaded(packageName, null, MediaData(userId, true, bgColor, appName,
null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
- packageName, token, appIntent, device = null, active = false,
+ null, packageName, token, appIntent, device = null, active = false,
resumeAction = resumeAction, resumption = true, notificationKey = packageName,
hasCheckedForResume = true, lastActive = lastActive))
}
@@ -594,15 +618,55 @@
}
// Control buttons
+ // If flag is enabled and controller has a PlaybackState, create actions from session info
+ // Otherwise, use the notification actions
+ var actionIcons: List<MediaAction> = emptyList()
+ var actionsToShowCollapsed: List<Int> = emptyList()
+ var semanticActions: MediaButton? = null
+ if (mediaFlags.areMediaSessionActionsEnabled() && mediaController.playbackState != null) {
+ semanticActions = createActionsFromState(sbn.packageName, mediaController)
+ } else {
+ val actions = createActionsFromNotification(sbn)
+ actionIcons = actions.first
+ actionsToShowCollapsed = actions.second
+ }
+
+ val playbackLocation =
+ if (isRemoteCastNotification(sbn)) MediaData.PLAYBACK_CAST_REMOTE
+ else if (mediaController.playbackInfo?.playbackType ==
+ MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) MediaData.PLAYBACK_LOCAL
+ else MediaData.PLAYBACK_CAST_LOCAL
+ val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
+ val lastActive = systemClock.elapsedRealtime()
+ foregroundExecutor.execute {
+ val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
+ val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
+ val active = mediaEntries[key]?.active ?: true
+ onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
+ smallIcon, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed,
+ semanticActions, sbn.packageName, token, notif.contentIntent, null,
+ active, resumeAction = resumeAction, playbackLocation = playbackLocation,
+ notificationKey = key, hasCheckedForResume = hasCheckedForResume,
+ isPlaying = isPlaying, isClearable = sbn.isClearable(),
+ lastActive = lastActive))
+ }
+ }
+
+ /**
+ * Generate action buttons based on notification actions
+ */
+ private fun createActionsFromNotification(sbn: StatusBarNotification):
+ Pair<List<MediaAction>, List<Int>> {
+ val notif = sbn.notification
val actionIcons: MutableList<MediaAction> = ArrayList()
val actions = notif.actions
var actionsToShowCollapsed = notif.extras.getIntArray(
- Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() ?: mutableListOf<Int>()
+ Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() ?: mutableListOf()
if (actionsToShowCollapsed.size > MAX_COMPACT_ACTIONS) {
- Log.e(TAG, "Too many compact actions for $key, limiting to first $MAX_COMPACT_ACTIONS")
+ Log.e(TAG, "Too many compact actions for ${sbn.key}," +
+ "limiting to first $MAX_COMPACT_ACTIONS")
actionsToShowCollapsed = actionsToShowCollapsed.subList(0, MAX_COMPACT_ACTIONS)
}
- // TODO: b/153736623 look into creating actions when this isn't a media style notification
if (actions != null) {
for ((index, action) in actions.withIndex()) {
@@ -631,32 +695,150 @@
action.getIcon()
}.setTint(themeText)
val mediaAction = MediaAction(
- mediaActionIcon,
- runnable,
- action.title)
+ mediaActionIcon,
+ runnable,
+ action.title)
actionIcons.add(mediaAction)
}
}
+ return Pair(actionIcons, actionsToShowCollapsed)
+ }
- val playbackLocation =
- if (isRemoteCastNotification(sbn)) MediaData.PLAYBACK_CAST_REMOTE
- else if (mediaController.playbackInfo?.playbackType ==
- MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) MediaData.PLAYBACK_LOCAL
- else MediaData.PLAYBACK_CAST_LOCAL
- val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
- val lastActive = systemClock.elapsedRealtime()
- foregroundExecutor.execute {
- val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
- val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
- val active = mediaEntries[key]?.active ?: true
- onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
- smallIcon, artist, song, artWorkIcon, actionIcons,
- actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
- active, resumeAction = resumeAction, playbackLocation = playbackLocation,
- notificationKey = key, hasCheckedForResume = hasCheckedForResume,
- isPlaying = isPlaying, isClearable = sbn.isClearable(),
- lastActive = lastActive))
+ /**
+ * Generates action button info for this media session based on the PlaybackState
+ *
+ * @param packageName Package name for the media app
+ * @param controller MediaController for the current session
+ * @return a Pair consisting of a list of media actions, and a list of ints representing which
+ * of those actions should be shown in the compact player
+ */
+ private fun createActionsFromState(packageName: String, controller: MediaController):
+ MediaButton? {
+ val actions = MediaButton()
+ controller.playbackState?.let { state ->
+ // First, check for standard actions
+ actions.playOrPause = if (isPlayingState(state.state)) {
+ getStandardAction(controller, state.actions, PlaybackState.ACTION_PAUSE)
+ } else {
+ getStandardAction(controller, state.actions, PlaybackState.ACTION_PLAY)
+ }
+ val prevButton = getStandardAction(controller, state.actions,
+ PlaybackState.ACTION_SKIP_TO_PREVIOUS)
+ val nextButton = getStandardAction(controller, state.actions,
+ PlaybackState.ACTION_SKIP_TO_NEXT)
+
+ // Then, check for custom actions
+ val customActions = MutableList<MediaAction?>(4) { null }
+ var customCount = 0
+ for (i in 0..MAX_CUSTOM_ACTIONS) {
+ getCustomAction(state, packageName, controller, customCount)?.let {
+ customActions[customCount++] = it
+ }
+ }
+
+ // Finally, assign the remaining button slots: C A play/pause B D
+ // A = previous, else custom action (if not reserved)
+ // B = next, else custom action (if not reserved)
+ // C and D are always custom actions
+ val reservePrev = controller.extras?.getBoolean(
+ MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV) == true
+ val reserveNext = controller.extras?.getBoolean(
+ MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT) == true
+ var customIdx = 0
+
+ actions.prevOrCustom = if (prevButton != null) {
+ prevButton
+ } else if (!reservePrev) {
+ customActions[customIdx++]
+ } else {
+ null
+ }
+
+ actions.nextOrCustom = if (nextButton != null) {
+ nextButton
+ } else if (!reserveNext) {
+ customActions[customIdx++]
+ } else {
+ null
+ }
+
+ actions.startCustom = customActions[customIdx++]
+ actions.endCustom = customActions[customIdx++]
}
+ return actions
+ }
+
+ /**
+ * Get a [MediaAction] representing one of
+ * - [PlaybackState.ACTION_PLAY]
+ * - [PlaybackState.ACTION_PAUSE]
+ * - [PlaybackState.ACTION_SKIP_TO_PREVIOUS]
+ * - [PlaybackState.ACTION_SKIP_TO_NEXT]
+ */
+ private fun getStandardAction(
+ controller: MediaController,
+ stateActions: Long,
+ action: Long
+ ): MediaAction? {
+ if (stateActions and action == 0L) {
+ return null
+ }
+
+ return when (action) {
+ PlaybackState.ACTION_PLAY -> {
+ MediaAction(
+ Icon.createWithResource(context, com.android.internal.R.drawable.ic_media_play),
+ { controller.transportControls.play() },
+ context.getString(R.string.controls_media_button_play)
+ )
+ }
+ PlaybackState.ACTION_PAUSE -> {
+ MediaAction(
+ Icon.createWithResource(context,
+ com.android.internal.R.drawable.ic_media_pause),
+ { controller.transportControls.pause() },
+ context.getString(R.string.controls_media_button_pause)
+ )
+ }
+ PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
+ MediaAction(
+ Icon.createWithResource(context,
+ com.android.internal.R.drawable.ic_media_previous),
+ { controller.transportControls.skipToPrevious() },
+ context.getString(R.string.controls_media_button_prev)
+ )
+ }
+ PlaybackState.ACTION_SKIP_TO_NEXT -> {
+ MediaAction(
+ Icon.createWithResource(context, com.android.internal.R.drawable.ic_media_next),
+ { controller.transportControls.skipToNext() },
+ context.getString(R.string.controls_media_button_next)
+ )
+ }
+ else -> null
+ }
+ }
+
+ /**
+ * Get a [MediaAction] representing a [PlaybackState.CustomAction]
+ */
+ private fun getCustomAction(
+ state: PlaybackState,
+ packageName: String,
+ controller: MediaController,
+ index: Int
+ ): MediaAction? {
+ if (state.customActions.size <= index || state.customActions[index] == null) {
+ if (DEBUG) { Log.d(TAG, "not enough actions or action was null at $index") }
+ return null
+ }
+
+ val it = state.customActions[index]
+ return MediaAction(
+ Icon.createWithResource(packageName, it.icon),
+ { controller.transportControls.sendCustomAction(it, it.extras) },
+ it.name
+ )
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
new file mode 100644
index 0000000..b4a4b42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt
@@ -0,0 +1,32 @@
+/*
+ * 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 com.android.systemui.media
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+
+@SysUISingleton
+class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) {
+ /**
+ * Check whether media control actions should be based on PlaybackState instead of notification
+ */
+ fun areMediaSessionActionsEnabled(): Boolean {
+ return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 5afefa1..ab48a28 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.navigationbar.gestural;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import android.app.ActivityManager;
@@ -544,7 +546,8 @@
layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
layoutParams.windowAnimations = 0;
layoutParams.privateFlags |=
- WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION);
layoutParams.setTitle(TAG + mContext.getDisplayId());
layoutParams.setFitInsetsTypes(0 /* types */);
layoutParams.setTrustedOverlay();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index a16b92f..097cf35 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -33,6 +33,7 @@
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT;
import static com.android.systemui.people.PeopleSpaceUtils.convertDrawableToBitmap;
@@ -45,8 +46,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -75,7 +74,6 @@
import androidx.core.math.MathUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -339,8 +337,9 @@
views = new RemoteViews(mContext.getPackageName(),
R.layout.people_tile_suppressed_layout);
}
- Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon);
- Bitmap disabledBitmap = convertDrawableToDisabledBitmap(appIcon);
+ Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon).mutate();
+ appIcon.setColorFilter(getDisabledColorFilter());
+ Bitmap disabledBitmap = convertDrawableToBitmap(appIcon);
views.setImageViewBitmap(R.id.icon, disabledBitmap);
return views;
}
@@ -1262,8 +1261,9 @@
Context context, PeopleSpaceTile tile, int maxAvatarSize, boolean hasNewStory) {
Icon icon = tile.getUserIcon();
if (icon == null) {
- Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge);
- return convertDrawableToDisabledBitmap(placeholder);
+ Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge).mutate();
+ placeholder.setColorFilter(getDisabledColorFilter());
+ return convertDrawableToBitmap(placeholder);
}
PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context,
context.getPackageManager(),
@@ -1276,10 +1276,7 @@
hasNewStory);
if (isDndBlockingTileData(tile)) {
- // If DND is blocking the conversation, then display the icon in grayscale.
- ColorMatrix colorMatrix = new ColorMatrix();
- colorMatrix.setSaturation(0);
- personDrawable.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
+ personDrawable.setColorFilter(getDisabledColorFilter());
}
return convertDrawableToBitmap(personDrawable);
@@ -1375,11 +1372,4 @@
mAvatarSize = avatarSize;
}
}
-
- private static Bitmap convertDrawableToDisabledBitmap(Drawable icon) {
- Bitmap appIconAsBitmap = convertDrawableToBitmap(icon);
- FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap);
- drawable.setIsDisabled(true);
- return convertDrawableToBitmap(drawable);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 706f423..6f63a08 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -86,6 +86,7 @@
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -98,8 +99,10 @@
import javax.inject.Inject;
-public class InternetDialogController implements WifiEntry.DisconnectCallback,
- AccessPointController.AccessPointCallback {
+/**
+ * Controller for Internet Dialog.
+ */
+public class InternetDialogController implements AccessPointController.AccessPointCallback {
private static final String TAG = "InternetDialogController";
private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
@@ -881,20 +884,6 @@
return;
}
- boolean hasConnectedWifi = false;
- final int accessPointSize = accessPoints.size();
- for (int i = 0; i < accessPointSize; i++) {
- WifiEntry wifiEntry = accessPoints.get(i);
- if (wifiEntry.isDefaultNetwork() && wifiEntry.hasInternetAccess()) {
- mConnectedEntry = wifiEntry;
- hasConnectedWifi = true;
- break;
- }
- }
- if (!hasConnectedWifi) {
- mConnectedEntry = null;
- }
-
int count = MAX_WIFI_ENTRY_COUNT;
if (mHasEthernet) {
count -= 1;
@@ -902,15 +891,22 @@
if (hasActiveSubId()) {
count -= 1;
}
- if (hasConnectedWifi) {
- count -= 1;
+ if (count > accessPoints.size()) {
+ count = accessPoints.size();
}
- final List<WifiEntry> wifiEntries = accessPoints.stream()
- .filter(wifiEntry -> (!wifiEntry.isDefaultNetwork()
- || !wifiEntry.hasInternetAccess()))
- .limit(count)
- .collect(Collectors.toList());
- mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
+
+ WifiEntry connectedEntry = null;
+ final List<WifiEntry> wifiEntries = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ WifiEntry entry = accessPoints.get(i);
+ if (connectedEntry == null && entry.isDefaultNetwork() && entry.hasInternetAccess()) {
+ connectedEntry = entry;
+ } else {
+ wifiEntries.add(entry);
+ }
+ }
+ mConnectedEntry = connectedEntry;
+ mWifiEntriesCount = wifiEntries.size();
if (mCallback != null) {
mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry);
@@ -921,10 +917,6 @@
public void onSettingsActivityTriggered(Intent settingsIntent) {
}
- @Override
- public void onDisconnectResult(int status) {
- }
-
private class InternetTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DisplayInfoListener,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
deleted file mode 100644
index ac8b47d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 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 com.android.systemui.statusbar.policy;
-
-import android.app.StatusBarManager;
-import android.content.Context;
-import android.content.res.Configuration;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBar;
-
-import javax.inject.Inject;
-
-/**
- * Let {@link RemoteInputView} to control the visibility of QuickSetting.
- */
-@SysUISingleton
-public class RemoteInputQuickSettingsDisabler
- implements ConfigurationController.ConfigurationListener {
-
- private Context mContext;
- @VisibleForTesting boolean mRemoteInputActive;
- @VisibleForTesting boolean misLandscape;
- private int mLastOrientation;
- private final CommandQueue mCommandQueue;
-
- @Inject
- public RemoteInputQuickSettingsDisabler(Context context,
- ConfigurationController configController, CommandQueue commandQueue) {
- mContext = context;
- mCommandQueue = commandQueue;
- mLastOrientation = mContext.getResources().getConfiguration().orientation;
- configController.addCallback(this);
- }
-
- public int adjustDisableFlags(int state) {
- if (mRemoteInputActive && misLandscape) {
- state |= StatusBarManager.DISABLE2_QUICK_SETTINGS;
- }
-
- return state;
- }
-
- public void setRemoteInputActive(boolean active){
- if(mRemoteInputActive != active){
- mRemoteInputActive = active;
- recomputeDisableFlags();
- }
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- if (newConfig.orientation != mLastOrientation) {
- misLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
- mLastOrientation = newConfig.orientation;
- recomputeDisableFlags();
- }
- }
-
- /**
- * Reapplies the disable flags. Then the method adjustDisableFlags in this class will be invoked
- * in {@link QSFragment#disable(int, int, boolean)} and
- * {@link StatusBar#disable(int, int, boolean)}
- * to modify the disable flags according to the status of mRemoteInputActive and misLandscape.
- */
- private void recomputeDisableFlags() {
- mCommandQueue.recomputeDisableFlags(mContext.getDisplayId(), true);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
new file mode 100644
index 0000000..31ef2f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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 com.android.systemui.statusbar.policy
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.content.res.Configuration
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+
+/**
+ * Controls whether the disable flag [StatusBarManager.DISABLE2_QUICK_SETTINGS] should be set.
+ * This would happen when a [RemoteInputView] is active, the device is in landscape and not using
+ * split shade.
+ */
+@SysUISingleton
+class RemoteInputQuickSettingsDisabler @Inject constructor(
+ private val context: Context,
+ private val commandQueue: CommandQueue,
+ configController: ConfigurationController
+) : ConfigurationController.ConfigurationListener {
+
+ private var remoteInputActive = false
+ private var isLandscape: Boolean
+ private var shouldUseSplitNotificationShade: Boolean
+
+ init {
+ isLandscape =
+ context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
+ shouldUseSplitNotificationShade = Utils.shouldUseSplitNotificationShade(context.resources)
+ configController.addCallback(this)
+ }
+
+ fun adjustDisableFlags(state: Int): Int {
+ var mutableState = state
+ if (remoteInputActive &&
+ isLandscape &&
+ !shouldUseSplitNotificationShade
+ ) {
+ mutableState = state or StatusBarManager.DISABLE2_QUICK_SETTINGS
+ }
+ return mutableState
+ }
+
+ fun setRemoteInputActive(active: Boolean) {
+ if (remoteInputActive != active) {
+ remoteInputActive = active
+ recomputeDisableFlags()
+ }
+ }
+
+ override fun onConfigChanged(newConfig: Configuration) {
+ var needToRecompute = false
+
+ val newIsLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
+ if (newIsLandscape != isLandscape) {
+ isLandscape = newIsLandscape
+ needToRecompute = true
+ }
+
+ val newSplitShadeFlag = Utils.shouldUseSplitNotificationShade(context.resources)
+ if (newSplitShadeFlag != shouldUseSplitNotificationShade) {
+ shouldUseSplitNotificationShade = newSplitShadeFlag
+ needToRecompute = true
+ }
+ if (needToRecompute) {
+ recomputeDisableFlags()
+ }
+ }
+
+ /**
+ * Called in order to trigger a refresh of the disable flags after a relevant configuration
+ * change or when a [RemoteInputView] has changed its active state. The method
+ * [adjustDisableFlags] will be invoked to modify the disable flags according to
+ * [remoteInputActive], [isLandscape] and [shouldUseSplitNotificationShade].
+ */
+ private fun recomputeDisableFlags() {
+ commandQueue.recomputeDisableFlags(context.displayId, true)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index 8b394bf..97fce51 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -30,8 +30,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.UserHandle;
@@ -42,6 +40,7 @@
import android.widget.TextView;
import com.android.internal.R;
+import com.android.launcher3.icons.BaseIconFactory.IconOptions;
import com.android.launcher3.icons.IconFactory;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -280,9 +279,14 @@
final ApplicationInfo appInfo = appEntry.info;
UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
IconFactory iconFactory = IconFactory.obtain(context);
- Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
- appInfo.loadUnbadgedIcon(packageManager), user, true).icon;
- return new BitmapDrawable(context.getResources(), iconBmp);
+ try {
+ return iconFactory.createBadgedIconBitmap(
+ appInfo.loadUnbadgedIcon(packageManager),
+ new IconOptions().setUser(user))
+ .newIcon(context);
+ } finally {
+ iconFactory.recycle();
+ }
}
private static boolean showApplicationIcon(ApplicationInfo appInfo,
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 51de1321..e6fc49f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.unfold
+import android.animation.ValueAnimator
import android.content.Context
import android.graphics.PixelFormat
import android.hardware.devicestate.DeviceStateManager
@@ -111,7 +112,7 @@
Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn")
try {
// Add the view only if we are unfolding and this is the first screen on
- if (!isFolded && !isUnfoldHandled) {
+ if (!isFolded && !isUnfoldHandled && ValueAnimator.areAnimatorsEnabled()) {
addView(onOverlayReady)
isUnfoldHandled = true
} else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java
index 475dde2..dea42f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.java
@@ -18,15 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
-import android.content.Context;
+import android.content.res.Resources;
import androidx.test.filters.SmallTest;
@@ -51,13 +50,14 @@
public class FeatureFlagsReleaseTest extends SysuiTestCase {
FeatureFlagsRelease mFeatureFlagsRelease;
+ @Mock private Resources mResources;
@Mock private DumpManager mDumpManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlagsRelease = new FeatureFlagsRelease(mDumpManager);
+ mFeatureFlagsRelease = new FeatureFlagsRelease(mResources, mDumpManager);
}
@After
@@ -68,15 +68,34 @@
}
@Test
+ public void testBooleanResourceFlag() {
+ int flagId = 213;
+ int flagResourceId = 3;
+ ResourceBooleanFlag flag = new ResourceBooleanFlag(flagId, flagResourceId);
+ when(mResources.getBoolean(flagResourceId)).thenReturn(true);
+
+ assertThat(mFeatureFlagsRelease.isEnabled(flag)).isTrue();
+ }
+
+ @Test
public void testDump() {
+ int flagIdA = 213;
+ int flagIdB = 18;
+ int flagResourceId = 3;
+ BooleanFlag flagA = new BooleanFlag(flagIdA, true);
+ ResourceBooleanFlag flagB = new ResourceBooleanFlag(flagIdB, flagResourceId);
+ when(mResources.getBoolean(flagResourceId)).thenReturn(true);
+
// WHEN the flags have been accessed
- assertFalse(mFeatureFlagsRelease.isEnabled(1, false));
- assertTrue(mFeatureFlagsRelease.isEnabled(2, true));
+ assertThat(mFeatureFlagsRelease.isEnabled(1, false)).isFalse();
+ assertThat(mFeatureFlagsRelease.isEnabled(flagA)).isTrue();
+ assertThat(mFeatureFlagsRelease.isEnabled(flagB)).isTrue();
// THEN the dump contains the flags and the default values
String dump = dumpToString();
assertThat(dump).contains(" sysui_flag_1: false\n");
- assertThat(dump).contains(" sysui_flag_2: true\n");
+ assertThat(dump).contains(" sysui_flag_" + flagIdA + ": true\n");
+ assertThat(dump).contains(" sysui_flag_" + flagIdB + ": true\n");
}
private String dumpToString() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 41ce941..7cc0172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -19,6 +19,7 @@
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
import android.graphics.drawable.RippleDrawable
import android.media.MediaMetadata
import android.media.session.MediaSession
@@ -97,6 +98,7 @@
@Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
@Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock private lateinit var falsingManager: FalsingManager
+ @Mock private lateinit var mediaFlags: MediaFlags
private lateinit var appIcon: ImageView
private lateinit var albumView: ImageView
private lateinit var titleText: TextView
@@ -123,6 +125,7 @@
private lateinit var session: MediaSession
private val device = MediaDeviceData(true, null, DEVICE_NAME)
private val disabledDevice = MediaDeviceData(false, null, "Disabled Device")
+ private lateinit var mediaData: MediaData
private val clock = FakeSystemClock()
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -135,7 +138,7 @@
player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
- mediaOutputDialogFactory, mediaCarouselController, falsingManager, clock)
+ mediaOutputDialogFactory, mediaCarouselController, falsingManager, mediaFlags, clock)
whenever(seekBarViewModel.progress).thenReturn(seekBarData)
// Mock out a view holder for the player to attach to.
@@ -165,14 +168,19 @@
whenever(holder.totalTimeView).thenReturn(totalTimeView)
action0 = ImageButton(context)
whenever(holder.action0).thenReturn(action0)
+ whenever(holder.getAction(R.id.action0)).thenReturn(action0)
action1 = ImageButton(context)
whenever(holder.action1).thenReturn(action1)
+ whenever(holder.getAction(R.id.action1)).thenReturn(action1)
action2 = ImageButton(context)
whenever(holder.action2).thenReturn(action2)
+ whenever(holder.getAction(R.id.action2)).thenReturn(action2)
action3 = ImageButton(context)
whenever(holder.action3).thenReturn(action3)
+ whenever(holder.getAction(R.id.action3)).thenReturn(action3)
action4 = ImageButton(context)
whenever(holder.action4).thenReturn(action4)
+ whenever(holder.getAction(R.id.action4)).thenReturn(action4)
whenever(holder.longPressText).thenReturn(longPressText)
whenever(longPressText.handler).thenReturn(handler)
settings = View(context)
@@ -200,6 +208,26 @@
setPlaybackState(playbackBuilder.build())
}
session.setActive(true)
+
+ mediaData = MediaData(
+ userId = USER_ID,
+ initialized = true,
+ backgroundColor = BG_COLOR,
+ app = APP,
+ appIcon = null,
+ artist = ARTIST,
+ song = TITLE,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = PACKAGE,
+ token = session.sessionToken,
+ clickIntent = null,
+ device = device,
+ active = true,
+ resumeAction = null)
+
+ whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false)
}
@After
@@ -210,18 +238,50 @@
@Test
fun bindWhenUnattached() {
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, null, null, device, true, null)
+ val state = mediaData.copy(token = null)
player.bindPlayer(state, PACKAGE)
assertThat(player.isPlaying()).isFalse()
}
@Test
+ fun bindSemanticActions() {
+ whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+ val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+ val semanticActions = MediaButton(
+ playOrPause = MediaAction(icon, Runnable {}, "play"),
+ nextOrCustom = MediaAction(icon, Runnable {}, "next"),
+ startCustom = MediaAction(icon, null, "custom 1"),
+ endCustom = MediaAction(icon, null, "custom 2")
+ )
+ val state = mediaData.copy(semanticActions = semanticActions)
+
+ player.attachPlayer(holder)
+ player.bindPlayer(state, PACKAGE)
+
+ verify(expandedSet).setVisibility(R.id.action0, ConstraintSet.VISIBLE)
+ assertThat(action0.contentDescription).isEqualTo("custom 1")
+ assertThat(action0.isEnabled()).isFalse()
+
+ verify(expandedSet).setVisibility(R.id.action1, ConstraintSet.INVISIBLE)
+ assertThat(action1.isEnabled()).isFalse()
+
+ verify(expandedSet).setVisibility(R.id.action2, ConstraintSet.VISIBLE)
+ assertThat(action2.isEnabled()).isTrue()
+ assertThat(action2.contentDescription).isEqualTo("play")
+
+ verify(expandedSet).setVisibility(R.id.action3, ConstraintSet.VISIBLE)
+ assertThat(action3.isEnabled()).isTrue()
+ assertThat(action3.contentDescription).isEqualTo("next")
+
+ verify(expandedSet).setVisibility(R.id.action4, ConstraintSet.VISIBLE)
+ assertThat(action4.contentDescription).isEqualTo("custom 2")
+ assertThat(action4.isEnabled()).isFalse()
+ }
+
+ @Test
fun bindText() {
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
- player.bindPlayer(state, PACKAGE)
+ player.bindPlayer(mediaData, PACKAGE)
assertThat(titleText.getText()).isEqualTo(TITLE)
assertThat(artistText.getText()).isEqualTo(ARTIST)
}
@@ -229,9 +289,7 @@
@Test
fun bindDevice() {
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
- player.bindPlayer(state, PACKAGE)
+ player.bindPlayer(mediaData, PACKAGE)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
assertThat(seamless.contentDescription).isEqualTo(DEVICE_NAME)
assertThat(seamless.isEnabled()).isTrue()
@@ -242,8 +300,7 @@
seamless.id = 1
val fallbackString = context.getString(R.string.media_seamless_other_device)
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null)
+ val state = mediaData.copy(device = disabledDevice)
player.bindPlayer(state, PACKAGE)
assertThat(seamless.isEnabled()).isFalse()
assertThat(seamlessText.getText()).isEqualTo(fallbackString)
@@ -254,8 +311,7 @@
fun bindNullDevice() {
val fallbackString = context.getResources().getString(R.string.media_seamless_other_device)
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
+ val state = mediaData.copy(device = null)
player.bindPlayer(state, PACKAGE)
assertThat(seamless.isEnabled()).isTrue()
assertThat(seamlessText.getText()).isEqualTo(fallbackString)
@@ -265,9 +321,7 @@
@Test
fun bindDeviceResumptionPlayer() {
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null,
- resumption = true)
+ val state = mediaData.copy(resumption = true)
player.bindPlayer(state, PACKAGE)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
assertThat(seamless.isEnabled()).isFalse()
@@ -323,9 +377,7 @@
fun dismissButtonClick() {
val mediaKey = "key for dismissal"
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
- notificationKey = KEY)
+ val state = mediaData.copy(notificationKey = KEY)
player.bindPlayer(state, mediaKey)
assertThat(dismiss.isEnabled).isEqualTo(true)
@@ -337,9 +389,7 @@
fun dismissButtonDisabled() {
val mediaKey = "key for dismissal"
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
- isClearable = false, notificationKey = KEY)
+ val state = mediaData.copy(isClearable = false, notificationKey = KEY)
player.bindPlayer(state, mediaKey)
assertThat(dismiss.isEnabled).isEqualTo(false)
@@ -351,9 +401,7 @@
whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
player.attachPlayer(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
- notificationKey = KEY)
+ val state = mediaData.copy(notificationKey = KEY)
player.bindPlayer(state, mediaKey)
assertThat(dismiss.isEnabled).isEqualTo(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 09c83e5..7a487b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -74,8 +74,9 @@
mManager = new MediaDataCombineLatest();
mManager.addListener(mListener);
- mMediaData = new MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, true, null,
+ mMediaData = new MediaData(
+ USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
+ new ArrayList<>(), new ArrayList<>(), null, PACKAGE, null, null, null, true, null,
MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L);
mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 5a3c43c..6b203bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -96,11 +96,24 @@
setUser(USER_MAIN)
// Set up test media data
- dataMain = MediaData(USER_MAIN, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, null, null, device, true, null)
-
- dataGuest = MediaData(USER_GUEST, true, BG_COLOR, APP, null, ARTIST, TITLE, null,
- emptyList(), emptyList(), PACKAGE, null, null, device, true, null)
+ dataMain = MediaData(
+ userId = USER_MAIN,
+ initialized = true,
+ backgroundColor = BG_COLOR,
+ app = APP,
+ appIcon = null,
+ artist = ARTIST,
+ song = TITLE,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = PACKAGE,
+ token = null,
+ clickIntent = null,
+ device = device,
+ active = true,
+ resumeAction = null)
+ dataGuest = dataMain.copy(userId = USER_GUEST)
`when`(smartspaceData.targetId).thenReturn(SMARTSPACE_KEY)
`when`(smartspaceData.isActive).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index e2019e0..d0b957c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -11,12 +11,15 @@
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
+import android.media.session.PlaybackState
import android.os.Bundle
import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.media.utils.MediaConstants
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
@@ -67,6 +70,7 @@
@JvmField @Rule val mockito = MockitoJUnit.rule()
@Mock lateinit var mediaControllerFactory: MediaControllerFactory
@Mock lateinit var controller: MediaController
+ @Mock lateinit var transportControls: MediaController.TransportControls
@Mock lateinit var playbackInfo: MediaController.PlaybackInfo
lateinit var session: MediaSession
lateinit var metadataBuilder: MediaMetadata.Builder
@@ -87,6 +91,7 @@
@Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
@Mock private lateinit var mediaRecommendationItem: SmartspaceAction
@Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction
+ @Mock private lateinit var mediaFlags: MediaFlags
lateinit var mediaDataManager: MediaDataManager
lateinit var mediaNotification: StatusBarNotification
@Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
@@ -122,7 +127,8 @@
useMediaResumption = true,
useQsMediaPlayer = true,
systemClock = clock,
- tunerService = tunerService
+ tunerService = tunerService,
+ mediaFlags = mediaFlags
)
verify(tunerService).addTunable(capture(tunableCaptor),
eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -140,6 +146,7 @@
putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
}
whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller)
+ whenever(controller.transportControls).thenReturn(transportControls)
whenever(controller.playbackInfo).thenReturn(playbackInfo)
whenever(playbackInfo.playbackType).thenReturn(
MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL)
@@ -161,6 +168,7 @@
whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem))
whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L)
+ whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false)
}
@After
@@ -583,4 +591,157 @@
assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo(
MediaDataManager.MAX_COMPACT_ACTIONS)
}
+
+ @Test
+ fun testPlaybackActions_noState_usesNotification() {
+ val desc = "Notification Action"
+ whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+ whenever(controller.playbackState).thenReturn(null)
+
+ val notifWithAction = SbnBuilder().run {
+ setPkg(PACKAGE_NAME)
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+ it.addAction(android.R.drawable.ic_media_play, desc, null)
+ }
+ build()
+ }
+ mediaDataManager.onNotificationAdded(KEY, notifWithAction)
+
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(0))
+
+ assertThat(mediaDataCaptor.value!!.semanticActions).isNull()
+ assertThat(mediaDataCaptor.value!!.actions).hasSize(1)
+ assertThat(mediaDataCaptor.value!!.actions[0]!!.contentDescription).isEqualTo(desc)
+ }
+
+ @Test
+ fun testPlaybackActions_hasPrevNext() {
+ val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
+ whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+ val stateActions = PlaybackState.ACTION_PLAY or
+ PlaybackState.ACTION_SKIP_TO_PREVIOUS or
+ PlaybackState.ACTION_SKIP_TO_NEXT
+ val stateBuilder = PlaybackState.Builder()
+ .setActions(stateActions)
+ customDesc.forEach {
+ stateBuilder.addCustomAction("action: $it", it, android.R.drawable.ic_media_pause)
+ }
+ whenever(controller.playbackState).thenReturn(stateBuilder.build())
+
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(0))
+
+ assertThat(mediaDataCaptor.value!!.semanticActions).isNotNull()
+ val actions = mediaDataCaptor.value!!.semanticActions!!
+
+ assertThat(actions.playOrPause).isNotNull()
+ assertThat(actions.playOrPause!!.contentDescription).isEqualTo(
+ context.getString(R.string.controls_media_button_play))
+ actions.playOrPause!!.action!!.run()
+ verify(transportControls).play()
+
+ assertThat(actions.prevOrCustom).isNotNull()
+ assertThat(actions.prevOrCustom!!.contentDescription).isEqualTo(
+ context.getString(R.string.controls_media_button_prev))
+ actions.prevOrCustom!!.action!!.run()
+ verify(transportControls).skipToPrevious()
+
+ assertThat(actions.nextOrCustom).isNotNull()
+ assertThat(actions.nextOrCustom!!.contentDescription).isEqualTo(
+ context.getString(R.string.controls_media_button_next))
+ actions.nextOrCustom!!.action!!.run()
+ verify(transportControls).skipToNext()
+
+ assertThat(actions.startCustom).isNotNull()
+ assertThat(actions.startCustom!!.contentDescription).isEqualTo(customDesc[0])
+
+ assertThat(actions.endCustom).isNotNull()
+ assertThat(actions.endCustom!!.contentDescription).isEqualTo(customDesc[1])
+ }
+
+ @Test
+ fun testPlaybackActions_noPrevNext_usesCustom() {
+ val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
+ whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+ val stateActions = PlaybackState.ACTION_PLAY
+ val stateBuilder = PlaybackState.Builder()
+ .setActions(stateActions)
+ customDesc.forEach {
+ stateBuilder.addCustomAction("action: $it", it, android.R.drawable.ic_media_pause)
+ }
+ whenever(controller.playbackState).thenReturn(stateBuilder.build())
+
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(0))
+
+ assertThat(mediaDataCaptor.value!!.semanticActions).isNotNull()
+ val actions = mediaDataCaptor.value!!.semanticActions!!
+
+ assertThat(actions.playOrPause).isNotNull()
+ assertThat(actions.playOrPause!!.contentDescription).isEqualTo(
+ context.getString(R.string.controls_media_button_play))
+
+ assertThat(actions.prevOrCustom).isNotNull()
+ assertThat(actions.prevOrCustom!!.contentDescription).isEqualTo(customDesc[0])
+
+ assertThat(actions.nextOrCustom).isNotNull()
+ assertThat(actions.nextOrCustom!!.contentDescription).isEqualTo(customDesc[1])
+
+ assertThat(actions.startCustom).isNotNull()
+ assertThat(actions.startCustom!!.contentDescription).isEqualTo(customDesc[2])
+
+ assertThat(actions.endCustom).isNotNull()
+ assertThat(actions.endCustom!!.contentDescription).isEqualTo(customDesc[3])
+ }
+
+ @Test
+ fun testPlaybackActions_reservedSpace() {
+ val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
+ whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true)
+ val stateActions = PlaybackState.ACTION_PLAY
+ val stateBuilder = PlaybackState.Builder()
+ .setActions(stateActions)
+ customDesc.forEach {
+ stateBuilder.addCustomAction("action: $it", it, android.R.drawable.ic_media_pause)
+ }
+ val extras = Bundle().apply {
+ putBoolean(MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true)
+ putBoolean(MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true)
+ }
+ whenever(controller.playbackState).thenReturn(stateBuilder.build())
+ whenever(controller.extras).thenReturn(extras)
+
+ mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(0))
+
+ assertThat(mediaDataCaptor.value!!.semanticActions).isNotNull()
+ val actions = mediaDataCaptor.value!!.semanticActions!!
+
+ assertThat(actions.playOrPause).isNotNull()
+ assertThat(actions.playOrPause!!.contentDescription).isEqualTo(
+ context.getString(R.string.controls_media_button_play))
+
+ assertThat(actions.prevOrCustom).isNull()
+ assertThat(actions.nextOrCustom).isNull()
+
+ assertThat(actions.startCustom).isNotNull()
+ assertThat(actions.startCustom!!.contentDescription).isEqualTo(customDesc[0])
+
+ assertThat(actions.endCustom).isNotNull()
+ assertThat(actions.endCustom!!.contentDescription).isEqualTo(customDesc[1])
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 7dadbad..3d59497 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -102,9 +102,24 @@
// Create a media sesssion and notification for testing.
session = MediaSession(context, SESSION_KEY)
- mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
- emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
- device = null, active = true, resumeAction = null)
+ mediaData = MediaData(
+ userId = USER_ID,
+ initialized = true,
+ backgroundColor = 0,
+ app = PACKAGE,
+ appIcon = null,
+ artist = null,
+ song = SESSION_TITLE,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = PACKAGE,
+ token = session.sessionToken,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null)
+
whenever(controllerFactory.create(session.sessionToken))
.thenReturn(controller)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index 421f9be..ceeb0db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -163,8 +163,27 @@
isPlaying: Boolean?,
location: Int,
resumption: Boolean
- ) =
- MediaData(0, false, 0, app, null, null, null, null, emptyList(), emptyList<Int>(),
- "package:" + app, null, null, null, true, null, location, resumption, "key:" + app,
- false, isPlaying)
+ ) = MediaData(
+ userId = 0,
+ initialized = false,
+ backgroundColor = 0,
+ app = app,
+ appIcon = null,
+ artist = null,
+ song = null,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = "package: $app",
+ token = null,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null,
+ playbackLocation = location,
+ resumption = resumption,
+ notificationKey = "key: $app",
+ hasCheckedForResume = false,
+ isPlaying = isPlaying
+ )
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index de2235d..8c2fed5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -95,13 +95,26 @@
setPlaybackState(playbackBuilder.build())
}
session.setActive(true)
- mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
- emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
- device = null, active = true, resumeAction = null)
- resumeData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
- emptyList(), emptyList(), PACKAGE, null, clickIntent = null,
- device = null, active = false, resumeAction = null, resumption = true)
+ mediaData = MediaData(
+ userId = USER_ID,
+ initialized = true,
+ backgroundColor = 0,
+ app = PACKAGE,
+ appIcon = null,
+ artist = null,
+ song = SESSION_TITLE,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = PACKAGE,
+ token = session.sessionToken,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null)
+
+ resumeData = mediaData.copy(token = null, active = false, resumption = true)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index c1562c1..6f4e619 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -185,8 +185,8 @@
protected Fragment instantiate(Context context, String className, Bundle arguments) {
CommandQueue commandQueue = new CommandQueue(context);
return new QSFragment(
- new RemoteInputQuickSettingsDisabler(context, mock(ConfigurationController.class),
- commandQueue),
+ new RemoteInputQuickSettingsDisabler(context, commandQueue,
+ mock(ConfigurationController.class)),
mock(QSTileHost.class),
mock(StatusBarStateController.class),
commandQueue,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index ca8903b..95e7a98c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
+import static android.provider.Settings.Global.AIRPLANE_MODE_ON;
+
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
@@ -19,7 +21,6 @@
import static org.mockito.Mockito.when;
import android.animation.Animator;
-import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
@@ -37,7 +38,6 @@
import android.view.View;
import android.view.WindowManager;
-import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -47,7 +47,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -70,7 +69,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -78,8 +76,6 @@
public class InternetDialogControllerTest extends SysuiTestCase {
private static final int SUB_ID = 1;
- private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
- private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
//SystemUIToast
private static final int GRAVITY_FLAGS = Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL;
@@ -142,7 +138,7 @@
private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableResources mTestableResources;
- private MockInternetDialogController mInternetDialogController;
+ private InternetDialogController mInternetDialogController;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private List<WifiEntry> mAccessPoints = new ArrayList<>();
private List<WifiEntry> mWifiEntries = new ArrayList<>();
@@ -170,7 +166,7 @@
when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS);
when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator);
- mInternetDialogController = new MockInternetDialogController(mContext,
+ mInternetDialogController = new InternetDialogController(mContext,
mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
mSubscriptionManager, mTelephonyManager, mWifiManager,
mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
@@ -225,7 +221,7 @@
@Test
public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("airplane_mode")));
@@ -233,7 +229,7 @@
@Test
public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
getResourcesString("quick_settings_internet_label")));
@@ -241,14 +237,14 @@
@Test
public void getSubtitleText_withAirplaneModeOn_returnNull() {
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
}
@Test
public void getSubtitleText_withWifiOff_returnWifiIsOff() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(false);
assertThat(mInternetDialogController.getSubtitleText(false))
@@ -263,7 +259,7 @@
@Test
public void getSubtitleText_withNoWifiEntry_returnSearchWifi() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
@@ -280,7 +276,7 @@
@Test
public void getSubtitleText_withWifiEntry_returnTapToConnect() {
// The preconditions WiFi Entries is already in setUp()
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
assertThat(mInternetDialogController.getSubtitleText(false))
@@ -295,7 +291,7 @@
@Test
public void getSubtitleText_deviceLockedWithWifiOn_returnUnlockToViewNetworks() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
@@ -305,7 +301,7 @@
@Test
public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
@@ -319,7 +315,7 @@
@Test
public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
when(mWifiManager.isWifiEnabled()).thenReturn(true);
mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
@@ -420,7 +416,7 @@
@Test
public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
@@ -433,7 +429,7 @@
@Test
public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mWifiEntry1);
@@ -448,7 +444,7 @@
@Test
public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -463,7 +459,7 @@
@Test
public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -480,7 +476,7 @@
@Test
public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -497,7 +493,7 @@
// Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
@@ -508,7 +504,7 @@
@Test
public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mConnectedEntry);
mAccessPoints.add(mWifiEntry1);
@@ -526,7 +522,7 @@
// Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
@@ -537,7 +533,7 @@
@Test
public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(true);
+ fakeAirplaneModeEnabled(true);
mAccessPoints.clear();
mAccessPoints.add(mWifiEntry1);
mAccessPoints.add(mWifiEntry2);
@@ -566,7 +562,7 @@
// Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
reset(mInternetDialogCallback);
- mInternetDialogController.setAirplaneModeEnabled(false);
+ fakeAirplaneModeEnabled(false);
mInternetDialogController.onAccessPointsChanged(mAccessPoints);
@@ -576,6 +572,23 @@
}
@Test
+ public void onAccessPointsChanged_wifiIsDefaultButNoInternetAccess_putIntoWifiEntries() {
+ reset(mInternetDialogCallback);
+ mAccessPoints.clear();
+ when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+ when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+ when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
+ mAccessPoints.add(mWifiEntry1);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ }
+
+ @Test
public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() {
when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
.thenReturn(true);
@@ -641,38 +654,7 @@
mContext.getPackageName());
}
- private class MockInternetDialogController extends InternetDialogController {
-
- private GlobalSettings mGlobalSettings;
- private boolean mIsAirplaneModeOn;
-
- MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
- ActivityStarter starter, AccessPointController accessPointController,
- SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
- @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
- @Main Handler handler, @Main Executor mainExecutor,
- BroadcastDispatcher broadcastDispatcher,
- KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
- KeyguardStateController keyguardStateController, WindowManager windowManager,
- ToastFactory toastFactory, Handler workerHandler,
- CarrierConfigTracker carrierConfigTracker,
- LocationController locationController,
- DialogLaunchAnimator dialogLaunchAnimator) {
- super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
- telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
- broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
- keyguardStateController, windowManager, toastFactory, workerHandler,
- carrierConfigTracker, locationController, dialogLaunchAnimator);
- mGlobalSettings = globalSettings;
- }
-
- @Override
- boolean isAirplaneModeEnabled() {
- return mIsAirplaneModeOn;
- }
-
- public void setAirplaneModeEnabled(boolean enabled) {
- mIsAirplaneModeOn = enabled;
- }
+ private void fakeAirplaneModeEnabled(boolean enabled) {
+ when(mGlobalSettings.getInt(eq(AIRPLANE_MODE_ON), anyInt())).thenReturn(enabled ? 1 : 0);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
deleted file mode 100644
index b359b9c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2017 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 com.android.systemui.statusbar.policy;
-
-import static junit.framework.TestCase.assertTrue;
-
-import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.content.res.Configuration;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RemoteInputQuickSettingsDisablerTest extends SysuiTestCase {
-
- @Mock
- private CommandQueue mCommandQueue;
- private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mRemoteInputQuickSettingsDisabler = new RemoteInputQuickSettingsDisabler(mContext,
- mock(ConfigurationController.class), mCommandQueue);
- }
-
- @Test
- public void shouldEnableQuickSetting_afterDeactiviate() {
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
- assertFalse(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
- @Test
- public void shouldDisableQuickSetting_afteActiviate() {
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.FALSE);
- mRemoteInputQuickSettingsDisabler.setRemoteInputActive(Boolean.TRUE);
- assertTrue(mRemoteInputQuickSettingsDisabler.mRemoteInputActive);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
- @Test
- public void testChangeToLandscape() {
- Configuration c = new Configuration(mContext.getResources().getConfiguration());
- c.orientation = Configuration.ORIENTATION_PORTRAIT;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- c.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- assertTrue(mRemoteInputQuickSettingsDisabler.misLandscape);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
- @Test
- public void testChangeToPortrait() {
- Configuration c = new Configuration(mContext.getResources().getConfiguration());
- c.orientation = Configuration.ORIENTATION_LANDSCAPE;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- c.orientation = Configuration.ORIENTATION_PORTRAIT;
- mRemoteInputQuickSettingsDisabler.onConfigChanged(c);
- assertFalse(mRemoteInputQuickSettingsDisabler.misLandscape);
- verify(mCommandQueue, atLeastOnce()).recomputeDisableFlags(anyInt(), anyBoolean());
- }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
new file mode 100644
index 0000000..1ab0582
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 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 com.android.systemui.statusbar.policy
+
+import android.app.StatusBarManager
+import android.content.res.Configuration
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class RemoteInputQuickSettingsDisablerTest : SysuiTestCase() {
+
+ @Mock lateinit var commandQueue: CommandQueue
+ private lateinit var remoteInputQuickSettingsDisabler: RemoteInputQuickSettingsDisabler
+ private lateinit var configuration: Configuration
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ remoteInputQuickSettingsDisabler = RemoteInputQuickSettingsDisabler(
+ mContext,
+ commandQueue, Mockito.mock(ConfigurationController::class.java)
+ )
+ configuration = Configuration(mContext.resources.configuration)
+
+ // Default these conditions to what they need to be to disable QS.
+ mContext.orCreateTestableResources
+ .addOverride(R.bool.config_use_split_notification_shade, /* value= */false)
+ remoteInputQuickSettingsDisabler.setRemoteInputActive(true)
+ configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+ }
+
+ @Test
+ fun whenRemoteInputActiveAndLandscapeAndNotSplitShade_shouldDisableQs() {
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun whenRemoteInputNotActive_shouldNotDisableQs() {
+ remoteInputQuickSettingsDisabler.setRemoteInputActive(false)
+
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun whenSplitShadeEnabled_shouldNotDisableQs() {
+ mContext.orCreateTestableResources
+ .addOverride(R.bool.config_use_split_notification_shade, /* value= */true)
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun whenPortrait_shouldNotDisableQs() {
+ configuration.orientation = Configuration.ORIENTATION_PORTRAIT
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+
+ assertThat(
+ shouldDisableQs(
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(0)
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun whenRemoteInputChanges_recomputeTriggered() {
+ remoteInputQuickSettingsDisabler.setRemoteInputActive(false)
+
+ verify(commandQueue, atLeastOnce()).recomputeDisableFlags(
+ anyInt(), anyBoolean()
+ )
+ }
+
+ @Test
+ fun whenConfigChanges_recomputeTriggered() {
+ configuration.orientation = Configuration.ORIENTATION_PORTRAIT
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+
+ verify(commandQueue, atLeastOnce()).recomputeDisableFlags(
+ anyInt(), anyBoolean()
+ )
+ }
+
+ private fun shouldDisableQs(state: Int): Boolean {
+ return state and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
new file mode 100644
index 0000000..db7a8516
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -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 com.android.systemui.unfold.util
+
+import android.animation.ValueAnimator
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
+
+ @Mock
+ lateinit var contentResolver: ContentResolver
+
+ @Mock
+ lateinit var sourceProvider: UnfoldTransitionProgressProvider
+
+ @Mock
+ lateinit var sinkProvider: TransitionProgressListener
+
+ lateinit var progressProvider: ScaleAwareTransitionProgressProvider
+
+ private val sourceProviderListenerCaptor =
+ ArgumentCaptor.forClass(TransitionProgressListener::class.java)
+
+ private val animatorDurationScaleListenerCaptor =
+ ArgumentCaptor.forClass(ContentObserver::class.java)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ progressProvider = ScaleAwareTransitionProgressProvider(
+ sourceProvider,
+ contentResolver
+ )
+
+ verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
+ verify(contentResolver).registerContentObserver(any(), any(),
+ animatorDurationScaleListenerCaptor.capture())
+
+ progressProvider.addCallback(sinkProvider)
+ }
+
+ @Test
+ fun onTransitionStarted_animationsEnabled_eventReceived() {
+ setAnimationsEnabled(true)
+
+ source.onTransitionStarted()
+
+ verify(sinkProvider).onTransitionStarted()
+ }
+
+ @Test
+ fun onTransitionStarted_animationsNotEnabled_eventNotReceived() {
+ setAnimationsEnabled(false)
+
+ source.onTransitionStarted()
+
+ verifyNoMoreInteractions(sinkProvider)
+ }
+
+ @Test
+ fun onTransitionEnd_animationsEnabled_eventReceived() {
+ setAnimationsEnabled(true)
+
+ source.onTransitionFinished()
+
+ verify(sinkProvider).onTransitionFinished()
+ }
+
+ @Test
+ fun onTransitionEnd_animationsNotEnabled_eventNotReceived() {
+ setAnimationsEnabled(false)
+
+ source.onTransitionFinished()
+
+ verifyNoMoreInteractions(sinkProvider)
+ }
+
+ @Test
+ fun onTransitionProgress_animationsEnabled_eventReceived() {
+ setAnimationsEnabled(true)
+
+ source.onTransitionProgress(42f)
+
+ verify(sinkProvider).onTransitionProgress(42f)
+ }
+
+ @Test
+ fun onTransitionProgress_animationsNotEnabled_eventNotReceived() {
+ setAnimationsEnabled(false)
+
+ source.onTransitionProgress(42f)
+
+ verifyNoMoreInteractions(sinkProvider)
+ }
+
+ private fun setAnimationsEnabled(enabled: Boolean) {
+ val durationScale = if (enabled) {
+ 1f
+ } else {
+ 0f
+ }
+ ValueAnimator.setDurationScale(durationScale)
+ animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
+ }
+
+ private val source: TransitionProgressListener
+ get() = sourceProviderListenerCaptor.value
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 049018cf..e0cd472 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -406,9 +406,8 @@
@Override
public List<AssociationInfo> getAssociations(String packageName, int userId) {
- final int callingUid = getCallingUserId();
if (!checkCallerCanManageAssociationsForPackage(getContext(), userId, packageName)) {
- throw new SecurityException("Caller (uid=" + callingUid + ") does not have "
+ throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
+ "permissions to get associations for u" + userId + "/" + packageName);
}
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/PermissionsUtils.java
index 0d38fa3..45097f0 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/PermissionsUtils.java
@@ -154,14 +154,14 @@
}
static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
- if (getCallingUserId() == SYSTEM_UID) return true;
+ if (getCallingUid() == SYSTEM_UID) return true;
return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
}
static void enforceCallerCanManagerCompanionDevice(@NonNull Context context,
@Nullable String message) {
- if (getCallingUserId() == SYSTEM_UID) return;
+ if (getCallingUid() == SYSTEM_UID) return;
context.enforceCallingPermission(MANAGE_COMPANION_DEVICES, message);
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 6a26bea..b47ea4f 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -551,9 +551,9 @@
logd("canBeSpatialized false due to usage:" + attributes.getUsage());
return false;
}
- AudioDeviceAttributes[] devices =
- // going through adapter to take advantage of routing cache
- (AudioDeviceAttributes[]) mASA.getDevicesForAttributes(attributes).toArray();
+ AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
+ // going through adapter to take advantage of routing cache
+ mASA.getDevicesForAttributes(attributes).toArray(devices);
final boolean able = AudioSystem.canBeSpatialized(attributes, format, devices);
logd("canBeSpatialized returning " + able);
return able;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 641f24f..e8922eb 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -251,20 +251,29 @@
if (priorUserStates != null) {
synchronized (mPm.mLock) {
- for (int i = 0; i < allUsers.length; i++) {
- TempUserState priorUserState = priorUserStates.get(allUsers[i]);
- int enabledState = priorUserState.enabledState;
- PackageSetting pkgSetting = mPm.getPackageSettingForMutation(packageName);
- pkgSetting.setEnabled(enabledState, allUsers[i],
- priorUserState.lastDisableAppCaller);
-
+ PackageSetting pkgSetting = mPm.getPackageSettingForMutation(packageName);
+ if (pkgSetting != null) {
AndroidPackage aPkg = pkgSetting.getPkg();
boolean pkgEnabled = aPkg != null && aPkg.isEnabled();
- if (!reEnableStub && priorUserState.installed
- && ((enabledState == COMPONENT_ENABLED_STATE_DEFAULT && pkgEnabled)
- || enabledState == COMPONENT_ENABLED_STATE_ENABLED)) {
- reEnableStub = true;
+ for (int i = 0; i < allUsers.length; i++) {
+ TempUserState priorUserState = priorUserStates.get(allUsers[i]);
+ int enabledState = priorUserState.enabledState;
+ pkgSetting.setEnabled(enabledState, allUsers[i],
+ priorUserState.lastDisableAppCaller);
+ if (!reEnableStub && priorUserState.installed
+ && (
+ (enabledState == COMPONENT_ENABLED_STATE_DEFAULT && pkgEnabled)
+ || enabledState == COMPONENT_ENABLED_STATE_ENABLED)) {
+ reEnableStub = true;
+ }
}
+ } else {
+ // This should not happen. If priorUserStates != null, we are uninstalling
+ // an update of a system app. In that case, mPm.mSettings.getPackageLpr()
+ // should return a non-null value for the target packageName because
+ // restoreDisabledSystemPackageLIF() is called during deletePackageLIF().
+ Slog.w(TAG, "Missing PackageSetting after uninstalling the update for"
+ + " system app: " + packageName + ". This should not happen.");
}
mPm.mSettings.writeAllUsersPackageRestrictionsLPr();
}
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 509702f..dfa6c66 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -126,13 +126,13 @@
return null;
}
- public OverlayConfig setUpSystemPackages(
+ public OverlayConfig initPackages(
WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds,
long startTime) {
PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser();
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
- // Prepare apex package info before scanning APKs, these information are needed when
+ // Prepare apex package info before scanning APKs, this information is needed when
// scanning apk in apex.
mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService);
@@ -289,7 +289,7 @@
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
- mInstallPackageHelper.installSystemPackagesFromDir(scanDir, parseFlags, scanFlags,
+ mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags, scanFlags,
currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index d2087ee..80c2a7e 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3720,7 +3720,7 @@
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public void installSystemPackagesFromDir(File scanDir, int parseFlags, int scanFlags,
+ public void installPackagesFromDir(File scanDir, int parseFlags, int scanFlags,
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fe5bce13..0c78ab8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1975,7 +1975,7 @@
mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
final int[] userIds = mUserManager.getUserIds();
- mOverlayConfig = mInitAndSystemPackageHelper.setUpSystemPackages(packageSettings,
+ mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings,
userIds, startTime);
// Resolve the storage manager.
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 75f3725..041c4fe 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -431,6 +431,8 @@
}
}
}
+ boolean wasNonInternal = permission != null && permission.mType != TYPE_CONFIG
+ && !permission.isInternal();
boolean wasNonRuntime = permission != null && permission.mType != TYPE_CONFIG
&& !permission.isRuntime();
if (permission == null) {
@@ -476,10 +478,10 @@
r.append("DUP:");
r.append(permissionInfo.name);
}
- if ((permission.isInternal() && ownerChanged)
+ if ((permission.isInternal() && (ownerChanged || wasNonInternal))
|| (permission.isRuntime() && (ownerChanged || wasNonRuntime))) {
// If this is an internal/runtime permission and the owner has changed, or this wasn't a
- // runtime permission, then permission state should be cleaned up.
+ // internal/runtime permission, then permission state should be cleaned up.
permission.mDefinitionChanged = true;
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5c15a84..298f102 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -523,6 +523,7 @@
private boolean mPendingKeyguardOccluded;
private boolean mKeyguardOccludedChanged;
+ private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
@@ -1852,6 +1853,9 @@
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
mLogger = new MetricsLogger();
+ mScreenOffSleepTokenAcquirer = mActivityTaskManagerInternal
+ .createSleepTokenAcquirer("ScreenOff");
+
Resources res = mContext.getResources();
mWakeOnDpadKeyPress =
res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
@@ -4521,6 +4525,7 @@
if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
if (displayId == DEFAULT_DISPLAY) {
+ updateScreenOffSleepToken(true);
mRequestedOrSleepingDefaultDisplay = false;
mDefaultDisplayPolicy.screenTurnedOff();
synchronized (mLock) {
@@ -4553,6 +4558,7 @@
if (displayId == DEFAULT_DISPLAY) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
0 /* cookie */);
+ updateScreenOffSleepToken(false);
mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
mBootAnimationDismissable = false;
@@ -5073,6 +5079,15 @@
}
}
+ // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
+ private void updateScreenOffSleepToken(boolean acquire) {
+ if (acquire) {
+ mScreenOffSleepTokenAcquirer.acquire(DEFAULT_DISPLAY);
+ } else {
+ mScreenOffSleepTokenAcquirer.release(DEFAULT_DISPLAY);
+ }
+ }
+
/** {@inheritDoc} */
@Override
public void enableScreenAfterBoot() {
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index d24a3df..cf0e350 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -66,6 +66,7 @@
KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE,
KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
+ KEY_ENHANCED_METRICS_COLLECTION_ENABLED,
})
@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@Retention(RetentionPolicy.SOURCE)
@@ -156,12 +157,18 @@
"time_detector_origin_priorities_override";
/**
- * The key to override the time detector lower bound configuration. The values is the number of
+ * The key to override the time detector lower bound configuration. The value is the number of
* milliseconds since the beginning of the Unix epoch.
*/
public static final @DeviceConfigKey String KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE =
"time_detector_lower_bound_millis_override";
+ /**
+ * The key to allow extra metrics / telemetry information to be collected from internal testers.
+ */
+ public static final @DeviceConfigKey String KEY_ENHANCED_METRICS_COLLECTION_ENABLED =
+ "enhanced_metrics_collection_enabled";
+
@GuardedBy("mListeners")
private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 65f077e..2291777 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -40,6 +40,7 @@
private final boolean mTelephonyDetectionSupported;
private final boolean mGeoDetectionSupported;
private final boolean mTelephonyFallbackSupported;
+ private final boolean mEnhancedMetricsCollectionEnabled;
private final boolean mAutoDetectionEnabledSetting;
private final @UserIdInt int mUserId;
private final boolean mUserConfigAllowed;
@@ -50,6 +51,7 @@
mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
mGeoDetectionSupported = builder.mGeoDetectionSupported;
mTelephonyFallbackSupported = builder.mTelephonyFallbackSupported;
+ mEnhancedMetricsCollectionEnabled = builder.mEnhancedMetricsCollectionEnabled;
mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting;
mUserId = builder.mUserId;
@@ -81,6 +83,15 @@
return mTelephonyFallbackSupported;
}
+ /**
+ * Returns {@code true} if the device can collect / report extra metrics information for QA
+ * / testers. These metrics might involve logging more expensive or more revealing data that
+ * would not be collected from the set of public users.
+ */
+ public boolean isEnhancedMetricsCollectionEnabled() {
+ return mEnhancedMetricsCollectionEnabled;
+ }
+
/** Returns the value of the auto time zone detection enabled setting. */
public boolean getAutoDetectionEnabledSetting() {
return mAutoDetectionEnabledSetting;
@@ -227,6 +238,7 @@
&& mTelephonyDetectionSupported == that.mTelephonyDetectionSupported
&& mGeoDetectionSupported == that.mGeoDetectionSupported
&& mTelephonyFallbackSupported == that.mTelephonyFallbackSupported
+ && mEnhancedMetricsCollectionEnabled == that.mEnhancedMetricsCollectionEnabled
&& mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting
&& mLocationEnabledSetting == that.mLocationEnabledSetting
&& mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting;
@@ -235,7 +247,8 @@
@Override
public int hashCode() {
return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
- mGeoDetectionSupported, mTelephonyFallbackSupported, mAutoDetectionEnabledSetting,
+ mGeoDetectionSupported, mTelephonyFallbackSupported,
+ mEnhancedMetricsCollectionEnabled, mAutoDetectionEnabledSetting,
mLocationEnabledSetting, mGeoDetectionEnabledSetting);
}
@@ -247,6 +260,7 @@
+ ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
+ ", mGeoDetectionSupported=" + mGeoDetectionSupported
+ ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported
+ + ", mEnhancedMetricsCollectionEnabled=" + mEnhancedMetricsCollectionEnabled
+ ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting
+ ", mLocationEnabledSetting=" + mLocationEnabledSetting
+ ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting
@@ -264,6 +278,7 @@
private boolean mTelephonyDetectionSupported;
private boolean mGeoDetectionSupported;
private boolean mTelephonyFallbackSupported;
+ private boolean mEnhancedMetricsCollectionEnabled;
private boolean mAutoDetectionEnabledSetting;
private boolean mLocationEnabledSetting;
private boolean mGeoDetectionEnabledSetting;
@@ -284,6 +299,7 @@
this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported;
this.mTelephonyFallbackSupported = toCopy.mTelephonyFallbackSupported;
this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
+ this.mEnhancedMetricsCollectionEnabled = toCopy.mEnhancedMetricsCollectionEnabled;
this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting;
this.mLocationEnabledSetting = toCopy.mLocationEnabledSetting;
this.mGeoDetectionEnabledSetting = toCopy.mGeoDetectionEnabledSetting;
@@ -323,6 +339,14 @@
}
/**
+ * Sets the value for enhanced metrics collection.
+ */
+ public Builder setEnhancedMetricsCollectionEnabled(boolean enabled) {
+ mEnhancedMetricsCollectionEnabled = enabled;
+ return this;
+ }
+
+ /**
* Sets the value of the automatic time zone detection enabled setting for this device.
*/
public Builder setAutoDetectionEnabledSetting(boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
index f156f8c..ecac267 100644
--- a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -34,11 +34,13 @@
* A class that provides time zone detector state information for metrics.
*
* <p>
- * Regarding time zone ID ordinals:
+ * Regarding the use of time zone ID ordinals in metrics / telemetry:
* <p>
- * We don't want to leak user location information by reporting time zone IDs. Instead, time zone
- * IDs are consistently identified within a given instance of this class by a numeric ID. This
- * allows comparison of IDs without revealing what those IDs are.
+ * For general metrics, we don't want to leak user location information by reporting time zone
+ * IDs. Instead, time zone IDs are consistently identified within a given instance of this class by
+ * a numeric ID (ordinal). This allows comparison of IDs without revealing what those IDs are.
+ * See {@link #isEnhancedMetricsCollectionEnabled()} for the setting that enables actual IDs to be
+ * collected.
*/
public final class MetricsTimeZoneDetectorState {
@@ -54,6 +56,7 @@
@NonNull private final ConfigurationInternal mConfigurationInternal;
private final int mDeviceTimeZoneIdOrdinal;
+ @Nullable private final String mDeviceTimeZoneId;
@Nullable private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
@Nullable private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
@Nullable private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
@@ -61,11 +64,13 @@
private MetricsTimeZoneDetectorState(
@NonNull ConfigurationInternal configurationInternal,
int deviceTimeZoneIdOrdinal,
+ @Nullable String deviceTimeZoneId,
@Nullable MetricsTimeZoneSuggestion latestManualSuggestion,
@Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion,
@Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) {
mConfigurationInternal = Objects.requireNonNull(configurationInternal);
mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal;
+ mDeviceTimeZoneId = deviceTimeZoneId;
mLatestManualSuggestion = latestManualSuggestion;
mLatestTelephonySuggestion = latestTelephonySuggestion;
mLatestGeolocationSuggestion = latestGeolocationSuggestion;
@@ -83,18 +88,24 @@
@Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
@Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) {
+ boolean includeZoneIds = configurationInternal.isEnhancedMetricsCollectionEnabled();
+ String metricDeviceTimeZoneId = includeZoneIds ? deviceTimeZoneId : null;
int deviceTimeZoneIdOrdinal =
tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
MetricsTimeZoneSuggestion latestCanonicalManualSuggestion =
- createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion);
+ createMetricsTimeZoneSuggestion(
+ tzIdOrdinalGenerator, latestManualSuggestion, includeZoneIds);
MetricsTimeZoneSuggestion latestCanonicalTelephonySuggestion =
- createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion);
+ createMetricsTimeZoneSuggestion(
+ tzIdOrdinalGenerator, latestTelephonySuggestion, includeZoneIds);
MetricsTimeZoneSuggestion latestCanonicalGeolocationSuggestion =
- createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion);
+ createMetricsTimeZoneSuggestion(
+ tzIdOrdinalGenerator, latestGeolocationSuggestion, includeZoneIds);
return new MetricsTimeZoneDetectorState(
- configurationInternal, deviceTimeZoneIdOrdinal, latestCanonicalManualSuggestion,
- latestCanonicalTelephonySuggestion, latestCanonicalGeolocationSuggestion);
+ configurationInternal, deviceTimeZoneIdOrdinal, metricDeviceTimeZoneId,
+ latestCanonicalManualSuggestion, latestCanonicalTelephonySuggestion,
+ latestCanonicalGeolocationSuggestion);
}
/** Returns true if the device supports telephony time zone detection. */
@@ -112,6 +123,11 @@
return mConfigurationInternal.isTelephonyFallbackSupported();
}
+ /** Returns true if enhanced metric collection is enabled. */
+ public boolean isEnhancedMetricsCollectionEnabled() {
+ return mConfigurationInternal.isEnhancedMetricsCollectionEnabled();
+ }
+
/** Returns true if user's location can be used generally. */
public boolean getUserLocationEnabledSetting() {
return mConfigurationInternal.getLocationEnabledSetting();
@@ -142,7 +158,7 @@
}
/**
- * Returns the ordinal for the device's currently set time zone ID.
+ * Returns the ordinal for the device's current time zone ID.
* See {@link MetricsTimeZoneDetectorState} for information about ordinals.
*/
public int getDeviceTimeZoneIdOrdinal() {
@@ -150,6 +166,16 @@
}
/**
+ * Returns the device's current time zone ID. This will only be populated if {@link
+ * #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link
+ * MetricsTimeZoneDetectorState} for details.
+ */
+ @Nullable
+ public String getDeviceTimeZoneId() {
+ return mDeviceTimeZoneId;
+ }
+
+ /**
* Returns a canonical form of the last manual suggestion received.
*/
@Nullable
@@ -183,6 +209,7 @@
}
MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o;
return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal
+ && Objects.equals(mDeviceTimeZoneId, that.mDeviceTimeZoneId)
&& mConfigurationInternal.equals(that.mConfigurationInternal)
&& Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion)
&& Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion)
@@ -191,7 +218,7 @@
@Override
public int hashCode() {
- return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal,
+ return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, mDeviceTimeZoneId,
mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion);
}
@@ -200,6 +227,7 @@
return "MetricsTimeZoneDetectorState{"
+ "mConfigurationInternal=" + mConfigurationInternal
+ ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
+ + ", mDeviceTimeZoneId=" + mDeviceTimeZoneId
+ ", mLatestManualSuggestion=" + mLatestManualSuggestion
+ ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
+ ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
@@ -209,34 +237,40 @@
@Nullable
private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
@NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
- @NonNull ManualTimeZoneSuggestion manualSuggestion) {
+ @NonNull ManualTimeZoneSuggestion manualSuggestion,
+ boolean includeFullZoneIds) {
if (manualSuggestion == null) {
return null;
}
- int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId());
- return MetricsTimeZoneSuggestion.createCertain(
- new int[] { zoneIdOrdinal });
+ String suggestionZoneId = manualSuggestion.getZoneId();
+ String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null;
+ int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) };
+ return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals);
}
@Nullable
private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
@NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
- @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) {
+ @NonNull TelephonyTimeZoneSuggestion telephonySuggestion,
+ boolean includeFullZoneIds) {
if (telephonySuggestion == null) {
return null;
}
- if (telephonySuggestion.getZoneId() == null) {
+ String suggestionZoneId = telephonySuggestion.getZoneId();
+ if (suggestionZoneId == null) {
return MetricsTimeZoneSuggestion.createUncertain();
}
- int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId());
- return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal });
+ String[] metricZoneIds = includeFullZoneIds ? new String[] { suggestionZoneId } : null;
+ int[] zoneIdOrdinals = new int[] { zoneIdOrdinalGenerator.ordinal(suggestionZoneId) };
+ return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals);
}
@Nullable
private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
@NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
- @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) {
+ @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion,
+ boolean includeFullZoneIds) {
if (geolocationSuggestion == null) {
return null;
}
@@ -245,7 +279,9 @@
if (zoneIds == null) {
return MetricsTimeZoneSuggestion.createUncertain();
}
- return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds));
+ String[] metricZoneIds = includeFullZoneIds ? zoneIds.toArray(new String[0]) : null;
+ int[] zoneIdOrdinals = zoneIdOrdinalGenerator.ordinals(zoneIds);
+ return MetricsTimeZoneSuggestion.createCertain(metricZoneIds, zoneIdOrdinals);
}
/**
@@ -254,33 +290,49 @@
* MetricsTimeZoneSuggestion proto definition.
*/
public static final class MetricsTimeZoneSuggestion {
- @Nullable
- private final int[] mZoneIdOrdinals;
+ @Nullable private final String[] mZoneIds;
+ @Nullable private final int[] mZoneIdOrdinals;
- MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) {
+ private MetricsTimeZoneSuggestion(
+ @Nullable String[] zoneIds, @Nullable int[] zoneIdOrdinals) {
+ mZoneIds = zoneIds;
mZoneIdOrdinals = zoneIdOrdinals;
}
@NonNull
static MetricsTimeZoneSuggestion createUncertain() {
- return new MetricsTimeZoneSuggestion(null);
+ return new MetricsTimeZoneSuggestion(null, null);
}
@NonNull
static MetricsTimeZoneSuggestion createCertain(
- @NonNull int[] zoneIdOrdinals) {
- return new MetricsTimeZoneSuggestion(zoneIdOrdinals);
+ @Nullable String[] zoneIds, @NonNull int[] zoneIdOrdinals) {
+ return new MetricsTimeZoneSuggestion(zoneIds, zoneIdOrdinals);
}
public boolean isCertain() {
return mZoneIdOrdinals != null;
}
+ /**
+ * Returns ordinals for the time zone IDs contained in the suggestion.
+ * See {@link MetricsTimeZoneDetectorState} for information about ordinals.
+ */
@Nullable
public int[] getZoneIdOrdinals() {
return mZoneIdOrdinals;
}
+ /**
+ * Returns the time zone IDs contained in the suggestion. This will only be populated if
+ * {@link #isEnhancedMetricsCollectionEnabled()} is {@code true}. See {@link
+ * MetricsTimeZoneDetectorState} for details.
+ */
+ @Nullable
+ public String[] getZoneIds() {
+ return mZoneIds;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -290,18 +342,22 @@
return false;
}
MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o;
- return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals);
+ return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals)
+ && Arrays.equals(mZoneIds, that.mZoneIds);
}
@Override
public int hashCode() {
- return Arrays.hashCode(mZoneIdOrdinals);
+ int result = Arrays.hashCode(mZoneIds);
+ result = 31 * result + Arrays.hashCode(mZoneIdOrdinals);
+ return result;
}
@Override
public String toString() {
return "MetricsTimeZoneSuggestion{"
+ "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals)
+ + ", mZoneIds=" + Arrays.toString(mZoneIds)
+ '}';
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
index 02ea433..4612f65 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
@@ -62,6 +62,7 @@
private static final Set<String> CONFIGURATION_INTERNAL_SERVER_FLAGS_KEYS_TO_WATCH =
Collections.unmodifiableSet(new ArraySet<>(new String[] {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
+ ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
@@ -296,6 +297,7 @@
isTelephonyTimeZoneDetectionFeatureSupported())
.setGeoDetectionFeatureSupported(isGeoTimeZoneDetectionFeatureSupported())
.setTelephonyFallbackSupported(isTelephonyFallbackSupported())
+ .setEnhancedMetricsCollectionEnabled(isEnhancedMetricsCollectionEnabled())
.setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting())
.setUserConfigAllowed(isUserConfigAllowed(userId))
.setLocationEnabledSetting(getLocationEnabledSetting(userId))
@@ -400,6 +402,17 @@
defaultEnabled);
}
+ /**
+ * Returns {@code true} if extra metrics / telemetry information can be collected. Used for
+ * internal testers.
+ */
+ private boolean isEnhancedMetricsCollectionEnabled() {
+ final boolean defaultEnabled = false;
+ return mServerFlags.getBoolean(
+ ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED,
+ defaultEnabled);
+ }
+
@Override
@NonNull
public synchronized String getPrimaryLocationTimeZoneProviderPackageName() {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 14784cf..f75608e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -364,6 +364,13 @@
}
}
+ @NonNull
+ MetricsTimeZoneDetectorState generateMetricsState() {
+ enforceManageTimeZoneDetectorPermission();
+
+ return mTimeZoneDetectorStrategy.generateMetricsState();
+ }
+
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index 2b912ad..8535b3d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -15,6 +15,7 @@
*/
package com.android.server.timezonedetector;
+import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_DUMP_METRICS;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_IS_GEO_DETECTION_ENABLED;
@@ -28,6 +29,7 @@
import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+import static com.android.server.timedetector.ServerFlags.KEY_ENHANCED_METRICS_COLLECTION_ENABLED;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT;
import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE;
@@ -80,6 +82,8 @@
return runSuggestTelephonyTimeZone();
case SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK:
return runEnableTelephonyFallback();
+ case SHELL_COMMAND_DUMP_METRICS:
+ return runDumpMetrics();
default: {
return handleDefaultCommands(cmd);
}
@@ -168,14 +172,22 @@
pw.println("Suggestion " + suggestion + " injected.");
return 0;
} catch (RuntimeException e) {
- pw.println(e.toString());
+ pw.println(e);
return 1;
}
}
private int runEnableTelephonyFallback() {
mInterface.enableTelephonyFallback();
- return 1;
+ return 0;
+ }
+
+ private int runDumpMetrics() {
+ final PrintWriter pw = getOutPrintWriter();
+ MetricsTimeZoneDetectorState metricsState = mInterface.generateMetricsState();
+ pw.println("MetricsTimeZoneDetectorState:");
+ pw.println(metricsState.toString());
+ return 0;
}
@Override
@@ -208,10 +220,10 @@
pw.println();
pw.printf(" %s <geolocation suggestion opts>\n",
SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE);
- pw.printf(" %s <manual suggestion opts>\n",
- SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE);
- pw.printf(" %s <telephony suggestion opts>\n",
- SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE);
+ pw.printf(" %s <manual suggestion opts>\n", SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE);
+ pw.printf(" %s <telephony suggestion opts>\n", SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE);
+ pw.printf(" %s\n", SHELL_COMMAND_DUMP_METRICS);
+ pw.printf(" Dumps the service metrics to stdout for inspection.\n");
pw.println();
GeolocationTimeZoneSuggestion.printCommandLineOpts(pw);
pw.println();
@@ -235,6 +247,8 @@
pw.printf(" %s\n", KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED);
pw.printf(" Used to enable / disable support for telephony detection fallback. Also see"
+ " the %s command.\n", SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK);
+ pw.printf(" %s\n", KEY_ENHANCED_METRICS_COLLECTION_ENABLED);
+ pw.printf(" Used to increase the detail of metrics collected / reported.\n");
pw.println();
pw.printf("[*] To be enabled, the user must still have location = on / auto time zone"
+ " detection = on.\n");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e38e9c1..7fa9861 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -126,6 +126,35 @@
}
/**
+ * Sleep tokens cause the activity manager to put the top activity to sleep.
+ * They are used by components such as dreams that may hide and block interaction
+ * with underlying activities.
+ * The Acquirer provides an interface that encapsulates the underlying work, so the user does
+ * not need to handle the token by him/herself.
+ */
+ public interface SleepTokenAcquirer {
+
+ /**
+ * Acquires a sleep token.
+ * @param displayId The display to apply to.
+ */
+ void acquire(int displayId);
+
+ /**
+ * Releases the sleep token.
+ * @param displayId The display to apply to.
+ */
+ void release(int displayId);
+ }
+
+ /**
+ * Creates a sleep token acquirer for the specified display with the specified tag.
+ *
+ * @param tag A string identifying the purpose (eg. "Dream").
+ */
+ public abstract SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag);
+
+ /**
* Returns home activity for the specified user.
*
* @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e4ed04de..0a85ba1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4544,25 +4544,17 @@
reason);
}
- /**
- * Sleep tokens cause the activity manager to put the top activity to sleep.
- * They are used by components such as dreams that may hide and block interaction
- * with underlying activities.
- */
- final class SleepTokenAcquirer {
+ final class SleepTokenAcquirerImpl implements ActivityTaskManagerInternal.SleepTokenAcquirer {
private final String mTag;
private final SparseArray<RootWindowContainer.SleepToken> mSleepTokens =
new SparseArray<>();
- SleepTokenAcquirer(@NonNull String tag) {
+ SleepTokenAcquirerImpl(@NonNull String tag) {
mTag = tag;
}
- /**
- * Acquires a sleep token.
- * @param displayId The display to apply to.
- */
- void acquire(int displayId) {
+ @Override
+ public void acquire(int displayId) {
synchronized (mGlobalLock) {
if (!mSleepTokens.contains(displayId)) {
mSleepTokens.append(displayId,
@@ -4572,11 +4564,8 @@
}
}
- /**
- * Releases the sleep token.
- * @param displayId The display to apply to.
- */
- void release(int displayId) {
+ @Override
+ public void release(int displayId) {
synchronized (mGlobalLock) {
final RootWindowContainer.SleepToken token = mSleepTokens.get(displayId);
if (token != null) {
@@ -5266,6 +5255,12 @@
final class LocalService extends ActivityTaskManagerInternal {
@Override
+ public SleepTokenAcquirer createSleepTokenAcquirer(@NonNull String tag) {
+ Objects.requireNonNull(tag);
+ return new SleepTokenAcquirerImpl(tag);
+ }
+
+ @Override
public ComponentName getHomeActivityForUser(int userId) {
synchronized (mGlobalLock) {
final ActivityRecord homeActivity =
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2e318bc..0956580 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -664,7 +664,7 @@
/** All tokens used to put activities on this root task to sleep (including mOffToken) */
final ArrayList<RootWindowContainer.SleepToken> mAllSleepTokens = new ArrayList<>();
/** The token acquirer to put root tasks on the display to sleep */
- private final ActivityTaskManagerService.SleepTokenAcquirer mOffTokenAcquirer;
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mOffTokenAcquirer;
private boolean mSleeping;
@@ -5481,14 +5481,6 @@
return mMetricsLogger;
}
- void acquireScreenOffToken(boolean acquire) {
- if (acquire) {
- mOffTokenAcquirer.acquire(mDisplayId);
- } else {
- mOffTokenAcquirer.release(mDisplayId);
- }
- }
-
void onDisplayChanged() {
mDisplay.getRealSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
@@ -5500,9 +5492,9 @@
final int displayState = mDisplayInfo.state;
if (displayId != DEFAULT_DISPLAY) {
if (displayState == Display.STATE_OFF) {
- acquireScreenOffToken(true /* acquire */);
+ mOffTokenAcquirer.acquire(mDisplayId);
} else if (displayState == Display.STATE_ON) {
- acquireScreenOffToken(false /* acquire */);
+ mOffTokenAcquirer.release(mDisplayId);
}
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
"Display %d state is now (%d), so update layer mirroring?",
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7a2a311..71ab5b6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -752,10 +752,6 @@
public void setAwake(boolean awake) {
mAwake = awake;
- // The screen off token for non-default display is controlled by DisplayContent.
- if (mDisplayContent.isDefaultDisplay) {
- mDisplayContent.acquireScreenOffToken(!awake);
- }
}
public boolean isAwake() {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 3200473..4684cb1 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -508,10 +508,10 @@
mDisplayContent.setLayoutNeeded();
if (useShellTransitions) {
- final boolean wasInTransition = mDisplayContent.inTransition();
+ final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
mDisplayContent.requestChangeTransitionIfNeeded(
ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
- if (wasInTransition) {
+ if (wasCollecting) {
// Use remote-rotation infra since the transition has already been requested
// TODO(shell-transitions): Remove this once lifecycle management can cover all
// rotation cases.
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index cabe414..baf7f87 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -75,14 +75,14 @@
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
private RootWindowContainer mRootWindowContainer;
- private final ActivityTaskManagerService.SleepTokenAcquirer mSleepTokenAcquirer;
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
KeyguardController(ActivityTaskManagerService service,
ActivityTaskSupervisor taskSupervisor) {
mService = service;
mTaskSupervisor = taskSupervisor;
- mSleepTokenAcquirer = mService.new SleepTokenAcquirer(KEYGUARD_SLEEP_TOKEN_TAG);
+ mSleepTokenAcquirer = mService.new SleepTokenAcquirerImpl(KEYGUARD_SLEEP_TOKEN_TAG);
}
void setWindowManager(WindowManagerService windowManager) {
@@ -518,10 +518,10 @@
private boolean mRequestDismissKeyguard;
private final ActivityTaskManagerService mService;
- private final ActivityTaskManagerService.SleepTokenAcquirer mSleepTokenAcquirer;
+ private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
KeyguardDisplayState(ActivityTaskManagerService service, int displayId,
- ActivityTaskManagerService.SleepTokenAcquirer acquirer) {
+ ActivityTaskManagerInternal.SleepTokenAcquirer acquirer) {
mService = service;
mDisplayId = displayId;
mSleepTokenAcquirer = acquirer;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 117b22a..c7b13eb 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -225,7 +225,7 @@
private static final String DISPLAY_OFF_SLEEP_TOKEN_TAG = "Display-off";
/** The token acquirer to put root tasks on the displays to sleep */
- final ActivityTaskManagerService.SleepTokenAcquirer mDisplayOffTokenAcquirer;
+ final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
/**
* The modes which affect which tasks are returned when calling
@@ -470,7 +470,7 @@
mService = service.mAtmService;
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
- mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirer(DISPLAY_OFF_SLEEP_TOKEN_TAG);
+ mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG);
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 978eb1d..f72f2cc 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -81,6 +81,7 @@
header_libs: [
"bionic_libc_platform_headers",
+ "bpf_connectivity_headers",
],
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 6ee6020c..a963785 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -49,6 +49,7 @@
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -112,6 +113,7 @@
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -177,6 +179,7 @@
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -240,6 +243,7 @@
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
new file mode 100644
index 0000000..9029ac5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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 com.android.server.timezonedetector;
+
+import static com.android.server.timezonedetector.MetricsTimeZoneDetectorState.DETECTION_MODE_GEO;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.UserIdInt;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+
+import com.android.server.timezonedetector.MetricsTimeZoneDetectorState.MetricsTimeZoneSuggestion;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.function.Function;
+
+/** Tests for {@link MetricsTimeZoneDetectorState}. */
+public class MetricsTimeZoneDetectorStateTest {
+
+ private static final @UserIdInt int ARBITRARY_USER_ID = 1;
+ private static final @ElapsedRealtimeLong long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L;
+ private static final String DEVICE_TIME_ZONE_ID = "DeviceTimeZoneId";
+
+ private static final ManualTimeZoneSuggestion MANUAL_TIME_ZONE_SUGGESTION =
+ new ManualTimeZoneSuggestion("ManualTimeZoneId");
+
+ private static final TelephonyTimeZoneSuggestion TELEPHONY_TIME_ZONE_SUGGESTION =
+ new TelephonyTimeZoneSuggestion.Builder(0)
+ .setZoneId("TelephonyZoneId")
+ .setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY)
+ .setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
+ .build();
+
+ private static final GeolocationTimeZoneSuggestion GEOLOCATION_TIME_ZONE_SUGGESTION =
+ GeolocationTimeZoneSuggestion.createCertainSuggestion(
+ ARBITRARY_ELAPSED_REALTIME_MILLIS,
+ Arrays.asList("GeoTimeZoneId1", "GeoTimeZoneId2"));
+
+ private final OrdinalGenerator<String> mOrdinalGenerator =
+ new OrdinalGenerator<>(Function.identity());
+
+ @Test
+ public void enhancedMetricsCollectionEnabled() {
+ final boolean enhancedMetricsCollectionEnabled = true;
+ ConfigurationInternal configurationInternal =
+ createConfigurationInternal(enhancedMetricsCollectionEnabled);
+
+ // Create the object.
+ MetricsTimeZoneDetectorState metricsTimeZoneDetectorState =
+ MetricsTimeZoneDetectorState.create(mOrdinalGenerator, configurationInternal,
+ DEVICE_TIME_ZONE_ID, MANUAL_TIME_ZONE_SUGGESTION,
+ TELEPHONY_TIME_ZONE_SUGGESTION, GEOLOCATION_TIME_ZONE_SUGGESTION);
+
+ // Assert the content.
+ assertCommonConfiguration(configurationInternal, metricsTimeZoneDetectorState);
+
+ assertEquals(DEVICE_TIME_ZONE_ID, metricsTimeZoneDetectorState.getDeviceTimeZoneId());
+ MetricsTimeZoneSuggestion expectedManualSuggestion =
+ MetricsTimeZoneSuggestion.createCertain(
+ new String[] { MANUAL_TIME_ZONE_SUGGESTION.getZoneId() },
+ new int[] { 1 });
+ assertEquals(expectedManualSuggestion,
+ metricsTimeZoneDetectorState.getLatestManualSuggestion());
+
+ MetricsTimeZoneSuggestion expectedTelephonySuggestion =
+ MetricsTimeZoneSuggestion.createCertain(
+ new String[] { TELEPHONY_TIME_ZONE_SUGGESTION.getZoneId() },
+ new int[] { 2 });
+ assertEquals(expectedTelephonySuggestion,
+ metricsTimeZoneDetectorState.getLatestTelephonySuggestion());
+
+ MetricsTimeZoneSuggestion expectedGeoSuggestion =
+ MetricsTimeZoneSuggestion.createCertain(
+ GEOLOCATION_TIME_ZONE_SUGGESTION.getZoneIds().toArray(new String[0]),
+ new int[] { 3, 4 });
+ assertEquals(expectedGeoSuggestion,
+ metricsTimeZoneDetectorState.getLatestGeolocationSuggestion());
+ }
+
+ @Test
+ public void enhancedMetricsCollectionDisabled() {
+ final boolean enhancedMetricsCollectionEnabled = false;
+ ConfigurationInternal configurationInternal =
+ createConfigurationInternal(enhancedMetricsCollectionEnabled);
+
+ // Create the object.
+ MetricsTimeZoneDetectorState metricsTimeZoneDetectorState =
+ MetricsTimeZoneDetectorState.create(mOrdinalGenerator, configurationInternal,
+ DEVICE_TIME_ZONE_ID, MANUAL_TIME_ZONE_SUGGESTION,
+ TELEPHONY_TIME_ZONE_SUGGESTION, GEOLOCATION_TIME_ZONE_SUGGESTION);
+
+ // Assert the content.
+ assertCommonConfiguration(configurationInternal, metricsTimeZoneDetectorState);
+
+ // When enhancedMetricsCollectionEnabled == false, no time zone IDs should be included.
+ assertNull(metricsTimeZoneDetectorState.getDeviceTimeZoneId());
+ final String[] omittedZoneIds = null;
+
+ MetricsTimeZoneSuggestion expectedManualSuggestion =
+ MetricsTimeZoneSuggestion.createCertain(
+ omittedZoneIds,
+ new int[] { 1 });
+ assertEquals(expectedManualSuggestion,
+ metricsTimeZoneDetectorState.getLatestManualSuggestion());
+
+ MetricsTimeZoneSuggestion expectedTelephonySuggestion =
+ MetricsTimeZoneSuggestion.createCertain(
+ omittedZoneIds,
+ new int[] { 2 });
+ assertEquals(expectedTelephonySuggestion,
+ metricsTimeZoneDetectorState.getLatestTelephonySuggestion());
+
+ MetricsTimeZoneSuggestion expectedGeoSuggestion =
+ MetricsTimeZoneSuggestion.createCertain(
+ omittedZoneIds,
+ new int[] { 3, 4 });
+ assertEquals(expectedGeoSuggestion,
+ metricsTimeZoneDetectorState.getLatestGeolocationSuggestion());
+ }
+
+ private static void assertCommonConfiguration(ConfigurationInternal configurationInternal,
+ MetricsTimeZoneDetectorState metricsTimeZoneDetectorState) {
+ assertEquals(configurationInternal.isTelephonyDetectionSupported(),
+ metricsTimeZoneDetectorState.isTelephonyDetectionSupported());
+ assertEquals(configurationInternal.isGeoDetectionSupported(),
+ metricsTimeZoneDetectorState.isGeoDetectionSupported());
+ assertEquals(configurationInternal.getAutoDetectionEnabledSetting(),
+ metricsTimeZoneDetectorState.getAutoDetectionEnabledSetting());
+ assertEquals(configurationInternal.getLocationEnabledSetting(),
+ metricsTimeZoneDetectorState.getUserLocationEnabledSetting());
+ assertEquals(configurationInternal.getGeoDetectionEnabledSetting(),
+ metricsTimeZoneDetectorState.getGeoDetectionEnabledSetting());
+ assertEquals(configurationInternal.isEnhancedMetricsCollectionEnabled(),
+ metricsTimeZoneDetectorState.isEnhancedMetricsCollectionEnabled());
+ assertEquals(0, metricsTimeZoneDetectorState.getDeviceTimeZoneIdOrdinal());
+ assertEquals(DETECTION_MODE_GEO, metricsTimeZoneDetectorState.getDetectionMode());
+ }
+
+ private static ConfigurationInternal createConfigurationInternal(
+ boolean enhancedMetricsCollectionEnabled) {
+ return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+ .setUserConfigAllowed(true)
+ .setTelephonyDetectionFeatureSupported(true)
+ .setGeoDetectionFeatureSupported(true)
+ .setEnhancedMetricsCollectionEnabled(enhancedMetricsCollectionEnabled)
+ .setAutoDetectionEnabledSetting(true)
+ .setLocationEnabledSetting(true)
+ .setGeoDetectionEnabledSetting(true)
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 193b2e3..e0e5ba0 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -379,6 +379,7 @@
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(autoDetectionEnabled)
.setLocationEnabledSetting(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index ef1b4f5..27f7814 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -87,10 +87,11 @@
private static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
- .setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
+ .setUserConfigAllowed(false)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -98,10 +99,11 @@
private static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_ENABLED =
new ConfigurationInternal.Builder(USER_ID)
- .setUserConfigAllowed(false)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
+ .setUserConfigAllowed(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(true)
@@ -109,10 +111,11 @@
private static final ConfigurationInternal CONFIG_AUTO_DETECT_NOT_SUPPORTED =
new ConfigurationInternal.Builder(USER_ID)
- .setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(false)
.setGeoDetectionFeatureSupported(false)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
+ .setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -120,10 +123,11 @@
private static final ConfigurationInternal CONFIG_AUTO_DISABLED_GEO_DISABLED =
new ConfigurationInternal.Builder(USER_ID)
- .setUserConfigAllowed(true)
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
+ .setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(false)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(false)
@@ -134,6 +138,7 @@
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
@@ -145,6 +150,7 @@
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setUserConfigAllowed(true)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
@@ -955,8 +961,20 @@
}
@Test
- public void testGenerateMetricsState() {
- ConfigurationInternal expectedInternalConfig = CONFIG_AUTO_DISABLED_GEO_DISABLED;
+ public void testGenerateMetricsState_enhancedMetricsCollection() {
+ testGenerateMetricsState(true);
+ }
+
+ @Test
+ public void testGenerateMetricsState_notEnhancedMetricsCollection() {
+ testGenerateMetricsState(false);
+ }
+
+ private void testGenerateMetricsState(boolean enhancedMetricsCollection) {
+ ConfigurationInternal expectedInternalConfig =
+ new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED_GEO_DISABLED)
+ .setEnhancedMetricsCollectionEnabled(enhancedMetricsCollection)
+ .build();
String expectedDeviceTimeZoneId = "InitialZoneId";
Script script = new Script()
@@ -1028,7 +1046,7 @@
tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,
expectedManualSuggestion, expectedTelephonySuggestion,
expectedGeolocationTimeZoneSuggestion);
- // Rely on MetricsTimeZoneDetectorState.equals() for time zone ID ordinal comparisons.
+ // Rely on MetricsTimeZoneDetectorState.equals() for time zone ID / ID ordinal comparisons.
assertEquals(expectedState, actualState);
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index a2df3130..9d01310 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -47,6 +47,7 @@
.setTelephonyDetectionFeatureSupported(true)
.setGeoDetectionFeatureSupported(true)
.setTelephonyFallbackSupported(false)
+ .setEnhancedMetricsCollectionEnabled(false)
.setAutoDetectionEnabledSetting(true)
.setLocationEnabledSetting(true)
.setGeoDetectionEnabledSetting(geoDetectionEnabledSetting)
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 3c799e0..8c78f74 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -101,6 +101,25 @@
}
}
+ /**
+ * Convenience method for running the provided action in the provided
+ * executor enclosed in
+ * {@link Binder#clearCallingIdentity}/{@link Binder#restoreCallingIdentity}
+ *
+ * Any exception thrown by the given action will need to be handled by caller.
+ *
+ */
+ public static void runWithCleanCallingIdentity(
+ @NonNull Runnable action, @NonNull Executor executor) {
+ if (action != null) {
+ if (executor != null) {
+ executor.execute(() -> runWithCleanCallingIdentity(action));
+ } else {
+ runWithCleanCallingIdentity(action);
+ }
+ }
+ }
+
/**
* Convenience method for running the provided action enclosed in
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index dfe5e6c9..6569de6 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -27,10 +27,12 @@
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* Provides the call initiation/termination, and media exchange between two IMS endpoints.
@@ -522,6 +524,7 @@
private final IImsCallSession miSession;
private boolean mClosed = false;
private Listener mListener;
+ private Executor mListenerExecutor = Runnable::run;
/** @hide */
public ImsCallSession(IImsCallSession iSession) {
@@ -538,9 +541,9 @@
}
/** @hide */
- public ImsCallSession(IImsCallSession iSession, Listener listener) {
+ public ImsCallSession(IImsCallSession iSession, Listener listener, Executor executor) {
this(iSession);
- setListener(listener);
+ setListener(listener, executor);
}
/**
@@ -738,10 +741,14 @@
* override the previous listener.
*
* @param listener to listen to the session events of this object
+ * @param executor an Executor that will execute callbacks
* @hide
*/
- public void setListener(Listener listener) {
+ public void setListener(Listener listener, Executor executor) {
mListener = listener;
+ if (executor != null) {
+ mListenerExecutor = executor;
+ }
}
/**
@@ -1206,42 +1213,48 @@
@Override
public void callSessionInitiating(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionInitiating(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionInitiating(
+ ImsCallSession.this, profile), mListenerExecutor);
}
}
@Override
public void callSessionProgressing(ImsStreamMediaProfile profile) {
if (mListener != null) {
- mListener.callSessionProgressing(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionProgressing(
+ ImsCallSession.this, profile), mListenerExecutor);
}
}
@Override
public void callSessionInitiated(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionStarted(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStarted(
+ ImsCallSession.this, profile), mListenerExecutor);
}
}
@Override
public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStartFailed(
+ ImsCallSession.this, reasonInfo), mListenerExecutor);
}
}
@Override
public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionStartFailed(
+ ImsCallSession.this, reasonInfo), mListenerExecutor);
}
}
@Override
public void callSessionTerminated(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionTerminated(
+ ImsCallSession.this, reasonInfo), mListenerExecutor);
}
}
@@ -1251,42 +1264,49 @@
@Override
public void callSessionHeld(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionHeld(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHeld(
+ ImsCallSession.this, profile), mListenerExecutor);
}
}
@Override
public void callSessionHoldFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHoldFailed(
+ ImsCallSession.this, reasonInfo), mListenerExecutor);
}
}
@Override
public void callSessionHoldReceived(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionHoldReceived(
+ ImsCallSession.this, profile), mListenerExecutor);
}
}
@Override
public void callSessionResumed(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionResumed(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionResumed(
+ ImsCallSession.this, profile), mListenerExecutor);
}
}
@Override
public void callSessionResumeFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionResumeFailed(
+ ImsCallSession.this, reasonInfo), mListenerExecutor);
}
}
@Override
public void callSessionResumeReceived(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionResumeReceived(ImsCallSession.this, profile),
+ mListenerExecutor);
}
}
@@ -1311,13 +1331,15 @@
@Override
public void callSessionMergeComplete(IImsCallSession newSession) {
if (mListener != null) {
- if (newSession != null) {
- // New session created after conference
- mListener.callSessionMergeComplete(new ImsCallSession(newSession));
- } else {
- // Session already exists. Hence no need to pass
- mListener.callSessionMergeComplete(null);
- }
+ TelephonyUtils.runWithCleanCallingIdentity(()-> {
+ if (newSession != null) {
+ // New session created after conference
+ mListener.callSessionMergeComplete(new ImsCallSession(newSession));
+ } else {
+ // Session already exists. Hence no need to pass
+ mListener.callSessionMergeComplete(null);
+ }
+ }, mListenerExecutor);
}
}
@@ -1329,7 +1351,9 @@
@Override
public void callSessionMergeFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo),
+ mListenerExecutor);
}
}
@@ -1339,21 +1363,27 @@
@Override
public void callSessionUpdated(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionUpdated(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionUpdated(ImsCallSession.this, profile),
+ mListenerExecutor);
}
}
@Override
public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo),
+ mListenerExecutor);
}
}
@Override
public void callSessionUpdateReceived(ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionUpdateReceived(ImsCallSession.this, profile),
+ mListenerExecutor);
}
}
@@ -1364,15 +1394,18 @@
public void callSessionConferenceExtended(IImsCallSession newSession,
ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionConferenceExtended(ImsCallSession.this,
- new ImsCallSession(newSession), profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionConferenceExtended(ImsCallSession.this,
+ new ImsCallSession(newSession), profile), mListenerExecutor);
}
}
@Override
public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionConferenceExtendFailed(
+ ImsCallSession.this, reasonInfo), mListenerExecutor);
}
}
@@ -1380,8 +1413,9 @@
public void callSessionConferenceExtendReceived(IImsCallSession newSession,
ImsCallProfile profile) {
if (mListener != null) {
- mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
- new ImsCallSession(newSession), profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+ new ImsCallSession(newSession), profile), mListenerExecutor);
}
}
@@ -1392,30 +1426,36 @@
@Override
public void callSessionInviteParticipantsRequestDelivered() {
if (mListener != null) {
- mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionInviteParticipantsRequestDelivered(
+ ImsCallSession.this), mListenerExecutor);
}
}
@Override
public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
- reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo), mListenerExecutor);
}
}
@Override
public void callSessionRemoveParticipantsRequestDelivered() {
if (mListener != null) {
- mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionRemoveParticipantsRequestDelivered(
+ ImsCallSession.this), mListenerExecutor);
}
}
@Override
public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
- reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo), mListenerExecutor);
}
}
@@ -1425,7 +1465,9 @@
@Override
public void callSessionConferenceStateUpdated(ImsConferenceState state) {
if (mListener != null) {
- mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state),
+ mListenerExecutor);
}
}
@@ -1435,7 +1477,9 @@
@Override
public void callSessionUssdMessageReceived(int mode, String ussdMessage) {
if (mListener != null) {
- mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode,
+ ussdMessage), mListenerExecutor);
}
}
@@ -1453,8 +1497,9 @@
@Override
public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
if (mListener != null) {
- mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
- targetNetworkType);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType), mListenerExecutor);
}
}
@@ -1465,8 +1510,9 @@
public void callSessionHandover(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
- targetNetworkType, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo), mListenerExecutor);
}
}
@@ -1477,8 +1523,9 @@
public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
- targetNetworkType, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo), mListenerExecutor);
}
}
@@ -1488,7 +1535,9 @@
@Override
public void callSessionTtyModeReceived(int mode) {
if (mListener != null) {
- mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionTtyModeReceived(ImsCallSession.this, mode),
+ mListenerExecutor);
}
}
@@ -1500,14 +1549,18 @@
*/
public void callSessionMultipartyStateChanged(boolean isMultiParty) {
if (mListener != null) {
- mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionMultipartyStateChanged(ImsCallSession.this,
+ isMultiParty), mListenerExecutor);
}
}
@Override
public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppServiceInfo ) {
if (mListener != null) {
- mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionSuppServiceReceived(ImsCallSession.this,
+ suppServiceInfo), mListenerExecutor);
}
}
@@ -1517,7 +1570,9 @@
@Override
public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) {
if (mListener != null) {
- mListener.callSessionRttModifyRequestReceived(ImsCallSession.this, callProfile);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionRttModifyRequestReceived(ImsCallSession.this,
+ callProfile), mListenerExecutor);
}
}
@@ -1527,7 +1582,9 @@
@Override
public void callSessionRttModifyResponseReceived(int status) {
if (mListener != null) {
- mListener.callSessionRttModifyResponseReceived(status);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionRttModifyResponseReceived(status),
+ mListenerExecutor);
}
}
@@ -1537,7 +1594,8 @@
@Override
public void callSessionRttMessageReceived(String rttMessage) {
if (mListener != null) {
- mListener.callSessionRttMessageReceived(rttMessage);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionRttMessageReceived(rttMessage), mListenerExecutor);
}
}
@@ -1547,21 +1605,26 @@
@Override
public void callSessionRttAudioIndicatorChanged(ImsStreamMediaProfile profile) {
if (mListener != null) {
- mListener.callSessionRttAudioIndicatorChanged(profile);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionRttAudioIndicatorChanged(profile),
+ mListenerExecutor);
}
}
@Override
public void callSessionTransferred() {
if (mListener != null) {
- mListener.callSessionTransferred(ImsCallSession.this);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionTransferred(ImsCallSession.this), mListenerExecutor);
}
}
@Override
public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo),
+ mListenerExecutor);
}
}
@@ -1572,7 +1635,8 @@
@Override
public void callSessionDtmfReceived(char dtmf) {
if (mListener != null) {
- mListener.callSessionDtmfReceived(dtmf);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callSessionDtmfReceived(
+ dtmf), mListenerExecutor);
}
}
@@ -1582,7 +1646,8 @@
@Override
public void callQualityChanged(CallQuality callQuality) {
if (mListener != null) {
- mListener.callQualityChanged(callQuality);
+ TelephonyUtils.runWithCleanCallingIdentity(()-> mListener.callQualityChanged(
+ callQuality), mListenerExecutor);
}
}
@@ -1594,8 +1659,9 @@
public void callSessionRtpHeaderExtensionsReceived(
@NonNull List<RtpHeaderExtension> extensions) {
if (mListener != null) {
- mListener.callSessionRtpHeaderExtensionsReceived(
- new ArraySet<RtpHeaderExtension>(extensions));
+ TelephonyUtils.runWithCleanCallingIdentity(()->
+ mListener.callSessionRtpHeaderExtensionsReceived(
+ new ArraySet<RtpHeaderExtension>(extensions)), mListenerExecutor);
}
}
}
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index da5468e..b21d7b5 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -44,6 +44,7 @@
import com.android.cts.install.lib.Install;
import com.android.cts.install.lib.InstallUtils;
import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
import org.junit.After;
import org.junit.Before;
@@ -108,6 +109,7 @@
@Test
public void cleanUp() throws Exception {
Files.deleteIfExists(mTestStateFile.toPath());
+ Uninstall.packages(TestApp.A, TestApp.B);
}
@Test