Loading services/core/java/com/android/server/apphibernation/AppHibernationService.java +52 −24 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; Loading Loading @@ -101,6 +102,7 @@ public final class AppHibernationService extends SystemService { private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>(); private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore; private final Injector mInjector; private final Executor mBackgroundExecutor; @VisibleForTesting boolean mIsServiceEnabled; Loading @@ -126,6 +128,7 @@ public final class AppHibernationService extends SystemService { mIActivityManager = injector.getActivityManager(); mUserManager = injector.getUserManager(); mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore(); mBackgroundExecutor = injector.getBackgroundExecutor(); mInjector = injector; final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); Loading @@ -147,11 +150,13 @@ public final class AppHibernationService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { mBackgroundExecutor.execute(() -> { List<GlobalLevelState> states = mGlobalLevelHibernationDiskStore.readHibernationStates(); synchronized (mLock) { initializeGlobalHibernationStates(states); } }); } if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mIsServiceEnabled = isAppHibernationEnabled(); Loading @@ -170,16 +175,15 @@ public final class AppHibernationService extends SystemService { * @return true if package is hibernating for the user */ boolean isHibernatingForUser(String packageName, int userId) { if (!checkHibernationEnabled("isHibernatingForUser")) { String methodName = "isHibernatingForUser"; if (!checkHibernationEnabled(methodName)) { return false; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, "isHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user " + userId); userId = handleIncomingUser(userId, methodName); if (!checkUserStatesExist(userId, methodName)) { return false; } synchronized (mLock) { Loading Loading @@ -225,16 +229,15 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { if (!checkHibernationEnabled("setHibernatingForUser")) { String methodName = "setHibernatingForUser"; if (!checkHibernationEnabled(methodName)) { return; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, "setHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user " + userId); userId = handleIncomingUser(userId, methodName); if (!checkUserStatesExist(userId, methodName)) { return; } synchronized (mLock) { Loading Loading @@ -298,16 +301,15 @@ public final class AppHibernationService extends SystemService { */ @NonNull List<String> getHibernatingPackagesForUser(int userId) { ArrayList<String> hibernatingPackages = new ArrayList<>(); if (!checkHibernationEnabled("getHibernatingPackagesForUser")) { String methodName = "getHibernatingPackagesForUser"; if (!checkHibernationEnabled(methodName)) { return hibernatingPackages; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, "getHibernatingPackagesForUser"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.w(TAG, "Attempt to get hibernating packages for a stopped or nonexistent user " + userId); userId = handleIncomingUser(userId, methodName); if (!checkUserStatesExist(userId, methodName)) { return hibernatingPackages; } synchronized (mLock) { Loading Loading @@ -477,11 +479,16 @@ public final class AppHibernationService extends SystemService { HibernationStateDiskStore<UserLevelState> diskStore = mInjector.getUserLevelDiskStore(userId); mUserDiskStores.put(userId, diskStore); mBackgroundExecutor.execute(() -> { List<UserLevelState> storedStates = diskStore.readHibernationStates(); synchronized (mLock) { // Ensure user hasn't stopped in the time to execute. if (mUserManager.isUserUnlockingOrUnlocked(userId)) { initializeUserHibernationStates(userId, storedStates); } } }); } @Override public void onUserStopping(@NonNull TargetUser user) { Loading Loading @@ -550,6 +557,20 @@ public final class AppHibernationService extends SystemService { } } private boolean checkUserStatesExist(int userId, String methodName) { if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.e(TAG, String.format( "Attempt to call %s on stopped or nonexistent user %d", methodName, userId)); return false; } if (!mUserStates.contains(userId)) { Slog.w(TAG, String.format( "Attempt to call %s before states have been read from disk", methodName)); return false; } return true; } private boolean checkHibernationEnabled(String methodName) { if (!mIsServiceEnabled) { Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName)); Loading Loading @@ -720,6 +741,8 @@ public final class AppHibernationService extends SystemService { UserManager getUserManager(); Executor getBackgroundExecutor(); HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore(); HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId); Loading Loading @@ -757,6 +780,11 @@ public final class AppHibernationService extends SystemService { return mContext.getSystemService(UserManager.class); } @Override public Executor getBackgroundExecutor() { return mScheduledExecutorService; } @Override public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() { File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME); Loading services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java +1 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ class HibernationStateDiskStore<T> { * @return the parsed list of hibernation states, null if file does not exist */ @Nullable @WorkerThread List<T> readHibernationStates() { synchronized (this) { if (!mHibernationFile.exists()) { Loading services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +10 −3 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; /** * Tests for {@link com.android.server.apphibernation.AppHibernationService} Loading Loading @@ -116,8 +117,8 @@ public final class AppHibernationServiceTest { 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); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); mAppHibernationService.mIsServiceEnabled = true; } Loading Loading @@ -150,8 +151,8 @@ public final class AppHibernationServiceTest { throws RemoteException { // WHEN a new user is added and a package from the user is hibernated UserInfo user2 = addUser(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); // THEN the new user's package is hibernated Loading Loading @@ -188,8 +189,8 @@ public final class AppHibernationServiceTest { // GIVEN an unlocked user with all packages installed UserInfo userInfo = addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3}); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); // WHEN packages are hibernated for the user mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); Loading Loading @@ -258,6 +259,12 @@ public final class AppHibernationServiceTest { return mUserManager; } @Override public Executor getBackgroundExecutor() { // Just execute immediately in tests. return r -> r.run(); } @Override public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() { return Mockito.mock(HibernationStateDiskStore.class); Loading Loading
services/core/java/com/android/server/apphibernation/AppHibernationService.java +52 −24 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; Loading Loading @@ -101,6 +102,7 @@ public final class AppHibernationService extends SystemService { private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>(); private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore; private final Injector mInjector; private final Executor mBackgroundExecutor; @VisibleForTesting boolean mIsServiceEnabled; Loading @@ -126,6 +128,7 @@ public final class AppHibernationService extends SystemService { mIActivityManager = injector.getActivityManager(); mUserManager = injector.getUserManager(); mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore(); mBackgroundExecutor = injector.getBackgroundExecutor(); mInjector = injector; final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); Loading @@ -147,11 +150,13 @@ public final class AppHibernationService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { mBackgroundExecutor.execute(() -> { List<GlobalLevelState> states = mGlobalLevelHibernationDiskStore.readHibernationStates(); synchronized (mLock) { initializeGlobalHibernationStates(states); } }); } if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mIsServiceEnabled = isAppHibernationEnabled(); Loading @@ -170,16 +175,15 @@ public final class AppHibernationService extends SystemService { * @return true if package is hibernating for the user */ boolean isHibernatingForUser(String packageName, int userId) { if (!checkHibernationEnabled("isHibernatingForUser")) { String methodName = "isHibernatingForUser"; if (!checkHibernationEnabled(methodName)) { return false; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, "isHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user " + userId); userId = handleIncomingUser(userId, methodName); if (!checkUserStatesExist(userId, methodName)) { return false; } synchronized (mLock) { Loading Loading @@ -225,16 +229,15 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { if (!checkHibernationEnabled("setHibernatingForUser")) { String methodName = "setHibernatingForUser"; if (!checkHibernationEnabled(methodName)) { return; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, "setHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user " + userId); userId = handleIncomingUser(userId, methodName); if (!checkUserStatesExist(userId, methodName)) { return; } synchronized (mLock) { Loading Loading @@ -298,16 +301,15 @@ public final class AppHibernationService extends SystemService { */ @NonNull List<String> getHibernatingPackagesForUser(int userId) { ArrayList<String> hibernatingPackages = new ArrayList<>(); if (!checkHibernationEnabled("getHibernatingPackagesForUser")) { String methodName = "getHibernatingPackagesForUser"; if (!checkHibernationEnabled(methodName)) { return hibernatingPackages; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, "getHibernatingPackagesForUser"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.w(TAG, "Attempt to get hibernating packages for a stopped or nonexistent user " + userId); userId = handleIncomingUser(userId, methodName); if (!checkUserStatesExist(userId, methodName)) { return hibernatingPackages; } synchronized (mLock) { Loading Loading @@ -477,11 +479,16 @@ public final class AppHibernationService extends SystemService { HibernationStateDiskStore<UserLevelState> diskStore = mInjector.getUserLevelDiskStore(userId); mUserDiskStores.put(userId, diskStore); mBackgroundExecutor.execute(() -> { List<UserLevelState> storedStates = diskStore.readHibernationStates(); synchronized (mLock) { // Ensure user hasn't stopped in the time to execute. if (mUserManager.isUserUnlockingOrUnlocked(userId)) { initializeUserHibernationStates(userId, storedStates); } } }); } @Override public void onUserStopping(@NonNull TargetUser user) { Loading Loading @@ -550,6 +557,20 @@ public final class AppHibernationService extends SystemService { } } private boolean checkUserStatesExist(int userId, String methodName) { if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.e(TAG, String.format( "Attempt to call %s on stopped or nonexistent user %d", methodName, userId)); return false; } if (!mUserStates.contains(userId)) { Slog.w(TAG, String.format( "Attempt to call %s before states have been read from disk", methodName)); return false; } return true; } private boolean checkHibernationEnabled(String methodName) { if (!mIsServiceEnabled) { Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName)); Loading Loading @@ -720,6 +741,8 @@ public final class AppHibernationService extends SystemService { UserManager getUserManager(); Executor getBackgroundExecutor(); HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore(); HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId); Loading Loading @@ -757,6 +780,11 @@ public final class AppHibernationService extends SystemService { return mContext.getSystemService(UserManager.class); } @Override public Executor getBackgroundExecutor() { return mScheduledExecutorService; } @Override public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() { File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME); Loading
services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java +1 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ class HibernationStateDiskStore<T> { * @return the parsed list of hibernation states, null if file does not exist */ @Nullable @WorkerThread List<T> readHibernationStates() { synchronized (this) { if (!mHibernationFile.exists()) { Loading
services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +10 −3 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; /** * Tests for {@link com.android.server.apphibernation.AppHibernationService} Loading Loading @@ -116,8 +117,8 @@ public final class AppHibernationServiceTest { 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); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); mAppHibernationService.mIsServiceEnabled = true; } Loading Loading @@ -150,8 +151,8 @@ public final class AppHibernationServiceTest { throws RemoteException { // WHEN a new user is added and a package from the user is hibernated UserInfo user2 = addUser(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); // THEN the new user's package is hibernated Loading Loading @@ -188,8 +189,8 @@ public final class AppHibernationServiceTest { // GIVEN an unlocked user with all packages installed UserInfo userInfo = addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3}); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); // WHEN packages are hibernated for the user mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); Loading Loading @@ -258,6 +259,12 @@ public final class AppHibernationServiceTest { return mUserManager; } @Override public Executor getBackgroundExecutor() { // Just execute immediately in tests. return r -> r.run(); } @Override public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() { return Mockito.mock(HibernationStateDiskStore.class); Loading