Merge changes from topic "DistractingPackageHelper_tm-dev" into tm-dev

* changes:
  Add DistractingPackageHelperTest
  Introduce DistractingPackageHelper
This commit is contained in:
Songchun Fan
2022-03-31 14:35:44 +00:00
committed by Android (Google) Code Review
8 changed files with 568 additions and 235 deletions

View File

@@ -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,

View File

@@ -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 */));
}
}

View File

@@ -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

View File

@@ -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
@@ -6120,6 +6039,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService
return mSuspendPackageHelper;
}
@NonNull
@Override
protected DistractingPackageHelper getDistractingPackageHelper() {
return mDistractingPackageHelper;
}
@NonNull
@Override
protected ProtectedPackages getProtectedPackages() {

View File

@@ -116,4 +116,5 @@ public final class PackageManagerServiceTestParams {
public DexOptHelper dexOptHelper;
public SuspendPackageHelper suspendPackageHelper;
public StorageEventHelper storageEventHelper;
public DistractingPackageHelper distractingPackageHelper;
}

View File

@@ -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())
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}