Loading services/core/java/com/android/server/apphibernation/AppHibernationService.java +24 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; Loading Loading @@ -418,19 +419,29 @@ public final class AppHibernationService extends SystemService { } if (diskStates != null) { Set<String> installedPackages = new ArraySet<>(); Map<String, PackageInfo> installedPackages = new ArrayMap<>(); for (int i = 0, size = packages.size(); i < size; i++) { installedPackages.add(packages.get(i).packageName); installedPackages.put(packages.get(i).packageName, packages.get(i)); } for (int i = 0, size = diskStates.size(); i < size; i++) { String packageName = diskStates.get(i).packageName; if (!installedPackages.contains(packageName)) { PackageInfo pkgInfo = installedPackages.get(packageName); UserLevelState currentState = diskStates.get(i); if (pkgInfo == null) { Slog.w(TAG, String.format( "No hibernation state associated with package %s user %d. Maybe" + "the package was uninstalled? ", packageName, userId)); continue; } userLevelStates.put(packageName, diskStates.get(i)); if (pkgInfo.applicationInfo != null && (pkgInfo.applicationInfo.flags &= ApplicationInfo.FLAG_STOPPED) == 0 && currentState.hibernated) { // App is not stopped but is hibernated. Disk state is stale, so unhibernate // the app. currentState.hibernated = false; currentState.lastUnhibernatedMs = System.currentTimeMillis(); } userLevelStates.put(packageName, currentState); } } mUserStates.put(userId, userLevelStates); Loading Loading @@ -487,6 +498,15 @@ public final class AppHibernationService extends SystemService { // Ensure user hasn't stopped in the time to execute. if (mUserManager.isUserUnlockingOrUnlocked(userId)) { initializeUserHibernationStates(userId, storedStates); // Globally unhibernate a package if the unlocked user does not have it // hibernated. for (UserLevelState userState : mUserStates.get(userId).values()) { String pkgName = userState.packageName; GlobalLevelState globalState = mGlobalHibernationStates.get(pkgName); if (globalState.hibernated && !userState.hibernated) { setHibernatingGlobally(pkgName, false); } } } } }); Loading services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +76 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.apphibernation; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalAnswers.returnsArgAt; import static org.mockito.ArgumentMatchers.any; Loading @@ -35,6 +36,7 @@ import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; Loading Loading @@ -89,7 +91,7 @@ public final class AppHibernationServiceTest { @Mock private UserManager mUserManager; @Mock private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore; private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore; @Captor private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor; Loading Loading @@ -207,6 +209,61 @@ public final class AppHibernationServiceTest { assertTrue(hibernatingPackages.contains(PACKAGE_NAME_2)); } @Test public void testUserLevelStatesInitializedFromDisk() throws RemoteException { // GIVEN states stored on disk that match with package manager's force-stop states List<UserLevelState> diskStates = new ArrayList<>(); diskStates.add(makeUserLevelState(PACKAGE_NAME_1, false /* hibernated */)); diskStates.add(makeUserLevelState(PACKAGE_NAME_2, true /* hibernated */)); doReturn(diskStates).when(mUserLevelDiskStore).readHibernationStates(); List<PackageInfo> packageInfos = new ArrayList<>(); packageInfos.add(makePackageInfo(PACKAGE_NAME_1)); PackageInfo stoppedPkg = makePackageInfo(PACKAGE_NAME_2); stoppedPkg.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED; packageInfos.add(stoppedPkg); // WHEN a user is unlocked and the states are initialized UserInfo user2 = addUser(USER_ID_2, packageInfos); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); // THEN the hibernation states are initialized to the disk states assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2)); assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_2)); } @Test public void testNonForceStoppedAppsNotHibernatedOnUnlock() throws RemoteException { // GIVEN a package that is hibernated on disk but not force-stopped List<UserLevelState> diskStates = new ArrayList<>(); diskStates.add(makeUserLevelState(PACKAGE_NAME_1, true /* hibernated */)); doReturn(diskStates).when(mUserLevelDiskStore).readHibernationStates(); // WHEN a user is unlocked and the states are initialized UserInfo user2 = addUser(USER_ID_2, new String[]{PACKAGE_NAME_1}); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); // THEN the app is not hibernating for the user assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2)); } @Test public void testUnhibernatedPackageForUserUnhibernatesPackageGloballyOnUnlock() throws RemoteException { // GIVEN a package that is globally hibernating mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true); // WHEN a user is unlocked and the package is not hibernating for the user UserInfo user2 = addUser(USER_ID_2); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); // THEN the package is no longer globally hibernating assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1)); } /** * Add a mock user with one package. */ Loading @@ -218,12 +275,19 @@ public final class AppHibernationServiceTest { * Add a mock user with the packages specified. */ private UserInfo addUser(int userId, String[] packageNames) throws RemoteException { UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */); mUserInfos.add(userInfo); List<PackageInfo> userPackages = new ArrayList<>(); for (String pkgName : packageNames) { userPackages.add(makePackageInfo(pkgName)); } return addUser(userId, userPackages); } /** * Add a mock user with the package infos specified. */ private UserInfo addUser(int userId, List<PackageInfo> userPackages) throws RemoteException { UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */); mUserInfos.add(userInfo); doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager) .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId)); return userInfo; Loading @@ -232,9 +296,17 @@ public final class AppHibernationServiceTest { private static PackageInfo makePackageInfo(String packageName) { PackageInfo pkg = new PackageInfo(); pkg.packageName = packageName; pkg.applicationInfo = new ApplicationInfo(); return pkg; } private static UserLevelState makeUserLevelState(String packageName, boolean hibernated) { UserLevelState state = new UserLevelState(); state.packageName = packageName; state.hibernated = hibernated; return state; } private class MockInjector implements AppHibernationService.Injector { private final Context mContext; Loading Loading @@ -280,7 +352,7 @@ public final class AppHibernationServiceTest { @Override public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) { return mock(HibernationStateDiskStore.class); return mUserLevelDiskStore; } @Override Loading Loading
services/core/java/com/android/server/apphibernation/AppHibernationService.java +24 −4 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; Loading Loading @@ -418,19 +419,29 @@ public final class AppHibernationService extends SystemService { } if (diskStates != null) { Set<String> installedPackages = new ArraySet<>(); Map<String, PackageInfo> installedPackages = new ArrayMap<>(); for (int i = 0, size = packages.size(); i < size; i++) { installedPackages.add(packages.get(i).packageName); installedPackages.put(packages.get(i).packageName, packages.get(i)); } for (int i = 0, size = diskStates.size(); i < size; i++) { String packageName = diskStates.get(i).packageName; if (!installedPackages.contains(packageName)) { PackageInfo pkgInfo = installedPackages.get(packageName); UserLevelState currentState = diskStates.get(i); if (pkgInfo == null) { Slog.w(TAG, String.format( "No hibernation state associated with package %s user %d. Maybe" + "the package was uninstalled? ", packageName, userId)); continue; } userLevelStates.put(packageName, diskStates.get(i)); if (pkgInfo.applicationInfo != null && (pkgInfo.applicationInfo.flags &= ApplicationInfo.FLAG_STOPPED) == 0 && currentState.hibernated) { // App is not stopped but is hibernated. Disk state is stale, so unhibernate // the app. currentState.hibernated = false; currentState.lastUnhibernatedMs = System.currentTimeMillis(); } userLevelStates.put(packageName, currentState); } } mUserStates.put(userId, userLevelStates); Loading Loading @@ -487,6 +498,15 @@ public final class AppHibernationService extends SystemService { // Ensure user hasn't stopped in the time to execute. if (mUserManager.isUserUnlockingOrUnlocked(userId)) { initializeUserHibernationStates(userId, storedStates); // Globally unhibernate a package if the unlocked user does not have it // hibernated. for (UserLevelState userState : mUserStates.get(userId).values()) { String pkgName = userState.packageName; GlobalLevelState globalState = mGlobalHibernationStates.get(pkgName); if (globalState.hibernated && !userState.hibernated) { setHibernatingGlobally(pkgName, false); } } } } }); Loading
services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +76 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.apphibernation; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalAnswers.returnsArgAt; import static org.mockito.ArgumentMatchers.any; Loading @@ -35,6 +36,7 @@ import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManagerInternal; Loading Loading @@ -89,7 +91,7 @@ public final class AppHibernationServiceTest { @Mock private UserManager mUserManager; @Mock private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore; private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore; @Captor private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor; Loading Loading @@ -207,6 +209,61 @@ public final class AppHibernationServiceTest { assertTrue(hibernatingPackages.contains(PACKAGE_NAME_2)); } @Test public void testUserLevelStatesInitializedFromDisk() throws RemoteException { // GIVEN states stored on disk that match with package manager's force-stop states List<UserLevelState> diskStates = new ArrayList<>(); diskStates.add(makeUserLevelState(PACKAGE_NAME_1, false /* hibernated */)); diskStates.add(makeUserLevelState(PACKAGE_NAME_2, true /* hibernated */)); doReturn(diskStates).when(mUserLevelDiskStore).readHibernationStates(); List<PackageInfo> packageInfos = new ArrayList<>(); packageInfos.add(makePackageInfo(PACKAGE_NAME_1)); PackageInfo stoppedPkg = makePackageInfo(PACKAGE_NAME_2); stoppedPkg.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED; packageInfos.add(stoppedPkg); // WHEN a user is unlocked and the states are initialized UserInfo user2 = addUser(USER_ID_2, packageInfos); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); // THEN the hibernation states are initialized to the disk states assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2)); assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_2)); } @Test public void testNonForceStoppedAppsNotHibernatedOnUnlock() throws RemoteException { // GIVEN a package that is hibernated on disk but not force-stopped List<UserLevelState> diskStates = new ArrayList<>(); diskStates.add(makeUserLevelState(PACKAGE_NAME_1, true /* hibernated */)); doReturn(diskStates).when(mUserLevelDiskStore).readHibernationStates(); // WHEN a user is unlocked and the states are initialized UserInfo user2 = addUser(USER_ID_2, new String[]{PACKAGE_NAME_1}); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); // THEN the app is not hibernating for the user assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2)); } @Test public void testUnhibernatedPackageForUserUnhibernatesPackageGloballyOnUnlock() throws RemoteException { // GIVEN a package that is globally hibernating mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true); // WHEN a user is unlocked and the package is not hibernating for the user UserInfo user2 = addUser(USER_ID_2); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); // THEN the package is no longer globally hibernating assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1)); } /** * Add a mock user with one package. */ Loading @@ -218,12 +275,19 @@ public final class AppHibernationServiceTest { * Add a mock user with the packages specified. */ private UserInfo addUser(int userId, String[] packageNames) throws RemoteException { UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */); mUserInfos.add(userInfo); List<PackageInfo> userPackages = new ArrayList<>(); for (String pkgName : packageNames) { userPackages.add(makePackageInfo(pkgName)); } return addUser(userId, userPackages); } /** * Add a mock user with the package infos specified. */ private UserInfo addUser(int userId, List<PackageInfo> userPackages) throws RemoteException { UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */); mUserInfos.add(userInfo); doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager) .getInstalledPackages(intThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId)); return userInfo; Loading @@ -232,9 +296,17 @@ public final class AppHibernationServiceTest { private static PackageInfo makePackageInfo(String packageName) { PackageInfo pkg = new PackageInfo(); pkg.packageName = packageName; pkg.applicationInfo = new ApplicationInfo(); return pkg; } private static UserLevelState makeUserLevelState(String packageName, boolean hibernated) { UserLevelState state = new UserLevelState(); state.packageName = packageName; state.hibernated = hibernated; return state; } private class MockInjector implements AppHibernationService.Injector { private final Context mContext; Loading Loading @@ -280,7 +352,7 @@ public final class AppHibernationServiceTest { @Override public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) { return mock(HibernationStateDiskStore.class); return mUserLevelDiskStore; } @Override Loading