Merge "Unhibernate apps on boot that are not force-stopped" into sc-dev
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index aa51800..881a047 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -35,6 +35,7 @@
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;
@@ -418,19 +419,29 @@
}
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);
@@ -487,6 +498,15 @@
// 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);
+ }
+ }
}
}
});
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 f280aea..6d76ad8 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -19,6 +19,7 @@
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;
@@ -35,6 +36,7 @@
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;
@@ -89,7 +91,7 @@
@Mock
private UserManager mUserManager;
@Mock
- private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
+ private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore;
@Captor
private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
@@ -207,6 +209,61 @@
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.
*/
@@ -218,12 +275,19 @@
* 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;
@@ -232,9 +296,17 @@
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;
@@ -280,7 +352,7 @@
@Override
public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
- return mock(HibernationStateDiskStore.class);
+ return mUserLevelDiskStore;
}
@Override