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

Commit 89575897 authored by Kevin Han's avatar Kevin Han
Browse files

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
parent 6857f6ed
Loading
Loading
Loading
Loading
+20 −38
Original line number Diff line number Diff line
@@ -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");
        synchronized (mLock) {
            if (!mUserStates.contains(userId)) {
                throw new IllegalArgumentException("No user associated with user id " + userId);
        if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
            Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user "
                    + userId);
            return;
        }
        synchronized (mLock) {
            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)) {
+20 −27
Original line number Diff line number Diff line
@@ -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) {