Merge "Unhibernate apps on boot that are not force-stopped" into sc-dev

This commit is contained in:
Kevin Han
2021-04-21 23:55:30 +00:00
committed by Android (Google) Code Review
2 changed files with 100 additions and 8 deletions

View File

@@ -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;
@@ -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);
@@ -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);
}
}
}
}
});

View File

@@ -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;
@@ -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;
@@ -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;
@@ -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.
*/
@@ -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;
@@ -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;
@@ -280,7 +352,7 @@ public final class AppHibernationServiceTest {
@Override
public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
return mock(HibernationStateDiskStore.class);
return mUserLevelDiskStore;
}
@Override