Delete vdex/odex when package is globally hibernating

Delete vdex/odex files when a package is marked globally hibernating. We
expose the delete OAT artifacts API in PackageManagerInternal for app
hibernation service to call.

Bug: 183140824
Test: atest AppHibernationServiceTest
Test: atest PackageManagerServiceHibernationTests
Test: manual test

Change-Id: I2ca9f561f79992e37e0bad2d4bb7dda4cd21b4ed
This commit is contained in:
Kevin Han
2021-03-13 16:15:48 -08:00
parent 1d7bd5166c
commit 71edf551d3
6 changed files with 78 additions and 8 deletions

View File

@@ -1141,4 +1141,9 @@ public abstract class PackageManagerInternal {
*/
public abstract boolean isPackageFrozen(
@NonNull String packageName, int callingUid, int userId);
/**
* Deletes the OAT artifacts of a package.
*/
public abstract void deleteOatArtifactsOfPackage(String packageName);
}

View File

@@ -38,6 +38,7 @@ import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
@@ -91,8 +92,10 @@ public final class AppHibernationService extends SystemService {
private final Object mLock = new Object();
private final Context mContext;
private final IPackageManager mIPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
@GuardedBy("mLock")
private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores =
@@ -123,6 +126,7 @@ public final class AppHibernationService extends SystemService {
super(injector.getContext());
mContext = injector.getContext();
mIPackageManager = injector.getPackageManager();
mPackageManagerInternal = injector.getPackageManagerInternal();
mIActivityManager = injector.getActivityManager();
mUserManager = injector.getUserManager();
mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
@@ -210,8 +214,9 @@ public final class AppHibernationService extends SystemService {
synchronized (mLock) {
GlobalLevelState state = mGlobalHibernationStates.get(packageName);
if (state == null) {
throw new IllegalArgumentException(
String.format("Package %s is not installed", packageName));
// This API can be legitimately called before installation finishes as part of
// dex optimization, so we just return false here.
return false;
}
return state.hibernated;
}
@@ -364,7 +369,7 @@ public final class AppHibernationService extends SystemService {
@GuardedBy("mLock")
private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
// TODO(175830194): Delete vdex/odex when DexManager API is built out
mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName);
state.hibernated = true;
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@@ -707,6 +712,8 @@ public final class AppHibernationService extends SystemService {
IPackageManager getPackageManager();
PackageManagerInternal getPackageManagerInternal();
IActivityManager getActivityManager();
UserManager getUserManager();
@@ -738,6 +745,11 @@ public final class AppHibernationService extends SystemService {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
@Override
public PackageManagerInternal getPackageManagerInternal() {
return LocalServices.getService(PackageManagerInternal.class);
}
@Override
public IActivityManager getActivityManager() {
return ActivityManager.getService();

View File

@@ -252,7 +252,6 @@ import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
import android.content.pm.parsing.component.ParsedUsesPermission;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Resources;
@@ -371,6 +370,7 @@ import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.apphibernation.AppHibernationService;
import com.android.server.compat.CompatChange;
import com.android.server.compat.PlatformCompat;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -12304,9 +12304,15 @@ public class PackageManagerService extends IPackageManager.Stub
public ArraySet<String> getOptimizablePackages() {
ArraySet<String> pkgs = new ArraySet<>();
final boolean hibernationEnabled = AppHibernationService.isAppHibernationEnabled();
AppHibernationManagerInternal appHibernationManager =
mInjector.getLocalService(AppHibernationManagerInternal.class);
synchronized (mLock) {
for (AndroidPackage p : mPackages.values()) {
if (PackageDexOptimizer.canOptimizePackage(p)) {
// Checking hibernation state is an inexpensive call.
boolean isHibernating = hibernationEnabled
&& appHibernationManager.isHibernatingGlobally(p.getPackageName());
if (PackageDexOptimizer.canOptimizePackage(p) && !isHibernating) {
pkgs.add(p.getPackageName());
}
}
@@ -27329,6 +27335,11 @@ public class PackageManagerService extends IPackageManager.Stub
return PackageManagerService.this.getPackageStartability(
packageName, callingUid, userId) == PACKAGE_STARTABILITY_FROZEN;
}
@Override
public void deleteOatArtifactsOfPackage(String packageName) {
PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
}
}
@GuardedBy("mLock")

View File

@@ -36,6 +36,7 @@ import android.os.SystemProperties
import android.os.UserHandle
import android.os.UserManager
import android.os.incremental.IncrementalManager
import android.provider.DeviceConfig
import android.util.ArrayMap
import android.util.DisplayMetrics
import android.util.EventLog
@@ -131,6 +132,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
.mockStatic(LockGuard::class.java)
.mockStatic(EventLog::class.java)
.mockStatic(LocalServices::class.java)
.mockStatic(DeviceConfig::class.java)
.apply(withSession)
session = apply.startMocking()
whenever(mocks.settings.insertPackageSettingLPw(

View File

@@ -17,8 +17,13 @@
package com.android.server.pm
import android.os.Build
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
import com.android.server.apphibernation.AppHibernationManagerInternal
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.testutils.whenever
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -33,7 +38,10 @@ class PackageManagerServiceHibernationTests {
companion object {
val TEST_PACKAGE_NAME = "test.package"
val TEST_PACKAGE_2_NAME = "test.package2"
val TEST_USER_ID = 0
val KEY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled"
}
@Rule
@@ -47,6 +55,8 @@ class PackageManagerServiceHibernationTests {
@Throws(Exception::class)
fun setup() {
MockitoAnnotations.initMocks(this)
wheneverStatic { DeviceConfig.getBoolean(
NAMESPACE_APP_HIBERNATION, KEY_APP_HIBERNATION_ENABLED, false) }.thenReturn(true)
rule.system().stageNominalSystemState()
whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
.thenReturn(appHibernationManager)
@@ -68,6 +78,28 @@ class PackageManagerServiceHibernationTests {
verify(appHibernationManager).setHibernatingGlobally(TEST_PACKAGE_NAME, false)
}
@Test
fun testGetOptimizablePackages_ExcludesGloballyHibernatingPackages() {
rule.system().stageScanExistingPackage(
TEST_PACKAGE_NAME,
1L,
rule.system().dataAppDirectory,
withPackage = { it.apply { isHasCode = true } })
rule.system().stageScanExistingPackage(
TEST_PACKAGE_2_NAME,
1L,
rule.system().dataAppDirectory,
withPackage = { it.apply { isHasCode = true } })
val pm = createPackageManagerService()
rule.system().validateFinalState()
whenever(appHibernationManager.isHibernatingGlobally(TEST_PACKAGE_2_NAME)).thenReturn(true)
val optimizablePkgs = pm.optimizablePackages
assertTrue(optimizablePkgs.contains(TEST_PACKAGE_NAME))
assertFalse(optimizablePkgs.contains(TEST_PACKAGE_2_NAME))
}
private fun createPackageManagerService(): PackageManagerService {
return PackageManagerService(rule.mocks().injector,
false /*coreOnly*/,

View File

@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.IActivityManager;
@@ -36,6 +37,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.net.Uri;
@@ -53,7 +55,6 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -81,6 +82,8 @@ public final class AppHibernationServiceTest {
@Mock
private IPackageManager mIPackageManager;
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
private IActivityManager mIActivityManager;
@Mock
private UserManager mUserManager;
@@ -253,6 +256,11 @@ public final class AppHibernationServiceTest {
return mIPackageManager;
}
@Override
public PackageManagerInternal getPackageManagerInternal() {
return mPackageManagerInternal;
}
@Override
public UserManager getUserManager() {
return mUserManager;
@@ -260,12 +268,12 @@ public final class AppHibernationServiceTest {
@Override
public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
return Mockito.mock(HibernationStateDiskStore.class);
return mock(HibernationStateDiskStore.class);
}
@Override
public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
return Mockito.mock(HibernationStateDiskStore.class);
return mock(HibernationStateDiskStore.class);
}
}
}