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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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*/,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user