diff options
6 files changed, 78 insertions, 8 deletions
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index c2957780c9d7..a9eb2c110867 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -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); } diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index ebe9d96ee1f6..5a99e0e77a6c 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -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; @@ -92,8 +93,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 = @@ -125,6 +128,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(); @@ -214,8 +218,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; } @@ -366,7 +371,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); } @@ -728,6 +733,8 @@ public final class AppHibernationService extends SystemService { IPackageManager getPackageManager(); + PackageManagerInternal getPackageManagerInternal(); + IActivityManager getActivityManager(); UserManager getUserManager(); @@ -762,6 +769,11 @@ public final class AppHibernationService extends SystemService { } @Override + public PackageManagerInternal getPackageManagerInternal() { + return LocalServices.getService(PackageManagerInternal.class); + } + + @Override public IActivityManager getActivityManager() { return ActivityManager.getService(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b27c0bd7034b..a8bdedf19cc7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -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") diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 17c6b6fba861..db0c3aece023 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -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( diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt index a0e208f662e3..46487ea27959 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt @@ -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*/, diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 2f0d71a2a579..0dbf3fe0cea3 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -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; @@ -82,6 +83,8 @@ public final class AppHibernationServiceTest { @Mock private IPackageManager mIPackageManager; @Mock + private PackageManagerInternal mPackageManagerInternal; + @Mock private IActivityManager mIActivityManager; @Mock private UserManager mUserManager; @@ -255,6 +258,11 @@ public final class AppHibernationServiceTest { } @Override + public PackageManagerInternal getPackageManagerInternal() { + return mPackageManagerInternal; + } + + @Override public UserManager getUserManager() { return mUserManager; } @@ -267,12 +275,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); } } } |