From 0ccbd4e52c4f70ea50df3fe7860b23971f7c46bb Mon Sep 17 00:00:00 2001 From: Tim Peng Date: Tue, 1 Jun 2021 12:06:26 +0800 Subject: Disable group volume control bar in output switcher systemUI dialog -Disable static(Pre-defined) group volume seekbar -Disable dynamic group volume seekbar -Disable group volume seekbar in edit page Bug: 189270378 Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest MediaOutputGroupAdapterTest MediaOutputGroupDialogTest Change-Id: Ifc560e21a3ae8a4830be5c5c5d1fb94f452e36b0 --- packages/SystemUI/res/layout/media_output_list_item.xml | 1 + .../android/systemui/media/dialog/MediaOutputBaseAdapter.java | 9 +++++++++ .../com/android/systemui/media/dialog/MediaOutputController.java | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml index b563633f24fe..16c03e124794 100644 --- a/packages/SystemUI/res/layout/media_output_list_item.xml +++ b/packages/SystemUI/res/layout/media_output_list_item.xml @@ -81,6 +81,7 @@ android:visibility="gone"/> diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index c6373f5e0c5f..1ff7772be260 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -202,6 +202,9 @@ public abstract class MediaOutputBaseAdapter extends } void initSeekbar(MediaDevice device) { + if (!mController.isVolumeControlEnabled(device)) { + disableSeekBar(); + } mSeekBar.setMax(device.getMaxVolume()); mSeekBar.setMin(0); final int currentVolume = device.getCurrentVolume(); @@ -230,6 +233,7 @@ public abstract class MediaOutputBaseAdapter extends } void initSessionSeekbar() { + disableSeekBar(); mSeekBar.setMax(mController.getSessionVolumeMax()); mSeekBar.setMin(0); final int currentVolume = mController.getSessionVolume(); @@ -318,5 +322,10 @@ public abstract class MediaOutputBaseAdapter extends PorterDuff.Mode.SRC_IN)); return BluetoothUtils.buildAdvancedDrawable(mContext, drawable); } + + private void disableSeekBar() { + mSeekBar.setEnabled(false); + mSeekBar.setOnTouchListener((v, event) -> true); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 5dd2f065cd4f..784d8e0c467f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -475,6 +475,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK)); } + boolean isVolumeControlEnabled(@NonNull MediaDevice device) { + return !device.getFeatures().contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK); + } + private final MediaController.Callback mCb = new MediaController.Callback() { @Override public void onMetadataChanged(MediaMetadata metadata) { -- cgit v1.2.3 From 9e80f5e2388799778a678390c27a0d0f332136bb Mon Sep 17 00:00:00 2001 From: Hui Yu Date: Thu, 3 Jun 2021 12:53:03 -0700 Subject: Add a device config for FGS denied notification. 1. Add a flag mFgsStartRestrictionNotificationEnabled to control whether display a notification when a FGS is restricted by BG-FGS-start. Default is false. 2. Use device config key "default_fgs_starts_restriction_notification_enabled" to turn it on/off. Bug: 187977406 Test: adb shell device_config get activity_manager default_fgs_starts_restriction_notification_enabled Test: adb shell device_config put activity_manager default_fgs_starts_restriction_notification_enabled [true|false] Change-Id: Ie1e1d5351b9f8326a9cb3d6685a8f2cf53978685 --- .../java/com/android/server/am/ActiveServices.java | 4 +++- .../server/am/ActivityManagerConstants.java | 24 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 144ab9b061a2..8136e1493eb1 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -6117,8 +6117,10 @@ public final class ActiveServices { && code != REASON_UID_VISIBLE; } - // TODO: remove this notification after feature development is done private void showFgsBgRestrictedNotificationLocked(ServiceRecord r) { + if (!mAm.mConstants.mFgsStartRestrictionNotificationEnabled /* default is false */) { + return; + } final Context context = mAm.mContext; final String title = "Foreground Service BG-Launch Restricted"; final String content = "App restricted: " + r.mRecentCallingPackage; diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 0d19efc20785..445d0ba2ee6d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -197,6 +197,13 @@ final class ActivityManagerConstants extends ContentObserver { private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED = "default_fgs_starts_restriction_enabled"; + /** + * Default value for mFgsStartRestrictionNotificationEnabled if not explicitly set in + * Settings.Global. + */ + private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED = + "default_fgs_starts_restriction_notification_enabled"; + /** * Default value for mFgsStartRestrictionCheckCallerTargetSdk if not explicitly set in * Settings.Global. @@ -432,6 +439,10 @@ final class ActivityManagerConstants extends ContentObserver { // at all. volatile boolean mFlagFgsStartRestrictionEnabled = true; + // Whether to display a notification when a service is restricted from startForeground due to + // foreground service background start restriction. + volatile boolean mFgsStartRestrictionNotificationEnabled = false; + /** * Indicates whether the foreground service background start restriction is enabled for * caller app that is targeting S+. @@ -652,6 +663,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED: updateFgsStartsRestriction(); break; + case KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED: + updateFgsStartsRestrictionNotification(); + break; case KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK: updateFgsStartsRestrictionCheckCallerTargetSdk(); break; @@ -953,6 +967,13 @@ final class ActivityManagerConstants extends ContentObserver { /*defaultValue*/ true); } + private void updateFgsStartsRestrictionNotification() { + mFgsStartRestrictionNotificationEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED, + /*defaultValue*/ false); + } + private void updateFgsStartsRestrictionCheckCallerTargetSdk() { mFgsStartRestrictionCheckCallerTargetSdk = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -1272,6 +1293,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mFlagBackgroundFgsStartRestrictionEnabled); pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED); pw.print("="); pw.println(mFlagFgsStartRestrictionEnabled); + pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED); + pw.print("="); + pw.println(mFgsStartRestrictionNotificationEnabled); pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK); pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk); pw.print(" "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE); -- cgit v1.2.3 From 260d0a85ddf986d41340980ef5abf0a878d65d9c Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Thu, 3 Jun 2021 16:07:56 -0700 Subject: Don't export HeapDumpProvider. Stop exporting HeapDumpProvider so apps can only access generated dumps when the user explicitly shares them. Bug: 184046948 Test: capture system heap dump in developer options and confirm test app get SecurityException if it tries to access the dump directly, but gets access when the dump is shared through the notification flow Change-Id: Ibdca7cde4f563baa39163869289da5b79fc3a6db (cherry picked from commit a60c62bcb74e0146820f75f1da49581d1709b63c) --- packages/Shell/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 570c2ab3efd1..3e820c39665c 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -346,7 +346,7 @@ + android:exported="false" /> Date: Fri, 4 Jun 2021 10:04:58 -0700 Subject: Delay unmuting sensor when device is unlocked The sensor privacy service also checks if the device is locked before unmuting as an extra layer of security. It appears that the callback when requesting unlock is invoked before the state in KeyguardManager is updatd so we need to delay our call to unmute. Test: Set pin and unblock camera Fixes: 189946442 Change-Id: Ic9bf1a6abb9073acb11d9b569efcc665eacff63f --- .../systemui/sensorprivacy/SensorUseStartedActivity.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt index 06c1c6f8cefa..7a37984cf6ed 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -32,6 +32,7 @@ import android.widget.ImageView import com.android.internal.app.AlertActivity import com.android.internal.widget.DialogTitle import com.android.systemui.R +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -46,13 +47,15 @@ import javax.inject.Inject class SensorUseStartedActivity @Inject constructor( private val sensorPrivacyController: IndividualSensorPrivacyController, private val keyguardStateController: KeyguardStateController, - private val keyguardDismissUtil: KeyguardDismissUtil + private val keyguardDismissUtil: KeyguardDismissUtil, + @Background private val bgHandler: Handler ) : AlertActivity(), DialogInterface.OnClickListener { companion object { private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L + private const val UNLOCK_DELAY_MILLIS = 200L private const val CAMERA = SensorPrivacyManager.Sensors.CAMERA private const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE @@ -179,9 +182,12 @@ class SensorUseStartedActivity @Inject constructor( BUTTON_POSITIVE -> { if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) { keyguardDismissUtil.executeWhenUnlocked({ - disableSensorPrivacy() + bgHandler.postDelayed({ + disableSensorPrivacy() + }, UNLOCK_DELAY_MILLIS) + false - }, false, false) + }, false, true) } else { disableSensorPrivacy() } @@ -201,7 +207,7 @@ class SensorUseStartedActivity @Inject constructor( sensorPrivacyController .suppressSensorPrivacyReminders(sensorUsePackageName, false) } else { - Handler(mainLooper).postDelayed({ + bgHandler.postDelayed({ sensorPrivacyController .suppressSensorPrivacyReminders(sensorUsePackageName, false) }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS) -- cgit v1.2.3 From c32b60099a3682436c2a394be58bd3b1539eb2f3 Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Fri, 4 Jun 2021 20:32:31 +0800 Subject: Skip close activity transition since no closing app can be visible The layer of transition animation is usually shift to animation layer, so when transition controller going to apply a activity close transition to a bottom activity, the closing animation surface will be on top of the task, then suddenly occludes the top activity. An easy fix is to skip the close activity transtion if there is no closing activity can be visible. Bug: 186860966 Test: atest AppTransitionTests AppTransitionControllerTest Change-Id: I3869530fc1eaa87ed1403fed86ff68a1e4de5b23 --- .../com/android/server/wm/AppTransitionController.java | 8 +++++++- .../android/server/wm/AppTransitionControllerTest.java | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index e7c51a4ac65a..50ad5fa8a1ad 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -412,7 +412,13 @@ public class AppTransitionController { return TRANSIT_OLD_TASK_CLOSE; } if (isActivityClosing) { - return TRANSIT_OLD_ACTIVITY_CLOSE; + for (int i = closingApps.size() - 1; i >= 0; i--) { + if (closingApps.valueAt(i).visibleIgnoringKeyguard) { + return TRANSIT_OLD_ACTIVITY_CLOSE; + } + } + // Skip close activity transition since no closing app can be visible + return WindowManager.TRANSIT_OLD_UNSET; } } if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH) diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 57cf865268ef..c555612fef23 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -77,6 +77,24 @@ public class AppTransitionControllerTest extends WindowTestsBase { return r; } + @Test + public void testSkipOccludedActivityCloseTransition() { + final ActivityRecord behind = createActivityRecord(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final ActivityRecord topOpening = createActivityRecord(behind.getTask()); + topOpening.setOccludesParent(true); + topOpening.setVisible(true); + + mDisplayContent.prepareAppTransition(TRANSIT_OPEN); + mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); + mDisplayContent.mClosingApps.add(behind); + + assertEquals(WindowManager.TRANSIT_OLD_UNSET, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null, null, false)); + } + @Test @FlakyTest(bugId = 131005232) public void testTranslucentOpen() { -- cgit v1.2.3 From 7d6e3a0b24a53529532e6a0c14688f0681e44b62 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Thu, 3 Jun 2021 16:07:56 -0700 Subject: Don't export HeapDumpProvider. Stop exporting HeapDumpProvider so apps can only access generated dumps when the user explicitly shares them. Bug: 184046948 Test: capture system heap dump in developer options and confirm test app get SecurityException if it tries to access the dump directly, but gets access when the dump is shared through the notification flow Change-Id: Ibdca7cde4f563baa39163869289da5b79fc3a6db (cherry picked from commit a60c62bcb74e0146820f75f1da49581d1709b63c) --- packages/Shell/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 570c2ab3efd1..3e820c39665c 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -346,7 +346,7 @@ + android:exported="false" /> Date: Fri, 4 Jun 2021 16:07:23 -0700 Subject: Improve ellipsize performance Instead of iterate all ellipsized characters, only iterate the necessary ranges for copying. Bug: 188913943 Test: atest CtsTextTestCases CtsGraphicsTestCases CtsWidgetTestCases Change-Id: I3d03b1e3897e427c23fbe51315f412c57a4ce9e9 (cherry picked from commit 2c6121f3e3c52965ae33317e4fe7a273fd1742c6) --- core/java/android/text/Layout.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index c02f7ef65a77..3154acccc6d8 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2280,7 +2280,10 @@ public abstract class Layout { final int ellipsisStringLen = ellipsisString.length(); // Use the ellipsis string only if there are that at least as many characters to replace. final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen; - for (int i = 0; i < ellipsisCount; i++) { + final int min = Math.max(0, start - ellipsisStart - lineStart); + final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart); + + for (int i = min; i < max; i++) { final char c; if (useEllipsisString && i < ellipsisStringLen) { c = ellipsisString.charAt(i); @@ -2289,9 +2292,7 @@ public abstract class Layout { } final int a = i + ellipsisStart + lineStart; - if (start <= a && a < end) { - dest[destoff + a - start] = c; - } + dest[destoff + a - start] = c; } } -- cgit v1.2.3 From 5238a7a79b7e24099e3482d81dd2253bc98f2427 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Fri, 4 Jun 2021 16:07:23 -0700 Subject: Improve ellipsize performance Instead of iterate all ellipsized characters, only iterate the necessary ranges for copying. Bug: 188913943 Test: atest CtsTextTestCases CtsGraphicsTestCases CtsWidgetTestCases Change-Id: I3d03b1e3897e427c23fbe51315f412c57a4ce9e9 (cherry picked from commit 2c6121f3e3c52965ae33317e4fe7a273fd1742c6) --- core/java/android/text/Layout.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 2c2c2953ed51..826c237777d0 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2350,7 +2350,10 @@ public abstract class Layout { final int ellipsisStringLen = ellipsisString.length(); // Use the ellipsis string only if there are that at least as many characters to replace. final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen; - for (int i = 0; i < ellipsisCount; i++) { + final int min = Math.max(0, start - ellipsisStart - lineStart); + final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart); + + for (int i = min; i < max; i++) { final char c; if (useEllipsisString && i < ellipsisStringLen) { c = ellipsisString.charAt(i); @@ -2359,9 +2362,7 @@ public abstract class Layout { } final int a = i + ellipsisStart + lineStart; - if (start <= a && a < end) { - dest[destoff + a - start] = c; - } + dest[destoff + a - start] = c; } } -- cgit v1.2.3 From 599f1b76fad4876fb060240db6d11de7da605834 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Fri, 4 Jun 2021 16:07:23 -0700 Subject: Improve ellipsize performance Instead of iterate all ellipsized characters, only iterate the necessary ranges for copying. Bug: 188913943 Test: atest CtsTextTestCases CtsGraphicsTestCases CtsWidgetTestCases Change-Id: I3d03b1e3897e427c23fbe51315f412c57a4ce9e9 (cherry picked from commit 2c6121f3e3c52965ae33317e4fe7a273fd1742c6) --- core/java/android/text/Layout.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 8a4497a0f0ce..6baea1aea471 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2350,7 +2350,10 @@ public abstract class Layout { final int ellipsisStringLen = ellipsisString.length(); // Use the ellipsis string only if there are that at least as many characters to replace. final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen; - for (int i = 0; i < ellipsisCount; i++) { + final int min = Math.max(0, start - ellipsisStart - lineStart); + final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart); + + for (int i = min; i < max; i++) { final char c; if (useEllipsisString && i < ellipsisStringLen) { c = ellipsisString.charAt(i); @@ -2359,9 +2362,7 @@ public abstract class Layout { } final int a = i + ellipsisStart + lineStart; - if (start <= a && a < end) { - dest[destoff + a - start] = c; - } + dest[destoff + a - start] = c; } } -- cgit v1.2.3 From 40d7a4535aba93073264bf8998daf588f0923986 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Fri, 4 Jun 2021 16:07:23 -0700 Subject: Improve ellipsize performance Instead of iterate all ellipsized characters, only iterate the necessary ranges for copying. Bug: 188913943 Test: atest CtsTextTestCases CtsGraphicsTestCases CtsWidgetTestCases Change-Id: I3d03b1e3897e427c23fbe51315f412c57a4ce9e9 (cherry picked from commit 2c6121f3e3c52965ae33317e4fe7a273fd1742c6) --- core/java/android/text/Layout.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 8a4497a0f0ce..6baea1aea471 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2350,7 +2350,10 @@ public abstract class Layout { final int ellipsisStringLen = ellipsisString.length(); // Use the ellipsis string only if there are that at least as many characters to replace. final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen; - for (int i = 0; i < ellipsisCount; i++) { + final int min = Math.max(0, start - ellipsisStart - lineStart); + final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart); + + for (int i = min; i < max; i++) { final char c; if (useEllipsisString && i < ellipsisStringLen) { c = ellipsisString.charAt(i); @@ -2359,9 +2362,7 @@ public abstract class Layout { } final int a = i + ellipsisStart + lineStart; - if (start <= a && a < end) { - dest[destoff + a - start] = c; - } + dest[destoff + a - start] = c; } } -- cgit v1.2.3 From 50b67699e1677107df061bbdefacf67bc1bf0e62 Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Fri, 4 Jun 2021 16:07:23 -0700 Subject: Improve ellipsize performance Instead of iterate all ellipsized characters, only iterate the necessary ranges for copying. Bug: 188913943 Test: atest CtsTextTestCases CtsGraphicsTestCases CtsWidgetTestCases Change-Id: I3d03b1e3897e427c23fbe51315f412c57a4ce9e9 (cherry picked from commit 2c6121f3e3c52965ae33317e4fe7a273fd1742c6) --- core/java/android/text/Layout.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index f0f0867d414b..505f400c60d2 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2351,7 +2351,10 @@ public abstract class Layout { final int ellipsisStringLen = ellipsisString.length(); // Use the ellipsis string only if there are that at least as many characters to replace. final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen; - for (int i = 0; i < ellipsisCount; i++) { + final int min = Math.max(0, start - ellipsisStart - lineStart); + final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart); + + for (int i = min; i < max; i++) { final char c; if (useEllipsisString && i < ellipsisStringLen) { c = ellipsisString.charAt(i); @@ -2360,9 +2363,7 @@ public abstract class Layout { } final int a = i + ellipsisStart + lineStart; - if (start <= a && a < end) { - dest[destoff + a - start] = c; - } + dest[destoff + a - start] = c; } } -- cgit v1.2.3 From ae1912b62f7dfa361acfbe472cb8a49cd60f746e Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Tue, 8 Jun 2021 16:12:39 -0700 Subject: Improve ellipsize performance Instead of iterate all ellipsized characters, only iterate the necessary ranges for copying. Bug: 188913943 Test: atest CtsTextTestCases CtsGraphicsTestCases CtsWidgetTestCases Change-Id: I3d03b1e3897e427c23fbe51315f412c57a4ce9e9 Merged-In: I3d03b1e3897e427c23fbe51315f412c57a4ce9e9 --- core/java/android/text/Layout.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 8f9233f06839..38240fa7e9d9 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -2214,20 +2214,20 @@ public abstract class Layout { int ellipsisStart = getEllipsisStart(line); int linestart = getLineStart(line); - for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) { + final int min = Math.max(0, start - ellipsisStart - linestart); + final int max = Math.min(ellipsisCount, end - ellipsisStart - linestart); + + for (int i = min; i < max; i++) { char c; - if (i == ellipsisStart) { + if (i == 0) { c = getEllipsisChar(method); // ellipsis } else { c = '\uFEFF'; // 0-width space } - int a = i + linestart; - - if (a >= start && a < end) { - dest[destoff + a - start] = c; - } + int a = i + ellipsisStart + linestart; + dest[destoff + a - start] = c; } } -- cgit v1.2.3 From 06b083ffaedcf9288ad73c8ea885a925d23f08d6 Mon Sep 17 00:00:00 2001 From: Tim Peng Date: Thu, 10 Jun 2021 15:16:20 +0800 Subject: [Output Switcher] Adjust casting volume in output switcher cause icons to flicker -Only update current active item Bug: 190146269 Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest MediaOutputGroupAdapterTest MediaOutputGroupDialogTest Change-Id: I603e895b7e0e08874c53527cbb5ff177e2b97c5b --- .../systemui/media/dialog/MediaOutputAdapter.java | 22 ++++++++++++---------- .../media/dialog/MediaOutputBaseAdapter.java | 8 +++++++- .../media/dialog/MediaOutputBaseDialog.java | 7 ++++++- .../media/dialog/MediaOutputGroupAdapter.java | 6 +++--- .../media/dialog/MediaOutputBaseDialogTest.java | 1 + 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 0d5faff65aab..d64ecc8610dc 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -46,7 +46,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private ViewGroup mConnectedItem; - private boolean mInclueDynamicGroup; + private boolean mIncludeDynamicGroup; public MediaOutputAdapter(MediaOutputController controller) { super(controller); @@ -56,7 +56,6 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { super.onCreateViewHolder(viewGroup, viewType); - return new MediaDeviceViewHolder(mHolderView); } @@ -66,7 +65,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { if (position == size && mController.isZeroMode()) { viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */, true /* bottomMargin */); - } else if (mInclueDynamicGroup) { + } else if (mIncludeDynamicGroup) { if (position == 0) { viewHolder.onBind(CUSTOMIZED_ITEM_DYNAMIC_GROUP, true /* topMargin */, false /* bottomMargin */); @@ -76,11 +75,12 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { // from "position - 1". viewHolder.onBind(((List) (mController.getMediaDevices())) .get(position - 1), - false /* topMargin */, position == size /* bottomMargin */); + false /* topMargin */, position == size /* bottomMargin */, position); } } else if (position < size) { viewHolder.onBind(((List) (mController.getMediaDevices())).get(position), - position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */); + position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */, + position); } else if (DEBUG) { Log.d(TAG, "Incorrect position: " + position); } @@ -88,8 +88,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override public int getItemCount() { - mInclueDynamicGroup = mController.getSelectedMediaDevice().size() > 1; - if (mController.isZeroMode() || mInclueDynamicGroup) { + mIncludeDynamicGroup = mController.getSelectedMediaDevice().size() > 1; + if (mController.isZeroMode() || mIncludeDynamicGroup) { // Add extra one for "pair new" or dynamic group return mController.getMediaDevices().size() + 1; } @@ -120,9 +120,10 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } @Override - void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) { - super.onBind(device, topMargin, bottomMargin); - final boolean currentlyConnected = !mInclueDynamicGroup && isCurrentlyConnected(device); + void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) { + super.onBind(device, topMargin, bottomMargin, position); + final boolean currentlyConnected = !mIncludeDynamicGroup + && isCurrentlyConnected(device); if (currentlyConnected) { mConnectedItem = mContainerLayout; } @@ -160,6 +161,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */, false /* showSubtitle */); initSeekbar(device); + mCurrentActivePosition = position; } else { setSingleLineLayout(getItemTitle(device), false /* bFocused */); mContainerLayout.setOnClickListener(v -> onItemClick(v, device)); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index bcef43c93be4..0bc5cbd0f01b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -64,10 +64,12 @@ public abstract class MediaOutputBaseAdapter extends Context mContext; View mHolderView; boolean mIsDragging; + int mCurrentActivePosition; public MediaOutputBaseAdapter(MediaOutputController controller) { mController = controller; mIsDragging = false; + mCurrentActivePosition = -1; } @Override @@ -99,6 +101,10 @@ public abstract class MediaOutputBaseAdapter extends return mIsAnimating; } + int getCurrentActivePosition() { + return mCurrentActivePosition; + } + /** * ViewHolder for binding device view. */ @@ -136,7 +142,7 @@ public abstract class MediaOutputBaseAdapter extends mCheckBox = view.requireViewById(R.id.check_box); } - void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) { + void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) { mDeviceId = device.getId(); ThreadUtils.postOnBackgroundThread(() -> { Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index 8a9a6e694658..cdcdf9a1d4de 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -174,7 +174,12 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mHeaderTitle.setGravity(Gravity.NO_GRAVITY); } if (!mAdapter.isDragging() && !mAdapter.isAnimating()) { - mAdapter.notifyDataSetChanged(); + int currentActivePosition = mAdapter.getCurrentActivePosition(); + if (currentActivePosition >= 0) { + mAdapter.notifyItemChanged(currentActivePosition); + } else { + mAdapter.notifyDataSetChanged(); + } } // Show when remote media session is available mStopButton.setVisibility(getStopButtonVisibility()); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java index 24e076bb22f1..968c3506f39f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java @@ -68,7 +68,7 @@ public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter { final int size = mGroupMediaDevices.size(); if (newPosition < size) { viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */, - newPosition == (size - 1) /* bottomMargin */); + newPosition == (size - 1) /* bottomMargin */, position); return; } if (DEBUG) { @@ -94,8 +94,8 @@ public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter { } @Override - void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) { - super.onBind(device, topMargin, bottomMargin); + void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) { + super.onBind(device, topMargin, bottomMargin, position); mDivider.setVisibility(View.GONE); mAddIcon.setVisibility(View.GONE); mBottomDivider.setVisibility(View.GONE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index 589ae2e7f4f4..9bd07b88417d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -155,6 +155,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { @Test public void refresh_notInDragging_verifyUpdateAdapter() { + when(mMediaOutputBaseAdapter.getCurrentActivePosition()).thenReturn(-1); when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false); mMediaOutputBaseDialogImpl.refresh(); -- cgit v1.2.3 From c49c3b993fe9a1c262686a23704fb29aed180759 Mon Sep 17 00:00:00 2001 From: Chen Xu Date: Thu, 10 Jun 2021 14:43:21 -0700 Subject: improvements of CBR on compainion devices improve cbr on companion devices by display notification only when there is an active bluetooth connections. If devices target on S+, it requires a new runtime permission bluetooth_connect, grant those runtime permission for default emergency alert module. Bug: 181773113 Test: Manual test Change-Id: Ie4d70e1d23e20015f6085db05ca8523b1f3c2ad4 --- .../com/android/server/pm/permission/DefaultPermissionGrantPolicy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index bad7e5c27b7b..dab980a9e4b2 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -641,7 +641,7 @@ final class DefaultPermissionGrantPolicy { // Cell Broadcast Receiver grantSystemFixedPermissionsToSystemPackage(pm, getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId), - userId, SMS_PERMISSIONS); + userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS); // Carrier Provisioning Service grantPermissionsToSystemPackage(pm, -- cgit v1.2.3 From e9831122e6c0fad4b0ddf394c239b6cfc039f221 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 22 Apr 2021 16:55:09 -0400 Subject: Fix a potential thread safety issue in VectorDrawable Bug: 158839504 Bug: 185178568 Test: speculative Change-Id: Id9f229f08fe5897dda25441fbaa15c98f8130de9 --- graphics/java/android/graphics/drawable/VectorDrawable.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index aa19b2a0e94c..ad5b3b3885be 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -348,15 +348,19 @@ public class VectorDrawable extends Drawable { private final Rect mTmpBounds = new Rect(); public VectorDrawable() { - this(new VectorDrawableState(null), null); + this(null, null); } /** * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) { - mVectorState = state; + private VectorDrawable(@Nullable VectorDrawableState state, @Nullable Resources res) { + // As the mutable, not-thread-safe native instance is stored in VectorDrawableState, we + // need to always do a defensive copy even if mutate() isn't called. Otherwise + // draw() being called on 2 different VectorDrawable instances could still hit the same + // underlying native object. + mVectorState = new VectorDrawableState(state); updateLocalState(res); } -- cgit v1.2.3 From 6edabc03017fdaa60e99e47fb0da2c297949b671 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 22 Apr 2021 16:55:09 -0400 Subject: Fix a potential thread safety issue in VectorDrawable Bug: 158839504 Bug: 185178568 Test: speculative Change-Id: Id9f229f08fe5897dda25441fbaa15c98f8130de9 --- graphics/java/android/graphics/drawable/VectorDrawable.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index c71585f32155..8902a79b1bb7 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -342,15 +342,19 @@ public class VectorDrawable extends Drawable { private final Rect mTmpBounds = new Rect(); public VectorDrawable() { - this(new VectorDrawableState(null), null); + this(null, null); } /** * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) { - mVectorState = state; + private VectorDrawable(@Nullable VectorDrawableState state, @Nullable Resources res) { + // As the mutable, not-thread-safe native instance is stored in VectorDrawableState, we + // need to always do a defensive copy even if mutate() isn't called. Otherwise + // draw() being called on 2 different VectorDrawable instances could still hit the same + // underlying native object. + mVectorState = new VectorDrawableState(state); updateLocalState(res); } -- cgit v1.2.3 From 32207ceb2fb408d06924b46919fc438477fddcf0 Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 22 Apr 2021 16:55:09 -0400 Subject: Fix a potential thread safety issue in VectorDrawable Bug: 158839504 Bug: 185178568 Test: speculative Change-Id: Id9f229f08fe5897dda25441fbaa15c98f8130de9 --- graphics/java/android/graphics/drawable/VectorDrawable.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index ceac3253e178..1772ab018a6d 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -264,15 +264,19 @@ public class VectorDrawable extends Drawable { private final Rect mTmpBounds = new Rect(); public VectorDrawable() { - this(new VectorDrawableState(null), null); + this(null, null); } /** * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) { - mVectorState = state; + private VectorDrawable(@Nullable VectorDrawableState state, @Nullable Resources res) { + // As the mutable, not-thread-safe native instance is stored in VectorDrawableState, we + // need to always do a defensive copy even if mutate() isn't called. Otherwise + // draw() being called on 2 different VectorDrawable instances could still hit the same + // underlying native object. + mVectorState = new VectorDrawableState(state); updateLocalState(res); } -- cgit v1.2.3 From 304f3af54526f3d80cc037e18f4cf89f1053737c Mon Sep 17 00:00:00 2001 From: John Reck Date: Thu, 22 Apr 2021 16:55:09 -0400 Subject: Fix a potential thread safety issue in VectorDrawable Bug: 158839504 Bug: 185178568 Test: speculative Change-Id: Id9f229f08fe5897dda25441fbaa15c98f8130de9 --- graphics/java/android/graphics/drawable/VectorDrawable.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index e6fa866df3ab..a831bb86009c 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -348,15 +348,19 @@ public class VectorDrawable extends Drawable { private final Rect mTmpBounds = new Rect(); public VectorDrawable() { - this(new VectorDrawableState(null), null); + this(null, null); } /** * The one constructor to rule them all. This is called by all public * constructors to set the state and initialize local properties. */ - private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) { - mVectorState = state; + private VectorDrawable(@Nullable VectorDrawableState state, @Nullable Resources res) { + // As the mutable, not-thread-safe native instance is stored in VectorDrawableState, we + // need to always do a defensive copy even if mutate() isn't called. Otherwise + // draw() being called on 2 different VectorDrawable instances could still hit the same + // underlying native object. + mVectorState = new VectorDrawableState(state); updateLocalState(res); } -- cgit v1.2.3 From 9ecd6fbab69b0d3139abfeea99ff620329e19547 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Mon, 14 Jun 2021 22:57:11 +0800 Subject: Fixing the menu overlap with the system bars under half oval type. Action: Avoid overlapping with system bars under landscape mode, will update the margins of the menu to align the edge of system bars. But the change will also affect the shadow of the menu under landscape mode, it may cut off the shadow edge. Bug: 190339392 Test: atest AccessibilityFloatingMenuViewTest -c Change-Id: If338c02618c9b45db48e5062b84ac50359c4471a --- .../AccessibilityFloatingMenuView.java | 42 +++++++++++++++------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java index 5bb55222e09b..e891e5b64b3d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java @@ -16,6 +16,7 @@ package com.android.systemui.accessibility.floatingmenu; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.util.MathUtils.constrain; import static android.util.MathUtils.sq; import static android.view.WindowInsets.Type.ime; @@ -200,6 +201,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout mListView = listView; mWindowManager = context.getSystemService(WindowManager.class); + mLastConfiguration = new Configuration(getResources().getConfiguration()); mAdapter = new AccessibilityTargetAdapter(mTargets); mUiHandler = createUiHandler(); mPosition = position; @@ -243,7 +245,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout } }); - mLastConfiguration = new Configuration(getResources().getConfiguration()); initListView(); updateStrokeWith(getResources().getConfiguration().uiMode, mAlignment); @@ -567,8 +568,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout final int currentX = (int) event.getX(); final int currentY = (int) event.getY(); + final int marginStartEnd = getMarginStartEndWith(mLastConfiguration); final Rect touchDelegateBounds = - new Rect(mMargin, mMargin, mMargin + getLayoutWidth(), mMargin + getLayoutHeight()); + new Rect(marginStartEnd, mMargin, marginStartEnd + getLayoutWidth(), + mMargin + getLayoutHeight()); if (action == MotionEvent.ACTION_DOWN && touchDelegateBounds.contains(currentX, currentY)) { mIsDownInEnlargedTouchArea = true; @@ -682,15 +685,13 @@ public class AccessibilityFloatingMenuView extends FrameLayout mListView.setLayoutManager(layoutManager); mListView.addOnItemTouchListener(this); mListView.animate().setInterpolator(new OvershootInterpolator()); - updateListView(); + updateListViewWith(mLastConfiguration); addView(mListView); } - private void updateListView() { - final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams(); - layoutParams.setMargins(mMargin, mMargin, mMargin, mMargin); - mListView.setLayoutParams(layoutParams); + private void updateListViewWith(Configuration configuration) { + updateMarginWith(configuration); final int elevation = getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation); @@ -719,13 +720,15 @@ public class AccessibilityFloatingMenuView extends FrameLayout @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + mLastConfiguration.setTo(newConfig); + final int diff = newConfig.diff(mLastConfiguration); if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) { updateAccessibilityTitle(mCurrentLayoutParams); } updateDimensions(); - updateListView(); + updateListViewWith(newConfig); updateItemViewWith(mSizeType); updateColor(); updateStrokeWith(newConfig.uiMode, mAlignment); @@ -733,8 +736,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout updateRadiusWith(mSizeType, mRadiusType, mTargets.size()); updateScrollModeWith(hasExceededMaxLayoutHeight()); setSystemGestureExclusion(); - - mLastConfiguration.setTo(newConfig); } @VisibleForTesting @@ -756,11 +757,11 @@ public class AccessibilityFloatingMenuView extends FrameLayout } private int getMinWindowX() { - return -mMargin; + return -getMarginStartEndWith(mLastConfiguration); } private int getMaxWindowX() { - return mScreenWidth - mMargin - getLayoutWidth(); + return mScreenWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth(); } private int getMaxWindowY() { @@ -805,6 +806,15 @@ public class AccessibilityFloatingMenuView extends FrameLayout return layoutBottomY > imeY ? (layoutBottomY - imeY) : 0; } + private void updateMarginWith(Configuration configuration) { + // Avoid overlapping with system bars under landscape mode, update the margins of the menu + // to align the edge of system bars. + final int marginStartEnd = getMarginStartEndWith(configuration); + final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams(); + layoutParams.setMargins(marginStartEnd, mMargin, marginStartEnd, mMargin); + mListView.setLayoutParams(layoutParams); + } + private void updateOffsetWith(@ShapeType int shapeType, @Alignment int side) { final float halfWidth = getLayoutWidth() / 2.0f; final float offset = (shapeType == ShapeType.OVAL) ? 0 : halfWidth; @@ -896,6 +906,12 @@ public class AccessibilityFloatingMenuView extends FrameLayout return (mPadding + mIconHeight) * mTargets.size() + mPadding; } + private int getMarginStartEndWith(Configuration configuration) { + return configuration != null + && configuration.orientation == ORIENTATION_PORTRAIT + ? mMargin : 0; + } + private @DimenRes int getRadiusResId(@SizeType int sizeType, int itemCount) { return sizeType == SizeType.SMALL ? getSmallSizeResIdWith(itemCount) @@ -932,7 +948,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout } private int getWindowWidth() { - return mMargin * 2 + getLayoutWidth(); + return getMarginStartEndWith(mLastConfiguration) * 2 + getLayoutWidth(); } private int getWindowHeight() { -- cgit v1.2.3 From 76e8e04703cb49a4984145a18f4552c4bcf72172 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 7 Jun 2021 15:02:45 -0700 Subject: Fix side effects of trace-ipc and dumpheap commands These shell commands were implicitly deleting any client-named file for which the system uid had deletion capability. They no longer do this, instead using only the client's own capabilities and file manipulation modes. Bug: 185398942 Test: manual "adb shell cmd activity dumpheap system_server /data/system/last-fstrim" Test: atest CtsPermissionTestCases:ShellCommandPermissionTest Change-Id: Ie61ab2c3f4bfbd04de09ca99c1116d1129461e8f --- .../com/android/server/am/ActivityManagerShellCommand.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 149e3baa90e7..122af56bd575 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -94,7 +94,6 @@ import com.android.internal.util.MemInfoReader; import com.android.server.compat.PlatformCompat; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -787,8 +786,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - File file = new File(filename); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(filename, "w"); if (fd == null) { return -1; @@ -942,16 +940,16 @@ final class ActivityManagerShellCommand extends ShellCommand { String logNameTimeString = LOG_NAME_TIME_FORMATTER.format(localDateTime); heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof"; } - pw.println("File: " + heapFile); - pw.flush(); - File file = new File(heapFile); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(heapFile, "w"); if (fd == null) { return -1; } + pw.println("File: " + heapFile); + pw.flush(); + final CountDownLatch latch = new CountDownLatch(1); final RemoteCallback finishCallback = new RemoteCallback(new OnResultListener() { -- cgit v1.2.3 From 3555fa2f09972cb238eeeee6b0d7ed286d731989 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 7 Jun 2021 15:02:45 -0700 Subject: Fix side effects of trace-ipc and dumpheap commands These shell commands were implicitly deleting any client-named file for which the system uid had deletion capability. They no longer do this, instead using only the client's own capabilities and file manipulation modes. Bug: 185398942 Test: manual "adb shell cmd activity dumpheap system_server /data/system/last-fstrim" Test: atest CtsPermissionTestCases:ShellCommandPermissionTest Merged-In: Ie61ab2c3f4bfbd04de09ca99c1116d1129461e8f Change-Id: Ie61ab2c3f4bfbd04de09ca99c1116d1129461e8f --- .../com/android/server/am/ActivityManagerShellCommand.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 8f16ed4768de..a19e475a46a1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -90,7 +90,6 @@ import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -759,8 +758,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - File file = new File(filename); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(filename, "w"); if (fd == null) { return -1; @@ -914,16 +912,16 @@ final class ActivityManagerShellCommand extends ShellCommand { t.set(System.currentTimeMillis()); heapFile = "/data/local/tmp/heapdump-" + t.format("%Y%m%d-%H%M%S") + ".prof"; } - pw.println("File: " + heapFile); - pw.flush(); - File file = new File(heapFile); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(heapFile, "w"); if (fd == null) { return -1; } + pw.println("File: " + heapFile); + pw.flush(); + final CountDownLatch latch = new CountDownLatch(1); final RemoteCallback finishCallback = new RemoteCallback(new OnResultListener() { -- cgit v1.2.3 From a9da1722cc6994d32b5eb2ca5618dba7013d447b Mon Sep 17 00:00:00 2001 From: Joanne Chung Date: Thu, 10 Jun 2021 18:34:19 +0800 Subject: Bug fix: Move the implementation out from View.onTranslationResponse. Keep onTranslationResponse() only to save TranslationResponse. We can move set TransformationMethod logic to TextViewTranslationCallback. With the current implementation, if the developers overrides onTranslationResponse(), they don't have a chance to use the TextView default ViewTranslationCallback implementation because they don't set the TranslationTransformation, they must implement their solution. If we move logic to TextViewViewTranslationCallback and the developers only overrides onTranslationResponse(), they still have a chance to use the default TextViewViewTranslationCallback if the developers set the TranslationResponse for View. Bug: 178353965 Test: manual to make sure translation still works. Test: atest CtsTranslationTestCases Change-Id: Iada7f3efbbc7705ecf962c78e275ed942816707f --- .../view/translation/UiTranslationController.java | 2 +- core/java/android/widget/TextView.java | 31 ---------- .../widget/TextViewTranslationCallback.java | 69 ++++++++-------------- 3 files changed, 25 insertions(+), 77 deletions(-) diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 5ac878d88100..592993cc3d3e 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -424,7 +424,7 @@ public class UiTranslationController { if (callback == null) { if (view instanceof TextView) { // developer doesn't provide their override, we set the default TextView - // implememtation. + // implementation. callback = new TextViewTranslationCallback(); view.setViewTranslationCallback(callback); } else { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3c4fd5e93580..9032d62bbb49 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -129,7 +129,6 @@ import android.text.method.TextKeyListener; import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; import android.text.method.TransformationMethod2; -import android.text.method.TranslationTransformationMethod; import android.text.method.WordIterator; import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; @@ -199,7 +198,6 @@ import android.view.translation.TranslationSpec; import android.view.translation.UiTranslationController; import android.view.translation.ViewTranslationCallback; import android.view.translation.ViewTranslationRequest; -import android.view.translation.ViewTranslationResponse; import android.widget.RemoteViews.RemoteView; import com.android.internal.annotations.VisibleForTesting; @@ -13938,33 +13936,4 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } requestsCollector.accept(requestBuilder.build()); } - - /** - * - * Called when the content from {@link #onCreateViewTranslationRequest} had been translated by - * the TranslationService. The default implementation will replace the current - * {@link TransformationMethod} to transform the original text to the translated text display. - * - * @param response a {@link ViewTranslationResponse} that contains the translated information - * which can be shown in the view. - */ - @Override - public void onViewTranslationResponse(@NonNull ViewTranslationResponse response) { - // set ViewTranslationResponse - super.onViewTranslationResponse(response); - // TODO(b/178353965): move to ViewTranslationCallback.onShow() - ViewTranslationCallback callback = getViewTranslationCallback(); - if (callback instanceof TextViewTranslationCallback) { - TextViewTranslationCallback textViewDefaultCallback = - (TextViewTranslationCallback) callback; - TranslationTransformationMethod oldTranslationMethod = - textViewDefaultCallback.getTranslationTransformation(); - TransformationMethod originalTranslationMethod = oldTranslationMethod != null - ? oldTranslationMethod.getOriginalTransformationMethod() : mTransformation; - TranslationTransformationMethod newTranslationMethod = - new TranslationTransformationMethod(response, originalTranslationMethod); - // TODO(b/178353965): well-handle setTransformationMethod. - textViewDefaultCallback.setTranslationTransformation(newTranslationMethod); - } - } } diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java index a7d5ee465299..e1b04f8957e5 100644 --- a/core/java/android/widget/TextViewTranslationCallback.java +++ b/core/java/android/widget/TextViewTranslationCallback.java @@ -56,26 +56,6 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { private CharSequence mContentDescription; - /** - * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the - * view that provides the translatable information by {@link View#createTranslationRequest} and - * sent by the platform. - */ - void setTranslationTransformation(TranslationTransformationMethod method) { - if (method == null) { - if (DEBUG) { - Log.w(TAG, "setTranslationTransformation: should not set null " - + "TranslationTransformationMethod"); - } - return; - } - mTranslationTransformation = method; - } - - TranslationTransformationMethod getTranslationTransformation() { - return mTranslationTransformation; - } - private void clearTranslationTransformation() { if (DEBUG) { Log.v(TAG, "clearTranslationTransformation: " + mTranslationTransformation); @@ -88,34 +68,33 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { */ @Override public boolean onShowTranslation(@NonNull View view) { - if (view.getViewTranslationResponse() == null) { - Log.wtf(TAG, "onShowTranslation() shouldn't be called before " + ViewTranslationResponse response = view.getViewTranslationResponse(); + if (response == null) { + Log.e(TAG, "onShowTranslation() shouldn't be called before " + "onViewTranslationResponse()."); return false; } - if (mTranslationTransformation != null) { - final TransformationMethod transformation = mTranslationTransformation; - runWithAnimation( - (TextView) view, - () -> { - mIsShowingTranslation = true; - ((TextView) view).setTransformationMethod(transformation); - }); - ViewTranslationResponse response = view.getViewTranslationResponse(); - if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) { - CharSequence translatedContentDescription = - response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText(); - if (!TextUtils.isEmpty(translatedContentDescription)) { - mContentDescription = view.getContentDescription(); - view.setContentDescription(translatedContentDescription); - } - } - } else { - if (DEBUG) { - // TODO(b/182433547): remove before S release - Log.w(TAG, "onShowTranslation(): no translated text."); + if (mTranslationTransformation == null) { + TransformationMethod originalTranslationMethod = + ((TextView) view).getTransformationMethod(); + mTranslationTransformation = new TranslationTransformationMethod(response, + originalTranslationMethod); + } + final TransformationMethod transformation = mTranslationTransformation; + runWithAnimation( + (TextView) view, + () -> { + mIsShowingTranslation = true; + // TODO(b/178353965): well-handle setTransformationMethod. + ((TextView) view).setTransformationMethod(transformation); + }); + if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) { + CharSequence translatedContentDescription = + response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText(); + if (!TextUtils.isEmpty(translatedContentDescription)) { + mContentDescription = view.getContentDescription(); + view.setContentDescription(translatedContentDescription); } - return false; } return true; } @@ -126,7 +105,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { @Override public boolean onHideTranslation(@NonNull View view) { if (view.getViewTranslationResponse() == null) { - Log.wtf(TAG, "onHideTranslation() shouldn't be called before " + Log.e(TAG, "onHideTranslation() shouldn't be called before " + "onViewTranslationResponse()."); return false; } -- cgit v1.2.3 From 46e946a251f133af59f2b95f279c02ca17c7a0a9 Mon Sep 17 00:00:00 2001 From: TYM Tsai Date: Wed, 16 Jun 2021 22:47:11 +0800 Subject: Notify Content Capture the selection was changed The ContentCapture service is interested in the selection changes. When the selection was changed, notifies a text changed event to ContentCapture. Bug: 184311217 Test: manual 1.long press trigger selection, event is sent. 2.drag selection indicators, send event after the drag is done. Test: atest CtsContentCaptureServiceTestCases Change-Id: Ie45f617b132bc240b6cf61ee7ebc3041275f1694 --- core/java/android/widget/SelectionActionModeHelper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index eb6bce4a2f59..3fed9beeaaba 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -565,6 +565,7 @@ public final class SelectionActionModeHelper { */ public void onSmartSelection(SelectionResult result) { onClassifiedSelection(result); + mTextView.notifyContentCaptureTextChanged(); mLogger.logSelectionModified( result.mStart, result.mEnd, result.mClassification, result.mSelection); } @@ -595,6 +596,7 @@ public final class SelectionActionModeHelper { mSelectionStart = selectionStart; mSelectionEnd = selectionEnd; mAllowReset = false; + mTextView.notifyContentCaptureTextChanged(); mLogger.logSelectionModified(selectionStart, selectionEnd, classification, null); } } -- cgit v1.2.3 From 2614378b0b823d4d71a479671a61d071f549b000 Mon Sep 17 00:00:00 2001 From: Nikita Iashchenko Date: Tue, 20 Apr 2021 23:22:03 +0000 Subject: Remove usage of Math.randomLongInternal As a part of internal libcore API cleanup some of the functions previously exposed are getting removed from public surface. Math#randomLongInternal is a wrapper around java.util.Random and has no specific implications so its usages are get refactored. Bug: 154796679 Test: m droid Merged-In: I019000d2b35c256e7c8a251a95bd9384d3503335 Change-Id: I019000d2b35c256e7c8a251a95bd9384d3503335 --- .../src/com/android/server/usage/AppStandbyControllerTests.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index a24691791938..b878d9bab46a 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -114,6 +114,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -166,6 +167,8 @@ public class AppStandbyControllerTests { /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */ private static boolean isPackageInstalled = true; + private static final Random sRandom = new Random(); + private MyInjector mInjector; private AppStandbyController mController; @@ -294,7 +297,7 @@ public class AppStandbyControllerTests { @Override File getDataSystemDirectory() { - return new File(getContext().getFilesDir(), Long.toString(Math.randomLongInternal())); + return new File(getContext().getFilesDir(), Long.toString(sRandom.nextLong())); } @Override -- cgit v1.2.3 From 93ac0dad60cd9252d175ea1738ec9f6a49f4489b Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 17 Jun 2021 13:56:23 -0400 Subject: Add flag for remote streams Test: manual, with remote stream active Bug: 191356254 Change-Id: I59947b8d04ba717eac0e5a501965de97d200cc9c --- core/res/res/values/config.xml | 3 + core/res/res/values/symbols.xml | 2 + .../volume/VolumeDialogControllerImpl.java | 132 ++++++++++++--------- 3 files changed, 79 insertions(+), 58 deletions(-) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ee33d48768c5..6ad88e161545 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1890,6 +1890,9 @@ STREAM_MUSIC as if it's on TV platform. --> false + + true + false diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7d685a202538..d49e22a72eb9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4400,4 +4400,6 @@ + + diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a5ccc47b8072..2f4372648bb4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -128,7 +128,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final MediaSessions mMediaSessions; protected C mCallbacks = new C(); private final State mState = new State(); - protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks(); + protected final MediaSessionsCallbacks mMediaSessionsCallbacksW; private final Optional mVibrator; private final boolean mHasVibrator; private boolean mShowA11yStream; @@ -179,6 +179,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mWorkerLooper = theadFactory.buildLooperOnNewThread( VolumeDialogControllerImpl.class.getSimpleName()); mWorker = new W(mWorkerLooper); + mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext); mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW); mAudio = audioManager; mNoMan = notificationManager; @@ -1148,83 +1149,98 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final HashMap mRemoteStreams = new HashMap<>(); private int mNextStream = DYNAMIC_STREAM_START_INDEX; + private final boolean mShowRemoteSessions; + + public MediaSessionsCallbacks(Context context) { + mShowRemoteSessions = context.getResources().getBoolean( + com.android.internal.R.bool.config_volumeShowRemoteSessions); + } @Override public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) { - addStream(token, "onRemoteUpdate"); + if (mShowRemoteSessions) { + addStream(token, "onRemoteUpdate"); - int stream = 0; - synchronized (mRemoteStreams) { - stream = mRemoteStreams.get(token); - } - Slog.d(TAG, "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume()); - boolean changed = mState.states.indexOfKey(stream) < 0; - final StreamState ss = streamStateW(stream); - ss.dynamic = true; - ss.levelMin = 0; - ss.levelMax = pi.getMaxVolume(); - if (ss.level != pi.getCurrentVolume()) { - ss.level = pi.getCurrentVolume(); - changed = true; - } - if (!Objects.equals(ss.remoteLabel, name)) { - ss.name = -1; - ss.remoteLabel = name; - changed = true; - } - if (changed) { - Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax); - mCallbacks.onStateChanged(mState); + int stream = 0; + synchronized (mRemoteStreams) { + stream = mRemoteStreams.get(token); + } + Slog.d(TAG, + "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume()); + boolean changed = mState.states.indexOfKey(stream) < 0; + final StreamState ss = streamStateW(stream); + ss.dynamic = true; + ss.levelMin = 0; + ss.levelMax = pi.getMaxVolume(); + if (ss.level != pi.getCurrentVolume()) { + ss.level = pi.getCurrentVolume(); + changed = true; + } + if (!Objects.equals(ss.remoteLabel, name)) { + ss.name = -1; + ss.remoteLabel = name; + changed = true; + } + if (changed) { + Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax); + mCallbacks.onStateChanged(mState); + } } } @Override public void onRemoteVolumeChanged(Token token, int flags) { - addStream(token, "onRemoteVolumeChanged"); - int stream = 0; - synchronized (mRemoteStreams) { - stream = mRemoteStreams.get(token); - } - final boolean showUI = shouldShowUI(flags); - Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI); - boolean changed = updateActiveStreamW(stream); - if (showUI) { - changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC); - } - if (changed) { - Slog.d(TAG, "onRemoteChanged: updatingState"); - mCallbacks.onStateChanged(mState); - } - if (showUI) { - mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED); + if (mShowRemoteSessions) { + addStream(token, "onRemoteVolumeChanged"); + int stream = 0; + synchronized (mRemoteStreams) { + stream = mRemoteStreams.get(token); + } + final boolean showUI = shouldShowUI(flags); + Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI); + boolean changed = updateActiveStreamW(stream); + if (showUI) { + changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC); + } + if (changed) { + Slog.d(TAG, "onRemoteChanged: updatingState"); + mCallbacks.onStateChanged(mState); + } + if (showUI) { + mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED); + } } } @Override public void onRemoteRemoved(Token token) { - int stream = 0; - synchronized (mRemoteStreams) { - if (!mRemoteStreams.containsKey(token)) { - Log.d(TAG, "onRemoteRemoved: stream doesn't exist, " - + "aborting remote removed for token:" + token.toString()); - return; + if (mShowRemoteSessions) { + int stream = 0; + synchronized (mRemoteStreams) { + if (!mRemoteStreams.containsKey(token)) { + Log.d(TAG, "onRemoteRemoved: stream doesn't exist, " + + "aborting remote removed for token:" + token.toString()); + return; + } + stream = mRemoteStreams.get(token); } - stream = mRemoteStreams.get(token); - } - mState.states.remove(stream); - if (mState.activeStream == stream) { - updateActiveStreamW(-1); + mState.states.remove(stream); + if (mState.activeStream == stream) { + updateActiveStreamW(-1); + } + mCallbacks.onStateChanged(mState); } - mCallbacks.onStateChanged(mState); } public void setStreamVolume(int stream, int level) { - final Token t = findToken(stream); - if (t == null) { - Log.w(TAG, "setStreamVolume: No token found for stream: " + stream); - return; + if (mShowRemoteSessions) { + final Token t = findToken(stream); + if (t == null) { + Log.w(TAG, "setStreamVolume: No token found for stream: " + stream); + return; + } + mMediaSessions.setVolume(t, level); } - mMediaSessions.setVolume(t, level); } private Token findToken(int stream) { -- cgit v1.2.3 From c48554facc00efdfec9fdccd4c46db40da779cfc Mon Sep 17 00:00:00 2001 From: Nikita Iashchenko Date: Mon, 26 Apr 2021 19:08:53 +0100 Subject: Move IoUtils#deleteContents from CorePlatformApi set to framework As a part of internal core libraries cleanup move usages of IoUtils#deleteContents from CorePlatformApi set to framework. Bug: 154796679 Test: m update-api Merged-In: If7037029026b6753ab64be09aa52c40e04d5c7b1 Change-Id: If7037029026b6753ab64be09aa52c40e04d5c7b1 --- .../com/android/internal/util/FileRotatorTest.java | 9 ++--- .../net/NetworkPolicyManagerServiceTest.java | 4 +-- .../com/android/internal/util/test/FsUtil.java | 40 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java diff --git a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java index 3f9e62e7b180..952721320c90 100644 --- a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java @@ -29,6 +29,8 @@ import android.util.Log; import com.android.internal.util.FileRotator.Reader; import com.android.internal.util.FileRotator.Writer; +import com.android.internal.util.test.FsUtil; + import com.google.android.collect.Lists; import java.io.DataInputStream; @@ -38,15 +40,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.ProtocolException; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; -import junit.framework.Assert; - -import libcore.io.IoUtils; - /** * Tests for {@link FileRotator}. */ @@ -67,7 +64,7 @@ public class FileRotatorTest extends AndroidTestCase { super.setUp(); mBasePath = getContext().getFilesDir(); - IoUtils.deleteContents(mBasePath); + FsUtil.deleteContents(mBasePath); } public void testEmpty() throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 23517a9b0458..70641c2938a7 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -162,13 +162,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; +import com.android.internal.util.test.FsUtil; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.usage.AppStandbyInternal; import com.google.common.util.concurrent.AbstractFuture; -import libcore.io.IoUtils; import libcore.io.Streams; import org.junit.After; @@ -2385,7 +2385,7 @@ public class NetworkPolicyManagerServiceTest { private void setNetpolicyXml(Context context) throws Exception { mPolicyDir = context.getFilesDir(); if (mPolicyDir.exists()) { - IoUtils.deleteContents(mPolicyDir); + FsUtil.deleteContents(mPolicyDir); } if (!TextUtils.isEmpty(mNetpolicyXml)) { final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml; diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java new file mode 100644 index 000000000000..e65661298b7c --- /dev/null +++ b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java @@ -0,0 +1,40 @@ +/* + * 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.internal.util.test; + +import java.io.File; + +public class FsUtil { + + /** + * Deletes all files under a given directory. Deliberately ignores errors, on the assumption + * that test cleanup is only supposed to be best-effort. + * + * @param dir directory to clear its contents + */ + public static void deleteContents(File dir) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteContents(file); + } + file.delete(); + } + } + } +} -- cgit v1.2.3 From 4ee75aa2754dbb85a8117ba97766b4688ddacd32 Mon Sep 17 00:00:00 2001 From: Peiyong Lin Date: Wed, 2 Jun 2021 21:01:42 +0000 Subject: Add more downscale factors. Add more downscale factors to allow a better granularity. Fixes: b/189970040, b/189969749, b/189970038, b/189969782 Fixes: b/189970036, b/189969744, b/189969779, b/189969734 Test: adb shell cmd game downscale NEW_FACTOR PACKAGE_NAME Test: atest CompatScaleTests Test: atest CompatChangesValidConfigTest Change-Id: I255f14aefd28fb63870935968123690971984e0c --- .../com/android/server/app/GameManagerService.java | 63 +++++++++--- .../server/app/GameManagerShellCommand.java | 72 ++++++------- .../com/android/server/wm/CompatModePackages.java | 112 +++++++++++++++++++++ 3 files changed, 198 insertions(+), 49 deletions(-) diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 697f6fa8eea8..ae1cd51e11f0 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -21,10 +21,18 @@ import static android.content.Intent.ACTION_PACKAGE_CHANGED; import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static com.android.server.wm.CompatModePackages.DOWNSCALED; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_30; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_35; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_40; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_45; import static com.android.server.wm.CompatModePackages.DOWNSCALE_50; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_55; import static com.android.server.wm.CompatModePackages.DOWNSCALE_60; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_65; import static com.android.server.wm.CompatModePackages.DOWNSCALE_70; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_75; import static com.android.server.wm.CompatModePackages.DOWNSCALE_80; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_85; import static com.android.server.wm.CompatModePackages.DOWNSCALE_90; import android.Manifest; @@ -213,6 +221,39 @@ public final class GameManagerService extends IGameManagerService.Stub { } } + // Turn the raw string to the corresponding CompatChange id. + static long getCompatChangeId(String raw) { + switch (raw) { + case "0.3": + return DOWNSCALE_30; + case "0.35": + return DOWNSCALE_35; + case "0.4": + return DOWNSCALE_40; + case "0.45": + return DOWNSCALE_45; + case "0.5": + return DOWNSCALE_50; + case "0.55": + return DOWNSCALE_55; + case "0.6": + return DOWNSCALE_60; + case "0.65": + return DOWNSCALE_65; + case "0.7": + return DOWNSCALE_70; + case "0.75": + return DOWNSCALE_75; + case "0.8": + return DOWNSCALE_80; + case "0.85": + return DOWNSCALE_85; + case "0.9": + return DOWNSCALE_90; + } + return 0; + } + /** * GamePackageConfiguration manages all game mode config details for its associated package. */ @@ -331,19 +372,7 @@ public final class GameManagerService extends IGameManagerService.Stub { * Get the corresponding compat change id for the current scaling string. */ public long getCompatChangeId() { - switch (mScaling) { - case "0.5": - return DOWNSCALE_50; - case "0.6": - return DOWNSCALE_60; - case "0.7": - return DOWNSCALE_70; - case "0.8": - return DOWNSCALE_80; - case "0.9": - return DOWNSCALE_90; - } - return 0; + return GameManagerService.getCompatChangeId(mScaling); } } @@ -663,10 +692,18 @@ public final class GameManagerService extends IGameManagerService.Stub { Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName); final ArrayMap overrides = new ArrayMap<>(); overrides.put(DOWNSCALED, COMPAT_ENABLED); + overrides.put(DOWNSCALE_30, COMPAT_DISABLED); + overrides.put(DOWNSCALE_35, COMPAT_DISABLED); + overrides.put(DOWNSCALE_40, COMPAT_DISABLED); + overrides.put(DOWNSCALE_45, COMPAT_DISABLED); overrides.put(DOWNSCALE_50, COMPAT_DISABLED); + overrides.put(DOWNSCALE_55, COMPAT_DISABLED); overrides.put(DOWNSCALE_60, COMPAT_DISABLED); + overrides.put(DOWNSCALE_65, COMPAT_DISABLED); overrides.put(DOWNSCALE_70, COMPAT_DISABLED); + overrides.put(DOWNSCALE_75, COMPAT_DISABLED); overrides.put(DOWNSCALE_80, COMPAT_DISABLED); + overrides.put(DOWNSCALE_85, COMPAT_DISABLED); overrides.put(DOWNSCALE_90, COMPAT_DISABLED); overrides.put(scaleId, COMPAT_ENABLED); final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig( diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java index 2074ffae1030..699f9e2d99f8 100644 --- a/services/core/java/com/android/server/app/GameManagerShellCommand.java +++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java @@ -16,6 +16,21 @@ package com.android.server.app; +import static com.android.server.wm.CompatModePackages.DOWNSCALED; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_30; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_35; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_40; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_45; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_50; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_55; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_60; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_65; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_70; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_75; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_80; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_85; +import static com.android.server.wm.CompatModePackages.DOWNSCALE_90; + import android.app.ActivityManager; import android.app.GameManager; import android.app.IGameManagerService; @@ -27,7 +42,6 @@ import android.util.ArraySet; import com.android.internal.compat.CompatibilityChangeConfig; import com.android.server.compat.PlatformCompat; -import com.android.server.wm.CompatModePackages; import java.io.PrintWriter; import java.util.Set; @@ -43,12 +57,21 @@ public class GameManagerShellCommand extends ShellCommand { public GameManagerShellCommand() {} private static final ArraySet DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{ - CompatModePackages.DOWNSCALED, - CompatModePackages.DOWNSCALE_90, - CompatModePackages.DOWNSCALE_80, - CompatModePackages.DOWNSCALE_70, - CompatModePackages.DOWNSCALE_60, - CompatModePackages.DOWNSCALE_50}); + DOWNSCALED, + DOWNSCALE_90, + DOWNSCALE_85, + DOWNSCALE_80, + DOWNSCALE_75, + DOWNSCALE_70, + DOWNSCALE_65, + DOWNSCALE_60, + DOWNSCALE_55, + DOWNSCALE_50, + DOWNSCALE_45, + DOWNSCALE_40, + DOWNSCALE_35, + DOWNSCALE_30, + }); @Override public int onCommand(String cmd) { @@ -62,32 +85,9 @@ public class GameManagerShellCommand extends ShellCommand { final String ratio = getNextArgRequired(); final String packageName = getNextArgRequired(); - final long changeId; - switch (ratio) { - case "0.5": - changeId = CompatModePackages.DOWNSCALE_50; - break; - case "0.6": - changeId = CompatModePackages.DOWNSCALE_60; - break; - case "0.7": - changeId = CompatModePackages.DOWNSCALE_70; - break; - case "0.8": - changeId = CompatModePackages.DOWNSCALE_80; - break; - case "0.9": - changeId = CompatModePackages.DOWNSCALE_90; - break; - case "disable": - changeId = 0; - break; - default: - changeId = -1; - pw.println("Invalid scaling ratio '" + ratio + "'"); - break; - } - if (changeId == -1) { + final long changeId = GameManagerService.getCompatChangeId(ratio); + if (changeId == 0 && !ratio.equals("disable")) { + pw.println("Invalid scaling ratio '" + ratio + "'"); break; } @@ -96,10 +96,10 @@ public class GameManagerShellCommand extends ShellCommand { if (changeId == 0) { disabled = DOWNSCALE_CHANGE_IDS; } else { - enabled.add(CompatModePackages.DOWNSCALED); + enabled.add(DOWNSCALED); enabled.add(changeId); disabled = DOWNSCALE_CHANGE_IDS.stream() - .filter(it -> it != CompatModePackages.DOWNSCALED && it != changeId) + .filter(it -> it != DOWNSCALED && it != changeId) .collect(Collectors.toSet()); } @@ -204,7 +204,7 @@ public class GameManagerShellCommand extends ShellCommand { pw.println("Game manager (game) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(" downscale [0.5|0.6|0.7|0.8|0.9|disable] "); + pw.println(" downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65|0.7|0.75|0.8|0.85|0.9|disable] "); pw.println(" Force app to run at the specified scaling ratio."); pw.println(" mode [--user ] [1|2|3|standard|performance|battery] "); pw.println(" Force app to run in the specified game mode, if supported."); diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index 0ec01422474a..2ea043a5c623 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -75,10 +75,18 @@ public final class CompatModePackages { * CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling * changes. Disabling this change will prevent the following scaling factors from working: * CompatModePackages#DOWNSCALE_90 + * CompatModePackages#DOWNSCALE_85 * CompatModePackages#DOWNSCALE_80 + * CompatModePackages#DOWNSCALE_75 * CompatModePackages#DOWNSCALE_70 + * CompatModePackages#DOWNSCALE_65 * CompatModePackages#DOWNSCALE_60 + * CompatModePackages#DOWNSCALE_55 * CompatModePackages#DOWNSCALE_50 + * CompatModePackages#DOWNSCALE_45 + * CompatModePackages#DOWNSCALE_40 + * CompatModePackages#DOWNSCALE_35 + * CompatModePackages#DOWNSCALE_30 * * If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly * resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were enabled. @@ -98,6 +106,16 @@ public final class CompatModePackages { @Overridable public static final long DOWNSCALE_90 = 182811243L; + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_85 for a package will force the app to assume it's + * running on a display with 85% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_85 = 189969734L; + /** * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id * CompatModePackages#DOWNSCALE_80 for a package will force the app to assume it's @@ -108,6 +126,16 @@ public final class CompatModePackages { @Overridable public static final long DOWNSCALE_80 = 176926753L; + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's + * running on a display with 75% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_75 = 189969779L; + /** * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id * CompatModePackages#DOWNSCALE_70 for a package will force the app to assume it's @@ -118,6 +146,16 @@ public final class CompatModePackages { @Overridable public static final long DOWNSCALE_70 = 176926829L; + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_65 for a package will force the app to assume it's + * running on a display with 65% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_65 = 189969744L; + /** * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id * CompatModePackages#DOWNSCALE_60 for a package will force the app to assume it's @@ -128,6 +166,16 @@ public final class CompatModePackages { @Overridable public static final long DOWNSCALE_60 = 176926771L; + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_55 for a package will force the app to assume it's + * running on a display with 55% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_55 = 189970036L; + /** * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id * CompatModePackages#DOWNSCALE_50 for a package will force the app to assume it's @@ -138,6 +186,46 @@ public final class CompatModePackages { @Overridable public static final long DOWNSCALE_50 = 176926741L; + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_45 for a package will force the app to assume it's + * running on a display with 45% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_45 = 189969782L; + + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_40 for a package will force the app to assume it's + * running on a display with 40% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_40 = 189970038L; + + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_35 for a package will force the app to assume it's + * running on a display with 35% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_35 = 189969749L; + + /** + * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id + * CompatModePackages#DOWNSCALE_30 for a package will force the app to assume it's + * running on a display with 30% the vertical and horizontal resolution of the real display. + */ + @ChangeId + @Disabled + @Overridable + public static final long DOWNSCALE_30 = 189970040L; + /** * On Android TV applications that target pre-S are not expecting to receive a Window larger * than 1080p, so if needed we are downscaling their Windows to 1080p. @@ -291,18 +379,42 @@ public final class CompatModePackages { if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) { return 1f / 0.9f; } + if (CompatChanges.isChangeEnabled(DOWNSCALE_85, packageName, userHandle)) { + return 1f / 0.85f; + } if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) { return 1f / 0.8f; } + if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) { + return 1f / 0.75f; + } if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) { return 1f / 0.7f; } + if (CompatChanges.isChangeEnabled(DOWNSCALE_65, packageName, userHandle)) { + return 1f / 0.65f; + } if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) { return 1f / 0.6f; } + if (CompatChanges.isChangeEnabled(DOWNSCALE_55, packageName, userHandle)) { + return 1f / 0.55f; + } if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) { return 1f / 0.5f; } + if (CompatChanges.isChangeEnabled(DOWNSCALE_45, packageName, userHandle)) { + return 1f / 0.45f; + } + if (CompatChanges.isChangeEnabled(DOWNSCALE_40, packageName, userHandle)) { + return 1f / 0.4f; + } + if (CompatChanges.isChangeEnabled(DOWNSCALE_35, packageName, userHandle)) { + return 1f / 0.35f; + } + if (CompatChanges.isChangeEnabled(DOWNSCALE_30, packageName, userHandle)) { + return 1f / 0.3f; + } } if (mService.mHasLeanbackFeature) { -- cgit v1.2.3 From ed9c44a27331ff535611d715ccea5b3edcfffc6c Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Wed, 16 Jun 2021 12:11:43 +0800 Subject: Create splash screen if activity launched with FLAG_ACTIVITY_CLEAR_TASK When an activity launched with NEW_TASK and CLEAR_TASK, there will finish the original activities and reuse the same task to create a new activity, so there will need to wait for the new window drawn to trigger wm transition, which slow down the launch response. And because this condition should be a kind of warm launch, there should create a splash screen for it. Bug: 187808226 Test: atest ActivityRecordTests SplashscreenTests Test: manual verify by winscope Change-Id: I263edcd75fcd21999b9ceb9fac8bfcf8419539a3 --- .../core/java/com/android/server/wm/ActivityRecord.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 8514f3599832..3fb4c8822c64 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6254,12 +6254,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int resolvedTheme = evaluateStartingWindowTheme(packageName, theme, splashScreenTheme); + final boolean activityCreated = + mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(); + // If this activity is just created and all activities below are finish, treat this + // scenario as warm launch. + final boolean newSingleActivity = !newTask && !activityCreated + && task.getActivity((r) -> !r.finishing && r != this) == null; + final boolean shown = addStartingWindow(packageName, resolvedTheme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, - prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), - allowTaskSnapshot(), - mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(), - mSplashScreenStyleEmpty); + prev != null ? prev.appToken : null, + newTask || newSingleActivity, taskSwitch, isProcessRunning(), + allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty); if (shown) { mStartingWindowState = STARTING_WINDOW_SHOWN; } -- cgit v1.2.3 From e9565df5b8b8a56b47dc0525f670d5d0a85aea8d Mon Sep 17 00:00:00 2001 From: Yan Han Date: Wed, 16 Jun 2021 17:47:31 +0200 Subject: Support "Mute" and "Restore Volume" CEC commands Adds support for ["Mute Function"] and ["Restore Volume Function"] on all HDMI devices. These commands are currently ignored because they are not mapped to an Android keycode. Support for these commands is required for HDMI CEC 2.0. Bug: 191230341 Test: Manual; atest HdmiCecLocalDeviceTest Change-Id: I5146e9e99595a9a124e0ce2176f5e70a29923a49 --- .../android/server/hdmi/HdmiCecLocalDevice.java | 19 +++++++++++ .../server/hdmi/HdmiCecLocalDeviceTest.java | 39 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 505e743ed9a1..a2cb78d27a57 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -23,6 +23,7 @@ import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.input.InputManager; import android.hardware.tv.cec.V1_0.SendMessageResult; +import android.media.AudioManager; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -685,11 +686,29 @@ abstract class HdmiCecLocalDevice { Message.obtain(mHandler, MSG_USER_CONTROL_RELEASE_TIMEOUT), FOLLOWER_SAFETY_TIMEOUT); return Constants.HANDLED; + } else if (params.length > 0) { + // Handle CEC UI commands that are not mapped to an Android keycode + return handleUnmappedCecKeycode(params[0]); } return Constants.ABORT_INVALID_OPERAND; } + @ServiceThreadOnly + @Constants.HandleMessageResult + protected int handleUnmappedCecKeycode(int cecKeycode) { + if (cecKeycode == HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION) { + mService.getAudioManager().adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_MUTE, AudioManager.FLAG_SHOW_UI); + return Constants.HANDLED; + } else if (cecKeycode == HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION) { + mService.getAudioManager().adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_UNMUTE, AudioManager.FLAG_SHOW_UI); + return Constants.HANDLED; + } + return Constants.ABORT_INVALID_OPERAND; + } + @ServiceThreadOnly @Constants.HandleMessageResult protected int handleUserControlReleased() { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java index 68803023c451..6502e4813171 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java @@ -32,9 +32,15 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; +import android.media.AudioManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -45,6 +51,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; @@ -123,8 +131,12 @@ public class HdmiCecLocalDeviceTest { private boolean isControlEnabled; private int mPowerStatus; + @Mock + private AudioManager mAudioManager; + @Before public void SetUp() { + MockitoAnnotations.initMocks(this); Context context = InstrumentationRegistry.getTargetContext(); @@ -161,6 +173,11 @@ public class HdmiCecLocalDeviceTest { void wakeUp() { mWakeupMessageReceived = true; } + + @Override + AudioManager getAudioManager() { + return mAudioManager; + } }; mHdmiControlService.setIoLooper(mTestLooper.getLooper()); mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context)); @@ -418,6 +435,28 @@ public class HdmiCecLocalDeviceTest { assertThat(mStandbyMessageReceived).isTrue(); } + @Test + public void handleUserControlPressed_muteFunction() { + @Constants.HandleMessageResult int result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_TV, ADDR_PLAYBACK_1, + HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION)); + + assertEquals(result, Constants.HANDLED); + verify(mAudioManager, times(1)) + .adjustStreamVolume(anyInt(), eq(AudioManager.ADJUST_MUTE), anyInt()); + } + + @Test + public void handleUserControlPressed_restoreVolumeFunction() { + @Constants.HandleMessageResult int result = mHdmiLocalDevice.handleUserControlPressed( + HdmiCecMessageBuilder.buildUserControlPressed(ADDR_TV, ADDR_PLAYBACK_1, + HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION)); + + assertEquals(result, Constants.HANDLED); + verify(mAudioManager, times(1)) + .adjustStreamVolume(anyInt(), eq(AudioManager.ADJUST_UNMUTE), anyInt()); + } + @Test public void handleVendorCommand_notHandled() { HdmiCecMessage vendorCommand = HdmiCecMessageBuilder.buildVendorCommand(ADDR_TV, -- cgit v1.2.3 From c0095e5184d1c33a79f6805f7227283ced96b856 Mon Sep 17 00:00:00 2001 From: Issei Suzuki Date: Wed, 16 Jun 2021 16:31:51 +0200 Subject: Stop animation when screen times out and the user turns on the phone. Test: manual. 1. set lock mode to none (keep showing Settings activity) 2. wait until the screen times out 3. tap screen to turn on the screen 4. verity no animation is applied on the Settings activity Bug: 189438446 Change-Id: I5d7d0983e1d3f1dc5731eac1606417863a890e65 --- .../core/java/com/android/server/wm/RootWindowContainer.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ea80b8bb5286..968cd3e37276 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -220,6 +220,9 @@ class RootWindowContainer extends WindowContainer // transaction from the global transaction. private final SurfaceControl.Transaction mDisplayTransaction; + // The tag for the token to put root tasks on the displays to sleep. + private static final String DISPLAY_OFF_SLEEP_TOKEN_TAG = "Display-off"; + /** The token acquirer to put root tasks on the displays to sleep */ final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer; @@ -449,7 +452,7 @@ class RootWindowContainer extends WindowContainer mService = service.mAtmService; mTaskSupervisor = mService.mTaskSupervisor; mTaskSupervisor.mRootWindowContainer = this; - mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off"); + mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG); } boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { @@ -2653,12 +2656,14 @@ class RootWindowContainer extends WindowContainer Slog.d(TAG, "Remove non-exist sleep token: " + token + " from " + Debug.getCallers(6)); } mSleepTokens.remove(token.mHashKey); - final DisplayContent display = getDisplayContent(token.mDisplayId); if (display != null) { display.mAllSleepTokens.remove(token); if (display.mAllSleepTokens.isEmpty()) { mService.updateSleepIfNeededLocked(); + if (token.mTag.equals(DISPLAY_OFF_SLEEP_TOKEN_TAG)) { + display.mSkipAppTransitionAnimation = true; + } } } } -- cgit v1.2.3 From 4c0b9a5fbdcd06df8844affd1253a734aaa27392 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 7 Jun 2021 15:02:45 -0700 Subject: Fix side effects of trace-ipc and dumpheap commands These shell commands were implicitly deleting any client-named file for which the system uid had deletion capability. They no longer do this, instead using only the client's own capabilities and file manipulation modes. Bug: 185398942 Test: manual "adb shell cmd activity dumpheap system_server /data/system/last-fstrim" Test: atest CtsPermissionTestCases:ShellCommandPermissionTest Merged-In: Ie61ab2c3f4bfbd04de09ca99c1116d1129461e8f Change-Id: Ie61ab2c3f4bfbd04de09ca99c1116d1129461e8f --- .../java/com/android/server/am/ActivityManagerShellCommand.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index adb7056408ba..f37fcb54e7c2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -76,7 +76,6 @@ import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -719,8 +718,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - File file = new File(filename); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(filename, "w"); if (fd == null) { return -1; @@ -871,8 +869,7 @@ final class ActivityManagerShellCommand extends ShellCommand { String process = getNextArgRequired(); String heapFile = getNextArgRequired(); - File file = new File(heapFile); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(heapFile, "w"); if (fd == null) { return -1; -- cgit v1.2.3 From 6984eaa9f4a369228259047c2f797d022afb8f3b Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 7 Jun 2021 15:02:45 -0700 Subject: Fix side effects of trace-ipc and dumpheap commands These shell commands were implicitly deleting any client-named file for which the system uid had deletion capability. They no longer do this, instead using only the client's own capabilities and file manipulation modes. Bug: 185398942 Test: manual "adb shell cmd activity dumpheap system_server /data/system/last-fstrim" Test: atest CtsPermissionTestCases:ShellCommandPermissionTest Merged-In: Ie61ab2c3f4bfbd04de09ca99c1116d1129461e8f Change-Id: Ie61ab2c3f4bfbd04de09ca99c1116d1129461e8f --- .../java/com/android/server/am/ActivityManagerShellCommand.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 9575fddf04f2..7b7e73e6f8f4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -60,7 +60,6 @@ import com.android.internal.util.HexDump; import com.android.internal.util.Preconditions; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -660,8 +659,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - File file = new File(filename); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openOutputFileForSystem(filename); if (fd == null) { return -1; @@ -812,8 +810,7 @@ final class ActivityManagerShellCommand extends ShellCommand { String process = getNextArgRequired(); String heapFile = getNextArgRequired(); - File file = new File(heapFile); - file.delete(); + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openOutputFileForSystem(heapFile); if (fd == null) { return -1; -- cgit v1.2.3 From 0abccabffee50a3960d0e116651921530c9d20c7 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Wed, 16 Jun 2021 00:20:52 +0000 Subject: DO NOT MERGE Add cross-user check for getDefaultSmsPackage(). Bug: 177927831 Test: atest RoleSecurityTest Change-Id: I1254804fb72a299e782d45f938acdf979a82f904 --- services/core/java/com/android/server/role/RoleManagerService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 369e7fcd82af..6f39293256ab 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -636,6 +636,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C @Override public String getDefaultSmsPackage(int userId) { + userId = handleIncomingUser(userId, false, "getDefaultSmsPackage"); + if (!mUserManagerInternal.exists(userId)) { + Slog.e(LOG_TAG, "user " + userId + " does not exist"); + return null; + } + long identity = Binder.clearCallingIdentity(); try { return CollectionUtils.firstOrNull( -- cgit v1.2.3 From cf1bd25a37123624688f9965608a48d362ab4eb0 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Wed, 16 Jun 2021 00:20:52 +0000 Subject: DO NOT MERGE Add cross-user check for getDefaultSmsPackage(). Bug: 177927831 Test: atest RoleSecurityTest Change-Id: I1254804fb72a299e782d45f938acdf979a82f904 --- services/core/java/com/android/server/role/RoleManagerService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 392792dbae69..b75bce833e04 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -662,6 +662,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C @Override public String getDefaultSmsPackage(int userId) { + userId = handleIncomingUser(userId, false, "getDefaultSmsPackage"); + if (!mUserManagerInternal.exists(userId)) { + Slog.e(LOG_TAG, "user " + userId + " does not exist"); + return null; + } + long identity = Binder.clearCallingIdentity(); try { return CollectionUtils.firstOrNull( -- cgit v1.2.3 From 22e3824030d623a6d7d05e41dbc85f0a6e64590a Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Fri, 18 Jun 2021 08:53:18 +0000 Subject: Remove netlink-client dependency from net-module-wifi. Attempted to import net-utils-device-common lib in netlink-client to use Structure stuff and RdnssOption class to parse RDNSS RA option from the netlink message. However, seems to be some dependency issue like: Dependency path: via tag apex.dependencyTag: { name:javaLib payload:true sourceOnly:false} -> service-wifi{os:android,arch:common,apex:apex10000} via tag java.dependencyTag: { name:staticlib} -> wifi-service-pre-jarjar{os:android,arch:common,apex:apex10000} via tag java.dependencyTag: { name:staticlib} -> services.net-module-wifi{os:android,arch:common,apex:apex10000} via tag java.dependencyTag: { name:staticlib} -> netlink-client{os:android,arch:common,apex:apex10000} via tag java.dependencyTag: { name:staticlib} -> net-utils-device-common{os:android,arch:common,apex:apex10000} via tag java.dependencyTag: { name:staticlib} -> net-utils-framework-common{os:android,arch:common,apex:apex10000} If the net-module-wifi doesn't require netlink-client lib, then probably we can remove the netlink-client dependency and fix above conflict. Bug: 163492391 Test: m Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1736250 Merged-In: Icd239381afa8d9b16ba769100e3fa2189b19d7b2 (cherry picked from commit 4b93b461fe598dcedf1459d286d0c30aaeaafcf0) Change-Id: If5e0307f8b0b0201a76a75cf0e78b31e20a20e1a --- services/net/Android.bp | 1 - 1 file changed, 1 deletion(-) diff --git a/services/net/Android.bp b/services/net/Android.bp index f92db86bb880..8352d26ccc49 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -59,7 +59,6 @@ java_library { // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the // classes generated by netd_aidl_interfaces-platform-java above. "netd_aidl_interface-V3-java", - "netlink-client", "networkstack-client", "modules-utils-build_system", ], -- cgit v1.2.3 From 4a6d59b696d5284464a772d4478aecf26fdd4d6a Mon Sep 17 00:00:00 2001 From: Joanne Chung Date: Mon, 21 Jun 2021 21:32:45 +0800 Subject: Fix TextViewTranslationTest test fail. The fail will only be found in the release build, we put the return in the wrong place. Bug: 189359744 Test: local disable flag and TextViewTranslationTest pass. Change-Id: Idd377145b3757abaf528a90a25b6c73e9940f9f8 --- core/java/android/widget/TextView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3c4fd5e93580..4daa2e311e3d 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -13925,8 +13925,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Log.w(LOG_TAG, "Cannot create translation request. editable = " + isTextEditable() + ", isPassword = " + isPassword + ", selectable = " + isTextSelectable()); - return; } + return; } // TODO(b/176488462): apply the view's important for translation requestBuilder.setValue(ViewTranslationRequest.ID_TEXT, -- cgit v1.2.3 From 3f6ae29b01b0489ee1ee87eaa7b6b8633c3829fa Mon Sep 17 00:00:00 2001 From: Taran Singh Date: Thu, 27 May 2021 23:17:59 +0000 Subject: Improve IME perf test show/hide stability Improve stability of perf tests for ImePerfTests: 1. testShowImeWarm 2. testHideIme To improve the stability of the test, check IME state and if state is not as expected, ignore the iteration rather than failing all the iterations and hence the whole test. Bug: 180449596 Test: Run the test using steps in Readme.md Change-Id: Ie42cb49bd8f79af10e7fa48f0a3afb19f253476c --- .../src/android/inputmethod/ImePerfTest.java | 50 +++++++++++++++++----- .../src/android/perftests/utils/TestUtils.java | 44 +++++++++++++++++++ 2 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 apct-tests/perftests/utils/src/android/perftests/utils/TestUtils.java diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java index 689fb3645daf..21c4491fc371 100644 --- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java +++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java @@ -18,6 +18,7 @@ package android.inputmethod; import static android.perftests.utils.ManualBenchmarkState.StatsReport; import static android.perftests.utils.PerfTestActivity.ID_EDITOR; +import static android.perftests.utils.TestUtils.getOnMainSync; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; @@ -25,6 +26,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertTrue; +import android.annotation.UiThread; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -64,6 +66,7 @@ import java.io.InputStreamReader; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; @@ -72,6 +75,7 @@ import java.util.concurrent.atomic.AtomicReference; public class ImePerfTest extends ImePerfTestBase implements ManualBenchmarkState.CustomizedIterationListener { private static final String TAG = ImePerfTest.class.getSimpleName(); + private static final long ANIMATION_NOT_STARTED = -1; @Rule public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter(); @@ -304,12 +308,18 @@ public class ImePerfTest extends ImePerfTestBase latchEnd.set(new CountDownLatch(2)); // For measuring hide, lets show IME first. if (!show) { - activity.runOnUiThread(() -> { - controller.show(WindowInsets.Type.ime()); + AtomicBoolean showCalled = new AtomicBoolean(); + getInstrumentation().runOnMainSync(() -> { + if (!isImeVisible(activity)) { + controller.show(WindowInsets.Type.ime()); + showCalled.set(true); + } }); - PollingCheck.check("IME show animation should finish ", TIMEOUT_1_S_IN_MS, - () -> latchStart.get().getCount() == 1 - && latchEnd.get().getCount() == 1); + if (showCalled.get()) { + PollingCheck.check("IME show animation should finish ", TIMEOUT_1_S_IN_MS, + () -> latchStart.get().getCount() == 1 + && latchEnd.get().getCount() == 1); + } } if (!mIsTraceStarted && !state.isWarmingUp()) { startAsyncAtrace(); @@ -317,23 +327,35 @@ public class ImePerfTest extends ImePerfTestBase } AtomicLong startTime = new AtomicLong(); - activity.runOnUiThread(() -> { + AtomicBoolean unexpectedVisibility = new AtomicBoolean(); + getInstrumentation().runOnMainSync(() -> { + boolean isVisible = isImeVisible(activity); startTime.set(SystemClock.elapsedRealtimeNanos()); - if (show) { + + if (show && !isVisible) { controller.show(WindowInsets.Type.ime()); - } else { + } else if (!show && isVisible) { controller.hide(WindowInsets.Type.ime()); + } else { + // ignore this iteration as unexpected IME visibility was encountered. + unexpectedVisibility.set(true); } }); - measuredTimeNs = waitForAnimationStart(latchStart, startTime); + if (!unexpectedVisibility.get()) { + long timeElapsed = waitForAnimationStart(latchStart, startTime); + if (timeElapsed != ANIMATION_NOT_STARTED) { + measuredTimeNs = timeElapsed; + } + } // hide IME before next iteration. if (show) { activity.runOnUiThread(() -> controller.hide(WindowInsets.Type.ime())); try { latchEnd.get().await(TIMEOUT_1_S_IN_MS * 5, TimeUnit.MILLISECONDS); - if (latchEnd.get().getCount() != 0) { + if (latchEnd.get().getCount() != 0 + && getOnMainSync(() -> isImeVisible(activity))) { Assert.fail("IME hide animation should finish."); } } catch (InterruptedException e) { @@ -350,12 +372,18 @@ public class ImePerfTest extends ImePerfTestBase addResultToState(state); } + @UiThread + private boolean isImeVisible(@NonNull final Activity activity) { + return activity.getWindow().getDecorView().getRootWindowInsets().isVisible( + WindowInsets.Type.ime()); + } + private long waitForAnimationStart( AtomicReference latchStart, AtomicLong startTime) { try { latchStart.get().await(TIMEOUT_1_S_IN_MS * 5, TimeUnit.MILLISECONDS); if (latchStart.get().getCount() != 0) { - Assert.fail("IME animation should start " + latchStart.get().getCount()); + return ANIMATION_NOT_STARTED; } } catch (InterruptedException e) { } diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TestUtils.java b/apct-tests/perftests/utils/src/android/perftests/utils/TestUtils.java new file mode 100644 index 000000000000..d8d3ee329638 --- /dev/null +++ b/apct-tests/perftests/utils/src/android/perftests/utils/TestUtils.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.perftests.utils; + +import android.app.Instrumentation; + +import androidx.annotation.NonNull; +import androidx.test.InstrumentationRegistry; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +public final class TestUtils { + + /** + * Retrieves a value that needs to be obtained on the main thread. + * + *

A simple utility method that helps to return an object from the UI thread.

+ * + * @param supplier callback to be called on the UI thread to return a value + * @param Type of the value to be returned + * @return Value returned from {@code supplier} + */ + public static T getOnMainSync(@NonNull Supplier supplier) { + final AtomicReference result = new AtomicReference<>(); + final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + instrumentation.runOnMainSync(() -> result.set(supplier.get())); + return result.get(); + } +} -- cgit v1.2.3 From c9f528479d17db032ce8bf259b3d46446d484cdb Mon Sep 17 00:00:00 2001 From: Alison Cichowlas Date: Mon, 21 Jun 2021 16:39:28 -0400 Subject: Direct share load cutback - minimal/low-risk version. Hide loading spinnies and make load time short, since we are no longer ever waiting for ChooserServices, just Shortcuts (and Shortcut ranking). For T (or possibly QPR) need to rip this out entirely - but for S let's keep it low-risk. Test: atest ChooserActivityTest Bug: 185292578, 184168028 Change-Id: I47fbb07721472498aa416708791553fb7332dd94 --- core/java/com/android/internal/app/ChooserActivity.java | 4 ++-- core/res/res/drawable/chooser_direct_share_icon_placeholder.xml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 862c9001e380..4857660a4d1d 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -464,8 +464,8 @@ public class ChooserActivity extends ResolverActivity implements private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5; private static final int LIST_VIEW_UPDATE_MESSAGE = 6; - private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 10000; - private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 3000; + private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 1000; + private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 300; private boolean mMinTimeoutPassed = false; diff --git a/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml index 838cb49ca79e..bd8dba866dd2 100644 --- a/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml +++ b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml @@ -26,7 +26,7 @@ + android:fillColor="@android:color/transparent"/> @@ -44,7 +44,7 @@ android:color="@android:color/transparent" android:offset="0.0" /> - -- cgit v1.2.3 From fbc5d5d58839f4513e99f00db3c1157b235a41bc Mon Sep 17 00:00:00 2001 From: Lucas Lin Date: Tue, 22 Jun 2021 03:16:30 +0000 Subject: Add OBSERVE_NETWORK_POLICY for shell to test CTS Both of NetworkPolicyManager#isUidNetworkingBlocked() and NetworkPolicyManager#isUidRestrictedOnMeteredNetworks() are required OBSERVE_NETWORK_POLICY but OBSERVE_NETWORK_POLICY is a signature permission that CTS cannot acquire. So add this permission for shell and call these 2 APIs with shell identity to fix the problem. Bug: 176289731 Test: atest CtsHostsideNetworkTests:HostsideNetworkPolicyManagerTests Original-Change: https://android-review.googlesource.com/1737413 Merged-In: Iba7321c82e1a3ed773f99ccd4b9e3c24c607353f Change-Id: Iba7321c82e1a3ed773f99ccd4b9e3c24c607353f --- packages/Shell/AndroidManifest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 959b5ca56687..f7123c180ead 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -419,6 +419,9 @@ + + + -- cgit v1.2.3 From 95de6aeb051c843eea7ea6942f34b4d2275b24a1 Mon Sep 17 00:00:00 2001 From: Hajime Morrita Date: Tue, 22 Jun 2021 00:47:17 -0700 Subject: Set pinner.pin_camera default to true. This was set to false while we conducted an experiment last year, and it concluded that this should be set to true. Although we set the flag to true through Phenotype, there are occasions where the flag isn't properly propagated, ending up in an undesirad state. Setting it true by default will eliminate such a race. Test: adb shell dumpsys pinner Bug: 184897981 Change-Id: I32db757cf915211978269dc4b836784d2052c6aa --- services/core/java/com/android/server/PinnerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 8561042a5056..8e53101b0eab 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -517,7 +517,7 @@ public final class PinnerService extends SystemService { boolean shouldPinCamera = mConfiguredToPinCamera && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, "pin_camera", - SystemProperties.getBoolean("pinner.pin_camera", false)); + SystemProperties.getBoolean("pinner.pin_camera", true)); if (shouldPinCamera) { pinKeys.add(KEY_CAMERA); } else if (DEBUG) { -- cgit v1.2.3 From 885432037f140d48905de59556cb2c78c5398d0a Mon Sep 17 00:00:00 2001 From: Lais Andrade Date: Tue, 15 Jun 2021 19:04:35 +0100 Subject: Split long ramps to fit HAL PWLE primitive limit Bug: 188431691 Fix: 183519628 Test: StepToRampAdapterTest Change-Id: I7f0baf387cadfb7f5d5032e98c0bdae2ad770904 --- .../android/server/vibrator/StepToRampAdapter.java | 76 ++++++++++++++++++++++ .../server/vibrator/StepToRampAdapterTest.java | 32 +++++++++ 2 files changed, 108 insertions(+) diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java index d439b94ec2fc..1d8c64bddd49 100644 --- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java +++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java @@ -22,6 +22,7 @@ import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; +import java.util.ArrayList; import java.util.List; /** @@ -59,6 +60,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter convertStepsToRamps(segments); int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex); newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex); + newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex); return newRepeatIndex; } @@ -210,11 +212,70 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return repeatIndex; } + /** + * Split {@link RampSegment} entries that have duration longer than {@link + * VibratorInfo#getPwlePrimitiveDurationMax()}. + */ + private int splitLongRampSegments(VibratorInfo info, List segments, + int repeatIndex) { + int maxDuration = info.getPwlePrimitiveDurationMax(); + if (maxDuration <= 0) { + // No limit set to PWLE primitive duration. + return repeatIndex; + } + + int segmentCount = segments.size(); + for (int i = 0; i < segmentCount; i++) { + if (!(segments.get(i) instanceof RampSegment)) { + continue; + } + RampSegment ramp = (RampSegment) segments.get(i); + int splits = ((int) ramp.getDuration() + maxDuration - 1) / maxDuration; + if (splits <= 1) { + continue; + } + segments.remove(i); + segments.addAll(i, splitRampSegment(ramp, splits)); + int addedSegments = splits - 1; + if (repeatIndex > i) { + repeatIndex += addedSegments; + } + i += addedSegments; + segmentCount += addedSegments; + } + + return repeatIndex; + } + private static RampSegment apply(StepSegment segment) { return new RampSegment(segment.getAmplitude(), segment.getAmplitude(), segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration()); } + private static List splitRampSegment(RampSegment ramp, int splits) { + List ramps = new ArrayList<>(splits); + long splitDuration = ramp.getDuration() / splits; + float previousAmplitude = ramp.getStartAmplitude(); + float previousFrequency = ramp.getStartFrequency(); + long accumulatedDuration = 0; + + for (int i = 1; i < splits; i++) { + accumulatedDuration += splitDuration; + RampSegment rampSplit = new RampSegment( + previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration), + previousFrequency, interpolateFrequency(ramp, accumulatedDuration), + (int) splitDuration); + ramps.add(rampSplit); + previousAmplitude = rampSplit.getEndAmplitude(); + previousFrequency = rampSplit.getEndFrequency(); + } + + ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency, + ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration))); + + return ramps; + } + private static RampSegment createRampDown(float amplitude, float frequency, long duration) { return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency, (int) duration); @@ -244,4 +305,19 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter } return false; } + + private static float interpolateAmplitude(RampSegment ramp, long duration) { + return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration, + ramp.getDuration()); + } + + private static float interpolateFrequency(RampSegment ramp, long duration) { + return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration, + ramp.getDuration()); + } + + private static float interpolate(float start, float end, long duration, long totalDuration) { + float position = (float) duration / totalDuration; + return start + position * (end - start); + } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java index f4eb2ded5a9d..32988efbab2c 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java @@ -67,6 +67,38 @@ public class StepToRampAdapterTest { assertEquals(originalSegments, segments); } + @Test + public void testRampSegments_withPwleDurationLimit_splitsLongRamps() { + List segments = new ArrayList<>(Arrays.asList( + new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, + /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1, + /* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25), + new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1, + /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5))); + List expectedSegments = Arrays.asList( + new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, + /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f, + /* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8), + new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f, + /* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f, + /* duration= */ 8), + new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1, + /* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9), + new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1, + /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)); + + VibratorInfo vibratorInfo = new VibratorInfo.Builder(0) + .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS) + .setPwlePrimitiveDurationMax(10) + .build(); + + // Update repeat index to skip the ramp splits. + assertEquals(4, mAdapter.apply(segments, 2, vibratorInfo)); + assertEquals(expectedSegments, segments); + } + @Test public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() { mAdapter = new StepToRampAdapter(50); -- cgit v1.2.3 From 987a2b41503c4b186e1a895b5812b3b11003d206 Mon Sep 17 00:00:00 2001 From: Mihir Patel Date: Tue, 22 Jun 2021 11:35:32 -0700 Subject: Guarding against null pointer exception when setting view structure info for content capture Test: Manual, CTS Bug: 189329389 Change-Id: Iaaacd59dc377079847951ab635df9bb602467390 --- core/java/android/widget/TextView.java | 35 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3c4fd5e93580..dc16ded08917 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -11848,23 +11848,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Get the text and trim it to the range we are reporting. CharSequence text = getText(); - if (expandedTopChar > 0 || expandedBottomChar < text.length()) { - text = text.subSequence(expandedTopChar, expandedBottomChar); - } - if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { - structure.setText(text); - } else { - structure.setText(text, selStart - expandedTopChar, selEnd - expandedTopChar); - - final int[] lineOffsets = new int[bottomLine - topLine + 1]; - final int[] lineBaselines = new int[bottomLine - topLine + 1]; - final int baselineOffset = getBaselineOffset(); - for (int i = topLine; i <= bottomLine; i++) { - lineOffsets[i - topLine] = layout.getLineStart(i); - lineBaselines[i - topLine] = layout.getLineBaseline(i) + baselineOffset; + if (text != null) { + if (expandedTopChar > 0 || expandedBottomChar < text.length()) { + text = text.subSequence(expandedTopChar, expandedBottomChar); + } + + if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { + structure.setText(text); + } else { + structure.setText(text, + selStart - expandedTopChar, + selEnd - expandedTopChar); + + final int[] lineOffsets = new int[bottomLine - topLine + 1]; + final int[] lineBaselines = new int[bottomLine - topLine + 1]; + final int baselineOffset = getBaselineOffset(); + for (int i = topLine; i <= bottomLine; i++) { + lineOffsets[i - topLine] = layout.getLineStart(i); + lineBaselines[i - topLine] = layout.getLineBaseline(i) + baselineOffset; + } + structure.setTextLines(lineOffsets, lineBaselines); } - structure.setTextLines(lineOffsets, lineBaselines); } } -- cgit v1.2.3 From 09e31b0ddcda446f53b773f2e34b0df5cd64ac44 Mon Sep 17 00:00:00 2001 From: Caitlin Cassidy Date: Tue, 22 Jun 2021 16:38:48 +0000 Subject: [Ongoing Call Chip] Fix NPE if the notification doesn't have a content intent. Test: atest (+ new unit tests) and manual Fixes: 191472854 Change-Id: I11aeed25a50da2c82a129247acf775a9fdffbad2 --- .../phone/ongoingcall/OngoingCallController.kt | 23 ++++++++------- .../phone/ongoingcall/OngoingCallControllerTest.kt | 33 ++++++++++++++++------ 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index b295f6659f81..d5965ec8fbfc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -85,7 +85,7 @@ class OngoingCallController @Inject constructor( val newOngoingCallInfo = CallNotificationInfo( entry.sbn.key, entry.sbn.notification.`when`, - entry.sbn.notification.contentIntent.intent, + entry.sbn.notification.contentIntent?.intent, entry.sbn.uid, entry.sbn.notification.extras.getInt( Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING @@ -176,14 +176,17 @@ class OngoingCallController @Inject constructor( systemClock.elapsedRealtime() timeView.start() - currentChipView.setOnClickListener { - logger.logChipClicked() - activityStarter.postStartActivityDismissingKeyguard( - currentCallNotificationInfo.intent, 0, - ActivityLaunchAnimator.Controller.fromView( - backgroundView, - InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) - ) + currentCallNotificationInfo.intent?.let { intent -> + currentChipView.setOnClickListener { + logger.logChipClicked() + activityStarter.postStartActivityDismissingKeyguard( + intent, + 0, + ActivityLaunchAnimator.Controller.fromView( + backgroundView, + InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP) + ) + } } setUpUidObserver(currentCallNotificationInfo) @@ -254,7 +257,7 @@ class OngoingCallController @Inject constructor( private data class CallNotificationInfo( val key: String, val callStartTime: Long, - val intent: Intent, + val intent: Intent?, val uid: Int, /** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */ val isOngoing: Boolean diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index ca3cff98b78c..94c9de0d2035 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -40,23 +40,23 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.* +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.nullable import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.never +import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.Mockito.reset - import org.mockito.MockitoAnnotations private const val CALL_UID = 900 @@ -140,6 +140,13 @@ class OngoingCallControllerTest : SysuiTestCase() { .onOngoingCallStateChanged(anyBoolean()) } + /** Regression test for b/191472854. */ + @Test + fun onEntryUpdated_notifHasNullContentIntent_noCrash() { + notifCollectionListener.onEntryUpdated( + createCallNotifEntry(ongoingCallStyle, nullContentIntent = true)) + } + /** * If a call notification is never added before #onEntryRemoved is called, then the listener * should never be notified. @@ -357,14 +364,22 @@ class OngoingCallControllerTest : SysuiTestCase() { private fun createScreeningCallNotifEntry() = createCallNotifEntry(screeningCallStyle) - private fun createCallNotifEntry(callStyle: Notification.CallStyle): NotificationEntry { + private fun createCallNotifEntry( + callStyle: Notification.CallStyle, + nullContentIntent: Boolean = false + ): NotificationEntry { val notificationEntryBuilder = NotificationEntryBuilder() notificationEntryBuilder.modifyNotification(context).style = callStyle - - val contentIntent = mock(PendingIntent::class.java) - `when`(contentIntent.intent).thenReturn(mock(Intent::class.java)) - notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent) notificationEntryBuilder.setUid(CALL_UID) + + if (nullContentIntent) { + notificationEntryBuilder.modifyNotification(context).setContentIntent(null) + } else { + val contentIntent = mock(PendingIntent::class.java) + `when`(contentIntent.intent).thenReturn(mock(Intent::class.java)) + notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent) + } + return notificationEntryBuilder.build() } -- cgit v1.2.3 From 01cf6b5f697e831054a3ca38c3d08151c7c70e74 Mon Sep 17 00:00:00 2001 From: Beverly Date: Tue, 22 Jun 2021 11:53:37 -0400 Subject: Update logic for toast app icons - Updated system apps without launcher icons don't require showing toast app icon - System apps don't require showing their toast app icon Fixes: 186050112 Test: manual Change-Id: Ibb6acd1de127a5a029891d1851ccf91dde6193cb --- .../com/android/systemui/toast/SystemUIToast.java | 37 +++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index c5e35a497956..8b394bfe35b7 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -16,13 +16,18 @@ package com.android.systemui.toast; +import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; +import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + import android.animation.Animator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; @@ -53,7 +58,7 @@ public class SystemUIToast implements ToastPlugin.Toast { final ToastPlugin.Toast mPluginToast; private final String mPackageName; - private final int mUserId; + @UserIdInt private final int mUserId; private final LayoutInflater mLayoutInflater; final int mDefaultX = 0; @@ -74,7 +79,7 @@ public class SystemUIToast implements ToastPlugin.Toast { } SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, - ToastPlugin.Toast pluginToast, String packageName, int userId, + ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId, int orientation) { mLayoutInflater = layoutInflater; mContext = context; @@ -248,6 +253,15 @@ public class SystemUIToast implements ToastPlugin.Toast { return null; } + final Context userContext; + try { + userContext = context.createPackageContextAsUser("android", + 0, new UserHandle(userId)); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not create user package context"); + return null; + } + final ApplicationsState appState = ApplicationsState.getInstance((Application) context.getApplicationContext()); if (!appState.isUserAdded(userId)) { @@ -255,9 +269,11 @@ public class SystemUIToast implements ToastPlugin.Toast { + "packageName=" + packageName); return null; } + + final PackageManager packageManager = userContext.getPackageManager(); final AppEntry appEntry = appState.getEntry(packageName, userId); if (appEntry == null || appEntry.info == null - || !ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(appEntry)) { + || !showApplicationIcon(appEntry.info, packageManager)) { return null; } @@ -265,7 +281,20 @@ public class SystemUIToast implements ToastPlugin.Toast { UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid); IconFactory iconFactory = IconFactory.obtain(context); Bitmap iconBmp = iconFactory.createBadgedIconBitmap( - appInfo.loadUnbadgedIcon(context.getPackageManager()), user, true).icon; + appInfo.loadUnbadgedIcon(packageManager), user, true).icon; return new BitmapDrawable(context.getResources(), iconBmp); } + + private static boolean showApplicationIcon(ApplicationInfo appInfo, + PackageManager packageManager) { + if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) { + return packageManager.getLaunchIntentForPackage(appInfo.packageName) + != null; + } + return !hasFlag(appInfo.flags, FLAG_SYSTEM); + } + + private static boolean hasFlag(int flags, int flag) { + return (flags & flag) != 0; + } } -- cgit v1.2.3 From 054ba1886b4eec327623ef5d26ae0c0749219159 Mon Sep 17 00:00:00 2001 From: Tim Peng Date: Wed, 23 Jun 2021 11:32:53 +0800 Subject: Output Switcher shows "+" button next to some non-expandable device -Remove + icon if there is not selectable device Bug: 191682821 Test: atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest MediaOutputGroupAdapterTest MediaOutputGroupDialogTest Change-Id: I89ccadad176e3f9ea0cc8796d4934ddc5a9d5def --- .../systemui/media/dialog/MediaOutputAdapter.java | 18 ++++++++++----- .../media/dialog/MediaOutputAdapterTest.java | 26 ++++++++++++++-------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 0d5faff65aab..bb0e8f29d34b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -128,7 +128,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } mBottomDivider.setVisibility(View.GONE); mCheckBox.setVisibility(View.GONE); - if (currentlyConnected && mController.isActiveRemoteDevice(device)) { + if (currentlyConnected && mController.isActiveRemoteDevice(device) + && mController.getSelectableMediaDevice().size() > 0) { // Init active device layout mDivider.setVisibility(View.VISIBLE); mDivider.setTransitionAlpha(1); @@ -186,11 +187,16 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mConnectedItem = mContainerLayout; mBottomDivider.setVisibility(View.GONE); mCheckBox.setVisibility(View.GONE); - mDivider.setVisibility(View.VISIBLE); - mDivider.setTransitionAlpha(1); - mAddIcon.setVisibility(View.VISIBLE); - mAddIcon.setTransitionAlpha(1); - mAddIcon.setOnClickListener(v -> onEndItemClick()); + if (mController.getSelectableMediaDevice().size() > 0) { + mDivider.setVisibility(View.VISIBLE); + mDivider.setTransitionAlpha(1); + mAddIcon.setVisibility(View.VISIBLE); + mAddIcon.setTransitionAlpha(1); + mAddIcon.setOnClickListener(v -> onEndItemClick()); + } else { + mDivider.setVisibility(View.GONE); + mAddIcon.setVisibility(View.GONE); + } mTitleIcon.setImageDrawable(getSpeakerDrawable()); final CharSequence sessionName = mController.getSessionName(); final CharSequence title = TextUtils.isEmpty(sessionName) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 6e216428992f..2c686618a361 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -135,8 +135,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); @@ -154,8 +152,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); @@ -176,9 +172,25 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); + } + + @Test + public void onBindViewHolder_bindConnectedDevice_withSelectableDevice_showAddIcon() { + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mMediaDevices); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); + } + + @Test + public void onBindViewHolder_bindConnectedDevice_withoutSelectableDevice_hideAddIcon() { + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(new ArrayList<>()); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE); + assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE); } @Test @@ -245,8 +257,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); } @@ -263,8 +273,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); } -- cgit v1.2.3 From 5a41b45a85286200f029b6ac06d004cb32c88dd0 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Thu, 10 Jun 2021 13:17:01 -0700 Subject: Revert "Deprecate Context#createApplicationContext" This reverts commit c54ebba25be71d77a4c0d92dba2f0b32c03a9792. Bug: 188059515 Test: atest FrameworksCoreTests:ContextTest Change-Id: I986563142dac135281889e811e6e5219d728d5d1 --- core/java/android/app/ActivityThread.java | 17 ++++---------- core/java/android/app/ContextImpl.java | 12 ++-------- core/java/android/app/Notification.java | 7 +++--- core/java/android/appwidget/AppWidgetHostView.java | 8 +++---- core/java/android/content/Context.java | 26 ---------------------- core/java/android/content/ContextWrapper.java | 8 ------- .../notification/StatusBarNotification.java | 7 ++++-- core/java/android/webkit/WebViewFactory.java | 10 +++------ core/java/android/widget/RemoteViews.java | 5 ++--- .../shared/plugins/PluginInstanceManager.java | 2 +- 10 files changed, 23 insertions(+), 79 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 034ad8e83fd3..46233d7a0688 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -36,7 +36,6 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility.PACK import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.app.backup.BackupAgent; @@ -62,7 +61,6 @@ import android.content.ContentCaptureOptions; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; -import android.content.Context.CreatePackageOptions; import android.content.IContentProvider; import android.content.IIntentReceiver; import android.content.Intent; @@ -73,7 +71,6 @@ import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionInfo; @@ -2376,22 +2373,16 @@ public final class ActivityThread extends ClientTransactionHandler @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - @CreatePackageOptions int flags) { + int flags) { return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); } public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - @CreatePackageOptions int flags, @UserIdInt int userId) { - return getPackageInfo(packageName, compatInfo, flags, userId, 0 /* packageFlags */); - } - - public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - @CreatePackageOptions int flags, @UserIdInt int userId, - @ApplicationInfoFlags int packageFlags) { + int flags, int userId) { final boolean differentUser = (UserHandle.myUserId() != userId); ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached( packageName, - packageFlags | PackageManager.GET_SHARED_LIBRARY_FILES + PackageManager.GET_SHARED_LIBRARY_FILES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, (userId < 0) ? UserHandle.myUserId() : userId); synchronized (mResourcesManager) { @@ -2434,7 +2425,7 @@ public final class ActivityThread extends ClientTransactionHandler @UnsupportedAppUsage(trackingBug = 171933273) public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, - @CreatePackageOptions int flags) { + int flags) { boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; boolean securityViolation = includeCode && ai.uid != 0 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 16b6ea5bcf42..5e99c79a7497 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -26,7 +26,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; import android.compat.annotation.UnsupportedAppUsage; -import android.content.AttributionSource; import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -48,7 +47,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetManager; import android.content.res.CompatResources; @@ -62,6 +60,7 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.content.AttributionSource; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -2494,13 +2493,6 @@ class ContextImpl extends Context { @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws NameNotFoundException { - return createPackageContextAsUser(packageName, flags, user, 0 /* packageFlags */); - } - - @Override - public Context createPackageContextAsUser( - @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user, - @ApplicationInfoFlags int packageFlags) throws PackageManager.NameNotFoundException { if (packageName.equals("system") || packageName.equals("android")) { // The system resources are loaded in every application, so we can safely copy // the context without reloading Resources. @@ -2511,7 +2503,7 @@ class ContextImpl extends Context { } LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), - flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier(), packageFlags); + flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams, mAttributionSource.getAttributionTag(), diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 506dfe09f3fa..6454d2027f58 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -6344,11 +6344,10 @@ public class Notification implements Parcelable ApplicationInfo applicationInfo = n.extras.getParcelable( EXTRA_BUILDER_APPLICATION_INFO); Context builderContext; - if (applicationInfo != null && applicationInfo.packageName != null) { + if (applicationInfo != null) { try { - builderContext = context.createPackageContextAsUser(applicationInfo.packageName, - Context.CONTEXT_RESTRICTED, - UserHandle.getUserHandleForUid(applicationInfo.uid)); + builderContext = context.createApplicationContext(applicationInfo, + Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found"); builderContext = context; // try with our context diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 3b11a19f9acc..ba3fc1e55c54 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -37,7 +37,6 @@ import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Parcelable; -import android.os.UserHandle; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; @@ -719,10 +718,9 @@ public class AppWidgetHostView extends FrameLayout { protected Context getRemoteContext() { try { // Return if cloned successfully, otherwise default - final ApplicationInfo info = mInfo.providerInfo.applicationInfo; - Context newContext = mContext.createPackageContextAsUser(info.packageName, - Context.CONTEXT_RESTRICTED, - UserHandle.getUserHandleForUid(info.uid)); + Context newContext = mContext.createApplicationContext( + mInfo.providerInfo.applicationInfo, + Context.CONTEXT_RESTRICTED); if (mColorResources != null) { mColorResources.apply(newContext); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9c60f431b06e..c02dcfd3d681 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -46,7 +46,6 @@ import android.app.time.TimeManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.res.AssetManager; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -6268,23 +6267,6 @@ public abstract class Context { return this; } - /** - * Similar to {@link #createPackageContextAsUser(String, int, UserHandle)}, but also allows - * specifying the flags used to retrieve the {@link ApplicationInfo} of the package. - * - * @hide - */ - @NonNull - public Context createPackageContextAsUser( - @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user, - @ApplicationInfoFlags int packageFlags) - throws PackageManager.NameNotFoundException { - if (Build.IS_ENG) { - throw new IllegalStateException("createPackageContextAsUser not overridden!"); - } - return this; - } - /** * Similar to {@link #createPackageContext(String, int)}, but for the own package with a * different {@link UserHandle}. For example, {@link #getContentResolver()} @@ -6304,18 +6286,10 @@ public abstract class Context { /** * Creates a context given an {@link android.content.pm.ApplicationInfo}. * - * @deprecated use {@link #createPackageContextAsUser(String, int, UserHandle, int)} - * If an application caches an ApplicationInfo and uses it to call this method, - * the app will not get the most recent version of Runtime Resource Overlays for - * that application. To make things worse, the LoadedApk stored in - * {@code ActivityThread#mResourcePackages} is updated using the old ApplicationInfo - * causing further uses of the cached LoadedApk to return outdated information. - * * @hide */ @SuppressWarnings("HiddenAbstractMethod") @UnsupportedAppUsage - @Deprecated public abstract Context createApplicationContext(ApplicationInfo application, @CreatePackageOptions int flags) throws PackageManager.NameNotFoundException; diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index cf0dc8c92ea1..6324d0ecb0e0 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1007,14 +1007,6 @@ public class ContextWrapper extends Context { return mBase.createPackageContextAsUser(packageName, flags, user); } - /** @hide */ - @Override - public Context createPackageContextAsUser(String packageName, int flags, UserHandle user, - int packageFlags) - throws PackageManager.NameNotFoundException { - return mBase.createPackageContextAsUser(packageName, flags, user, packageFlags); - } - /** @hide */ @Override public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) { diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 863d71f6f793..8e4a68e52697 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -434,8 +434,11 @@ public class StatusBarNotification implements Parcelable { public Context getPackageContext(Context context) { if (mContext == null) { try { - mContext = context.createPackageContextAsUser(pkg, Context.CONTEXT_RESTRICTED, user, - PackageManager.MATCH_UNINSTALLED_PACKAGES); + ApplicationInfo ai = context.getPackageManager() + .getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, + getUserId()); + mContext = context.createApplicationContext(ai, + Context.CONTEXT_RESTRICTED); } catch (PackageManager.NameNotFoundException e) { mContext = null; } diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 8d27cded6338..cf6807e41e8a 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -33,7 +33,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; -import android.os.UserHandle; import android.util.AndroidRuntimeException; import android.util.ArraySet; import android.util.Log; @@ -468,12 +467,9 @@ public final class WebViewFactory { sTimestamps.mCreateContextStart = SystemClock.uptimeMillis(); try { // Construct an app context to load the Java code into the current app. - Context webViewContext = initialApplication.createPackageContextAsUser( - ai.packageName, - Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY, - UserHandle.getUserHandleForUid(ai.uid), - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DEBUG_TRIAGED_MISSING); + Context webViewContext = initialApplication.createApplicationContext( + ai, + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); sPackageInfo = newPackageInfo; return webViewContext; } finally { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 8a044fd06dd5..e827f0a31bfd 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -5831,9 +5831,8 @@ public class RemoteViews implements Parcelable, Filter { return context; } try { - return context.createPackageContextAsUser(mApplication.packageName, - Context.CONTEXT_RESTRICTED, - UserHandle.getUserHandleForUid(mApplication.uid)); + return context.createApplicationContext(mApplication, + Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found"); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index c90833c5b8f2..2b35bcd9a3ea 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -345,7 +345,7 @@ public class PluginInstanceManager { // Create our own ClassLoader so we can use our own code as the parent. ClassLoader classLoader = mManager.getClassLoader(info); Context pluginContext = new PluginContextWrapper( - mContext.createPackageContext(pkg, 0), classLoader); + mContext.createApplicationContext(info, 0), classLoader); Class pluginClass = Class.forName(cls, true, classLoader); // TODO: Only create the plugin before version check if we need it for // legacy version check. -- cgit v1.2.3 From 25360a2f65e34611ab5f7a84505d1017b36fb228 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 11 Jun 2021 14:16:41 -0700 Subject: Version LoadedApk cache using base code path When a context is requested through Context#createApplicationContext, the underlying LoadedApk cached in ActivityThread#mResourcePackages is updated using the ApplicationInfo passed in. This creates unexpected problems when using APIs that expect to be able to read resources from a previous version of a package after that package has been updated. With this change, contexts requested through Context#createPackageContext and Context#createApplicationContext will will have an underlying LoadedAPK that is tied to the version of the package represented by the ApplicationInfo. If createPackageContext is invoked before a package updates and then after a package updates, the two contexts will have different underlying LoaddApks. The ActivityThread#mPackages and ActivityThread#mResourcePackages now only hold LoadedAPKs of that were created using ApplicationInfos that were sent to this process from system_server in order to run the application. Whenever a newer version of an ApplicationInfo is delivered from system_server for one of these packages, the previously cached LoadedAPK will be updated using the new ApplicationInfo. This will usually be because of an overlay change or "don't kill" package upgrade. Bug: 188059515 Test: atest FrameworksCoreTests:ContextTest Change-Id: Iea54abf38449c9d8ab9dae16d36d95cbb4d7d8b0 --- core/java/android/app/ActivityThread.java | 525 ++++++++++++--------- core/java/android/app/ContextImpl.java | 4 +- core/tests/coretests/Android.bp | 2 + core/tests/coretests/apks/res_upgrade/Android.bp | 22 + .../coretests/apks/res_upgrade/AndroidManifest.xml | 20 + .../apks/res_upgrade/res_after/values/values.xml | 22 + .../apks/res_upgrade/res_before/values/values.xml | 22 + .../coretests/src/android/content/ContextTest.java | 146 +++++- 8 files changed, 530 insertions(+), 233 deletions(-) create mode 100644 core/tests/coretests/apks/res_upgrade/Android.bp create mode 100644 core/tests/coretests/apks/res_upgrade/AndroidManifest.xml create mode 100644 core/tests/coretests/apks/res_upgrade/res_after/values/values.xml create mode 100644 core/tests/coretests/apks/res_upgrade/res_before/values/values.xml diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 46233d7a0688..f71e9fd033da 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -218,6 +218,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.net.InetAddress; @@ -228,6 +230,7 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -416,6 +419,16 @@ public final class ActivityThread extends ClientTransactionHandler @GuardedBy("mResourcesManager") @UnsupportedAppUsage final ArrayMap> mResourcePackages = new ArrayMap<>(); + + @GuardedBy("mResourcesManager") + private final ArrayMap, WeakReference> mPackageNonAMS = + new ArrayMap<>(); + @GuardedBy("mResourcesManager") + private final ArrayMap, WeakReference> mResourcePackagesNonAMS = + new ArrayMap<>(); + @GuardedBy("mResourcesManager") + private final ReferenceQueue mPackageRefQueue = new ReferenceQueue<>(); + @GuardedBy("mResourcesManager") final ArrayList mRelaunchingActivities = new ArrayList<>(); @GuardedBy("mResourcesManager") @@ -2371,66 +2384,281 @@ public final class ActivityThread extends ClientTransactionHandler return mH; } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - int flags) { - return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); + /** + * If {@code retainReferences} is false, prunes all {@link LoadedApk} representing any of the + * specified packages from the package caches. + * + * @return whether the cache contains a loaded apk representing any of the specified packages + */ + private boolean clearCachedApks() { + synchronized (mResourcesManager) { + Reference enqueuedRef = mPackageRefQueue.poll(); + if (enqueuedRef == null) { + return false; + } + + final HashSet> deadReferences = new HashSet<>(); + for (; enqueuedRef != null; enqueuedRef = mPackageRefQueue.poll()) { + deadReferences.add(enqueuedRef); + } + + return cleanWeakMapValues(mPackages, deadReferences) + || cleanWeakMapValues(mResourcePackages, deadReferences) + || cleanWeakMapValues(mPackageNonAMS, deadReferences) + || cleanWeakMapValues(mResourcePackages, deadReferences); + } } - public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, - int flags, int userId) { - final boolean differentUser = (UserHandle.myUserId() != userId); - ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached( - packageName, - PackageManager.GET_SHARED_LIBRARY_FILES - | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - (userId < 0) ? UserHandle.myUserId() : userId); + private static boolean cleanWeakMapValues(ArrayMap> map, + HashSet> deadReferences) { + boolean hasPkgInfo = false; + for (int i = map.size() - 1; i >= 0; i--) { + if (deadReferences.contains(map.valueAt(i))) { + map.removeAt(i); + hasPkgInfo = true; + } + } + return hasPkgInfo; + } + + /** + * Retrieves the previously cached {@link LoadedApk} that was created/updated with the most + * recent {@link ApplicationInfo} sent from the activity manager service. + */ + @Nullable + private LoadedApk peekLatestCachedApkFromAMS(@NonNull String packageName, boolean includeCode) { synchronized (mResourcesManager) { WeakReference ref; - if (differentUser) { - // Caching not supported across users - ref = null; - } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) { - ref = mPackages.get(packageName); + if (includeCode) { + return ((ref = mPackages.get(packageName)) != null) ? ref.get() : null; } else { - ref = mResourcePackages.get(packageName); + return ((ref = mResourcePackages.get(packageName)) != null) ? ref.get() : null; } + } + } - LoadedApk packageInfo = ref != null ? ref.get() : null; - if (ai != null && packageInfo != null) { - if (!isLoadedApkResourceDirsUpToDate(packageInfo, ai)) { - List oldPaths = new ArrayList<>(); - LoadedApk.makePaths(this, ai, oldPaths); - packageInfo.updateApplicationInfo(ai, oldPaths); + /** + * Updates the previously cached {@link LoadedApk} that was created/updated using an + * {@link ApplicationInfo} sent from activity manager service. + * + * If {@code appInfo} is null, the most up-to-date {@link ApplicationInfo} will be fetched and + * used to update the cached package; otherwise, the specified app info will be used. + */ + private boolean updateLatestCachedApkFromAMS(@NonNull String packageName, + @Nullable ApplicationInfo appInfo, boolean updateActivityRecords) { + final LoadedApk[] loadedPackages = new LoadedApk[]{ + peekLatestCachedApkFromAMS(packageName, true), + peekLatestCachedApkFromAMS(packageName, false) + }; + + try { + if (appInfo == null) { + appInfo = sPackageManager.getApplicationInfo( + packageName, PackageManager.GET_SHARED_LIBRARY_FILES, + UserHandle.myUserId()); + } + } catch (RemoteException e) { + Slog.v(TAG, "Failed to get most recent app info for '" + packageName + "'", e); + return false; + } + + boolean hasPackage = false; + final String[] oldResDirs = new String[loadedPackages.length]; + for (int i = loadedPackages.length - 1; i >= 0; i--) { + final LoadedApk loadedPackage = loadedPackages[i]; + if (loadedPackage == null) { + continue; + } + + // If the package is being updated, yet it still has a valid LoadedApk object, the + // package was updated with PACKAGE_REMOVED_DONT_KILL. Adjust it's internal references + // to the application info and resources. + hasPackage = true; + if (updateActivityRecords && mActivities.size() > 0) { + for (ActivityClientRecord ar : mActivities.values()) { + if (ar.activityInfo.applicationInfo.packageName.equals(packageName)) { + ar.activityInfo.applicationInfo = appInfo; + ar.packageInfo = loadedPackage; + } } + } - if (packageInfo.isSecurityViolation() - && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) { - throw new SecurityException( - "Requesting code from " + packageName - + " to be run in process " - + mBoundApplication.processName - + "/" + mBoundApplication.appInfo.uid); + updateLoadedApk(loadedPackage, appInfo); + oldResDirs[i] = loadedPackage.getResDir(); + } + if (hasPackage) { + synchronized (mResourcesManager) { + mResourcesManager.applyNewResourceDirs(appInfo, oldResDirs); + } + } + return hasPackage; + } + + private static List makeNonAMSKey(@NonNull ApplicationInfo appInfo) { + final List paths = new ArrayList<>(); + paths.add(appInfo.sourceDir); + if (appInfo.resourceDirs != null) { + for (String path : appInfo.resourceDirs) { + paths.add(path); + } + } + return paths; + } + + /** + * Retrieves the previously cached {@link LoadedApk}. + * + * If {@code isAppInfoFromAMS} is true, then {@code appInfo} will be used to update the paths + * of the previously cached {@link LoadedApk} that was created/updated using an + * {@link ApplicationInfo} sent from activity manager service. + */ + @Nullable + private LoadedApk retrieveCachedApk(@NonNull ApplicationInfo appInfo, boolean includeCode, + boolean isAppInfoFromAMS) { + if (UserHandle.myUserId() != UserHandle.getUserId(appInfo.uid)) { + // Caching not supported across users. + return null; + } + + if (isAppInfoFromAMS) { + LoadedApk loadedPackage = peekLatestCachedApkFromAMS(appInfo.packageName, includeCode); + if (loadedPackage != null) { + updateLoadedApk(loadedPackage, appInfo); + } + return loadedPackage; + } + + synchronized (mResourcesManager) { + WeakReference ref; + if (includeCode) { + return ((ref = mPackageNonAMS.get(makeNonAMSKey(appInfo))) != null) + ? ref.get() : null; + } else { + return ((ref = mResourcePackagesNonAMS.get(makeNonAMSKey(appInfo))) != null) + ? ref.get() : null; + } + } + } + + private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk, + ApplicationInfo appInfo) { + boolean baseDirsUpToDate = loadedApk.getResDir().equals(appInfo.sourceDir); + boolean resourceDirsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.resourceDirs), + ArrayUtils.defeatNullable(loadedApk.getOverlayDirs())); + boolean overlayPathsUpToDate = Arrays.equals( + ArrayUtils.defeatNullable(appInfo.overlayPaths), + ArrayUtils.defeatNullable(loadedApk.getOverlayPaths())); + + return (loadedApk.mResources == null || loadedApk.mResources.getAssets().isUpToDate()) + && baseDirsUpToDate && resourceDirsUpToDate && overlayPathsUpToDate; + } + + private void updateLoadedApk(@NonNull LoadedApk loadedPackage, + @NonNull ApplicationInfo appInfo) { + if (isLoadedApkResourceDirsUpToDate(loadedPackage, appInfo)) { + return; + } + + final List oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, appInfo, oldPaths); + loadedPackage.updateApplicationInfo(appInfo, oldPaths); + } + + @Nullable + private LoadedApk createLoadedPackage(@NonNull ApplicationInfo appInfo, + @Nullable CompatibilityInfo compatInfo, @Nullable ClassLoader baseLoader, + boolean securityViolation, boolean includeCode, boolean registerPackage, + boolean isAppInfoFromAMS) { + final LoadedApk packageInfo = + new LoadedApk(this, appInfo, compatInfo, baseLoader, + securityViolation, includeCode + && (appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); + + if (mSystemThread && "android".equals(appInfo.packageName)) { + packageInfo.installSystemApplicationInfo(appInfo, + getSystemContext().mPackageInfo.getClassLoader()); + } + + if (UserHandle.myUserId() != UserHandle.getUserId(appInfo.uid)) { + // Caching not supported across users + return packageInfo; + } + + synchronized (mResourcesManager) { + if (includeCode) { + if (isAppInfoFromAMS) { + mPackages.put(appInfo.packageName, + new WeakReference<>(packageInfo, mPackageRefQueue)); + } else { + mPackageNonAMS.put(makeNonAMSKey(appInfo), + new WeakReference<>(packageInfo, mPackageRefQueue)); + } + } else { + if (isAppInfoFromAMS) { + mResourcePackages.put(appInfo.packageName, + new WeakReference<>(packageInfo, mPackageRefQueue)); + } else { + mResourcePackagesNonAMS.put(makeNonAMSKey(appInfo), + new WeakReference<>(packageInfo, mPackageRefQueue)); } - return packageInfo; } + return packageInfo; } + } + + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, + int flags) { + return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); + } + public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, + int flags, int userId) { + final ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached( + packageName, + PackageManager.GET_SHARED_LIBRARY_FILES + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + (userId < 0) ? UserHandle.myUserId() : userId); + LoadedApk packageInfo = null; if (ai != null) { - return getPackageInfo(ai, compatInfo, flags); + packageInfo = retrieveCachedApk(ai, + (flags & Context.CONTEXT_INCLUDE_CODE) != 0, + false); } - return null; + if (packageInfo != null) { + if (packageInfo.isSecurityViolation() + && (flags & Context.CONTEXT_IGNORE_SECURITY) == 0) { + throw new SecurityException( + "Requesting code from " + packageName + + " to be run in process " + + mBoundApplication.processName + + "/" + mBoundApplication.appInfo.uid); + } + return packageInfo; + } + + return ai == null ? null : getPackageInfo(ai, compatInfo, flags, false); } + /** + * @deprecated Use {@link #getPackageInfo(ApplicationInfo, CompatibilityInfo, int, boolean)} + * instead. + */ + @Deprecated @UnsupportedAppUsage(trackingBug = 171933273) public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) { + return getPackageInfo(ai, compatInfo, flags, true); + } + + public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, + @Context.CreatePackageOptions int flags, boolean isAppInfoFromAMS) { boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; boolean securityViolation = includeCode && ai.uid != 0 - && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null - ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid) - : true); + && ai.uid != Process.SYSTEM_UID && (mBoundApplication == null + || !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid)); boolean registerPackage = includeCode && (flags&Context.CONTEXT_REGISTER_PACKAGE) != 0; if ((flags&(Context.CONTEXT_INCLUDE_CODE |Context.CONTEXT_IGNORE_SECURITY)) @@ -2440,107 +2668,47 @@ public final class ActivityThread extends ClientTransactionHandler + " (with uid " + ai.uid + ")"; if (mBoundApplication != null) { msg = msg + " to be run in process " - + mBoundApplication.processName + " (with uid " - + mBoundApplication.appInfo.uid + ")"; + + mBoundApplication.processName + " (with uid " + + mBoundApplication.appInfo.uid + ")"; } throw new SecurityException(msg); } } return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode, - registerPackage); + registerPackage, isAppInfoFromAMS); } @Override @UnsupportedAppUsage public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { - return getPackageInfo(ai, compatInfo, null, false, true, false); + return getPackageInfo(ai, compatInfo, null, false, true, false, true); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) { synchronized (mResourcesManager) { - WeakReference ref; - if (includeCode) { - ref = mPackages.get(packageName); - } else { - ref = mResourcePackages.get(packageName); - } - return ref != null ? ref.get() : null; + return peekLatestCachedApkFromAMS(packageName, includeCode); } } private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode, - boolean registerPackage) { - final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); - synchronized (mResourcesManager) { - WeakReference ref; - if (differentUser) { - // Caching not supported across users - ref = null; - } else if (includeCode) { - ref = mPackages.get(aInfo.packageName); - } else { - ref = mResourcePackages.get(aInfo.packageName); - } - - LoadedApk packageInfo = ref != null ? ref.get() : null; - - if (packageInfo != null) { - if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) { - List oldPaths = new ArrayList<>(); - LoadedApk.makePaths(this, aInfo, oldPaths); - packageInfo.updateApplicationInfo(aInfo, oldPaths); - } - - return packageInfo; - } - - if (localLOGV) { - Slog.v(TAG, (includeCode ? "Loading code package " - : "Loading resource-only package ") + aInfo.packageName - + " (in " + (mBoundApplication != null - ? mBoundApplication.processName : null) - + ")"); - } - - packageInfo = - new LoadedApk(this, aInfo, compatInfo, baseLoader, - securityViolation, includeCode - && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); - - if (mSystemThread && "android".equals(aInfo.packageName)) { - packageInfo.installSystemApplicationInfo(aInfo, - getSystemContext().mPackageInfo.getClassLoader()); - } - - if (differentUser) { - // Caching not supported across users - } else if (includeCode) { - mPackages.put(aInfo.packageName, - new WeakReference(packageInfo)); - } else { - mResourcePackages.put(aInfo.packageName, - new WeakReference(packageInfo)); - } - + boolean registerPackage, boolean isAppInfoFromAMS) { + LoadedApk packageInfo = retrieveCachedApk(aInfo, includeCode, + isAppInfoFromAMS); + if (packageInfo != null) { return packageInfo; } - } - - private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk, - ApplicationInfo appInfo) { - Resources packageResources = loadedApk.mResources; - boolean resourceDirsUpToDate = Arrays.equals( - ArrayUtils.defeatNullable(appInfo.resourceDirs), - ArrayUtils.defeatNullable(loadedApk.getOverlayDirs())); - boolean overlayPathsUpToDate = Arrays.equals( - ArrayUtils.defeatNullable(appInfo.overlayPaths), - ArrayUtils.defeatNullable(loadedApk.getOverlayPaths())); - - return (packageResources == null || packageResources.getAssets().isUpToDate()) - && resourceDirsUpToDate && overlayPathsUpToDate; + if (localLOGV) { + Slog.v(TAG, (includeCode ? "Loading code package " + : "Loading resource-only package ") + aInfo.packageName + + " (in " + (mBoundApplication != null + ? mBoundApplication.processName : null) + + ")"); + } + return createLoadedPackage(aInfo, compatInfo, baseLoader, + securityViolation, includeCode, registerPackage, isAppInfoFromAMS); } @UnsupportedAppUsage @@ -3502,7 +3670,7 @@ public final class ActivityThread extends ClientTransactionHandler ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) { r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, - Context.CONTEXT_INCLUDE_CODE); + Context.CONTEXT_INCLUDE_CODE, true); } ComponentName component = r.intent.getComponent(); @@ -5985,40 +6153,10 @@ public final class ActivityThread extends ClientTransactionHandler @VisibleForTesting(visibility = PACKAGE) public void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) { - // Updates triggered by package installation go through a package update - // receiver. Here we try to capture ApplicationInfo changes that are - // caused by other sources, such as overlays. That means we want to be as conservative - // about code changes as possible. Take the diff of the old ApplicationInfo and the new - // to see if anything needs to change. - LoadedApk apk; - LoadedApk resApk; - // Update all affected loaded packages with new package information - synchronized (mResourcesManager) { - WeakReference ref = mPackages.get(ai.packageName); - apk = ref != null ? ref.get() : null; - ref = mResourcePackages.get(ai.packageName); - resApk = ref != null ? ref.get() : null; - } - - final String[] oldResDirs = new String[2]; - - if (apk != null) { - oldResDirs[0] = apk.getResDir(); - final ArrayList oldPaths = new ArrayList<>(); - LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths); - apk.updateApplicationInfo(ai, oldPaths); - } - if (resApk != null) { - oldResDirs[1] = resApk.getResDir(); - final ArrayList oldPaths = new ArrayList<>(); - LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths); - resApk.updateApplicationInfo(ai, oldPaths); - } - - synchronized (mResourcesManager) { - // Update all affected Resources objects to use new ResourcesImpl - mResourcesManager.applyNewResourceDirs(ai, oldResDirs); - } + // Updates triggered by package installation go through a package update receiver. Here we + // try to capture ApplicationInfo changes that are caused by other sources, such as + // overlays. That means we want to be as conservative about code changes as possible. + updateLatestCachedApkFromAMS(ai.packageName, ai, false); } /** @@ -6195,29 +6333,7 @@ public final class ActivityThread extends ClientTransactionHandler case ApplicationThreadConstants.PACKAGE_REMOVED: case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL: { - final boolean killApp = cmd == ApplicationThreadConstants.PACKAGE_REMOVED; - if (packages == null) { - break; - } - synchronized (mResourcesManager) { - for (int i = packages.length - 1; i >= 0; i--) { - if (!hasPkgInfo) { - WeakReference ref = mPackages.get(packages[i]); - if (ref != null && ref.get() != null) { - hasPkgInfo = true; - } else { - ref = mResourcePackages.get(packages[i]); - if (ref != null && ref.get() != null) { - hasPkgInfo = true; - } - } - } - if (killApp) { - mPackages.remove(packages[i]); - mResourcePackages.remove(packages[i]); - } - } - } + hasPkgInfo = clearCachedApks(); break; } case ApplicationThreadConstants.PACKAGE_REPLACED: @@ -6225,68 +6341,19 @@ public final class ActivityThread extends ClientTransactionHandler if (packages == null) { break; } - - List packagesHandled = new ArrayList<>(); - - synchronized (mResourcesManager) { - for (int i = packages.length - 1; i >= 0; i--) { - String packageName = packages[i]; - WeakReference ref = mPackages.get(packageName); - LoadedApk pkgInfo = ref != null ? ref.get() : null; - if (pkgInfo != null) { - hasPkgInfo = true; - } else { - ref = mResourcePackages.get(packageName); - pkgInfo = ref != null ? ref.get() : null; - if (pkgInfo != null) { - hasPkgInfo = true; - } - } - // If the package is being replaced, yet it still has a valid - // LoadedApk object, the package was updated with _DONT_KILL. - // Adjust it's internal references to the application info and - // resources. - if (pkgInfo != null) { - packagesHandled.add(packageName); - try { - final ApplicationInfo aInfo = - sPackageManager.getApplicationInfo( - packageName, - PackageManager.GET_SHARED_LIBRARY_FILES, - UserHandle.myUserId()); - - if (mActivities.size() > 0) { - for (ActivityClientRecord ar : mActivities.values()) { - if (ar.activityInfo.applicationInfo.packageName - .equals(packageName)) { - ar.activityInfo.applicationInfo = aInfo; - ar.packageInfo = pkgInfo; - } - } - } - - final String[] oldResDirs = { pkgInfo.getResDir() }; - - final ArrayList oldPaths = new ArrayList<>(); - LoadedApk.makePaths(this, pkgInfo.getApplicationInfo(), oldPaths); - pkgInfo.updateApplicationInfo(aInfo, oldPaths); - - synchronized (mResourcesManager) { - // Update affected Resources objects to use new ResourcesImpl - mResourcesManager.applyNewResourceDirs(aInfo, oldResDirs); - } - } catch (RemoteException e) { - } - } + final List packagesHandled = new ArrayList<>(); + for (int i = packages.length - 1; i >= 0; i--) { + final String packageName = packages[i]; + if (updateLatestCachedApkFromAMS(packageName, null, true)) { + hasPkgInfo = true; + packagesHandled.add(packageName); } } - try { getPackageManager().notifyPackagesReplacedReceived( packagesHandled.toArray(new String[0])); } catch (RemoteException ignored) { } - break; } } @@ -6845,7 +6912,7 @@ public final class ActivityThread extends ClientTransactionHandler ii.copyTo(instrApp); instrApp.initForUser(UserHandle.myUserId()); final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, - appContext.getClassLoader(), false, true, false); + appContext.getClassLoader(), false, true, false, true); // The test context's op package name == the target app's op package name, because // the app ops manager checks the op package name against the real calling UID, diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5e99c79a7497..d24196813387 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -26,6 +26,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiContext; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -60,7 +61,6 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.content.AttributionSource; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -2461,7 +2461,7 @@ class ContextImpl extends Context { public Context createApplicationContext(ApplicationInfo application, int flags) throws NameNotFoundException { LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(), - flags | CONTEXT_REGISTER_PACKAGE); + flags | CONTEXT_REGISTER_PACKAGE, false); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY, mAttributionSource.getAttributionTag(), diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 93e4a294a2cb..8fae80d602d9 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -121,6 +121,8 @@ java_genrule { ":FrameworksCoreTests_keyset_splat_api", ":FrameworksCoreTests_locales", ":FrameworksCoreTests_overlay_config", + ":FrameworksCoreTests_res_version_after", + ":FrameworksCoreTests_res_version_before", ":FrameworksCoreTests_version_1", ":FrameworksCoreTests_version_1_diff", ":FrameworksCoreTests_version_1_nosys", diff --git a/core/tests/coretests/apks/res_upgrade/Android.bp b/core/tests/coretests/apks/res_upgrade/Android.bp new file mode 100644 index 000000000000..c58614f2c946 --- /dev/null +++ b/core/tests/coretests/apks/res_upgrade/Android.bp @@ -0,0 +1,22 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "FrameworksCoreTests_res_version_before", + defaults: ["FrameworksCoreTests_apks_defaults"], + resource_dirs: ["res_before"], + certificate: ":FrameworksCoreTests_unit_test_cert", +} + +android_test_helper_app { + name: "FrameworksCoreTests_res_version_after", + defaults: ["FrameworksCoreTests_apks_defaults"], + resource_dirs: ["res_after"], + certificate: ":FrameworksCoreTests_unit_test_cert", +} diff --git a/core/tests/coretests/apks/res_upgrade/AndroidManifest.xml b/core/tests/coretests/apks/res_upgrade/AndroidManifest.xml new file mode 100644 index 000000000000..1c607c9fd249 --- /dev/null +++ b/core/tests/coretests/apks/res_upgrade/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/core/tests/coretests/apks/res_upgrade/res_after/values/values.xml b/core/tests/coretests/apks/res_upgrade/res_after/values/values.xml new file mode 100644 index 000000000000..db4fd54c7aee --- /dev/null +++ b/core/tests/coretests/apks/res_upgrade/res_after/values/values.xml @@ -0,0 +1,22 @@ + + + + + + after + + diff --git a/core/tests/coretests/apks/res_upgrade/res_before/values/values.xml b/core/tests/coretests/apks/res_upgrade/res_before/values/values.xml new file mode 100644 index 000000000000..63fc79020e3f --- /dev/null +++ b/core/tests/coretests/apks/res_upgrade/res_before/values/values.xml @@ -0,0 +1,22 @@ + + + + + + before + + diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index d1776fb7e5c1..8488a84dbe01 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -25,15 +25,23 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.app.ActivityThread; +import android.app.PendingIntent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.inputmethodservice.InputMethodService; import android.media.ImageReader; +import android.os.FileUtils; import android.os.UserHandle; import android.view.Display; @@ -42,12 +50,20 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.compatibility.common.util.ShellIdentityUtils; +import com.android.frameworks.coretests.R; + import org.junit.Test; import org.junit.runner.RunWith; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.TimeUnit; + /** - * Build/Install/Run: - * atest FrameworksCoreTests:ContextTest + * Build/Install/Run: + * atest FrameworksCoreTests:ContextTest */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -216,6 +232,132 @@ public class ContextTest { assertFalse(context.isUiContext()); } + private static class TestReceiver extends BroadcastReceiver implements AutoCloseable { + private static final String INTENT_ACTION = "com.android.server.pm.test.test_app.action"; + private final ArrayBlockingQueue mResults = new ArrayBlockingQueue<>(1); + + public IntentSender makeIntentSender() { + return PendingIntent.getBroadcast(getContext(), 0, new Intent(INTENT_ACTION), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED) + .getIntentSender(); + } + + public void waitForIntent() throws InterruptedException { + assertNotNull(mResults.poll(30, TimeUnit.SECONDS)); + } + + @Override + public void onReceive(Context context, Intent intent) { + mResults.add(intent); + } + + public void register() { + getContext().registerReceiver(this, new IntentFilter(INTENT_ACTION)); + } + + @Override + public void close() throws Exception { + getContext().unregisterReceiver(this); + } + + private Context getContext() { + return InstrumentationRegistry.getInstrumentation().getContext(); + } + } + + @Test + public void applicationContextBeforeAndAfterUpgrade() throws Exception { + final Context context = InstrumentationRegistry.getInstrumentation().getContext(); + final String testPackageName = "com.android.frameworks.coretests.res_version"; + try { + final PackageManager pm = context.getPackageManager(); + final int versionRes = 0x7f010000; + + final Context appContext = ApplicationProvider.getApplicationContext(); + installApk(appContext, R.raw.res_version_before); + + ApplicationInfo info = pm.getApplicationInfo(testPackageName, 0); + final Context beforeContext = appContext.createApplicationContext(info, 0); + assertEquals("before", beforeContext.getResources().getString(versionRes)); + + installApk(appContext, R.raw.res_version_after); + + info = pm.getApplicationInfo(testPackageName, 0); + final Context afterContext = appContext.createApplicationContext(info, 0); + assertEquals("before", beforeContext.createConfigurationContext(Configuration.EMPTY) + .getResources().getString(versionRes)); + assertEquals("after", afterContext.createConfigurationContext(Configuration.EMPTY) + .getResources().getString(versionRes)); + assertNotEquals(beforeContext.getPackageResourcePath(), + afterContext.getPackageResourcePath()); + } finally { + uninstallPackage(context, testPackageName); + } + } + + @Test + public void packageContextBeforeAndAfterUpgrade() throws Exception { + final Context context = InstrumentationRegistry.getInstrumentation().getContext(); + final String testPackageName = "com.android.frameworks.coretests.res_version"; + try { + final int versionRes = 0x7f010000; + final Context appContext = ApplicationProvider.getApplicationContext(); + installApk(appContext, R.raw.res_version_before); + + final Context beforeContext = appContext.createPackageContext(testPackageName, 0); + assertEquals("before", beforeContext.getResources().getString(versionRes)); + + installApk(appContext, R.raw.res_version_after); + + final Context afterContext = appContext.createPackageContext(testPackageName, 0); + assertEquals("before", beforeContext.createConfigurationContext(Configuration.EMPTY) + .getResources().getString(versionRes)); + assertEquals("after", afterContext.createConfigurationContext(Configuration.EMPTY) + .getResources().getString(versionRes)); + assertNotEquals(beforeContext.getPackageResourcePath(), + afterContext.getPackageResourcePath()); + } finally { + uninstallPackage(context, testPackageName); + } + } + + private void installApk(Context context, int rawApkResId) throws Exception { + final PackageManager pm = context.getPackageManager(); + final PackageInstaller pi = pm.getPackageInstaller(); + final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( + PackageInstaller.SessionParams.MODE_FULL_INSTALL); + final int sessionId = pi.createSession(params); + + try (PackageInstaller.Session session = pi.openSession(sessionId)) { + // Copy the apk to the install session. + final Resources resources = context.getResources(); + try (InputStream is = resources.openRawResource(rawApkResId); + OutputStream sessionOs = session.openWrite("base", 0, -1)) { + FileUtils.copy(is, sessionOs); + } + + // Wait for the installation to finish + try (TestReceiver receiver = new TestReceiver()) { + receiver.register(); + ShellIdentityUtils.invokeMethodWithShellPermissions(session, + (s) -> { + s.commit(receiver.makeIntentSender()); + return true; + }); + receiver.waitForIntent(); + } + } + } + + private void uninstallPackage(Context context, String packageName) throws Exception { + try (TestReceiver receiver = new TestReceiver()) { + receiver.register(); + final PackageInstaller pi = context.getPackageManager().getPackageInstaller(); + pi.uninstall(packageName, receiver.makeIntentSender()); + receiver.waitForIntent(); + } + } + private Context createUiContext() { final Context appContext = ApplicationProvider.getApplicationContext(); final DisplayManager displayManager = appContext.getSystemService(DisplayManager.class); -- cgit v1.2.3 From db7d06c52903b8dccb7e8d9976a23c366430a42e Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Mon, 21 Jun 2021 00:33:10 +0800 Subject: Avoid locking profile task when it is already lock WorkLockActivity was started repeatedly on top of the task that contains work apps when turning screen on and off over and over. So, lots of the WorkLockActivity instances were created and added in the task, which caused system sluggish. Bug: 177457096 Test: manually test work challenges Test: RootWindowContainerTests Change-Id: Iac345471ef3badad6b9e5c0cc2873c60938663eb Merged-In: Iac345471ef3badad6b9e5c0cc2873c60938663eb (cherry picked from commit 805585ed1baa0ddeeab07aa1f77819333a73c93d) --- .../com/android/server/wm/RootWindowContainer.java | 10 +++++++++ .../server/wm/RootWindowContainerTests.java | 25 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ddad1dbd9b3d..1c1360f6f389 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; @@ -3381,6 +3382,15 @@ class RootWindowContainer extends WindowContainer mService.deferWindowLayout(); try { forAllLeafTasks(task -> { + final ActivityRecord top = task.topRunningActivity(); + if (top != null && !top.finishing + && ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER.equals(top.intent.getAction()) + && top.packageName.equals( + mService.getSysUiServiceComponentLocked().getPackageName())) { + // Do nothing since the task is already secure by sysui. + return; + } + if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId) != null) { mService.getTaskChangeNotificationController().notifyTaskProfileLocked( diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 1aff8a7b5382..e5c5bf54b0e6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; @@ -25,6 +26,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.ActivityStack.ActivityState.FINISHING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; @@ -37,11 +39,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.app.WindowConfiguration; import android.content.ComponentName; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; @@ -194,6 +200,8 @@ public class RootWindowContainerTests extends WindowTestsBase { .setUid(UserHandle.PER_USER_RANGE + 1) .setTask(task) .build(); + doReturn(true).when(topActivity).okToShowLocked(); + topActivity.intent.setAction(Intent.ACTION_MAIN); // Make sure the listeners will be notified for putting the task to locked state TaskChangeNotificationController controller = @@ -201,6 +209,23 @@ public class RootWindowContainerTests extends WindowTestsBase { spyOn(controller); mWm.mRoot.lockAllProfileTasks(0); verify(controller).notifyTaskProfileLocked(eq(task.mTaskId), eq(0)); + + // Create the work lock activity on top of the task + final ActivityRecord workLockActivity = + new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) + .setStack(stack) + .setUid(UserHandle.PER_USER_RANGE + 1) + .setTask(task) + .build(); + doReturn(true).when(workLockActivity).okToShowLocked(); + workLockActivity.intent.setAction(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER); + doReturn(workLockActivity.mActivityComponent).when( + mWm.mAtmService).getSysUiServiceComponentLocked(); + + // Make sure the listener won't be notified again. + clearInvocations(controller); + mWm.mRoot.lockAllProfileTasks(0); + verify(controller, never()).notifyTaskProfileLocked(eq(task.mTaskId), anyInt()); } } -- cgit v1.2.3 From 499234d859d4a12a0856951b71ebf57015913ffa Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Mon, 21 Jun 2021 00:33:10 +0800 Subject: Avoid locking profile task when it is already lock WorkLockActivity was started repeatedly on top of the task that contains work apps when turning screen on and off over and over. So, lots of the WorkLockActivity instances were created and added in the task, which caused system sluggish. Bug: 177457096 Test: manually test work challenges Test: RootWindowContainerTests Change-Id: Iac345471ef3badad6b9e5c0cc2873c60938663eb Merged-In: Iac345471ef3badad6b9e5c0cc2873c60938663eb (cherry picked from commit 805585ed1baa0ddeeab07aa1f77819333a73c93d) --- .../com/android/server/wm/RootWindowContainer.java | 10 +++++++++ .../server/wm/RootWindowContainerTests.java | 25 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index d236b75087da..15bd650104a6 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; @@ -3365,6 +3366,15 @@ class RootWindowContainer extends WindowContainer mService.deferWindowLayout(); try { forAllLeafTasks(task -> { + final ActivityRecord top = task.topRunningActivity(); + if (top != null && !top.finishing + && ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER.equals(top.intent.getAction()) + && top.packageName.equals( + mService.getSysUiServiceComponentLocked().getPackageName())) { + // Do nothing since the task is already secure by sysui. + return; + } + if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId) != null) { mService.getTaskChangeNotificationController().notifyTaskProfileLocked( diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 1aff8a7b5382..e5c5bf54b0e6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; @@ -25,6 +26,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.ActivityStack.ActivityState.FINISHING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; @@ -37,11 +39,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.app.WindowConfiguration; import android.content.ComponentName; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; @@ -194,6 +200,8 @@ public class RootWindowContainerTests extends WindowTestsBase { .setUid(UserHandle.PER_USER_RANGE + 1) .setTask(task) .build(); + doReturn(true).when(topActivity).okToShowLocked(); + topActivity.intent.setAction(Intent.ACTION_MAIN); // Make sure the listeners will be notified for putting the task to locked state TaskChangeNotificationController controller = @@ -201,6 +209,23 @@ public class RootWindowContainerTests extends WindowTestsBase { spyOn(controller); mWm.mRoot.lockAllProfileTasks(0); verify(controller).notifyTaskProfileLocked(eq(task.mTaskId), eq(0)); + + // Create the work lock activity on top of the task + final ActivityRecord workLockActivity = + new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) + .setStack(stack) + .setUid(UserHandle.PER_USER_RANGE + 1) + .setTask(task) + .build(); + doReturn(true).when(workLockActivity).okToShowLocked(); + workLockActivity.intent.setAction(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER); + doReturn(workLockActivity.mActivityComponent).when( + mWm.mAtmService).getSysUiServiceComponentLocked(); + + // Make sure the listener won't be notified again. + clearInvocations(controller); + mWm.mRoot.lockAllProfileTasks(0); + verify(controller, never()).notifyTaskProfileLocked(eq(task.mTaskId), anyInt()); } } -- cgit v1.2.3 From 9331d0e67f1d179bb2215f2a5fc2662c66b7b450 Mon Sep 17 00:00:00 2001 From: Ahaan Ugale Date: Tue, 22 Jun 2021 22:46:42 -0700 Subject: Autofill: Shell command for saved password count This is for testing. Will be replaced with a TestAPI in the future. Bug: 184396750 Test: adb shell cmd autofill get saved-password-count 0 Test: atest CtsAutoFillServiceTestCases:SavedDatasetsInfoTest Change-Id: I6560b34e085bb36dc21d22479141b1310326c91b --- .../server/autofill/AutofillManagerService.java | 20 +++++++++++++++++ .../autofill/AutofillManagerServiceImpl.java | 10 +++++++++ .../AutofillManagerServiceShellCommand.java | 25 ++++++++++++++++++++++ .../android/server/autofill/RemoteFillService.java | 5 +++++ 4 files changed, 60 insertions(+) diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 312cde6c5152..de5f47dd5dbb 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -662,6 +662,26 @@ public final class AutofillManagerService return false; } + /** + * Requests a count of saved passwords from the current service. + * + * @return {@code true} if the request succeeded + */ + // Called by Shell command + boolean requestSavedPasswordCount(@UserIdInt int userId, @NonNull IResultReceiver receiver) { + enforceCallingPermissionForManagement(); + synchronized (mLock) { + final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + if (service != null) { + service.requestSavedPasswordCount(receiver); + return true; + } else if (sVerbose) { + Slog.v(TAG, "requestSavedPasswordCount(): no service for " + userId); + } + } + return false; + } + private void setLoggingLevelsLocked(boolean debug, boolean verbose) { com.android.server.autofill.Helper.sDebug = debug; android.view.autofill.Helper.sDebug = debug; diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index a80efc461176..5f2d4e82883c 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -76,6 +76,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.os.IResultReceiver; import com.android.server.LocalServices; import com.android.server.autofill.AutofillManagerService.AutofillCompatState; import com.android.server.autofill.AutofillManagerService.DisabledInfoCache; @@ -1180,6 +1181,15 @@ final class AutofillManagerServiceImpl return false; } + @GuardedBy("mLock") + void requestSavedPasswordCount(IResultReceiver receiver) { + RemoteFillService remoteService = + new RemoteFillService( + getContext(), mInfo.getServiceInfo().getComponentName(), mUserId, + /* callbacks= */ null, mMaster.isInstantServiceAllowed()); + remoteService.onSavedPasswordCountRequest(receiver); + } + @GuardedBy("mLock") @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() { if (mRemoteAugmentedAutofillService == null) { diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java index 68e6290c987a..1eaa59a0871a 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java @@ -17,6 +17,7 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; +import static android.service.autofill.AutofillService.EXTRA_RESULT; import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS; @@ -89,6 +90,9 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { pw.println(" get bind-instant-service-allowed"); pw.println(" Gets whether binding to services provided by instant apps is allowed"); pw.println(""); + pw.println(" get saved-password-count"); + pw.println(" Gets the number of saved passwords in the current service."); + pw.println(""); pw.println(" set log_level [off | debug | verbose]"); pw.println(" Sets the Autofill log level."); pw.println(""); @@ -145,6 +149,8 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return getBindInstantService(pw); case "default-augmented-service-enabled": return getDefaultAugmentedServiceEnabled(pw); + case "saved-password-count": + return getSavedPasswordCount(pw); default: pw.println("Invalid set: " + what); return -1; @@ -342,6 +348,25 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return 0; } + private int getSavedPasswordCount(PrintWriter pw) { + final int userId = getNextIntArgRequired(); + CountDownLatch latch = new CountDownLatch(1); + IResultReceiver resultReceiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) { + pw.println("resultCode=" + resultCode); + if (resultCode == 0 && resultData != null) { + pw.println("value=" + resultData.getInt(EXTRA_RESULT)); + } + latch.countDown(); + } + }; + if (mService.requestSavedPasswordCount(userId, resultReceiver)) { + waitForLatch(pw, latch); + } + return 0; + } + private int requestDestroy(PrintWriter pw) { if (!isNextArgSessions(pw)) { return -1; diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index b0755ac836e0..94872b09cd36 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -41,6 +41,7 @@ import android.util.Slog; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.ServiceConnector; +import com.android.internal.os.IResultReceiver; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -225,6 +226,10 @@ final class RemoteFillService extends ServiceConnector.Impl { })); } + void onSavedPasswordCountRequest(IResultReceiver receiver) { + run(service -> service.onSavedPasswordCountRequest(receiver)); + } + public void destroy() { unbind(); } -- cgit v1.2.3 From 9ecf891254286397982cac1dc9bf4c5cd9c8e007 Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Tue, 22 Jun 2021 16:57:30 +0800 Subject: Allow transfer starting window to windowDisableStarting activity. Fix a black screen case when a trampoline activity finish itself after it start another activity in the same task. It was not allow to create a starting window on second activity if it has declared as disable starting, but if the starting window already exists, we should allow the transfer from the previous activity. Bug: 190671916 Test: cold launch PreDeferredSetupWizardActivity from notification. Test: atest ActivityRecordTests Change-Id: I75d89dc4152818b145951cbb9489d8bc2aa54340 --- .../java/com/android/server/wm/ActivityRecord.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 55d1920681c5..331eeee4f181 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -38,6 +38,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -1788,17 +1789,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * Evaluate the theme for a starting window. + * @param prev Previous activity which may have a starting window. * @param originalTheme The original theme which read from activity or application. * @param replaceTheme The replace theme which requested from starter. * @return Resolved theme. */ - private int evaluateStartingWindowTheme(String pkg, int originalTheme, int replaceTheme) { + private int evaluateStartingWindowTheme(ActivityRecord prev, String pkg, int originalTheme, + int replaceTheme) { // Skip if the package doesn't want a starting window. - if (!validateStartingWindowTheme(pkg, originalTheme)) { + if (!validateStartingWindowTheme(prev, pkg, originalTheme)) { return 0; } int selectedTheme = originalTheme; - if (replaceTheme != 0 && validateStartingWindowTheme(pkg, replaceTheme)) { + if (replaceTheme != 0 && validateStartingWindowTheme(prev, pkg, replaceTheme)) { // allow to replace theme selectedTheme = replaceTheme; } @@ -1835,7 +1838,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return LAUNCH_SOURCE_TYPE_APPLICATION; } - private boolean validateStartingWindowTheme(String pkg, int theme) { + private boolean validateStartingWindowTheme(ActivityRecord prev, String pkg, int theme) { // If this is a translucent window, then don't show a starting window -- the current // effect (a full-screen opaque starting window that fades away to the real contents // when it is ready) does not work for this. @@ -1872,7 +1875,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return false; } if (windowDisableStarting && !launchedFromSystemSurface()) { - return false; + // Check if previous activity can transfer the starting window to this activity. + return prev != null && prev.getActivityType() == ACTIVITY_TYPE_STANDARD + && prev.mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE + && (prev.mStartingData != null + || (prev.mStartingWindow != null && prev.mStartingSurface != null)); } return true; } @@ -6278,7 +6285,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mSplashScreenStyleEmpty = shouldUseEmptySplashScreen(sourceRecord); - final int resolvedTheme = evaluateStartingWindowTheme(packageName, theme, + final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme, splashScreenTheme); final boolean shown = addStartingWindow(packageName, resolvedTheme, -- cgit v1.2.3 From 98360bcb04d15550445c04210a630842130624f7 Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Wed, 23 Jun 2021 15:31:18 +0800 Subject: Update the text size for collapsedTextAppearance Fix: 177967930 Test: visual verified 1) Navigate to either sub-setting page 2) Rotate the screen to landscape mode 3) See if the size of title is the same as the one in portrait mode Change-Id: I91621b6be298ae886432d3b06996be0ccc7b974c --- packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml index 2a72a1ad65db..63d397c69353 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml @@ -17,6 +17,7 @@ diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml index a03a1d3accfb..d6c6a60d56b8 100644 --- a/packages/SystemUI/res/xml/media_collapsed.xml +++ b/packages/SystemUI/res/xml/media_collapsed.xml @@ -92,7 +92,7 @@ android:layout_height="wrap_content" app:layout_constrainedWidth="true" android:layout_marginTop="@dimen/qs_media_info_spacing" - app:layout_constraintTop_toTopOf="@id/center_horizontal_guideline" + app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline" app:layout_constraintStart_toStartOf="@id/header_title" app:layout_constraintEnd_toStartOf="@id/media_action_barrier" app:layout_constraintHorizontal_bias="0"/> @@ -123,7 +123,7 @@ Date: Thu, 24 Jun 2021 13:23:59 -0700 Subject: Allow Shell more permissions for TvInputManagerTest. Bug: 171024152 Test: android.media.tv.cts.TvInputManagerTest Change-Id: Iabea3ea72a5476892c848eaa8ebf4f93ad9b1000 --- data/etc/privapp-permissions-platform.xml | 2 ++ packages/Shell/AndroidManifest.xml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 04291e3dcc5c..12eb50e5bee0 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -428,6 +428,8 @@ applications that come with the platform + + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index f7123c180ead..491f0d9f615f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -436,6 +436,8 @@ + + -- cgit v1.2.3 From 7e9bd7fe7c08962ebaecee865d9e15a4b3e4a56a Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Mon, 14 Jun 2021 16:28:58 +0800 Subject: Waiting a proper time to remove the snapshot starting window As CL[1] removed the delay time to remove starting window in shell, even the benifits is to make good touch responsiness when launched the activity, but may easier cause flickering since removing the starting window when the activity allDrawn doesn't means the UI are all set. (like some apps might needs adjust the surface frame or even IME needs more time wait for animation finish, if the snapshot window has these previews). In case flickering happens when the Shell removes the task snapshot window too quickly, partially reverts CL[1] delay removal logic in TaskSnapshotWindow#remove, and use TaskSnapshot#hasImeSurface to set a proper delay removal time: - General (no IME visible): 100ms - IME visible: 350ms [1]: I5fb0fa3a1e6a5e6210d3baf400a84c5892bd2e34 Fix: 189825624 Test: manual switch tasks with/without ime window, also test with some market input methods. Change-Id: I7865e17b57961e12a0cdcf068e412195123a6ec7 --- .../shell/startingsurface/TaskSnapshotWindow.java | 33 ++++++++++++++++++++-- .../startingsurface/TaskSnapshotWindowTest.java | 4 ++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index acf7f3367d71..382d5806e3c2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -16,6 +16,7 @@ package com.android.wm.shell.startingsurface; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; import static android.graphics.Color.alpha; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -63,6 +64,7 @@ import android.graphics.RectF; import android.hardware.HardwareBuffer; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.Trace; import android.util.MergedConfiguration; import android.util.Slog; @@ -116,11 +118,15 @@ public class TaskSnapshotWindow { private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_TASK_SNAPSHOT; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; + private static final long DELAY_REMOVAL_TIME_GENERAL = 100; + private static final long DELAY_REMOVAL_TIME_IME_VISIBLE = 350; + //tmp vars for unused relayout params private static final Point TMP_SURFACE_SIZE = new Point(); private final Window mWindow; private final Runnable mClearWindowHandler; + private final long mDelayRemovalTime; private final ShellExecutor mSplashScreenExecutor; private final SurfaceControl mSurfaceControl; private final IWindowSession mSession; @@ -132,8 +138,10 @@ public class TaskSnapshotWindow { private final RectF mTmpDstFrame = new RectF(); private final CharSequence mTitle; private boolean mHasDrawn; + private long mShownTime; private boolean mSizeMismatch; private final Paint mBackgroundPaint = new Paint(); + private final int mActivityType; private final int mStatusBarColor; private final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; @@ -190,6 +198,7 @@ public class TaskSnapshotWindow { final Point taskSize = snapshot.getTaskSize(); final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); final int orientation = snapshot.getOrientation(); + final int activityType = runningTaskInfo.topActivityType; final int displayId = runningTaskInfo.displayId; final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -207,10 +216,13 @@ public class TaskSnapshotWindow { taskDescription.setBackgroundColor(WHITE); } + final long delayRemovalTime = snapshot.hasImeSurface() ? DELAY_REMOVAL_TIME_IME_VISIBLE + : DELAY_REMOVAL_TIME_GENERAL; + final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance, - windowFlags, windowPrivateFlags, taskBounds, orientation, - topWindowInsetsState, clearWindowHandler, splashScreenExecutor); + windowFlags, windowPrivateFlags, taskBounds, orientation, activityType, + delayRemovalTime, topWindowInsetsState, clearWindowHandler, splashScreenExecutor); final Window window = snapshotSurface.mWindow; final InsetsState mTmpInsetsState = new InsetsState(); @@ -248,7 +260,8 @@ public class TaskSnapshotWindow { public TaskSnapshotWindow(SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds, - int currentOrientation, InsetsState topWindowInsetsState, Runnable clearWindowHandler, + int currentOrientation, int activityType, long delayRemovalTime, + InsetsState topWindowInsetsState, Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) { mSplashScreenExecutor = splashScreenExecutor; mSession = WindowManagerGlobal.getWindowSession(); @@ -264,6 +277,8 @@ public class TaskSnapshotWindow { windowPrivateFlags, appearance, taskDescription, 1f, topWindowInsetsState); mStatusBarColor = taskDescription.getStatusBarColor(); mOrientationOnCreation = currentOrientation; + mActivityType = activityType; + mDelayRemovalTime = delayRemovalTime; mTransaction = new SurfaceControl.Transaction(); mClearWindowHandler = clearWindowHandler; } @@ -286,6 +301,17 @@ public class TaskSnapshotWindow { } void remove() { + final long now = SystemClock.uptimeMillis(); + if ((now - mShownTime < mDelayRemovalTime) + // Show the latest content as soon as possible for unlocking to home. + && mActivityType != ACTIVITY_TYPE_HOME) { + final long delayTime = mShownTime + mDelayRemovalTime - now; + mSplashScreenExecutor.executeDelayed(() -> remove(), delayTime); + if (DEBUG) { + Slog.d(TAG, "Defer removing snapshot surface in " + delayTime); + } + return; + } try { if (DEBUG) { Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn); @@ -326,6 +352,7 @@ public class TaskSnapshotWindow { } else { drawSizeMatchSnapshot(); } + mShownTime = SystemClock.uptimeMillis(); mHasDrawn = true; reportDrawn(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java index 5945840a8fa2..a098a6863493 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java @@ -16,6 +16,7 @@ package com.android.wm.shell.startingsurface; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; @@ -81,7 +82,8 @@ public class TaskSnapshotWindowTest { mWindow = new TaskSnapshotWindow(new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), 0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */, - taskBounds, ORIENTATION_PORTRAIT, new InsetsState(), + taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, + 100 /* delayRemovalTime */, new InsetsState(), null /* clearWindow */, new TestShellExecutor()); } -- cgit v1.2.3 From 92843817e32b556a2ce51fef8681b5e1f27b5895 Mon Sep 17 00:00:00 2001 From: Bill Lin Date: Thu, 24 Jun 2021 14:58:21 +0800 Subject: Apply itemTextAppearance for QuickSettings style The popup menu of QSCustomizer is ListItemMenuView which apply itemTextAppearance for the text visual. We customize itemTextAppearance in QuickSettings style - Theme.SystemUI.QuickSettings - @style/Control.MenuItem for popup menu text to ensure the font family coud be consistent with whole QuickSetings visual. Test: Build and look QS Customizer popup menu atest: SystemUITests Bug: 186905553 Change-Id: I7349ab4a03ce1910bd5d49f0d2f527a3d4846afe --- packages/SystemUI/res/values/styles.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7a5a3480d0c5..d2be349eeab9 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -404,6 +404,7 @@ @android:color/system_neutral1_800 @android:color/system_neutral1_1000 @android:color/system_neutral1_900 + @style/Control.MenuItem - + +