diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index c2957780c9d78..a9eb2c1108679 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 c6824d16cffb6..9733f27c3286f 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; @@ -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> mUserStates = new SparseArray<>(); private final SparseArray> 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(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b27c0bd7034bf..a8bdedf19cc7d 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 getOptimizablePackages() { ArraySet 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 17c6b6fba8612..db0c3aece0236 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 a0e208f662e33..46487ea279598 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 1b8ab21754587..ea48b5fd60e3d 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; @@ -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 getGlobalLevelDiskStore() { - return Mockito.mock(HibernationStateDiskStore.class); + return mock(HibernationStateDiskStore.class); } @Override public HibernationStateDiskStore getUserLevelDiskStore(int userId) { - return Mockito.mock(HibernationStateDiskStore.class); + return mock(HibernationStateDiskStore.class); } } }