Add/remove user state based off user unlocking

Only store user hibernation state in memory when the user is not stopped
and is unlocked since we do not need the information. When a user
unlocks, we get their installed packages and initialize their
hibernation states in memory. When the user is stopped, we clear it.

Bug: 175829330
Test: atest AppHibernationServiceTest
Change-Id: Idb2dba02d53df72daedae0078405634a5db51e8d
This commit is contained in:
Kevin Han
2021-01-28 16:38:16 -08:00
parent 6857f6ed91
commit 89575897ca
2 changed files with 40 additions and 65 deletions

View File

@@ -18,8 +18,6 @@ package com.android.server.apphibernation;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -36,7 +34,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -48,6 +45,7 @@ import android.os.UserManager;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -107,11 +105,6 @@ public final class AppHibernationService extends SystemService {
final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_USER_ADDED);
intentFilter.addAction(ACTION_USER_REMOVED);
userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addAction(ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
@@ -123,19 +116,6 @@ public final class AppHibernationService extends SystemService {
publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_BOOT_COMPLETED) {
synchronized (mLock) {
final List<UserInfo> users = mUserManager.getUsers();
// TODO: Pull from persistent disk storage. For now, just make from scratch.
for (UserInfo user : users) {
addUserPackageStatesL(user.id);
}
}
}
}
/**
* Whether a package is hibernating for a given user.
*
@@ -145,11 +125,13 @@ public final class AppHibernationService extends SystemService {
*/
boolean isHibernatingForUser(String packageName, int userId) {
userId = handleIncomingUser(userId, "isHibernating");
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user "
+ userId);
return false;
}
synchronized (mLock) {
final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
if (packageStates == null) {
throw new IllegalArgumentException("No user associated with user id " + userId);
}
final UserPackageState pkgState = packageStates.get(packageName);
if (pkgState == null) {
throw new IllegalArgumentException(
@@ -181,10 +163,12 @@ public final class AppHibernationService extends SystemService {
*/
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
userId = handleIncomingUser(userId, "setHibernating");
if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
+ userId);
return;
}
synchronized (mLock) {
if (!mUserStates.contains(userId)) {
throw new IllegalArgumentException("No user associated with user id " + userId);
}
Map<String, UserPackageState> packageStates = mUserStates.get(userId);
UserPackageState pkgState = packageStates.get(packageName);
if (pkgState == null) {
@@ -310,15 +294,19 @@ public final class AppHibernationService extends SystemService {
mUserStates.put(userId, packages);
}
private void onUserAdded(int userId) {
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
// TODO: Pull from persistent disk storage. For now, just make from scratch.
synchronized (mLock) {
addUserPackageStatesL(userId);
addUserPackageStatesL(user.getUserIdentifier());
}
}
private void onUserRemoved(int userId) {
@Override
public void onUserStopping(@NonNull TargetUser user) {
synchronized (mLock) {
mUserStates.remove(userId);
// TODO: Flush to disk when persistence is implemented
mUserStates.remove(user.getUserIdentifier());
}
}
@@ -395,7 +383,7 @@ public final class AppHibernationService extends SystemService {
}
}
// Broadcast receiver for user and package add/removal events
// Broadcast receiver for package add/removal events
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -405,12 +393,6 @@ public final class AppHibernationService extends SystemService {
}
final String action = intent.getAction();
if (ACTION_USER_ADDED.equals(action)) {
onUserAdded(userId);
}
if (ACTION_USER_REMOVED.equals(action)) {
onUserRemoved(userId);
}
if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
final String packageName = intent.getData().getSchemeSpecificPart();
if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {

View File

@@ -25,7 +25,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
@@ -87,7 +86,7 @@ public final class AppHibernationServiceTest {
mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
mIActivityManager, mUserManager);
verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
mBroadcastReceiver = mReceiverCaptor.getValue();
doReturn(mUserInfos).when(mUserManager).getUsers();
@@ -95,12 +94,13 @@ public final class AppHibernationServiceTest {
doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), any(), any());
addUser(USER_ID_1);
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
UserInfo userInfo = addUser(USER_ID_1);
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
}
@Test
public void testSetHibernatingForUser_packageIsHibernating() throws RemoteException {
public void testSetHibernatingForUser_packageIsHibernating() {
// WHEN we hibernate a package for a user
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
@@ -109,8 +109,7 @@ public final class AppHibernationServiceTest {
}
@Test
public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating()
throws RemoteException {
public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() {
// WHEN a new package is added and it is hibernated
Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
@@ -124,17 +123,12 @@ public final class AppHibernationServiceTest {
}
@Test
public void testSetHibernatingForUser_newUserAdded_packageIsHibernating()
public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating()
throws RemoteException {
// WHEN a new user is added and a package from the user is hibernated
List<PackageInfo> userPackages = new ArrayList<>();
userPackages.add(makePackageInfo(PACKAGE_NAME_1));
doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
.getInstalledPackages(anyInt(), eq(USER_ID_2));
Intent intent = new Intent(Intent.ACTION_USER_ADDED);
intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
mBroadcastReceiver.onReceive(mContext, intent);
UserInfo user2 = addUser(USER_ID_2);
mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));
doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
// THEN the new user's package is hibernated
@@ -142,8 +136,7 @@ public final class AppHibernationServiceTest {
}
@Test
public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating()
throws RemoteException {
public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() {
// GIVEN a package is currently hibernated
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
@@ -159,7 +152,7 @@ public final class AppHibernationServiceTest {
}
@Test
public void testSetHibernatingGlobally_packageIsHibernatingGlobally() throws RemoteException {
public void testSetHibernatingGlobally_packageIsHibernatingGlobally() {
// WHEN we hibernate a package
mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
@@ -168,25 +161,25 @@ public final class AppHibernationServiceTest {
}
/**
* Add a mock user with one package. Must be called before
* {@link AppHibernationService#onBootPhase(int)} to work properly.
* Add a mock user with one package.
*/
private void addUser(int userId) throws RemoteException {
addUser(userId, new String[]{PACKAGE_NAME_1});
private UserInfo addUser(int userId) throws RemoteException {
return addUser(userId, new String[]{PACKAGE_NAME_1});
}
/**
* Add a mock user with the packages specified. Must be called before
* {@link AppHibernationService#onBootPhase(int)} to work properly
* Add a mock user with the packages specified.
*/
private void addUser(int userId, String[] packageNames) throws RemoteException {
mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */));
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));
}
doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
.getInstalledPackages(anyInt(), eq(userId));
return userInfo;
}
private static PackageInfo makePackageInfo(String packageName) {