Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 487e16c4 authored by Kevin Han's avatar Kevin Han
Browse files

Move disk reads to background

Move disk reading to background to improve performance on user
unlocking.

Bug: 182098676
Test: atest AppHibernationServiceTest
Change-Id: I087bd9d29f4f90c818a03648ee75e3cbfb2ded41
parent f0df72a9
Loading
Loading
Loading
Loading
+52 −24
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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 */);
@@ -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();
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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));
@@ -720,6 +741,8 @@ public final class AppHibernationService extends SystemService {

        UserManager getUserManager();

        Executor getBackgroundExecutor();

        HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();

        HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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()) {
+10 −3
Original line number Diff line number Diff line
@@ -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}
@@ -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;
    }
@@ -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
@@ -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);
@@ -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);