diff options
| author | 2022-03-31 14:35:44 +0000 | |
|---|---|---|
| committer | 2022-03-31 14:35:44 +0000 | |
| commit | 58f33528ff0161a6c777fa51b853a149228af850 (patch) | |
| tree | f3378c593ad80c6687bc47349ec0b52efdf66b92 | |
| parent | 2e9d7c9822bb5a6a767069fdc861c5f108c11120 (diff) | |
| parent | 588c0df0ef6b240b72f6dd1cf4a357e72196b0cb (diff) | |
Merge changes from topic "DistractingPackageHelper_tm-dev" into tm-dev
* changes:
Add DistractingPackageHelperTest
Introduce DistractingPackageHelper
8 files changed, 568 insertions, 235 deletions
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java index f1394d403bf7..bf1196d6b969 100644 --- a/services/core/java/com/android/server/pm/BroadcastHelper.java +++ b/services/core/java/com/android/server/pm/BroadcastHelper.java @@ -320,17 +320,6 @@ public final class BroadcastHelper { broadcastAllowlist, null); } - public void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId, - int distractionFlags) { - final Bundle extras = new Bundle(3); - extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); - extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); - extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); - sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras, - Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null, null, - null); - } - public void sendFirstLaunchBroadcast(String pkgName, String installerPkg, int[] userIds, int[] instantUserIds) { sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0, diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java new file mode 100644 index 000000000000..7dc45b58a773 --- /dev/null +++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.RESTRICTION_NONE; + +import android.annotation.NonNull; +import android.content.Intent; +import android.content.pm.PackageManager.DistractionRestriction; +import android.os.Bundle; +import android.os.Handler; +import android.os.UserHandle; +import android.util.ArraySet; +import android.util.IntArray; +import android.util.Slog; + +import com.android.internal.util.ArrayUtils; +import com.android.server.pm.pkg.PackageStateInternal; + +import java.util.ArrayList; +import java.util.List; + +/** + * Mark, unmark, or remove any {@link DistractionRestriction restrictions} set on given packages. + */ +public final class DistractingPackageHelper { + + // TODO(b/198166813): remove PMS dependency + private final PackageManagerService mPm; + private final PackageManagerServiceInjector mInjector; + private final BroadcastHelper mBroadcastHelper; + private final SuspendPackageHelper mSuspendPackageHelper; + + /** + * Constructor for {@link PackageManagerService}. + */ + DistractingPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector, + BroadcastHelper broadcastHelper, SuspendPackageHelper suspendPackageHelper) { + mPm = pm; + mInjector = injector; + mBroadcastHelper = broadcastHelper; + mSuspendPackageHelper = suspendPackageHelper; + } + + /** + * Mark or unmark the given packages as distracting to the given user. + * + * @param packageNames Packages to mark as distracting. + * @param restrictionFlags Any combination of restrictions to impose on the given packages. + * {@link DistractionRestriction#RESTRICTION_NONE} can be used to + * clear any existing restrictions. + * @param userId the user for which changes are taking place. + * @param callingUid The caller's uid. + * + * @return A list of packages that could not have the {@code restrictionFlags} set. The system + * may prevent restricting critical packages to preserve normal device function. + */ + String[] setDistractingPackageRestrictionsAsUser(@NonNull Computer snapshot, + String[] packageNames, int restrictionFlags, int userId, int callingUid) { + if (ArrayUtils.isEmpty(packageNames)) { + return packageNames; + } + if (restrictionFlags != RESTRICTION_NONE + && !mSuspendPackageHelper.isSuspendAllowedForUser(snapshot, userId, callingUid)) { + Slog.w(PackageManagerService.TAG, + "Cannot restrict packages due to restrictions on user " + userId); + return packageNames; + } + + final List<String> changedPackagesList = new ArrayList<>(packageNames.length); + final IntArray changedUids = new IntArray(packageNames.length); + final List<String> unactionedPackages = new ArrayList<>(packageNames.length); + + final ArraySet<String> changesToCommit = new ArraySet<>(); + final boolean[] canRestrict = (restrictionFlags != RESTRICTION_NONE) + ? mSuspendPackageHelper.canSuspendPackageForUser(snapshot, packageNames, userId, + callingUid) : null; + for (int i = 0; i < packageNames.length; i++) { + final String packageName = packageNames[i]; + final PackageStateInternal packageState = + snapshot.getPackageStateInternal(packageName); + if (packageState == null + || snapshot.shouldFilterApplication(packageState, callingUid, userId)) { + Slog.w(PackageManagerService.TAG, + "Could not find package setting for package: " + packageName + + ". Skipping..."); + unactionedPackages.add(packageName); + continue; + } + if (canRestrict != null && !canRestrict[i]) { + unactionedPackages.add(packageName); + continue; + } + final int oldDistractionFlags = packageState.getUserStateOrDefault(userId) + .getDistractionFlags(); + if (restrictionFlags != oldDistractionFlags) { + changedPackagesList.add(packageName); + changedUids.add(UserHandle.getUid(userId, packageState.getAppId())); + changesToCommit.add(packageName); + } + } + + mPm.commitPackageStateMutation(null /* initialState */, mutator -> { + final int size = changesToCommit.size(); + for (int index = 0; index < size; index++) { + mutator.forPackage(changesToCommit.valueAt(index)) + .userState(userId) + .setDistractionFlags(restrictionFlags); + } + }); + + if (!changedPackagesList.isEmpty()) { + final String[] changedPackages = changedPackagesList.toArray( + new String[changedPackagesList.size()]); + sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId, + restrictionFlags); + mPm.scheduleWritePackageRestrictions(userId); + } + return unactionedPackages.toArray(new String[0]); + } + + /** + * Removes any {@link DistractionRestriction restrictions} set on given packages. + * + * <p> Caller must flush package restrictions if it cares about immediate data consistency. + * + * @param packagesToChange The packages on which restrictions are to be removed. + * @param userId the user for which changes are taking place. + */ + void removeDistractingPackageRestrictions(@NonNull Computer snapshot, + String[] packagesToChange, int userId) { + if (ArrayUtils.isEmpty(packagesToChange)) { + return; + } + final List<String> changedPackages = new ArrayList<>(packagesToChange.length); + final IntArray changedUids = new IntArray(packagesToChange.length); + for (int i = 0; i < packagesToChange.length; i++) { + final String packageName = packagesToChange[i]; + final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName); + if (ps != null && ps.getUserStateOrDefault(userId).getDistractionFlags() + != RESTRICTION_NONE) { + changedPackages.add(ps.getPackageName()); + changedUids.add(UserHandle.getUid(userId, ps.getAppId())); + } + } + mPm.commitPackageStateMutation(null /* initialState */, mutator -> { + for (int index = 0; index < changedPackages.size(); index++) { + mutator.forPackage(changedPackages.get(index)) + .userState(userId) + .setDistractionFlags(RESTRICTION_NONE); + } + }); + + if (!changedPackages.isEmpty()) { + final String[] packageArray = changedPackages.toArray( + new String[changedPackages.size()]); + sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId, + RESTRICTION_NONE); + mPm.scheduleWritePackageRestrictions(userId); + } + } + + /** + * Send broadcast intents for packages distracting changes. + * + * @param pkgList The names of packages which have suspension changes. + * @param uidList The uids of packages which have suspension changes. + * @param userId The user where packages reside. + */ + void sendDistractingPackagesChanged(@NonNull String[] pkgList, + int[] uidList, int userId, int distractionFlags) { + final Bundle extras = new Bundle(3); + extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList); + extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList); + extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags); + + final Handler handler = mInjector.getHandler(); + handler.post(() -> mBroadcastHelper.sendPackageBroadcast( + Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */, extras, + Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */, + null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */, + null /* allowList */, null /* bOptions */)); + } +} diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 2b733754685e..2fe7913342a2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -83,6 +83,7 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @NonNull protected abstract PackageObserverHelper getPackageObserverHelper(); @NonNull protected abstract ResolveIntentHelper getResolveIntentHelper(); @NonNull protected abstract SuspendPackageHelper getSuspendPackageHelper(); + @NonNull protected abstract DistractingPackageHelper getDistractingPackageHelper(); @NonNull protected abstract ProtectedPackages getProtectedPackages(); @NonNull protected abstract UserNeedsBadgingCache getUserNeedsBadging(); @NonNull protected abstract InstantAppRegistry getInstantAppRegistry(); @@ -248,8 +249,8 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { @Override @Deprecated public final void removeDistractingPackageRestrictions(String packageName, int userId) { - mService.removeDistractingPackageRestrictions(snapshot(), new String[]{packageName}, - userId); + getDistractingPackageHelper().removeDistractingPackageRestrictions(snapshot(), + new String[]{packageName}, userId); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 55976b8d0610..4f152d6c0819 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -159,7 +159,6 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.ExceptionUtils; -import android.util.IntArray; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -939,6 +938,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService private final ResolveIntentHelper mResolveIntentHelper; private final DexOptHelper mDexOptHelper; private final SuspendPackageHelper mSuspendPackageHelper; + private final DistractingPackageHelper mDistractingPackageHelper; private final IntentResolverInterceptor mIntentResolverInterceptor; private final StorageEventHelper mStorageEventHelper; @@ -1684,6 +1684,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mResolveIntentHelper = testParams.resolveIntentHelper; mDexOptHelper = testParams.dexOptHelper; mSuspendPackageHelper = testParams.suspendPackageHelper; + mDistractingPackageHelper = testParams.distractingPackageHelper; mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); @@ -1843,6 +1844,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mProtectedPackages); mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper, mRemovePackageHelper); + mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper, + mSuspendPackageHelper); synchronized (mLock) { // Create the computer as soon as the state objects have been installed. The @@ -3066,43 +3069,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) { final String[] allPackages = snapshot.getAllAvailablePackageNames(); - removeDistractingPackageRestrictions(snapshot, allPackages, userId); + mDistractingPackageHelper.removeDistractingPackageRestrictions(snapshot, allPackages, + userId); } - /** - * Removes any {@link android.content.pm.PackageManager.DistractionRestriction restrictions} - * set on given packages. - * - * <p> Caller must flush package restrictions if it cares about immediate data consistency. - * - * @param packagesToChange The packages on which restrictions are to be removed. - * @param userId the user for which changes are taking place. - */ - void removeDistractingPackageRestrictions(@NonNull Computer snapshot, - String[] packagesToChange, int userId) { - final List<String> changedPackages = new ArrayList<>(); - final IntArray changedUids = new IntArray(); - for (String packageName : packagesToChange) { - final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName); - if (ps != null && ps.getUserStateOrDefault(userId).getDistractionFlags() != 0) { - changedPackages.add(ps.getPackageName()); - changedUids.add(UserHandle.getUid(userId, ps.getAppId())); - } - } - commitPackageStateMutation(null, mutator -> { - for (int index = 0; index < changedPackages.size(); index++) { - mutator.forPackage(changedPackages.get(index)) - .userState(userId) - .setDistractionFlags(0); - } - }); + private void enforceCanSetDistractingPackageRestrictionsAsUser(@NonNull Computer snapshot, + int callingUid, int userId, String callingMethod) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, + callingMethod); - if (!changedPackages.isEmpty()) { - final String[] packageArray = changedPackages.toArray( - new String[changedPackages.size()]); - mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged( - packageArray, changedUids.toArray(), userId, 0)); - scheduleWritePackageRestrictions(userId); + if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID + && UserHandle.getUserId(callingUid) != userId) { + throw new SecurityException("Calling uid " + callingUid + " cannot call for user " + + userId); } } @@ -5592,73 +5571,13 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, int restrictionFlags, int userId) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, - "setDistractingPackageRestrictionsAsUser"); - final int callingUid = Binder.getCallingUid(); - if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID - && UserHandle.getUserId(callingUid) != userId) { - throw new SecurityException("Calling uid " + callingUid + " cannot call for user " - + userId); - } - Objects.requireNonNull(packageNames, "packageNames cannot be null"); final Computer snapshot = snapshotComputer(); - if (restrictionFlags != 0 - && !mSuspendPackageHelper.isSuspendAllowedForUser(snapshot, userId, - callingUid)) { - Slog.w(PackageManagerService.TAG, "Cannot restrict packages due to restrictions on user " + userId); - return packageNames; - } - - final List<String> changedPackagesList = new ArrayList<>(packageNames.length); - final IntArray changedUids = new IntArray(packageNames.length); - final List<String> unactionedPackages = new ArrayList<>(packageNames.length); - - ArraySet<String> changesToCommit = new ArraySet<>(); - final boolean[] canRestrict = (restrictionFlags != 0) - ? mSuspendPackageHelper.canSuspendPackageForUser(snapshot, packageNames, userId, - callingUid) : null; - for (int i = 0; i < packageNames.length; i++) { - final String packageName = packageNames[i]; - final PackageStateInternal packageState = - snapshot.getPackageStateInternal(packageName); - if (packageState == null - || snapshot.shouldFilterApplication(packageState, callingUid, userId)) { - Slog.w(PackageManagerService.TAG, "Could not find package setting for package: " + packageName - + ". Skipping..."); - unactionedPackages.add(packageName); - continue; - } - if (canRestrict != null && !canRestrict[i]) { - unactionedPackages.add(packageName); - continue; - } - final int oldDistractionFlags = packageState.getUserStateOrDefault(userId) - .getDistractionFlags(); - if (restrictionFlags != oldDistractionFlags) { - changedPackagesList.add(packageName); - changedUids.add(UserHandle.getUid(userId, packageState.getAppId())); - changesToCommit.add(packageName); - } - } - - commitPackageStateMutation(null, mutator -> { - final int size = changesToCommit.size(); - for (int index = 0; index < size; index++) { - mutator.forPackage(changesToCommit.valueAt(index)) - .userState(userId) - .setDistractionFlags(restrictionFlags); - } - }); - - if (!changedPackagesList.isEmpty()) { - final String[] changedPackages = changedPackagesList.toArray( - new String[changedPackagesList.size()]); - mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged( - changedPackages, changedUids.toArray(), userId, restrictionFlags)); - scheduleWritePackageRestrictions(userId); - } - return unactionedPackages.toArray(new String[0]); + enforceCanSetDistractingPackageRestrictionsAsUser(snapshot, callingUid, userId, + "setDistractingPackageRestrictionsAsUser"); + Objects.requireNonNull(packageNames, "packageNames cannot be null"); + return mDistractingPackageHelper.setDistractingPackageRestrictionsAsUser(snapshot, + packageNames, restrictionFlags, userId, callingUid); } @Override @@ -6122,6 +6041,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService @NonNull @Override + protected DistractingPackageHelper getDistractingPackageHelper() { + return mDistractingPackageHelper; + } + + @NonNull + @Override protected ProtectedPackages getProtectedPackages() { return mProtectedPackages; } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 144231c95f75..16829e0e8f6c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -116,4 +116,5 @@ public final class PackageManagerServiceTestParams { public DexOptHelper dexOptHelper; public SuspendPackageHelper suspendPackageHelper; public StorageEventHelper storageEventHelper; + public DistractingPackageHelper distractingPackageHelper; } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt new file mode 100644 index 000000000000..cf6c82f23730 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm + +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Binder +import com.android.server.testutils.any +import com.android.server.testutils.eq +import com.android.server.testutils.nullable +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +@RunWith(JUnit4::class) +class DistractingPackageHelperTest : PackageHelperTestBase() { + + lateinit var distractingPackageHelper: DistractingPackageHelper + + override fun setup() { + super.setup() + distractingPackageHelper = DistractingPackageHelper( + pms, rule.mocks().injector, broadcastHelper, suspendPackageHelper) + } + + @Test + fun setDistractingPackageRestrictionsAsUser() { + val unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), packagesToChange, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + testHandler.flush() + + verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) + verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), + nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), + nullable(), nullable(), nullable()) + + val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) + val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS) + assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) + assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS) + assertThat(unactionedPackages).isEmpty() + } + + @Test + fun setDistractingPackageRestrictionsAsUser_setSameDistractionRestrictionTwice() { + distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), packagesToChange, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + testHandler.flush() + clearInvocations(pms) + clearInvocations(broadcastHelper) + + val unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), packagesToChange, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + testHandler.flush() + verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) + verify(broadcastHelper, never()).sendPackageBroadcast( + eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(), + anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable()) + assertThat(unactionedPackages).isEmpty() + } + + @Test + fun setDistractingPackageRestrictionsAsUser_emptyPackageName() { + var unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), null /* packageNames */, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + assertThat(unactionedPackages).isNull() + + unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), arrayOfNulls(0) /* packageNames */, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + assertThat(unactionedPackages).isEmpty() + } + + @Test + fun setDistractingPackageRestrictionsAsUser_callerIsNotAllowed() { + val unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), arrayOf(TEST_PACKAGE_1), + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, Binder.getCallingUid()) + + assertThat(unactionedPackages).asList().hasSize(1) + assertThat(unactionedPackages).asList().contains(TEST_PACKAGE_1) + } + + @Test + fun setDistractingPackageRestrictionsAsUser_setCallerItself() { + val unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), arrayOf(DEVICE_OWNER_PACKAGE), + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, Binder.getCallingUid()) + + assertThat(unactionedPackages).asList().hasSize(1) + assertThat(unactionedPackages).asList().contains(DEVICE_OWNER_PACKAGE) + } + + @Test + fun setDistractingPackageRestrictionsAsUser_nonexistentPackage() { + val unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), arrayOf(NONEXISTENT_PACKAGE), + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + + assertThat(unactionedPackages).asList().hasSize(1) + assertThat(unactionedPackages).asList().contains(NONEXISTENT_PACKAGE) + } + + @Test + fun setDistractingPackageRestrictionsAsUser_setKnownPackages() { + val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, + INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, + PERMISSION_CONTROLLER_PACKAGE) + val unactionedPackages = distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), knownPackages, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + + assertThat(unactionedPackages.size).isEqualTo(knownPackages.size) + for (pkg in knownPackages) { + assertThat(unactionedPackages).asList().contains(pkg) + } + } + + @Test + fun removeDistractingPackageRestrictions() { + distractingPackageHelper.setDistractingPackageRestrictionsAsUser( + pms.snapshotComputer(), packagesToChange, + PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid) + testHandler.flush() + clearInvocations(pms) + clearInvocations(broadcastHelper) + + distractingPackageHelper.removeDistractingPackageRestrictions(pms.snapshotComputer(), + packagesToChange, TEST_USER_ID) + testHandler.flush() + + verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) + verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), + nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), + nullable(), nullable(), nullable()) + val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST) + val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS) + assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2) + assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_NONE) + } + + @Test + fun removeDistractingPackageRestrictions_notDistractingPackage() { + distractingPackageHelper.removeDistractingPackageRestrictions(pms.snapshotComputer(), + arrayOf(TEST_PACKAGE_1), TEST_USER_ID) + testHandler.flush() + + verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) + verify(broadcastHelper, never()).sendPackageBroadcast(eq( + Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(), + nullable(), nullable(), any(), nullable(), nullable(), nullable()) + } + + @Test + fun removeDistractingPackageRestrictions_emptyPackageName() { + distractingPackageHelper.removeDistractingPackageRestrictions(pms.snapshotComputer(), + null /* packagesToChange */, TEST_USER_ID) + testHandler.flush() + verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) + verify(broadcastHelper, never()).sendPackageBroadcast(eq( + Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(), + nullable(), nullable(), any(), nullable(), nullable(), nullable()) + + distractingPackageHelper.removeDistractingPackageRestrictions(pms.snapshotComputer(), + arrayOfNulls(0), TEST_USER_ID) + testHandler.flush() + verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID)) + verify(broadcastHelper, never()).sendPackageBroadcast(eq( + Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(), + nullable(), nullable(), any(), nullable(), nullable(), nullable()) + } +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt new file mode 100644 index 000000000000..bd012fc7837e --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm + +import android.os.Build +import android.os.Bundle +import android.os.UserHandle +import android.os.UserManager +import com.android.server.pm.pkg.PackageStateInternal +import com.android.server.testutils.TestHandler +import com.android.server.testutils.any +import com.android.server.testutils.eq +import com.android.server.testutils.whenever +import org.junit.Before +import org.junit.Rule +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.spy +import org.mockito.MockitoAnnotations + +open class PackageHelperTestBase { + + companion object { + const val TEST_PACKAGE_1 = "com.android.test.package1" + const val TEST_PACKAGE_2 = "com.android.test.package2" + const val DEVICE_OWNER_PACKAGE = "com.android.test.owner" + const val NONEXISTENT_PACKAGE = "com.android.test.nonexistent" + const val DEVICE_ADMIN_PACKAGE = "com.android.test.known.device.admin" + const val DEFAULT_HOME_PACKAGE = "com.android.test.known.home" + const val DIALER_PACKAGE = "com.android.test.known.dialer" + const val INSTALLER_PACKAGE = "com.android.test.known.installer" + const val UNINSTALLER_PACKAGE = "com.android.test.known.uninstaller" + const val VERIFIER_PACKAGE = "com.android.test.known.verifier" + const val PERMISSION_CONTROLLER_PACKAGE = "com.android.test.known.permission" + const val TEST_USER_ID = 0 + } + + lateinit var pms: PackageManagerService + lateinit var suspendPackageHelper: SuspendPackageHelper + lateinit var testHandler: TestHandler + lateinit var defaultAppProvider: DefaultAppProvider + lateinit var packageSetting1: PackageStateInternal + lateinit var packageSetting2: PackageStateInternal + lateinit var ownerSetting: PackageStateInternal + lateinit var packagesToChange: Array<String> + lateinit var uidsToChange: IntArray + + @Mock + lateinit var broadcastHelper: BroadcastHelper + @Mock + lateinit var protectedPackages: ProtectedPackages + + @Captor + lateinit var bundleCaptor: ArgumentCaptor<Bundle> + + @Rule + @JvmField + val rule = MockSystemRule() + var deviceOwnerUid = 0 + + @Before + @Throws(Exception::class) + open fun setup() { + MockitoAnnotations.initMocks(this) + rule.system().stageNominalSystemState() + pms = spy(createPackageManagerService( + TEST_PACKAGE_1, TEST_PACKAGE_2, DEVICE_OWNER_PACKAGE, DEVICE_ADMIN_PACKAGE, + DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, + VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)) + suspendPackageHelper = SuspendPackageHelper( + pms, rule.mocks().injector, broadcastHelper, protectedPackages) + defaultAppProvider = rule.mocks().defaultAppProvider + testHandler = rule.mocks().handler + packageSetting1 = pms.snapshotComputer().getPackageStateInternal(TEST_PACKAGE_1)!! + packageSetting2 = pms.snapshotComputer().getPackageStateInternal(TEST_PACKAGE_2)!! + ownerSetting = pms.snapshotComputer().getPackageStateInternal(DEVICE_OWNER_PACKAGE)!! + deviceOwnerUid = UserHandle.getUid(TEST_USER_ID, ownerSetting.appId) + packagesToChange = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) + uidsToChange = intArrayOf(packageSetting1.appId, packageSetting2.appId) + + whenever(protectedPackages.getDeviceOwnerOrProfileOwnerPackage(eq(TEST_USER_ID))) + .thenReturn(DEVICE_OWNER_PACKAGE) + whenever(rule.mocks().userManagerService.hasUserRestriction( + eq(UserManager.DISALLOW_APPS_CONTROL), eq(TEST_USER_ID))).thenReturn(true) + whenever(rule.mocks().userManagerService.hasUserRestriction( + eq(UserManager.DISALLOW_UNINSTALL_APPS), eq(TEST_USER_ID))).thenReturn(true) + mockKnownPackages(pms) + } + + private fun mockKnownPackages(pms: PackageManagerService) { + Mockito.doAnswer { it.arguments[0] == DEVICE_ADMIN_PACKAGE }.`when`(pms) + .isPackageDeviceAdmin(any(), any()) + Mockito.doReturn(DEFAULT_HOME_PACKAGE).`when`(defaultAppProvider) + .getDefaultHome(eq(TEST_USER_ID)) + Mockito.doReturn(DIALER_PACKAGE).`when`(defaultAppProvider) + .getDefaultDialer(eq(TEST_USER_ID)) + Mockito.doReturn(arrayOf(INSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal( + any(), eq(KnownPackages.PACKAGE_INSTALLER), eq(TEST_USER_ID)) + Mockito.doReturn(arrayOf(UNINSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal( + any(), eq(KnownPackages.PACKAGE_UNINSTALLER), eq(TEST_USER_ID)) + Mockito.doReturn(arrayOf(VERIFIER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal( + any(), eq(KnownPackages.PACKAGE_VERIFIER), eq(TEST_USER_ID)) + Mockito.doReturn(arrayOf(PERMISSION_CONTROLLER_PACKAGE)).`when`(pms) + .getKnownPackageNamesInternal(any(), + eq(KnownPackages.PACKAGE_PERMISSION_CONTROLLER), eq(TEST_USER_ID)) + } + + private fun createPackageManagerService(vararg stageExistingPackages: String): + PackageManagerService { + stageExistingPackages.forEach { + rule.system().stageScanExistingPackage(it, 1L, + rule.system().dataAppDirectory) + } + var pms = PackageManagerService(rule.mocks().injector, + false /* coreOnly */, + false /* factoryTest */, + MockSystem.DEFAULT_VERSION_INFO.fingerprint, + false /* isEngBuild */, + false /* isUserDebugBuild */, + Build.VERSION_CODES.CUR_DEVELOPMENT, + Build.VERSION.INCREMENTAL) + rule.system().validateFinalState() + return pms + } +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index 9774af31950a..3ba9ca591fb3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -19,106 +19,25 @@ package com.android.server.pm import android.content.Intent import android.content.pm.SuspendDialogInfo import android.os.Binder -import android.os.Build -import android.os.Bundle import android.os.PersistableBundle -import android.os.UserHandle -import android.os.UserManager import android.util.ArrayMap import android.util.SparseArray -import com.android.server.pm.KnownPackages import com.android.server.pm.pkg.PackageStateInternal -import com.android.server.testutils.TestHandler import com.android.server.testutils.any import com.android.server.testutils.eq import com.android.server.testutils.nullable import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito import org.mockito.Mockito.argThat -import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations @RunWith(JUnit4::class) -class SuspendPackageHelperTest { - - companion object { - const val TEST_PACKAGE_1 = "com.android.test.package1" - const val TEST_PACKAGE_2 = "com.android.test.package2" - const val DEVICE_OWNER_PACKAGE = "com.android.test.owner" - const val NONEXISTENT_PACKAGE = "com.android.test.nonexistent" - const val DEVICE_ADMIN_PACKAGE = "com.android.test.known.device.admin" - const val DEFAULT_HOME_PACKAGE = "com.android.test.known.home" - const val DIALER_PACKAGE = "com.android.test.known.dialer" - const val INSTALLER_PACKAGE = "com.android.test.known.installer" - const val UNINSTALLER_PACKAGE = "com.android.test.known.uninstaller" - const val VERIFIER_PACKAGE = "com.android.test.known.verifier" - const val PERMISSION_CONTROLLER_PACKAGE = "com.android.test.known.permission" - const val TEST_USER_ID = 0 - } - - lateinit var pms: PackageManagerService - lateinit var suspendPackageHelper: SuspendPackageHelper - lateinit var testHandler: TestHandler - lateinit var defaultAppProvider: DefaultAppProvider - lateinit var packageSetting1: PackageStateInternal - lateinit var packageSetting2: PackageStateInternal - lateinit var ownerSetting: PackageStateInternal - lateinit var packagesToSuspend: Array<String> - lateinit var uidsToSuspend: IntArray - - @Mock - lateinit var broadcastHelper: BroadcastHelper - @Mock - lateinit var protectedPackages: ProtectedPackages - - @Captor - lateinit var bundleCaptor: ArgumentCaptor<Bundle> - - @Rule - @JvmField - val rule = MockSystemRule() - var deviceOwnerUid = 0 - - @Before - @Throws(Exception::class) - fun setup() { - MockitoAnnotations.initMocks(this) - rule.system().stageNominalSystemState() - pms = spy(createPackageManagerService( - TEST_PACKAGE_1, TEST_PACKAGE_2, DEVICE_OWNER_PACKAGE, DEVICE_ADMIN_PACKAGE, - DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, - VERIFIER_PACKAGE, PERMISSION_CONTROLLER_PACKAGE)) - suspendPackageHelper = SuspendPackageHelper( - pms, rule.mocks().injector, broadcastHelper, protectedPackages) - defaultAppProvider = rule.mocks().defaultAppProvider - testHandler = rule.mocks().handler - packageSetting1 = pms.snapshotComputer().getPackageStateInternal(TEST_PACKAGE_1)!! - packageSetting2 = pms.snapshotComputer().getPackageStateInternal(TEST_PACKAGE_2)!! - ownerSetting = pms.snapshotComputer().getPackageStateInternal(DEVICE_OWNER_PACKAGE)!! - deviceOwnerUid = UserHandle.getUid(TEST_USER_ID, ownerSetting.appId) - packagesToSuspend = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2) - uidsToSuspend = intArrayOf(packageSetting1.appId, packageSetting2.appId) - - whenever(protectedPackages.getDeviceOwnerOrProfileOwnerPackage(eq(TEST_USER_ID))) - .thenReturn(DEVICE_OWNER_PACKAGE) - whenever(rule.mocks().userManagerService.hasUserRestriction( - eq(UserManager.DISALLOW_APPS_CONTROL), eq(TEST_USER_ID))).thenReturn(true) - whenever(rule.mocks().userManagerService.hasUserRestriction( - eq(UserManager.DISALLOW_UNINSTALL_APPS), eq(TEST_USER_ID))).thenReturn(true) - mockKnownPackages(pms) - } +class SuspendPackageHelperTest : PackageHelperTestBase() { @Test fun setPackagesSuspended() { @@ -388,7 +307,7 @@ class SuspendPackageHelperTest { mockAllowList(packageSetting2, allowList(10001, 10002, 10003)) suspendPackageHelper.sendPackagesSuspendedForUser(pms.snapshotComputer(), - Intent.ACTION_PACKAGES_SUSPENDED, packagesToSuspend, uidsToSuspend, TEST_USER_ID) + Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, TEST_USER_ID) testHandler.flush() verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable()) @@ -407,7 +326,7 @@ class SuspendPackageHelperTest { mockAllowList(packageSetting2, allowList(10001, 10002, 10007)) suspendPackageHelper.sendPackagesSuspendedForUser(pms.snapshotComputer(), - Intent.ACTION_PACKAGES_SUSPENDED, packagesToSuspend, uidsToSuspend, TEST_USER_ID) + Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, TEST_USER_ID) testHandler.flush() verify(broadcastHelper, times(2)).sendPackageBroadcast( any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), @@ -430,7 +349,7 @@ class SuspendPackageHelperTest { mockAllowList(packageSetting2, null) suspendPackageHelper.sendPackagesSuspendedForUser(pms.snapshotComputer(), - Intent.ACTION_PACKAGES_SUSPENDED, packagesToSuspend, uidsToSuspend, TEST_USER_ID) + Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, TEST_USER_ID) testHandler.flush() verify(broadcastHelper, times(2)).sendPackageBroadcast( any(), nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(), @@ -450,7 +369,7 @@ class SuspendPackageHelperTest { @Throws(Exception::class) fun sendPackagesSuspendModifiedForUser() { suspendPackageHelper.sendPackagesSuspendedForUser(pms.snapshotComputer(), - Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToSuspend, uidsToSuspend, + Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, TEST_USER_ID) testHandler.flush() verify(broadcastHelper).sendPackageBroadcast( @@ -475,40 +394,4 @@ class SuspendPackageHelperTest { )) .thenReturn(list) } - - private fun mockKnownPackages(pms: PackageManagerService) { - Mockito.doAnswer { it.arguments[0] == DEVICE_ADMIN_PACKAGE }.`when`(pms) - .isPackageDeviceAdmin(any(), any()) - Mockito.doReturn(DEFAULT_HOME_PACKAGE).`when`(defaultAppProvider) - .getDefaultHome(eq(TEST_USER_ID)) - Mockito.doReturn(DIALER_PACKAGE).`when`(defaultAppProvider) - .getDefaultDialer(eq(TEST_USER_ID)) - Mockito.doReturn(arrayOf(INSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal( - any(), eq(KnownPackages.PACKAGE_INSTALLER), eq(TEST_USER_ID)) - Mockito.doReturn(arrayOf(UNINSTALLER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal( - any(), eq(KnownPackages.PACKAGE_UNINSTALLER), eq(TEST_USER_ID)) - Mockito.doReturn(arrayOf(VERIFIER_PACKAGE)).`when`(pms).getKnownPackageNamesInternal( - any(), eq(KnownPackages.PACKAGE_VERIFIER), eq(TEST_USER_ID)) - Mockito.doReturn(arrayOf(PERMISSION_CONTROLLER_PACKAGE)).`when`(pms) - .getKnownPackageNamesInternal(any(), - eq(KnownPackages.PACKAGE_PERMISSION_CONTROLLER), eq(TEST_USER_ID)) - } - - private fun createPackageManagerService(vararg stageExistingPackages: String): - PackageManagerService { - stageExistingPackages.forEach { - rule.system().stageScanExistingPackage(it, 1L, - rule.system().dataAppDirectory) - } - var pms = PackageManagerService(rule.mocks().injector, - false /*coreOnly*/, - false /*factoryTest*/, - MockSystem.DEFAULT_VERSION_INFO.fingerprint, - false /*isEngBuild*/, - false /*isUserDebugBuild*/, - Build.VERSION_CODES.CUR_DEVELOPMENT, - Build.VERSION.INCREMENTAL) - rule.system().validateFinalState() - return pms - } } |