Merge "Add frozen counts support in PackageFreezer"

This commit is contained in:
Rhed Jao
2021-12-22 01:44:05 +00:00
committed by Android (Google) Code Review
8 changed files with 180 additions and 17 deletions

View File

@@ -146,7 +146,6 @@ import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationUtils;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedArraySet;
import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.WatchedSparseIntArray;
@@ -333,7 +332,7 @@ public class ComputerEngine implements Computer {
private final InstantAppRegistry mInstantAppRegistry;
private final ApplicationInfo mLocalAndroidApplication;
private final AppsFilter mAppsFilter;
private final WatchedArraySet<String> mFrozenPackages;
private final WatchedArrayMap<String, Integer> mFrozenPackages;
// Immutable service attribute
private final String mAppPredictionServicePackage;
@@ -3580,7 +3579,7 @@ public class ComputerEngine implements Computer {
return PackageManagerService.PACKAGE_STARTABILITY_NOT_SYSTEM;
}
if (mFrozenPackages.contains(packageName)) {
if (mFrozenPackages.containsKey(packageName)) {
return PackageManagerService.PACKAGE_STARTABILITY_FROZEN;
}

View File

@@ -504,6 +504,9 @@ final class DumpHelper {
ipw.println("(none)");
} else {
for (int i = 0; i < mPm.mFrozenPackages.size(); i++) {
ipw.print("package=");
ipw.print(mPm.mFrozenPackages.keyAt(i));
ipw.print(", refCounts=");
ipw.println(mPm.mFrozenPackages.valueAt(i));
}
}

View File

@@ -130,7 +130,7 @@ public final class MovePackageHelper {
"Device admin cannot be moved");
}
if (mPm.mFrozenPackages.contains(packageName)) {
if (mPm.mFrozenPackages.containsKey(packageName)) {
throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING,
"Failed to move already frozen package");
}
@@ -188,6 +188,7 @@ public final class MovePackageHelper {
for (int userId : installedUserIds) {
if (StorageManager.isFileEncryptedNativeOrEmulated()
&& !StorageManager.isUserKeyUnlocked(userId)) {
freezer.close();
throw new PackageManagerException(MOVE_FAILED_LOCKED_USER,
"User " + userId + " must be unlocked");
}
@@ -230,6 +231,7 @@ public final class MovePackageHelper {
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) throws RemoteException {
freezer.close();
throw new IllegalStateException();
}

View File

@@ -31,8 +31,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
final class PackageFreezer implements AutoCloseable {
private final String mPackageName;
private final boolean mWeFroze;
private final AtomicBoolean mClosed = new AtomicBoolean();
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -48,7 +46,7 @@ final class PackageFreezer implements AutoCloseable {
PackageFreezer(PackageManagerService pm) {
mPm = pm;
mPackageName = null;
mWeFroze = false;
mClosed.set(true);
mCloseGuard.open("close");
}
@@ -58,7 +56,9 @@ final class PackageFreezer implements AutoCloseable {
mPackageName = packageName;
final PackageSetting ps;
synchronized (mPm.mLock) {
mWeFroze = mPm.mFrozenPackages.add(mPackageName);
final int refCounts = mPm.mFrozenPackages
.getOrDefault(mPackageName, 0 /* defaultValue */) + 1;
mPm.mFrozenPackages.put(mPackageName, refCounts);
ps = mPm.mSettings.getPackageLPr(mPackageName);
}
if (ps != null) {
@@ -82,7 +82,11 @@ final class PackageFreezer implements AutoCloseable {
mCloseGuard.close();
if (mClosed.compareAndSet(false, true)) {
synchronized (mPm.mLock) {
if (mWeFroze) {
final int refCounts = mPm.mFrozenPackages
.getOrDefault(mPackageName, 0 /* defaultValue */) - 1;
if (refCounts > 0) {
mPm.mFrozenPackages.put(mPackageName, refCounts);
} else {
mPm.mFrozenPackages.remove(mPackageName);
}
}

View File

@@ -249,7 +249,6 @@ import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedArraySet;
import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.WatchedSparseIntArray;
@@ -646,15 +645,16 @@ public class PackageManagerService extends IPackageManager.Stub
final Settings mSettings;
/**
* Set of package names that are currently "frozen", which means active
* surgery is being done on the code/data for that package. The platform
* will refuse to launch frozen packages to avoid race conditions.
* Map of package names to frozen counts that are currently "frozen",
* which means active surgery is being done on the code/data for that
* package. The platform will refuse to launch frozen packages to avoid
* race conditions.
*
* @see PackageFreezer
*/
@GuardedBy("mLock")
final WatchedArraySet<String> mFrozenPackages = new WatchedArraySet<>();
private final SnapshotCache<WatchedArraySet<String>> mFrozenPackagesSnapshot =
final WatchedArrayMap<String, Integer> mFrozenPackages = new WatchedArrayMap<>();
private final SnapshotCache<WatchedArrayMap<String, Integer>> mFrozenPackagesSnapshot =
new SnapshotCache.Auto(mFrozenPackages, mFrozenPackages,
"PackageManagerService.mFrozenPackages");
@@ -1016,7 +1016,7 @@ public class PackageManagerService extends IPackageManager.Stub
public final AppsFilter appsFilter;
public final ComponentResolver componentResolver;
public final PackageManagerService service;
public final WatchedArraySet<String> frozenPackages;
public final WatchedArrayMap<String, Integer> frozenPackages;
Snapshot(int type) {
if (type == Snapshot.SNAPPED) {
@@ -7255,7 +7255,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
void checkPackageFrozen(String packageName) {
synchronized (mLock) {
if (!mFrozenPackages.contains(packageName)) {
if (!mFrozenPackages.containsKey(packageName)) {
Slog.wtf(TAG, "Expected " + packageName + " to be frozen!", new Throwable());
}
}

View File

@@ -59,6 +59,7 @@ android_test {
"mockingservicestests-utils-mockito",
"servicestests-core-utils",
"testables",
"kotlin-test",
// TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
"testng",
],

View File

@@ -0,0 +1,136 @@
/*
* 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.os.Build
import com.android.server.testutils.any
import com.android.server.testutils.spy
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.Mockito.eq
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import kotlin.test.assertFailsWith
@RunWith(JUnit4::class)
class PackageFreezerTest {
companion object {
const val TEST_PACKAGE = "com.android.test.package"
const val TEST_REASON = "test reason"
const val TEST_USER_ID = 0
}
@Rule
@JvmField
val rule = MockSystemRule()
lateinit var pms: PackageManagerService
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,
false /*snapshotEnabled*/)
rule.system().validateFinalState()
return pms
}
private fun frozenMessage(packageName: String) = "Package $packageName is currently frozen!"
private fun <T : Throwable> assertThrowContainsMessage(
exceptionClass: kotlin.reflect.KClass<T>,
message: String,
block: () -> Unit
) {
assertThat(assertFailsWith(exceptionClass, block).message).contains(message)
}
@Before
@Throws(Exception::class)
fun setup() {
rule.system().stageNominalSystemState()
pms = spy(createPackageManagerService(TEST_PACKAGE))
whenever(pms.killApplication(any(), any(), any(), any()))
}
@Test
fun freezePackage() {
val freezer = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms)
verify(pms, times(1))
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON))
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
}
freezer.close()
pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
}
@Test
fun freezePackage_twice() {
val freezer1 = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms)
val freezer2 = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms)
verify(pms, times(2))
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON))
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
}
freezer1.close()
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
}
freezer2.close()
pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
}
@Test
fun freezePackage_withoutClosing() {
var freezer: PackageFreezer? = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms)
verify(pms, times(1))
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON))
assertThrowContainsMessage(SecurityException::class, frozenMessage(TEST_PACKAGE)) {
pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
}
freezer = null
System.gc()
System.runFinalization()
pms.checkPackageStartable(TEST_PACKAGE, TEST_USER_ID)
}
}

View File

@@ -0,0 +1,18 @@
{
"presubmit": [
{
"name": "FrameworksMockingServicesTests",
"options": [
{
"include-filter": "com.android.server.pm"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"exclude-annotation": "org.junit.Ignore"
}
]
}
]
}