Merge changes from topic "br_suspended_pkg" into sc-dev
* changes: Adds multiple target packages support for `pm suspend` Applies package visibility for the broadcast of suspended packages
This commit is contained in:
@@ -15442,16 +15442,55 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
null);
|
||||
}
|
||||
|
||||
private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
|
||||
boolean suspended) {
|
||||
final Bundle extras = new Bundle(3);
|
||||
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
|
||||
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
|
||||
sendPackageBroadcast(
|
||||
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
|
||||
: Intent.ACTION_PACKAGES_UNSUSPENDED,
|
||||
null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
|
||||
new int[] {userId}, null, null, null);
|
||||
final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
|
||||
final List<IntArray> uidsToSend = new ArrayList(pkgList.length);
|
||||
final List<SparseArray<int[]>> allowListsToSend = new ArrayList(pkgList.length);
|
||||
final int[] userIds = new int[] {userId};
|
||||
// Get allow lists for the pkg in the pkgList. Merge into the existed pkgs and uids if
|
||||
// allow lists are the same.
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < pkgList.length; i++) {
|
||||
final String pkgName = pkgList[i];
|
||||
final int uid = uidList[i];
|
||||
SparseArray<int[]> allowList = mAppsFilter.getVisibilityAllowList(
|
||||
getPackageSettingInternal(pkgName, Process.SYSTEM_UID),
|
||||
userIds, mSettings.getPackagesLocked());
|
||||
if (allowList == null) {
|
||||
allowList = new SparseArray<>(0);
|
||||
}
|
||||
boolean merged = false;
|
||||
for (int j = 0; j < allowListsToSend.size(); j++) {
|
||||
if (Arrays.equals(allowListsToSend.get(j).get(userId), allowList.get(userId))) {
|
||||
pkgsToSend.get(j).add(pkgName);
|
||||
uidsToSend.get(j).add(uid);
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!merged) {
|
||||
pkgsToSend.add(new ArrayList<>(Arrays.asList(pkgName)));
|
||||
uidsToSend.add(IntArray.wrap(new int[] {uid}));
|
||||
allowListsToSend.add(allowList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < pkgsToSend.size(); i++) {
|
||||
final Bundle extras = new Bundle(3);
|
||||
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
|
||||
pkgsToSend.get(i).toArray(new String[pkgsToSend.get(i).size()]));
|
||||
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidsToSend.get(i).toArray());
|
||||
final SparseArray<int[]> allowList = allowListsToSend.get(i).size() == 0
|
||||
? null : allowListsToSend.get(i);
|
||||
sendPackageBroadcast(
|
||||
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
|
||||
: Intent.ACTION_PACKAGES_UNSUSPENDED,
|
||||
null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
|
||||
userIds, null, allowList, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -104,8 +104,8 @@ import com.android.server.FgThread;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemConfig;
|
||||
import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationShell;
|
||||
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationShell;
|
||||
|
||||
import dalvik.system.DexFile;
|
||||
|
||||
@@ -2251,8 +2251,8 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
}
|
||||
}
|
||||
|
||||
final String packageName = getNextArg();
|
||||
if (packageName == null) {
|
||||
final List<String> packageNames = getRemainingArgs();
|
||||
if (packageNames.isEmpty()) {
|
||||
pw.println("Error: package name not specified");
|
||||
return 1;
|
||||
}
|
||||
@@ -2270,12 +2270,15 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
try {
|
||||
final int translatedUserId =
|
||||
translateUserId(userId, UserHandle.USER_NULL, "runSuspend");
|
||||
mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
|
||||
((appExtras.size() > 0) ? appExtras : null),
|
||||
mInterface.setPackagesSuspendedAsUser(packageNames.toArray(new String[] {}),
|
||||
suspendedState, ((appExtras.size() > 0) ? appExtras : null),
|
||||
((launcherExtras.size() > 0) ? launcherExtras : null),
|
||||
info, callingPackage, translatedUserId);
|
||||
pw.println("Package " + packageName + " new suspended state: "
|
||||
+ mInterface.isPackageSuspendedForUser(packageName, translatedUserId));
|
||||
for (int i = 0; i < packageNames.size(); i++) {
|
||||
final String packageName = packageNames.get(i);
|
||||
pw.println("Package " + packageName + " new suspended state: "
|
||||
+ mInterface.isPackageSuspendedForUser(packageName, translatedUserId));
|
||||
}
|
||||
return 0;
|
||||
} catch (RemoteException | IllegalArgumentException e) {
|
||||
pw.println(e.toString());
|
||||
@@ -3643,11 +3646,11 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
pw.println(" hide [--user USER_ID] PACKAGE_OR_COMPONENT");
|
||||
pw.println(" unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
|
||||
pw.println("");
|
||||
pw.println(" suspend [--user USER_ID] TARGET-PACKAGE");
|
||||
pw.println(" Suspends the specified package (as user).");
|
||||
pw.println(" suspend [--user USER_ID] PACKAGE [PACKAGE...]");
|
||||
pw.println(" Suspends the specified package(s) (as user).");
|
||||
pw.println("");
|
||||
pw.println(" unsuspend [--user USER_ID] TARGET-PACKAGE");
|
||||
pw.println(" Unsuspends the specified package (as user).");
|
||||
pw.println(" unsuspend [--user USER_ID] PACKAGE [PACKAGE...]");
|
||||
pw.println(" Unsuspends the specified package(s) (as user).");
|
||||
pw.println("");
|
||||
pw.println(" grant [--user USER_ID] PACKAGE PERMISSION");
|
||||
pw.println(" revoke [--user USER_ID] PACKAGE PERMISSION");
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.SparseArray
|
||||
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.android.server.utils.WatchedArrayMap
|
||||
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.Mockito.spy
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
class SuspendPackagesBroadcastTest {
|
||||
|
||||
companion object {
|
||||
const val TEST_PACKAGE_1 = "com.android.test.package1"
|
||||
const val TEST_PACKAGE_2 = "com.android.test.package2"
|
||||
const val TEST_USER_ID = 0
|
||||
}
|
||||
|
||||
lateinit var pms: PackageManagerService
|
||||
lateinit var packageSetting1: PackageSetting
|
||||
lateinit var packageSetting2: PackageSetting
|
||||
lateinit var packagesToSuspend: Array<String>
|
||||
lateinit var uidsToSuspend: IntArray
|
||||
|
||||
@Captor
|
||||
lateinit var bundleCaptor: ArgumentCaptor<Bundle>
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val rule = MockSystemRule()
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
rule.system().stageNominalSystemState()
|
||||
pms = spy(createPackageManagerService(TEST_PACKAGE_1, TEST_PACKAGE_2))
|
||||
packageSetting1 = pms.getPackageSetting(TEST_PACKAGE_1)!!
|
||||
packageSetting2 = pms.getPackageSetting(TEST_PACKAGE_2)!!
|
||||
packagesToSuspend = arrayOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
|
||||
uidsToSuspend = intArrayOf(packageSetting1.appId, packageSetting2.appId)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun sendPackagesSuspendedForUser_withSameVisibilityAllowList() {
|
||||
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
|
||||
mockAllowList(packageSetting2, allowList(10001, 10002, 10003))
|
||||
|
||||
pms.sendPackagesSuspendedForUser(
|
||||
packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
|
||||
verify(pms).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
|
||||
anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
|
||||
|
||||
var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
|
||||
var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
|
||||
assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
|
||||
assertThat(changedUids).asList().containsExactly(
|
||||
packageSetting1.appId, packageSetting2.appId)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun sendPackagesSuspendedForUser_withDifferentVisibilityAllowList() {
|
||||
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
|
||||
mockAllowList(packageSetting2, allowList(10001, 10002, 10007))
|
||||
|
||||
pms.sendPackagesSuspendedForUser(
|
||||
packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
|
||||
verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
|
||||
anyInt(), nullable(), nullable(), any(), nullable(), any(), nullable())
|
||||
|
||||
bundleCaptor.allValues.forEach {
|
||||
var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
|
||||
var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
|
||||
assertThat(changedPackages?.size).isEqualTo(1)
|
||||
assertThat(changedUids?.size).isEqualTo(1)
|
||||
assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
|
||||
assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun sendPackagesSuspendedForUser_withNullVisibilityAllowList() {
|
||||
mockAllowList(packageSetting1, allowList(10001, 10002, 10003))
|
||||
mockAllowList(packageSetting2, null)
|
||||
|
||||
pms.sendPackagesSuspendedForUser(
|
||||
packagesToSuspend, uidsToSuspend, TEST_USER_ID, /* suspended = */ true)
|
||||
verify(pms, times(2)).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
|
||||
anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable())
|
||||
|
||||
bundleCaptor.allValues.forEach {
|
||||
var changedPackages = it.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
|
||||
var changedUids = it.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
|
||||
assertThat(changedPackages?.size).isEqualTo(1)
|
||||
assertThat(changedUids?.size).isEqualTo(1)
|
||||
assertThat(changedPackages?.get(0)).isAnyOf(TEST_PACKAGE_1, TEST_PACKAGE_2)
|
||||
assertThat(changedUids?.get(0)).isAnyOf(packageSetting1.appId, packageSetting2.appId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun allowList(vararg uids: Int) = SparseArray<IntArray>().apply {
|
||||
this.put(TEST_USER_ID, uids)
|
||||
}
|
||||
|
||||
private fun mockAllowList(pkgSetting: PackageSetting, list: SparseArray<IntArray>?) {
|
||||
whenever(rule.mocks().injector.appsFilter.getVisibilityAllowList(eq(pkgSetting),
|
||||
any(IntArray::class.java), any() as WatchedArrayMap<String, PackageSetting>))
|
||||
.thenReturn(list)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -93,3 +93,24 @@ inline fun <reified T> mockThrowOnUnmocked(block: T.() -> Unit = {}) =
|
||||
spyThrowOnUnmocked<T>(null, block)
|
||||
|
||||
inline fun <reified T : Any> nullable() = ArgumentMatchers.nullable(T::class.java)
|
||||
|
||||
/**
|
||||
* Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when
|
||||
* null is returned.
|
||||
*
|
||||
* Generic T is nullable because implicitly bounded by Any?.
|
||||
*/
|
||||
fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
|
||||
|
||||
/**
|
||||
* Wrapper around [Mockito.any] for generic types.
|
||||
*/
|
||||
inline fun <reified T> any() = any(T::class.java)
|
||||
|
||||
/**
|
||||
* Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
|
||||
* null is returned.
|
||||
*
|
||||
* Generic T is nullable because implicitly bounded by Any?.
|
||||
*/
|
||||
fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
|
||||
|
||||
Reference in New Issue
Block a user