From d6d33148b87c855347872e9c1736f5e1ba0def38 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Thu, 30 Nov 2023 23:12:39 +0000 Subject: Added throttle when reporting shortcut usage Bug: 304290201 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bd88f35c6797b1795d1150af92760531ff73f14f) Merged-In: I96370cbd4f6a55f894c1a93307e5f82dfd394652 Change-Id: I96370cbd4f6a55f894c1a93307e5f82dfd394652 --- .../com/android/server/pm/ShortcutPackage.java | 32 +++++++++++++ .../com/android/server/pm/ShortcutService.java | 18 ++----- .../android/server/pm/ShortcutManagerTest1.java | 55 +++++++++++++++++++++- .../android/server/pm/ShortcutManagerTest2.java | 2 + 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 8456927b2cdf..00da2439ced8 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -33,6 +33,7 @@ import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResults; import android.app.appsearch.SearchSpec; import android.app.appsearch.SetSchemaRequest; +import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -47,6 +48,7 @@ import android.graphics.drawable.Icon; import android.os.Binder; import android.os.PersistableBundle; import android.os.StrictMode; +import android.os.SystemClock; import android.text.format.Formatter; import android.util.ArrayMap; import android.util.ArraySet; @@ -160,6 +162,9 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String KEY_BITMAPS = "bitmaps"; private static final String KEY_BITMAP_BYTES = "bitmapBytes"; + @VisibleForTesting + public static final int REPORT_USAGE_BUFFER_SIZE = 3; + private final Executor mExecutor; /** @@ -195,6 +200,9 @@ class ShortcutPackage extends ShortcutPackageItem { private long mLastKnownForegroundElapsedTime; + @GuardedBy("mLock") + private List mLastReportedTime = new ArrayList<>(); + @GuardedBy("mLock") private boolean mIsAppSearchSchemaUpToDate; @@ -1677,6 +1685,30 @@ class ShortcutPackage extends ShortcutPackageItem { return condition[0]; } + void reportShortcutUsed(@NonNull final UsageStatsManagerInternal usageStatsManagerInternal, + @NonNull final String shortcutId) { + synchronized (mLock) { + final long currentTS = SystemClock.elapsedRealtime(); + final ShortcutService s = mShortcutUser.mService; + if (mLastReportedTime.isEmpty() + || mLastReportedTime.size() < REPORT_USAGE_BUFFER_SIZE) { + mLastReportedTime.add(currentTS); + } else if (currentTS - mLastReportedTime.get(0) > s.mSaveDelayMillis) { + mLastReportedTime.remove(0); + mLastReportedTime.add(currentTS); + } else { + return; + } + final long token = s.injectClearCallingIdentity(); + try { + usageStatsManagerInternal.reportShortcutUsage(getPackageName(), shortcutId, + getUser().getUserId()); + } finally { + s.injectRestoreCallingIdentity(token); + } + } + } + public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) { pw.println(); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index c6aba2ab9cbe..77ec06878e23 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -369,7 +369,7 @@ public class ShortcutService extends IShortcutService.Stub { private CompressFormat mIconPersistFormat; private int mIconPersistQuality; - private int mSaveDelayMillis; + int mSaveDelayMillis; private final IPackageManager mIPackageManager; private final PackageManagerInternal mPackageManagerInternal; @@ -2282,7 +2282,7 @@ public class ShortcutService extends IShortcutService.Stub { packageShortcutsChanged(ps, changedShortcuts, removedShortcuts); - reportShortcutUsedInternal(packageName, shortcut.getId(), userId); + ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcut.getId()); verifyStates(); } @@ -2686,25 +2686,17 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d", shortcutId, packageName, userId)); } + final ShortcutPackage ps; synchronized (mLock) { throwIfUserLockedL(userId); - final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId); + ps = getPackageShortcutsForPublisherLocked(packageName, userId); if (ps.findShortcutById(shortcutId) == null) { Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s", packageName, shortcutId)); return; } } - reportShortcutUsedInternal(packageName, shortcutId, userId); - } - - private void reportShortcutUsedInternal(String packageName, String shortcutId, int userId) { - final long token = injectClearCallingIdentity(); - try { - mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId); - } finally { - injectRestoreCallingIdentity(token); - } + ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcutId); } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 7c1845fcd54e..77e3a988790f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -404,8 +404,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { public void testPushDynamicShortcut() { // Change the max number of shortcuts. - mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5"); - + mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5," + + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1"); setCaller(CALLING_PACKAGE_1, USER_0); final ShortcutInfo s1 = makeShortcut("s1"); @@ -543,6 +543,57 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0)); } + public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled() + throws InterruptedException { + mService.updateConfigurationLocked( + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500"); + + // Verify calls to UsageStatsManagerInternal#reportShortcutUsage are throttled. + setCaller(CALLING_PACKAGE_1, USER_0); + for (int i = 0; i < ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i++) { + final ShortcutInfo si = makeShortcut("s" + i); + mManager.pushDynamicShortcut(si); + } + verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( + eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0)); + Mockito.reset(mMockUsageStatsManagerInternal); + for (int i = ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i <= 10; i++) { + final ShortcutInfo si = makeShortcut("s" + i); + mManager.pushDynamicShortcut(si); + } + verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( + any(), any(), anyInt()); + + // Verify pkg2 isn't blocked by pkg1, but consecutive calls from pkg2 are throttled as well. + setCaller(CALLING_PACKAGE_2, USER_0); + for (int i = 0; i < ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i++) { + final ShortcutInfo si = makeShortcut("s" + i); + mManager.pushDynamicShortcut(si); + } + verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( + eq(CALLING_PACKAGE_2), eq("s1"), eq(USER_0)); + Mockito.reset(mMockUsageStatsManagerInternal); + for (int i = ShortcutPackage.REPORT_USAGE_BUFFER_SIZE; i <= 10; i++) { + final ShortcutInfo si = makeShortcut("s" + i); + mManager.pushDynamicShortcut(si); + } + verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( + any(), any(), anyInt()); + + Mockito.reset(mMockUsageStatsManagerInternal); + // Let time passes which resets the throttle + Thread.sleep(505); + // Verify UsageStatsManagerInternal#reportShortcutUsed can be called again + setCaller(CALLING_PACKAGE_1, USER_0); + mManager.pushDynamicShortcut(makeShortcut("s10")); + setCaller(CALLING_PACKAGE_2, USER_0); + mManager.pushDynamicShortcut(makeShortcut("s10")); + verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( + eq(CALLING_PACKAGE_1), any(), eq(USER_0)); + verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage( + eq(CALLING_PACKAGE_2), any(), eq(USER_0)); + } + public void testUnlimitedCalls() { setCaller(CALLING_PACKAGE_1, USER_0); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 1cfaf7c83584..27c981aba85c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -2175,6 +2175,8 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { public void testReportShortcutUsed() { mRunningUsers.put(USER_10, true); + mService.updateConfigurationLocked( + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1"); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { reset(mMockUsageStatsManagerInternal); -- cgit v1.2.3 From 0cba15479a0b211debbfa504f2c232dba3a14071 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Thu, 22 Feb 2024 10:51:58 +0100 Subject: Check for NLS bind permission when rebinding services Also, after updating packages with NLS components, check the approved services and remove from approved list if missing permissions. Test: atest ManagedServicesTest Bug: 321707289 (cherry picked from commit 24b13a64f9f5e5aa7f45a2132806d6c74e2c62dc) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4d4224c1b9f87a156324153854a1567e842ecb22) Merged-In: I11901755ec430c6e3145def9d67e4e63cda00806 Change-Id: I11901755ec430c6e3145def9d67e4e63cda00806 --- .../server/notification/ManagedServices.java | 51 +++++++++++++++++-- .../server/notification/ManagedServicesTest.java | 57 ++++++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 12fc263e1c8c..98b288cd7676 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -939,6 +939,23 @@ abstract public class ManagedServices { return false; } + protected boolean isPackageOrComponentAllowedWithPermission(ComponentName component, + int userId) { + if (!(isPackageOrComponentAllowed(component.flattenToString(), userId) + || isPackageOrComponentAllowed(component.getPackageName(), userId))) { + return false; + } + return componentHasBindPermission(component, userId); + } + + private boolean componentHasBindPermission(ComponentName component, int userId) { + ServiceInfo info = getServiceInfo(component, userId); + if (info == null) { + return false; + } + return mConfig.bindPermission.equals(info.permission); + } + boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) { synchronized (mApproved) { ArraySet services = mUserSetServices.get(userId); @@ -1000,6 +1017,7 @@ abstract public class ManagedServices { for (int uid : uidList) { if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) { anyServicesInvolved = true; + trimApprovedListsForInvalidServices(pkgName, UserHandle.getUserId(uid)); } } } @@ -1132,8 +1150,7 @@ abstract public class ManagedServices { synchronized (mMutex) { if (enabled) { - if (isPackageOrComponentAllowed(component.flattenToString(), userId) - || isPackageOrComponentAllowed(component.getPackageName(), userId)) { + if (isPackageOrComponentAllowedWithPermission(component, userId)) { registerServiceLocked(component, userId); } else { Slog.d(TAG, component + " no longer has permission to be bound"); @@ -1252,6 +1269,33 @@ abstract public class ManagedServices { return removed; } + private void trimApprovedListsForInvalidServices(String packageName, int userId) { + synchronized (mApproved) { + final ArrayMap> approvedByType = mApproved.get(userId); + if (approvedByType == null) { + return; + } + for (int i = 0; i < approvedByType.size(); i++) { + final ArraySet approved = approvedByType.valueAt(i); + for (int j = approved.size() - 1; j >= 0; j--) { + final String approvedPackageOrComponent = approved.valueAt(j); + if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) { + final ComponentName component = ComponentName.unflattenFromString( + approvedPackageOrComponent); + if (component != null && !componentHasBindPermission(component, userId)) { + approved.removeAt(j); + if (DEBUG) { + Slog.v(TAG, "Removing " + approvedPackageOrComponent + + " from approved list; no bind permission found " + + mConfig.bindPermission); + } + } + } + } + } + } + } + protected String getPackageName(String packageOrComponent) { final ComponentName component = ComponentName.unflattenFromString(packageOrComponent); if (component != null) { @@ -1488,8 +1532,7 @@ abstract public class ManagedServices { void reregisterService(final ComponentName cn, final int userId) { // If rebinding a package that died, ensure it still has permission // after the rebind delay - if (isPackageOrComponentAllowed(cn.getPackageName(), userId) - || isPackageOrComponentAllowed(cn.flattenToString(), userId)) { + if (isPackageOrComponentAllowedWithPermission(cn, userId)) { registerService(cn, userId); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 541739d50024..4ef3a6eb12ab 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -36,6 +36,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -883,6 +884,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a", 0, true); service.reregisterService(cn, 0); @@ -913,6 +915,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a", 0, false); service.reregisterService(cn, 0); @@ -943,6 +946,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a/a", 0, true); service.reregisterService(cn, 0); @@ -973,6 +977,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a/a", 0, false); service.reregisterService(cn, 0); @@ -1129,6 +1134,58 @@ public class ManagedServicesTest extends UiServiceTestCase { } } + @Test + public void testUpgradeAppNoPermissionNoRebind() throws Exception { + Context context = spy(getContext()); + doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any()); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, + mIpm, + APPROVAL_BY_COMPONENT); + + List packages = new ArrayList<>(); + packages.add("package"); + addExpectedServices(service, packages, 0); + + final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1"); + final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2"); + + // Both components are approved initially + mExpectedPrimaryComponentNames.clear(); + mExpectedPrimaryPackages.clear(); + mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2"); + mExpectedSecondaryComponentNames.clear(); + mExpectedSecondaryPackages.clear(); + + loadXml(service); + + //Component package/C1 loses bind permission + when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer( + (Answer) invocation -> { + ComponentName invocationCn = invocation.getArgument(0); + if (invocationCn != null) { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.packageName = invocationCn.getPackageName(); + serviceInfo.name = invocationCn.getClassName(); + if (invocationCn.equals(unapprovedComponent)) { + serviceInfo.permission = "none"; + } else { + serviceInfo.permission = service.getConfig().bindPermission; + } + serviceInfo.metaData = null; + return serviceInfo; + } + return null; + } + ); + + // Trigger package update + service.onPackagesChanged(false, new String[]{"package"}, new int[]{0}); + + assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent)); + assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent)); + } + @Test public void testSetPackageOrComponentEnabled() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { -- cgit v1.2.3 From fbff3ae537ff52ad238373c4e6e05431d3dc16df Mon Sep 17 00:00:00 2001 From: Yi-an Chen Date: Wed, 21 Feb 2024 01:56:22 +0000 Subject: Fix error handling for non-dynamic permissions We only allow removing dynamic permissions. When removePermission() is called for a non-dynamic permission, in addition to logging it, we should also return early to avoid the removePermission() call. Test: manual Bug: 321555066 Fixes: 321711213 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:528a87e90ff9354581d54fd37fbe9f95cccbcdb1) Merged-In: Ie2f43663bc71a06ffadb868d2d0eea5ee78f76e5 Change-Id: Ie2f43663bc71a06ffadb868d2d0eea5ee78f76e5 --- .../com/android/server/pm/permission/PermissionManagerServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 2499529f0fc0..a79f912747cd 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -695,6 +695,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt // TODO: switch this back to SecurityException Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + permName); + return; } mRegistry.removePermission(permName); } -- cgit v1.2.3 From d29506a33b0cee50a1033d59765166190ec2948e Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Tue, 6 Feb 2024 17:19:37 +0800 Subject: Hide window immediately if itself doesn't run hide animation The condition was overextended in commit 9bca6b4 which checks if the parent container of the window is animating. That causes the window to wait for animation finish to update visibility, but the animation finish callback won't happen because itself is not animating. Then the window that should be hidden remains on screen. Bug: 302431573 Test: atest WindowStateTests#testIsOnScreen_hiddenByPolicy (cherry picked from commit 9add9281ffc120c81a7d125892803f1beb5ddcb3) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:10a7f0914c87f4af521b5cbb13e84a83dacebf82) Merged-In: Iafc2b2c2a24d8fc8d147354ef2f0b4afeeb510c5 Change-Id: Iafc2b2c2a24d8fc8d147354ef2f0b4afeeb510c5 --- .../core/java/com/android/server/wm/WindowState.java | 6 ++++-- .../src/com/android/server/wm/WindowStateTests.java | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 06978a5338ea..3dfcf1ff55a8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3089,8 +3089,10 @@ class WindowState extends WindowContainer implements WindowManagerP return false; } if (doAnimation) { - mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); - if (!isAnimating(TRANSITION | PARENTS)) { + // If a hide animation is applied, then let onAnimationFinished + // -> checkPolicyVisibilityChange hide the window. Otherwise make doAnimation false + // to commit invisible immediately. + if (!mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false /* isEntrance */)) { doAnimation = false; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 0ddd3135506e..7fb5c9687796 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -229,6 +229,26 @@ public class WindowStateTests extends WindowTestsBase { assertTrue(window.isOnScreen()); window.hide(false /* doAnimation */, false /* requestAnim */); assertFalse(window.isOnScreen()); + + // Verifies that a window without animation can be hidden even if its parent is animating. + window.show(false /* doAnimation */, false /* requestAnim */); + assertTrue(window.isVisibleByPolicy()); + window.getParent().startAnimation(mTransaction, mock(AnimationAdapter.class), + false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION); + window.mAttrs.windowAnimations = 0; + window.hide(true /* doAnimation */, true /* requestAnim */); + assertFalse(window.isSelfAnimating(0, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION)); + assertFalse(window.isVisibleByPolicy()); + assertFalse(window.isOnScreen()); + + // Verifies that a window with animation can be hidden after the hide animation is finished. + window.show(false /* doAnimation */, false /* requestAnim */); + window.mAttrs.windowAnimations = android.R.style.Animation_Dialog; + window.hide(true /* doAnimation */, true /* requestAnim */); + assertTrue(window.isSelfAnimating(0, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION)); + assertTrue(window.isVisibleByPolicy()); + window.cancelAnimation(); + assertFalse(window.isVisibleByPolicy()); } @Test -- cgit v1.2.3 From 94e2c84ae8a364cf2539d7d7fe11c256eacb28af Mon Sep 17 00:00:00 2001 From: Dmitry Dementyev Date: Tue, 26 Mar 2024 10:31:44 -0700 Subject: Add more checkKeyIntent checks to AccountManagerService. Another verification is needed after Bundle modification. Bug: 321941232 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:36db8a1d61a881f89fdd3911886adcda6e1f0d7f) Merged-In: I9e45d758a2320328da5664b6341eafe6f285f297 Change-Id: I9e45d758a2320328da5664b6341eafe6f285f297 --- .../com/android/server/accounts/AccountManagerService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index cba28f3a596f..b45bcb405557 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3612,6 +3612,11 @@ public class AccountManagerService // Strip auth token from result. result.remove(AccountManager.KEY_AUTHTOKEN); + if (!checkKeyIntent(Binder.getCallingUid(), result)) { + onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, + "invalid intent in bundle returned"); + return; + } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, @@ -5201,6 +5206,11 @@ public class AccountManagerService } else { if (mStripAuthTokenFromResult) { result.remove(AccountManager.KEY_AUTHTOKEN); + if (!checkKeyIntent(Binder.getCallingUid(), result)) { + onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, + "invalid intent in bundle returned"); + return; + } } if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, getClass().getSimpleName() -- cgit v1.2.3 From 044b50636a0e42fcac4e49c959f013b04757ba17 Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Tue, 19 Mar 2024 16:49:51 -0500 Subject: Update media_controls_lock_screen setting behavior When the setting is disabled, hide the media carousel everywhere when the device is on lockscreen, not just in the keyguard layout Bug: 314333719 Test: manual Test: atest MediaHierarchyManagerTest Flag: NONE (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9f3903ed982b81a5634fb93b75ca3949daefdb17) Merged-In: I4f618e4013db894291e6fca9d49bceb1cb7e4bd9 Change-Id: I4f618e4013db894291e6fca9d49bceb1cb7e4bd9 --- .../media/controls/ui/KeyguardMediaController.kt | 40 ----------------- .../media/controls/ui/MediaHierarchyManager.kt | 27 ++++++++---- .../systemui/media/controls/ui/MediaHost.kt | 4 +- .../controls/ui/KeyguardMediaControllerTest.kt | 28 ------------ .../media/controls/ui/MediaHierarchyManagerTest.kt | 50 ++++++++++++++++++++++ 5 files changed, 72 insertions(+), 77 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt index 2883210805d3..2359f6ad3b43 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt @@ -18,16 +18,10 @@ package com.android.systemui.media.controls.ui import android.content.Context import android.content.res.Configuration -import android.database.ContentObserver -import android.net.Uri -import android.os.Handler -import android.os.UserHandle -import android.provider.Settings import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.dagger.MediaModule.KEYGUARD import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState @@ -36,7 +30,6 @@ import com.android.systemui.statusbar.notification.stack.MediaContainerView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.LargeScreenUtils -import com.android.systemui.util.settings.SecureSettings import javax.inject.Inject import javax.inject.Named @@ -52,8 +45,6 @@ constructor( private val bypassController: KeyguardBypassController, private val statusBarStateController: SysuiStatusBarStateController, private val context: Context, - private val secureSettings: SecureSettings, - @Main private val handler: Handler, configurationController: ConfigurationController, ) { @@ -77,26 +68,6 @@ constructor( } ) - val settingsObserver: ContentObserver = - object : ContentObserver(handler) { - override fun onChange(selfChange: Boolean, uri: Uri?) { - if (uri == lockScreenMediaPlayerUri) { - allowMediaPlayerOnLockScreen = - secureSettings.getBoolForUser( - Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, - true, - UserHandle.USER_CURRENT - ) - refreshMediaPosition() - } - } - } - secureSettings.registerContentObserverForUser( - Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, - settingsObserver, - UserHandle.USER_ALL - ) - // First let's set the desired state that we want for this host mediaHost.expansion = MediaHostState.EXPANDED mediaHost.showsOnlyActiveMedia = true @@ -142,16 +113,6 @@ constructor( private set private var splitShadeContainer: ViewGroup? = null - /** Track the media player setting status on lock screen. */ - private var allowMediaPlayerOnLockScreen: Boolean = - secureSettings.getBoolForUser( - Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, - true, - UserHandle.USER_CURRENT - ) - private val lockScreenMediaPlayerUri = - secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN) - /** * Attaches media container in single pane mode, situated at the top of the notifications list */ @@ -211,7 +172,6 @@ constructor( mediaHost.visible && !bypassController.bypassEnabled && keyguardOrUserSwitcher && - allowMediaPlayerOnLockScreen && shouldBeVisibleForSplitShade() if (visible) { showMediaPlayer() diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index fe8ebafdf9b4..6fcfa71c2f5d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -104,7 +104,7 @@ constructor( ) { /** Track the media player setting status on lock screen. */ - private var allowMediaPlayerOnLockScreen: Boolean = true + private var allowMediaPlayerOnLockScreen: Boolean = getMediaLockScreenSetting() private val lockScreenMediaPlayerUri = secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN) @@ -461,6 +461,7 @@ constructor( mediaCarouselController.logSmartspaceImpression(qsExpanded) } updateUserVisibility() + mediaCarouselController.updateHostVisibility() } override fun onDozeAmountChanged(linear: Float, eased: Float) { @@ -533,7 +534,6 @@ constructor( mediaCarouselController.updateHostVisibility = { mediaHosts.forEach { it?.updateViewVisibility() } } - panelEventsEvents.addShadeStateEventsListener( object : ShadeStateEventsListener { override fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) { @@ -547,12 +547,8 @@ constructor( object : ContentObserver(handler) { override fun onChange(selfChange: Boolean, uri: Uri?) { if (uri == lockScreenMediaPlayerUri) { - allowMediaPlayerOnLockScreen = - secureSettings.getBoolForUser( - Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, - true, - UserHandle.USER_CURRENT - ) + allowMediaPlayerOnLockScreen = getMediaLockScreenSetting() + mediaCarouselController.updateHostVisibility() } } } @@ -563,6 +559,14 @@ constructor( ) } + private fun getMediaLockScreenSetting(): Boolean { + return secureSettings.getBoolForUser( + Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, + true, + UserHandle.USER_CURRENT + ) + } + private fun updateConfiguration() { distanceForFullShadeTransition = context.resources.getDimensionPixelSize( @@ -602,6 +606,13 @@ constructor( mediaCarouselController.closeGuts() } + /** Return true if the carousel should be hidden because lockscreen is currently visible */ + fun isLockedAndHidden(): Boolean { + return !allowMediaPlayerOnLockScreen && + (statusbarState == StatusBarState.SHADE_LOCKED || + statusbarState == StatusBarState.KEYGUARD) + } + private fun createUniqueObjectHost(): UniqueObjectHostView { val viewHost = UniqueObjectHostView(context) viewHost.addOnAttachStateChangeListener( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt index be570b4a1119..26580e54cd62 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt @@ -199,7 +199,9 @@ constructor( */ fun updateViewVisibility() { state.visible = - if (showsOnlyActiveMedia) { + if (mediaHierarchyManager.isLockedAndHidden()) { + false + } else if (showsOnlyActiveMedia) { mediaDataManager.hasActiveMediaOrRecommendation() } else { mediaDataManager.hasAnyMediaOrRecommendation() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt index 91b0245be8d3..c1cf7e07aee2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.media.controls.ui -import android.provider.Settings import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -32,8 +31,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.settings.FakeSettings -import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertTrue import org.junit.Before @@ -60,10 +57,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() { private val mediaContainerView: MediaContainerView = MediaContainerView(context, null) private val hostView = UniqueObjectHostView(context) - private val settings = FakeSettings() private lateinit var keyguardMediaController: KeyguardMediaController - private lateinit var testableLooper: TestableLooper - private lateinit var fakeHandler: FakeHandler private lateinit var statusBarStateListener: StatusBarStateController.StateListener @Before @@ -79,16 +73,12 @@ class KeyguardMediaControllerTest : SysuiTestCase() { whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(mediaHost.hostView).thenReturn(hostView) hostView.layoutParams = FrameLayout.LayoutParams(100, 100) - testableLooper = TestableLooper.get(this) - fakeHandler = FakeHandler(testableLooper.looper) keyguardMediaController = KeyguardMediaController( mediaHost, bypassController, statusBarStateController, context, - settings, - fakeHandler, configurationController, ) keyguardMediaController.attachSinglePaneContainer(mediaContainerView) @@ -117,24 +107,6 @@ class KeyguardMediaControllerTest : SysuiTestCase() { assertThat(mediaContainerView.visibility).isEqualTo(visibility) } - @Test - fun testHiddenOnKeyguard_whenMediaOnLockScreenDisabled() { - settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 0) - - keyguardMediaController.refreshMediaPosition() - - assertThat(mediaContainerView.visibility).isEqualTo(GONE) - } - - @Test - fun testAvailableOnKeyguard_whenMediaOnLockScreenEnabled() { - settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1) - - keyguardMediaController.refreshMediaPosition() - - assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE) - } - @Test fun testActivatesSplitShadeContainerInSplitShadeMode() { val splitShadeContainer = FrameLayout(context) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt index 2ce236d4ba89..69bf97f6545f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt @@ -124,6 +124,7 @@ class MediaHierarchyManagerTest : SysuiTestCase() { verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture()) verify(statusBarStateController).addCallback(statusBarCallback.capture()) verify(dreamOverlayStateController).addCallback(dreamOverlayCallback.capture()) + whenever(mediaCarouselController.updateHostVisibility).thenReturn({}) setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP) setupHost(qsHost, MediaHierarchyManager.LOCATION_QS, QS_TOP) setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS, QQS_TOP) @@ -485,6 +486,55 @@ class MediaHierarchyManagerTest : SysuiTestCase() { assertThat(mediaCarouselScrollHandler.visibleToUser).isFalse() } + @Test + fun keyguardState_allowedOnLockscreen_updateVisibility() { + settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) + clearInvocations(mediaCarouselController) + + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + statusBarCallback.value.onStateChanged(StatusBarState.KEYGUARD) + + verify(mediaCarouselController).updateHostVisibility + assertThat(mediaHierarchyManager.isLockedAndHidden()).isFalse() + } + + @Test + fun keyguardState_notAllowedOnLockscreen_updateVisibility() { + settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) + clearInvocations(mediaCarouselController) + + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + statusBarCallback.value.onStateChanged(StatusBarState.KEYGUARD) + + verify(mediaCarouselController).updateHostVisibility + assertThat(mediaHierarchyManager.isLockedAndHidden()).isTrue() + } + + @Test + fun keyguardGone_updateVisibility() { + settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) + clearInvocations(mediaCarouselController) + + statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD, StatusBarState.SHADE) + statusBarCallback.value.onStateChanged(StatusBarState.SHADE) + + verify(mediaCarouselController).updateHostVisibility + assertThat(mediaHierarchyManager.isLockedAndHidden()).isFalse() + } + + @Test + fun lockscreenSettingChanged_updateVisibility() { + settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + statusBarCallback.value.onStateChanged(StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) + + settings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false) + + verify(mediaCarouselController).updateHostVisibility + assertThat(mediaHierarchyManager.isLockedAndHidden()).isTrue() + } + private fun enableSplitShade() { context .getOrCreateTestableResources() -- cgit v1.2.3 From c857d93b16f8ad4260f32a44ac4dede29c96adcb Mon Sep 17 00:00:00 2001 From: Kholoud Mohamed Date: Tue, 9 Jan 2024 17:53:40 +0000 Subject: Fix bug in screen capture and lock task migrations Bug: 318497672 Test: manual (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:9d809f92e8a026789115a0c6de7124da70101845) Merged-In: Id0ac7d06b57f690d114217f2a34c2a1e8c60a277 Change-Id: Id0ac7d06b57f690d114217f2a34c2a1e8c60a277 --- .../devicepolicy/DevicePolicyManagerService.java | 85 ++++++++++++++++++++-- .../com/android/server/devicepolicy/Owners.java | 13 ++++ .../android/server/devicepolicy/OwnersData.java | 7 ++ .../DevicePolicyManagerServiceMigrationTest.java | 3 + 4 files changed, 100 insertions(+), 8 deletions(-) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 23a1d4e0b37b..5e67708b1adb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3389,6 +3389,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (shouldMigrateToDevicePolicyEngine()) { migratePoliciesToDevicePolicyEngine(); } + + maybeMigratePoliciesPostUpgradeToDevicePolicyEngineLocked(); } maybeStartSecurityLogMonitorOnActivityManagerReady(); break; @@ -24151,11 +24153,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)); return mInjector.binderWithCleanCallingIdentity(() -> { - boolean canForceMigration = forceMigration && !hasNonTestOnlyActiveAdmins(); - if (!canForceMigration && !shouldMigrateToDevicePolicyEngine()) { - return false; + synchronized (getLockObject()) { + boolean canForceMigration = forceMigration && !hasNonTestOnlyActiveAdmins(); + if (!canForceMigration && !shouldMigrateToDevicePolicyEngine()) { + return false; + } + boolean migrated = migratePoliciesToDevicePolicyEngine(); + migrated &= migratePoliciesPostUpgradeToDevicePolicyEngineLocked(); + return migrated; } - return migratePoliciesToDevicePolicyEngine(); }); } @@ -24184,6 +24190,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { && !mOwners.isMigratedToPolicyEngine()); } + /** + * [b/318497672] Migrate policies that weren't migrated properly in the initial migration on + * update from Android T to Android U + */ + private void maybeMigratePoliciesPostUpgradeToDevicePolicyEngineLocked() { + if (!mOwners.isMigratedToPolicyEngine() || mOwners.isMigratedPostUpdate()) { + return; + } + migratePoliciesPostUpgradeToDevicePolicyEngineLocked(); + mOwners.markPostUpgradeMigration(); + } + + private boolean migratePoliciesPostUpgradeToDevicePolicyEngineLocked() { + try { + migrateScreenCapturePolicyLocked(); + migrateLockTaskPolicyLocked(); + return true; + } catch (Exception e) { + Slogf.e(LOG_TAG, e, "Error occurred during post upgrade migration to the device " + + "policy engine."); + return false; + } + } + /** * @return {@code true} if policies were migrated successfully, {@code false} otherwise. */ @@ -24197,7 +24227,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { migrateAutoTimezonePolicy(); migratePermissionGrantStatePolicies(); } - migrateScreenCapturePolicyLocked(); migratePermittedInputMethodsPolicyLocked(); migrateAccountManagementDisabledPolicyLocked(); migrateUserControlDisabledPackagesLocked(); @@ -24270,14 +24299,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void migrateScreenCapturePolicyLocked() { Binder.withCleanCallingIdentity(() -> { - if (mPolicyCache.getScreenCaptureDisallowedUser() == UserHandle.USER_NULL) { - return; - } ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(); if (admin != null && ((isDeviceOwner(admin) && admin.disableScreenCapture) || (admin.getParentActiveAdmin() != null && admin.getParentActiveAdmin().disableScreenCapture))) { + EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin( admin.info.getComponent(), admin.getUserHandle().getIdentifier(), @@ -24306,6 +24333,48 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } + private void migrateLockTaskPolicyLocked() { + Binder.withCleanCallingIdentity(() -> { + ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); + if (deviceOwner != null) { + int doUserId = deviceOwner.getUserHandle().getIdentifier(); + DevicePolicyData policies = getUserData(doUserId); + List packages = policies.mLockTaskPackages; + int features = policies.mLockTaskFeatures; + // TODO: find out about persistent preferred activities + if (!packages.isEmpty()) { + setLockTaskPolicyInPolicyEngine(deviceOwner, doUserId, packages, features); + } + } + + for (int userId : mUserManagerInternal.getUserIds()) { + ActiveAdmin profileOwner = getProfileOwnerLocked(userId); + if (profileOwner != null && canDPCManagedUserUseLockTaskLocked(userId)) { + DevicePolicyData policies = getUserData(userId); + List packages = policies.mLockTaskPackages; + int features = policies.mLockTaskFeatures; + if (!packages.isEmpty()) { + setLockTaskPolicyInPolicyEngine(profileOwner, userId, packages, features); + } + } + } + }); + } + + private void setLockTaskPolicyInPolicyEngine( + ActiveAdmin admin, int userId, List packages, int features) { + EnforcingAdmin enforcingAdmin = + EnforcingAdmin.createEnterpriseEnforcingAdmin( + admin.info.getComponent(), + userId, + admin); + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.LOCK_TASK, + enforcingAdmin, + new LockTaskPolicy(new HashSet<>(packages), features), + userId); + } + private void migratePermittedInputMethodsPolicyLocked() { Binder.withCleanCallingIdentity(() -> { List users = mUserManager.getUsers(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index bb275e45b55a..c842e29e3c70 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -616,6 +616,19 @@ class Owners { } } + void markPostUpgradeMigration() { + synchronized (mData) { + mData.mPoliciesMigratedPostUpdate = true; + mData.writeDeviceOwner(); + } + } + + boolean isMigratedPostUpdate() { + synchronized (mData) { + return mData.mPoliciesMigratedPostUpdate; + } + } + @GuardedBy("mData") void pushToAppOpsLocked() { if (!mSystemReady) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java index 37d4f95cac29..608ae140450e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java @@ -87,6 +87,8 @@ class OwnersData { private static final String ATTR_MIGRATED_TO_POLICY_ENGINE = "migratedToPolicyEngine"; + private static final String ATTR_MIGRATED_POST_UPGRADE = "migratedPostUpgrade"; + // Internal state for the device owner package. OwnerInfo mDeviceOwner; int mDeviceOwnerUserId = UserHandle.USER_NULL; @@ -114,6 +116,8 @@ class OwnersData { boolean mMigratedToPolicyEngine = false; + boolean mPoliciesMigratedPostUpdate = false; + OwnersData(PolicyPathProvider pathProvider) { mPathProvider = pathProvider; } @@ -397,6 +401,7 @@ class OwnersData { out.startTag(null, TAG_POLICY_ENGINE_MIGRATION); out.attributeBoolean(null, ATTR_MIGRATED_TO_POLICY_ENGINE, mMigratedToPolicyEngine); + out.attributeBoolean(null, ATTR_MIGRATED_POST_UPGRADE, mPoliciesMigratedPostUpdate); out.endTag(null, TAG_POLICY_ENGINE_MIGRATION); } @@ -457,6 +462,8 @@ class OwnersData { case TAG_POLICY_ENGINE_MIGRATION: mMigratedToPolicyEngine = parser.getAttributeBoolean( null, ATTR_MIGRATED_TO_POLICY_ENGINE, false); + mPoliciesMigratedPostUpdate = parser.getAttributeBoolean( + null, ATTR_MIGRATED_POST_UPGRADE, false); break; default: Slog.e(TAG, "Unexpected tag: " + tag); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index b539a76b521c..5eda84ba7629 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -146,6 +146,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { @SmallTest @Test + @Ignore("b/277916462") public void testCompMigrationUnAffiliated_skipped() throws Exception { prepareAdmin1AsDo(); prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID); @@ -217,6 +218,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { @SmallTest @Test + @Ignore("b/277916462") public void testCompMigration_keepSuspendedAppsWhenDpcIsRPlus() throws Exception { prepareAdmin1AsDo(); prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R); @@ -250,6 +252,7 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { @SmallTest @Test + @Ignore("b/277916462") public void testCompMigration_unsuspendAppsWhenDpcNotRPlus() throws Exception { prepareAdmin1AsDo(); prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q); -- cgit v1.2.3 From ac3e61bfea539d97286e0d6287b55ae3a796c5f9 Mon Sep 17 00:00:00 2001 From: Wen Zhang Date: Fri, 2 Feb 2024 12:01:12 +0000 Subject: [Bugfix]migrate user restrictions to DevicePolicyEngine Migrate user restrictions to DevicePolicyEngine, otherwise user restrictions will not be able to be queried when upgrading from Android 13 OTA to Android 14. Bug: 323452689 Bug: 318497672 Test: case 1:Android 13 upgrade to Android 14 1.install TestDPC on Android 13 2.set TestDPC as Device Owner 3.set disallow factory reset 4.update Android 14 via OTA 5.The reset to factory settings in the settings app is still unavailable. case 2:Android 14(bad version) upgrade to Android 14(new version) 1.install TestDPC on Android 13 2.set TestDPC as Device Owner 3.set disallow factory reset 4.update Android 14(bad version) via OTA 5.The reset to factory settings in the settings app is still available. 6.update Android 14(new version) via OTA 7.The reset to factory settings in the settings app is unavailable. (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:93ba63a2bfdda47fb9376efaad792bc96a106947) Merged-In: I7c63d52300a1c5aa0678f29c2e4b15195411517c Change-Id: I7c63d52300a1c5aa0678f29c2e4b15195411517c --- .../devicepolicy/DevicePolicyManagerService.java | 87 ++++++++++++++++++---- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5e67708b1adb..1549677fdbb2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -13497,27 +13497,47 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { CallerIdentity caller, EnforcingAdmin admin, String key, boolean enabled, boolean parent) { synchronized (getLockObject()) { + + int ownerType; if (isDeviceOwner(caller)) { - if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_DEVICE_OWNER, key)) { - setGlobalUserRestrictionInternal(admin, key, enabled); - } else { - setLocalUserRestrictionInternal(admin, key, enabled, caller.getUserId()); - } + ownerType = OWNER_TYPE_DEVICE_OWNER; + } else if (isProfileOwnerOfOrganizationOwnedDevice(caller)) { + ownerType = OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE; } else if (isProfileOwner(caller)) { - if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_PROFILE_OWNER, key) - || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller) - && UserRestrictionsUtils.isGlobal( - OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, key))) { - setGlobalUserRestrictionInternal(admin, key, enabled); - } else { - int affectedUserId = parent - ? getProfileParentId(caller.getUserId()) : caller.getUserId(); - setLocalUserRestrictionInternal(admin, key, enabled, affectedUserId); - } + ownerType = OWNER_TYPE_PROFILE_OWNER; } else { throw new IllegalStateException("Non-DO/Non-PO cannot set restriction " + key + " while targetSdkVersion is less than UPSIDE_DOWN_CAKE"); } + setBackwardCompatibleUserRestrictionLocked(ownerType, admin, caller.getUserId(), key, + enabled, parent); + } + } + + private void setBackwardCompatibleUserRestrictionLocked( + int ownerType, EnforcingAdmin admin, int userId, String key, boolean enabled, + boolean parent) { + if (ownerType == OWNER_TYPE_DEVICE_OWNER) { + if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_DEVICE_OWNER, key)) { + setGlobalUserRestrictionInternal(admin, key, enabled); + } else { + setLocalUserRestrictionInternal(admin, key, enabled, userId); + } + } else if (ownerType == OWNER_TYPE_PROFILE_OWNER + || ownerType == OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE) { + if (UserRestrictionsUtils.isGlobal(OWNER_TYPE_PROFILE_OWNER, key) + || (parent && ownerType == OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE + && UserRestrictionsUtils.isGlobal( + OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, key))) { + setGlobalUserRestrictionInternal(admin, key, enabled); + } else { + int affectedUserId = parent + ? getProfileParentId(userId) : userId; + setLocalUserRestrictionInternal(admin, key, enabled, affectedUserId); + } + } else { + throw new IllegalStateException("Non-DO/Non-PO cannot set restriction " + key + + " while targetSdkVersion is less than UPSIDE_DOWN_CAKE"); } } @@ -24206,6 +24226,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { migrateScreenCapturePolicyLocked(); migrateLockTaskPolicyLocked(); + migrateUserRestrictionsLocked(); return true; } catch (Exception e) { Slogf.e(LOG_TAG, e, "Error occurred during post upgrade migration to the device " @@ -24467,6 +24488,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } + private void migrateUserRestrictionsLocked() { + Binder.withCleanCallingIdentity(() -> { + List users = mUserManager.getUsers(); + for (UserInfo userInfo : users) { + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(userInfo.id); + if (admin == null) continue; + ComponentName adminComponent = admin.info.getComponent(); + int userId = userInfo.id; + EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin( + adminComponent, + userId, + admin); + int ownerType; + if (isDeviceOwner(admin)) { + ownerType = OWNER_TYPE_DEVICE_OWNER; + } else if (isProfileOwnerOfOrganizationOwnedDevice(adminComponent, userId)) { + ownerType = OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE; + } else if (isProfileOwner(adminComponent, userId)) { + ownerType = OWNER_TYPE_PROFILE_OWNER; + } else { + throw new IllegalStateException("Invalid DO/PO state"); + } + + for (final String restriction : admin.ensureUserRestrictions().keySet()) { + setBackwardCompatibleUserRestrictionLocked(ownerType, enforcingAdmin, userId, + restriction, /* enabled */ true, /* parent */ false); + } + for (final String restriction : admin.getParentActiveAdmin() + .ensureUserRestrictions().keySet()) { + setBackwardCompatibleUserRestrictionLocked(ownerType, enforcingAdmin, userId, + restriction, /* enabled */ true, /* parent */ true); + } + } + }); + } + private List getInstalledPackagesOnUser(int userId) { return mInjector.binderWithCleanCallingIdentity(() -> mContext.getPackageManager().getInstalledPackagesAsUser( -- cgit v1.2.3 From f4d2bf61b6a49012bc7f6eaaa1a9636783febaa4 Mon Sep 17 00:00:00 2001 From: Haoran Zhang Date: Wed, 13 Mar 2024 17:08:00 +0000 Subject: [DO NOT MERGE][Autofill Framework] Add in check for intent filter when setting/updating service For test, I registered two tests around on ABTD. CtsAutoFillServiceTestCases module is passing except three known failures: Test run link: - https://android-build.corp.google.com/builds/abtd/run/L33300030002610600 - https://android-build.corp.google.com/builds/abtd/run/L58100030002616607 Bug: b/324874908 Test: atest CtsAutoFillServiceTestCases (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:141d9d050346bfc4673c429382deb1b3d210f6ad) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b59fa891858c51480b387e24ae03a9b55b3e30a6) Merged-In: I51c2e3788ac29ff4d6b86aa2a735ff2ea1463a77 Change-Id: I51c2e3788ac29ff4d6b86aa2a735ff2ea1463a77 --- .../autofill/AutofillManagerServiceImpl.java | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 63a607c8d0d4..7be2dea3c2f6 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -32,8 +32,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.content.ComponentName; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.graphics.Rect; import android.metrics.LogMaker; @@ -250,6 +252,31 @@ final class AutofillManagerServiceImpl @Override // from PerUserSystemService protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) throws NameNotFoundException { + final List resolveInfos = + getContext().getPackageManager().queryIntentServicesAsUser( + new Intent(AutofillService.SERVICE_INTERFACE), + // The MATCH_INSTANT flag is added because curret autofill CTS module is + // defined in one apk, which makes the test autofill service installed in a + // instant app when the CTS tests are running in instant app mode. + // TODO: Remove MATCH_INSTANT flag after completing refactoring the CTS module + // to make the test autofill service a separate apk. + PackageManager.GET_META_DATA | PackageManager.MATCH_INSTANT, + mUserId); + boolean serviceHasAutofillIntentFilter = false; + for (ResolveInfo resolveInfo : resolveInfos) { + final ServiceInfo serviceInfo = resolveInfo.serviceInfo; + if (serviceInfo.getComponentName().equals(serviceComponent)) { + serviceHasAutofillIntentFilter = true; + break; + } + } + if (!serviceHasAutofillIntentFilter) { + Slog.w(TAG, + "Autofill service from '" + serviceComponent.getPackageName() + "' does" + + "not have intent filter " + AutofillService.SERVICE_INTERFACE); + throw new SecurityException("Service does not declare intent filter " + + AutofillService.SERVICE_INTERFACE); + } mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId); return mInfo.getServiceInfo(); } -- cgit v1.2.3 From ef5c843b3533ce67327f86850c2a56866e3e7035 Mon Sep 17 00:00:00 2001 From: Hans Boehm Date: Tue, 2 Jan 2024 16:53:13 -0800 Subject: Check hidden API exemptions Refuse to deal with newlines and null characters in HiddenApiSettings.update(). Also disallow nulls in process start arguments. Bug: 316153291 Test: Treehugger for now (cherry picked from commit 7ba059e2cf0a2c20f9a849719cdc32b12c933a44) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:60669aa49aba34c0950d6246bd95b54f91a3c8e8) Merged-In: I83cd60e46407a4a082f9f3c80e937dbd522dbac4 Change-Id: I83cd60e46407a4a082f9f3c80e937dbd522dbac4 --- core/java/android/os/ZygoteProcess.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 3cb5c60259eb..2b84b25b92e2 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -424,6 +424,8 @@ public class ZygoteProcess { throw new ZygoteStartFailedEx("Embedded newlines not allowed"); } else if (arg.indexOf('\r') >= 0) { throw new ZygoteStartFailedEx("Embedded carriage returns not allowed"); + } else if (arg.indexOf('\u0000') >= 0) { + throw new ZygoteStartFailedEx("Embedded nulls not allowed"); } } @@ -959,6 +961,14 @@ public class ZygoteProcess { return true; } + for (/* NonNull */ String s : mApiDenylistExemptions) { + // indexOf() is intrinsified and faster than contains(). + if (s.indexOf('\n') >= 0 || s.indexOf('\r') >= 0 || s.indexOf('\u0000') >= 0) { + Slog.e(LOG_TAG, "Failed to set API denylist exemptions: Bad character"); + mApiDenylistExemptions = Collections.emptyList(); + return false; + } + } try { state.mZygoteOutputWriter.write(Integer.toString(mApiDenylistExemptions.size() + 1)); state.mZygoteOutputWriter.newLine(); -- cgit v1.2.3 From 6a03b38b0578e33773d3a012647d8250a61413d4 Mon Sep 17 00:00:00 2001 From: Ameer Armaly Date: Fri, 8 Mar 2024 19:41:06 +0000 Subject: [RESTRICT AUTOMERGE] AccessibilityManagerService: remove uninstalled services from enabled list after service update. Bug: 326485767 Test: atest AccessibilityEndToEndTest#testUpdateServiceWithoutIntent_disablesService (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f6192d3a77520d40b6a93de8f45400e19f5ba29f) Merged-In: Ia86857d58ebab925ec6e55f9e5fa64e265326ec0 Change-Id: Ia86857d58ebab925ec6e55f9e5fa64e265326ec0 --- .../accessibility/AccessibilityManagerService.java | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 531227947ba0..7bb7c4a81a7d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2398,10 +2398,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.mComponentNameToServiceMap; boolean isUnlockingOrUnlocked = mUmi.isUserUnlockingOrUnlocked(userState.mUserId); + // Store the list of installed services. + mTempComponentNameSet.clear(); for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) { AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i); ComponentName componentName = ComponentName.unflattenFromString( installedService.getId()); + mTempComponentNameSet.add(componentName); AccessibilityServiceConnection service = componentNameToServiceMap.get(componentName); @@ -2461,6 +2464,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub audioManager.setAccessibilityServiceUids(mTempIntArray); } mActivityTaskManagerService.setAccessibilityServiceUids(mTempIntArray); + // If any services have been removed, remove them from the enabled list and the touch + // exploration granted list. + boolean anyServiceRemoved = + userState.mEnabledServices.removeIf((comp) -> !mTempComponentNameSet.contains(comp)) + || userState.mTouchExplorationGrantedServices.removeIf( + (comp) -> !mTempComponentNameSet.contains(comp)); + if (anyServiceRemoved) { + // Update the enabled services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userState.mEnabledServices, + userState.mUserId); + // Update the touch exploration granted services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + userState.mTouchExplorationGrantedServices, + userState.mUserId); + } + mTempComponentNameSet.clear(); updateAccessibilityEnabledSettingLocked(userState); } -- cgit v1.2.3 From 01ae6dca58d6d7cfeae984b80511f62783317650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Fri, 22 Mar 2024 14:26:23 +0100 Subject: Resolve message/conversation image Uris with the correct user id Bug: 317503801 Test: atest ExpandableNotificationRowTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:44524f06f9d652c596d542e0882eb2f17594f154) Merged-In: I11c5b39f2d9d8f0788acab43640a6d4abcd5a179 Change-Id: I11c5b39f2d9d8f0788acab43640a6d4abcd5a179 --- .../row/ExpandableNotificationRow.java | 39 +++++++++++++++++++--- .../row/NotificationInlineImageResolver.java | 7 +++- .../notification/row/RowInflaterTask.java | 34 ++++++++++++++++++- .../row/ExpandableNotificationRowTest.java | 25 ++++++++++++++ .../notification/row/NotificationTestHelper.java | 17 +++++----- .../com/android/systemui/SysuiTestableContext.java | 22 ++++++++++++ 6 files changed, 129 insertions(+), 15 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9f397fe9ac0c..85708394b09c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -40,6 +40,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.Trace; +import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; @@ -371,7 +372,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mUseIncreasedHeadsUpHeight; private float mTranslationWhenRemoved; private boolean mWasChildInGroupWhenRemoved; - private NotificationInlineImageResolver mImageResolver; + private final NotificationInlineImageResolver mImageResolver; @Nullable private OnExpansionChangedListener mExpansionChangedListener; @Nullable @@ -1630,13 +1631,41 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } /** - * Constructs an ExpandableNotificationRow. - * @param context context passed to image resolver + * Constructs an ExpandableNotificationRow. Used by layout inflation. + * + * @param context passed to image resolver * @param attrs attributes used to initialize parent view */ public ExpandableNotificationRow(Context context, AttributeSet attrs) { - super(context, attrs); - mImageResolver = new NotificationInlineImageResolver(context, + this(context, attrs, context); + Log.wtf(TAG, "This constructor shouldn't be called"); + } + + /** + * Constructs an ExpandableNotificationRow. Used by layout inflation (with a custom {@code + * AsyncLayoutFactory} in {@link RowInflaterTask}. + * + * @param context context context of the view + * @param attrs attributes used to initialize parent view + * @param entry notification that the row will be associated to (determines the user for the + * ImageResolver) + */ + public ExpandableNotificationRow(Context context, AttributeSet attrs, NotificationEntry entry) { + this(context, attrs, userContextForEntry(context, entry)); + } + + private static Context userContextForEntry(Context base, NotificationEntry entry) { + if (base.getUserId() == entry.getSbn().getNormalizedUserId()) { + return base; + } + return base.createContextAsUser( + UserHandle.of(entry.getSbn().getNormalizedUserId()), /* flags= */ 0); + } + + private ExpandableNotificationRow(Context sysUiContext, AttributeSet attrs, + Context userContext) { + super(sysUiContext, attrs); + mImageResolver = new NotificationInlineImageResolver(userContext, new NotificationInlineImageCache()); float radius = getResources().getDimension(R.dimen.notification_corner_radius_small); mSmallRoundness = radius / getMaxRadius(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index c620f448b3b7..3e932aa616b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -66,7 +66,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @param imageCache The implementation of internal cache. */ public NotificationInlineImageResolver(Context context, ImageCache imageCache) { - mContext = context.getApplicationContext(); + mContext = context; mImageCache = imageCache; if (mImageCache != null) { @@ -76,6 +76,11 @@ public class NotificationInlineImageResolver implements ImageResolver { updateMaxImageSizes(); } + @VisibleForTesting + public Context getContext() { + return mContext; + } + /** * Check if this resolver has its internal cache implementation. * @return True if has its internal cache, false otherwise. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java index 6feffe654630..b2fe1d82945b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java @@ -17,10 +17,15 @@ package com.android.systemui.statusbar.notification.row; import android.content.Context; +import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.asynclayoutinflater.view.AsyncLayoutFactory; import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import com.android.systemui.R; @@ -55,12 +60,39 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf mInflateOrigin = new Throwable("inflate requested here"); } mListener = listener; - AsyncLayoutInflater inflater = new AsyncLayoutInflater(context); + AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, + new RowAsyncLayoutInflater(entry)); mEntry = entry; entry.setInflationTask(this); inflater.inflate(R.layout.status_bar_notification_row, parent, this); } + @VisibleForTesting + static class RowAsyncLayoutInflater implements AsyncLayoutFactory { + private final NotificationEntry mEntry; + + RowAsyncLayoutInflater(NotificationEntry entry) { + mEntry = entry; + } + + @Nullable + @Override + public View onCreateView(@Nullable View parent, @NonNull String name, + @NonNull Context context, @NonNull AttributeSet attrs) { + if (name.equals(ExpandableNotificationRow.class.getName())) { + return new ExpandableNotificationRow(context, attrs, mEntry); + } + return null; + } + + @Nullable + @Override + public View onCreateView(@NonNull String name, @NonNull Context context, + @NonNull AttributeSet attrs) { + return null; + } + } + @Override public void abort() { mCancelled = true; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 4c83194783ab..35211ea2174e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -20,6 +20,8 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL; +import static com.android.systemui.statusbar.notification.row.NotificationTestHelper.PKG; +import static com.android.systemui.statusbar.notification.row.NotificationTestHelper.USER_HANDLE; import static com.google.common.truth.Truth.assertThat; @@ -40,10 +42,12 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.NotificationChannel; +import android.content.Context; import android.graphics.Color; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -56,6 +60,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.widget.CachingIconView; import com.android.systemui.SysuiTestCase; +import com.android.systemui.SysuiTestableContext; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -733,6 +738,26 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { verify(lowPriVectorDrawable, times(1)).start(); } + @Test + public void imageResolver_sameNotificationUser_usesContext() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(PKG, + USER_HANDLE.getUid(1234), USER_HANDLE); + + assertThat(row.getImageResolver().getContext()).isSameInstanceAs(mContext); + } + + @Test + public void imageResolver_differentNotificationUser_createsUserContext() throws Exception { + UserHandle user = new UserHandle(33); + Context userContext = new SysuiTestableContext(mContext); + mContext.prepareCreateContextAsUser(user, userContext); + + ExpandableNotificationRow row = mNotificationTestHelper.createRow(PKG, + user.getUid(1234), user); + + assertThat(row.getImageResolver().getContext()).isSameInstanceAs(userContext); + } + private void setDrawableIconsInImageView(CachingIconView icon, Drawable iconDrawable, Drawable rightIconDrawable) { ImageView iconView = mock(ImageView.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 813bae893569..b4be92e86ff0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -526,14 +526,6 @@ public class NotificationTestHelper { @InflationFlag int extraInflationFlags, int importance) throws Exception { - LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( - mContext.LAYOUT_INFLATER_SERVICE); - mRow = (ExpandableNotificationRow) inflater.inflate( - R.layout.status_bar_notification_row, - null /* root */, - false /* attachToRoot */); - ExpandableNotificationRow row = mRow; - final NotificationChannel channel = new NotificationChannel( notification.getChannelId(), @@ -553,6 +545,15 @@ public class NotificationTestHelper { .setChannel(channel) .build(); + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + inflater.setFactory2(new RowInflaterTask.RowAsyncLayoutInflater(entry)); + mRow = (ExpandableNotificationRow) inflater.inflate( + R.layout.status_bar_notification_row, + null /* root */, + false /* attachToRoot */); + ExpandableNotificationRow row = mRow; + entry.setRow(row); mIconManager.createIcons(entry); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java index 5ff57aad9f5d..aa58b89f4676 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java @@ -14,6 +14,7 @@ package com.android.systemui; +import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -29,12 +30,15 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; +import java.util.HashMap; +import java.util.Map; import java.util.Set; public class SysuiTestableContext extends TestableContext { @GuardedBy("mRegisteredReceivers") private final Set mRegisteredReceivers = new ArraySet<>(); + private final Map mContextForUser = new HashMap<>(); public SysuiTestableContext(Context base) { super(base); @@ -152,4 +156,22 @@ public class SysuiTestableContext extends TestableContext { } super.unregisterReceiver(receiver); } + + /** + * Sets a Context object that will be returned as the result of {@link #createContextAsUser} + * for a specific {@code user}. + */ + public void prepareCreateContextAsUser(UserHandle user, Context context) { + mContextForUser.put(user, context); + } + + @Override + @NonNull + public Context createContextAsUser(UserHandle user, int flags) { + Context userContext = mContextForUser.get(user); + if (userContext != null) { + return userContext; + } + return super.createContextAsUser(user, flags); + } } -- cgit v1.2.3 From 783c1ea6e7fc4a22b37e4ae87ccbf465fb4939b9 Mon Sep 17 00:00:00 2001 From: Guojing Yuan Date: Thu, 14 Dec 2023 19:30:04 +0000 Subject: [CDM][CMD] Check permissions for CDM shell commands Override handleShellCommand instead of onShellCommand because Binder.onShellCommand checks the necessary permissions of the caller. Bug: 313428840 Test: manually tested CDM shell commands (cherry picked from commit 1761a0fee9c2cd9787bbb7fbdbe30b4c2b03396e) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:54c968aaa66e9364bc0380c9a57af5c6844759aa) Merged-In: I5539b3594feb5544c458c0fd1061b51a0a808900 Change-Id: I5539b3594feb5544c458c0fd1061b51a0a808900 --- .../server/companion/CompanionDeviceManagerService.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 5c36a6b07392..2194a0863238 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -83,9 +83,7 @@ import android.os.ParcelFileDescriptor; import android.os.PowerWhitelistManager; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.ResultReceiver; import android.os.ServiceManager; -import android.os.ShellCallback; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -931,13 +929,14 @@ public class CompanionDeviceManagerService extends SystemService { } @Override - public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, - String[] args, ShellCallback callback, ResultReceiver resultReceiver) - throws RemoteException { - new CompanionDeviceShellCommand(CompanionDeviceManagerService.this, mAssociationStore, - mDevicePresenceMonitor, mTransportManager, mSystemDataTransferRequestStore, - mAssociationRequestsProcessor) - .exec(this, in, out, err, args, callback, resultReceiver); + public int handleShellCommand(@NonNull ParcelFileDescriptor in, + @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, + @NonNull String[] args) { + return new CompanionDeviceShellCommand(CompanionDeviceManagerService.this, + mAssociationStore, mDevicePresenceMonitor, mTransportManager, + mSystemDataTransferRequestStore, mAssociationRequestsProcessor) + .exec(this, in.getFileDescriptor(), out.getFileDescriptor(), + err.getFileDescriptor(), args); } @Override -- cgit v1.2.3