From 89575897ca78603ca9ab64877e1f72e7a5cfb459 Mon Sep 17 00:00:00 2001 From: Kevin Han Date: Thu, 28 Jan 2021 16:38:16 -0800 Subject: [PATCH] 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 --- .../apphibernation/AppHibernationService.java | 58 +++++++------------ .../AppHibernationServiceTest.java | 47 +++++++-------- 2 files changed, 40 insertions(+), 65 deletions(-) diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index fded85cd91261..fc48e2df39e88 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -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 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 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 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)) { 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 45bca68295534..6777e1a5bc35e 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -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 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 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) {