/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.apphibernation;

import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import static android.content.pm.PackageManager.MATCH_ANY_USER;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.returnsArgAt;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.app.IActivityManager;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
import android.apphibernation.HibernationStats;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;

import com.android.server.LocalServices;
import com.android.server.SystemService;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;

/**
 * Tests for {@link com.android.server.apphibernation.AppHibernationService}
 */
@SmallTest
@Presubmit
public final class AppHibernationServiceTest {
    private static final String PACKAGE_SCHEME = "package";
    private static final String PACKAGE_NAME_1 = "package1";
    private static final String PACKAGE_NAME_2 = "package2";
    private static final String PACKAGE_NAME_3 = "package3";
    private static final int USER_ID_1 = 1;
    private static final int USER_ID_2 = 2;

    private final List<UserInfo> mUserInfos = new ArrayList<>();

    private AppHibernationService mAppHibernationService;
    private BroadcastReceiver mBroadcastReceiver;
    private UsageEventListener mUsageEventListener;

    @Mock
    private Context mContext;
    @Mock
    private IPackageManager mIPackageManager;
    @Mock
    private PackageManagerInternal mPackageManagerInternal;
    @Mock
    private IActivityManager mIActivityManager;
    @Mock
    private UserManager mUserManager;
    @Mock
    private StorageStatsManager mStorageStatsManager;
    @Mock
    private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore;
    @Mock
    private UsageStatsManagerInternal mUsageStatsManagerInternal;
    @Mock
    private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
    @Captor
    private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
    @Captor
    private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;

    @Before
    public void setUp() throws RemoteException, PackageManager.NameNotFoundException, IOException {
        // Share class loader to allow access to package-private classes
        System.setProperty("dexmaker.share_classloader", "true");
        MockitoAnnotations.initMocks(this);
        doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());

        LocalServices.removeServiceForTest(AppHibernationManagerInternal.class);
        mAppHibernationService = new AppHibernationService(new MockInjector(mContext));

        verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
        mBroadcastReceiver = mReceiverCaptor.getValue();
        verify(mUsageStatsManagerInternal).registerListener(mUsageEventListenerCaptor.capture());
        mUsageEventListener = mUsageEventListenerCaptor.getValue();

        doReturn(mUserInfos).when(mUserManager).getUsers();
        doReturn(true).when(mPackageManagerInternal).canQueryPackage(anyInt(), any());
        doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
                anyInt(), anyBoolean(), anyBoolean(), any(), any());

        List<PackageInfo> packages = new ArrayList<>();
        packages.add(makePackageInfo(PACKAGE_NAME_1));
        packages.add(makePackageInfo(PACKAGE_NAME_2));
        packages.add(makePackageInfo(PACKAGE_NAME_3));
        doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
                longThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
        doReturn(mock(ApplicationInfo.class)).when(mIPackageManager).getApplicationInfo(
                any(), anyLong(), anyInt());
        StorageStats storageStats = new StorageStats();
        doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage(
                (UUID) any(), anyString(), any());
        mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);

        UserInfo userInfo = addUser(USER_ID_1);
        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1);
        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));

        mAppHibernationService.sIsServiceEnabled = true;
    }

    @Test
    public void testSetHibernatingForUser_packageIsHibernating() throws Exception {
        // WHEN we hibernate a package for a user
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);

        // THEN the package is marked hibernating for the user
        assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
        verify(mIActivityManager).forceStopPackage(PACKAGE_NAME_1, USER_ID_1);
        verify(mIPackageManager).deleteApplicationCacheFilesAsUser(
                eq(PACKAGE_NAME_1), eq(USER_ID_1), any());
    }

    @Test
    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 */));
        intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
        mBroadcastReceiver.onReceive(mContext, intent);

        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_1, true);

        // THEN the new package is hibernated
        assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_1));
    }

    @Test
    public void testSetHibernatingForUser_newUserUnlocked_packageIsHibernating()
            throws RemoteException {
        // WHEN a new user is added and a package from the user is hibernated
        UserInfo user2 = addUser(USER_ID_2);
        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
        assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2));
    }

    @Test
    public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() {
        // GIVEN a package is currently hibernated
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);

        // WHEN the package is removed but marked as replacing
        Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
                Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
        intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
        intent.putExtra(Intent.EXTRA_REPLACING, true);
        mBroadcastReceiver.onReceive(mContext, intent);

        // THEN the package is still hibernating
        assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
    }

    @Test
    public void testSetHibernatingGlobally_packageIsHibernatingGlobally() throws RemoteException {
        // WHEN we hibernate a package
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);

        // THEN the package is marked hibernating for the user
        assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
        verify(mPackageManagerInternal).deleteOatArtifactsOfPackage(PACKAGE_NAME_1);
    }

    @Test
    public void testGetHibernatingPackagesForUser_returnsCorrectPackages() throws RemoteException {
        // 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});
        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);
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);

        // THEN the hibernating packages returned matches
        List<String> hibernatingPackages =
                mAppHibernationService.getHibernatingPackagesForUser(USER_ID_2);
        assertEquals(2, hibernatingPackages.size());
        assertTrue(hibernatingPackages.contains(PACKAGE_NAME_1));
        assertTrue(hibernatingPackages.contains(PACKAGE_NAME_2));
    }

    @Test
    public void testGetHibernatingPackagesForUser_doesNotReturnPackagesThatArentVisible()
            throws RemoteException {
        // GIVEN an unlocked user with all packages installed but only some are visible to the
        // caller
        UserInfo userInfo =
                addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
        doReturn(false).when(mPackageManagerInternal).canQueryPackage(anyInt(), eq(PACKAGE_NAME_2));
        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);
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);

        // THEN the hibernating packages returned does not contain the package that was not visible
        List<String> hibernatingPackages =
                mAppHibernationService.getHibernatingPackagesForUser(USER_ID_2);
        assertEquals(1, hibernatingPackages.size());
        assertTrue(hibernatingPackages.contains(PACKAGE_NAME_1));
        assertFalse(hibernatingPackages.contains(PACKAGE_NAME_2));
    }

    @Test
    public void testUserLevelStatesInitializedFromDisk() throws RemoteException {
        // GIVEN states stored on disk that match with package manager's force-stop states
        List<UserLevelState> diskStates = new ArrayList<>();
        diskStates.add(makeUserLevelState(PACKAGE_NAME_1, false /* hibernated */));
        diskStates.add(makeUserLevelState(PACKAGE_NAME_2, true /* hibernated */));
        doReturn(diskStates).when(mUserLevelDiskStore).readHibernationStates();

        List<PackageInfo> packageInfos = new ArrayList<>();
        packageInfos.add(makePackageInfo(PACKAGE_NAME_1));
        PackageInfo stoppedPkg = makePackageInfo(PACKAGE_NAME_2);
        stoppedPkg.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
        packageInfos.add(stoppedPkg);

        // WHEN a user is unlocked and the states are initialized
        UserInfo user2 = addUser(USER_ID_2, packageInfos);
        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));

        // THEN the hibernation states are initialized to the disk states
        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2));
        assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_2));
    }

    @Test
    public void testNonForceStoppedAppsNotHibernatedOnUnlock() throws RemoteException {
        // GIVEN a package that is hibernated on disk but not force-stopped
        List<UserLevelState> diskStates = new ArrayList<>();
        diskStates.add(makeUserLevelState(PACKAGE_NAME_1, true /* hibernated */));
        doReturn(diskStates).when(mUserLevelDiskStore).readHibernationStates();

        // WHEN a user is unlocked and the states are initialized
        UserInfo user2 = addUser(USER_ID_2, new String[]{PACKAGE_NAME_1});
        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));

        // THEN the app is not hibernating for the user
        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2));
    }

    @Test
    public void testUnhibernatedPackageForUserUnhibernatesPackageGloballyOnUnlock()
            throws RemoteException {
        // GIVEN a package that is globally hibernating
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);

        // WHEN a user is unlocked and the package is not hibernating for the user
        UserInfo user2 = addUser(USER_ID_2);
        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2));

        // THEN the package is no longer globally hibernating
        assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
    }

    @Test
    public void testUnhibernatingPackageForUserSendsBootCompleteBroadcast()
            throws RemoteException {
        // GIVEN a hibernating package for a user
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);

        // WHEN we unhibernate the package
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, false);

        // THEN we send the boot complete broadcasts
        ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
        verify(mIActivityManager, times(2)).broadcastIntentWithFeature(any(), any(),
                intentArgumentCaptor.capture(), any(), any(), anyInt(), any(), any(), any(), any(),
                any(), anyInt(), any(), anyBoolean(), anyBoolean(), eq(USER_ID_1));
        List<Intent> capturedIntents = intentArgumentCaptor.getAllValues();
        assertEquals(capturedIntents.get(0).getAction(), Intent.ACTION_LOCKED_BOOT_COMPLETED);
        assertEquals(capturedIntents.get(1).getAction(), Intent.ACTION_BOOT_COMPLETED);
    }

    @Test
    public void testHibernatingPackageIsUnhibernatedForUserWhenUserInteracted() {
        // GIVEN a package that is currently hibernated for a user
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);

        // WHEN the package is interacted with by user
        generateUsageEvent(USER_INTERACTION);

        // THEN the package is not hibernating anymore
        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
    }

    @Test
    public void testHibernatingPackageIsUnhibernatedForUserWhenActivityResumed() {
        // GIVEN a package that is currently hibernated for a user
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);

        // WHEN the package has activity resumed
        generateUsageEvent(ACTIVITY_RESUMED);

        // THEN the package is not hibernating anymore
        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
    }

    @Test
    public void testHibernatingPackageIsUnhibernatedForUserWhenComponentUsed() {
        // GIVEN a package that is currently hibernated for a user
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);

        // WHEN a package component is used
        generateUsageEvent(APP_COMPONENT_USED);

        // THEN the package is not hibernating anymore
        assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
    }

    @Test
    public void testHibernatingPackageIsUnhibernatedGloballyWhenUserInteracted() {
        // GIVEN a package that is currently hibernated globally
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);

        // WHEN the user interacts with the package
        generateUsageEvent(USER_INTERACTION);

        // THEN the package is not hibernating globally anymore
        assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
    }

    @Test
    public void testHibernatingPackageIsUnhibernatedGloballyWhenActivityResumed() {
        // GIVEN a package that is currently hibernated globally
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);

        // WHEN activity in package resumed
        generateUsageEvent(ACTIVITY_RESUMED);

        // THEN the package is not hibernating globally anymore
        assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
    }

    @Test
    public void testHibernatingPackageIsUnhibernatedGloballyWhenComponentUsed() {
        // GIVEN a package that is currently hibernated globally
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);

        // WHEN a package component is used
        generateUsageEvent(APP_COMPONENT_USED);

        // THEN the package is not hibernating globally anymore
        assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
    }

    @Test
    public void testGetHibernationStatsForUser_getsStatsForPackage()
            throws PackageManager.NameNotFoundException, IOException, RemoteException {
        // GIVEN a package is hibernating globally and for a user with some storage saved
        final long cacheSavings = 1000;
        StorageStats storageStats = new StorageStats();
        storageStats.cacheBytes = cacheSavings;
        doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage(
                (UUID) any(), eq(PACKAGE_NAME_1), any());
        final long oatDeletionSavings = 2000;
        doReturn(oatDeletionSavings).when(mPackageManagerInternal).deleteOatArtifactsOfPackage(
                PACKAGE_NAME_1);

        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);

        // WHEN we ask for the hibernation stats for the package
        Map<String, HibernationStats> statsMap =
                mAppHibernationService.getHibernationStatsForUser(
                        Set.of(PACKAGE_NAME_1), USER_ID_1);

        // THEN the stats exist for the package and add up to the OAT deletion and cache deletion
        // savings
        HibernationStats stats = statsMap.get(PACKAGE_NAME_1);
        assertNotNull(stats);
        assertEquals(cacheSavings + oatDeletionSavings, stats.getDiskBytesSaved());
    }

    @Test
    public void testGetHibernationStatsForUser_noExceptionThrownWhenPackageDoesntExist() {
        // WHEN we ask for the hibernation stats for a package that doesn't exist
        Map<String, HibernationStats> stats =
                mAppHibernationService.getHibernationStatsForUser(
                        Set.of(PACKAGE_NAME_1), USER_ID_1);

        // THEN no exception is thrown and empty stats are returned
        assertNotNull(stats);
    }

    @Test
    public void testGetHibernationStatsForUser_returnsAllIfNoPackagesSpecified()
            throws RemoteException {
        // GIVEN an unlocked user with all packages installed and they're all hibernating
        UserInfo userInfo =
                addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
        mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_2, true);
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);
        mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_3, true);
        mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_3, USER_ID_2, true);

        // WHEN we ask for the hibernation stats with no package specified
        Map<String, HibernationStats> stats =
                mAppHibernationService.getHibernationStatsForUser(
                        null /* packageNames */, USER_ID_2);

        // THEN all the package stats are returned
        assertTrue(stats.containsKey(PACKAGE_NAME_1));
        assertTrue(stats.containsKey(PACKAGE_NAME_2));
        assertTrue(stats.containsKey(PACKAGE_NAME_3));
    }

    /**
     * Mock a usage event occurring.
     *
     * @param usageEventId id of a usage event
     */
    private void generateUsageEvent(int usageEventId) {
        Event event = new Event(usageEventId, 0 /* timestamp */);
        event.mPackage = PACKAGE_NAME_1;
        mUsageEventListener.onUsageEvent(USER_ID_1, event);
    }

    /**
     * Add a mock user with one package.
     */
    private UserInfo addUser(int userId) throws RemoteException {
        return addUser(userId, new String[]{PACKAGE_NAME_1});
    }

    /**
     * Add a mock user with the packages specified.
     */
    private UserInfo addUser(int userId, String[] packageNames) throws RemoteException {
        List<PackageInfo> userPackages = new ArrayList<>();
        for (String pkgName : packageNames) {
            userPackages.add(makePackageInfo(pkgName));
        }
        return addUser(userId, userPackages);
    }

    /**
     * Add a mock user with the package infos specified.
     */
    private UserInfo addUser(int userId, List<PackageInfo> userPackages) throws RemoteException {
        UserInfo userInfo = new UserInfo(userId, "user_" + userId, 0 /* flags */);
        mUserInfos.add(userInfo);
        doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
                .getInstalledPackages(longThat(arg -> (arg & MATCH_ANY_USER) == 0), eq(userId));
        return userInfo;
    }

    private static PackageInfo makePackageInfo(String packageName) {
        PackageInfo pkg = new PackageInfo();
        pkg.packageName = packageName;
        pkg.applicationInfo = new ApplicationInfo();
        return pkg;
    }

    private static UserLevelState makeUserLevelState(String packageName, boolean hibernated) {
        UserLevelState state = new UserLevelState();
        state.packageName = packageName;
        state.hibernated = hibernated;
        return state;
    }

    private class MockInjector implements AppHibernationService.Injector {
        private final Context mContext;

        MockInjector(Context context) {
            mContext = context;
        }

        @Override
        public IActivityManager getActivityManager() {
            return mIActivityManager;
        }

        @Override
        public Context getContext() {
            return mContext;
        }

        @Override
        public IPackageManager getPackageManager() {
            return mIPackageManager;
        }

        @Override
        public PackageManagerInternal getPackageManagerInternal() {
            return mPackageManagerInternal;
        }

        @Override
        public UserManager getUserManager() {
            return mUserManager;
        }

        @Override
        public StorageStatsManager getStorageStatsManager() {
            return mStorageStatsManager;
        }

        @Override
        public UsageStatsManagerInternal getUsageStatsManagerInternal() {
            return mUsageStatsManagerInternal;
        }

        @Override
        public Executor getBackgroundExecutor() {
            // Just execute immediately in tests.
            return r -> r.run();
        }

        @Override
        public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
            return mock(HibernationStateDiskStore.class);
        }

        @Override
        public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
            return mUserLevelDiskStore;
        }

        @Override
        public boolean isOatArtifactDeletionEnabled() {
            return true;
        }
    }
}
