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

Commit fd04f04b authored by Kevin Han's avatar Kevin Han Committed by Android (Google) Code Review
Browse files

Merge "Add cache savings to hibernation savings stats"

parents e3c43139 a55cc008
Loading
Loading
Loading
Loading
+30 −4
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import android.app.ActivityThread;
import android.app.IActivityManager;
import android.app.StatsManager;
import android.app.StatsManager.StatsPullAtomCallback;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -78,6 +80,7 @@ import com.android.server.SystemService;

import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -111,6 +114,7 @@ public final class AppHibernationService extends SystemService {
    private final PackageManagerInternal mPackageManagerInternal;
    private final IActivityManager mIActivityManager;
    private final UserManager mUserManager;
    private final StorageStatsManager mStorageStatsManager;

    @GuardedBy("mLock")
    private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
@@ -147,6 +151,7 @@ public final class AppHibernationService extends SystemService {
        mPackageManagerInternal = injector.getPackageManagerInternal();
        mIActivityManager = injector.getActivityManager();
        mUserManager = injector.getUserManager();
        mStorageStatsManager = injector.getStorageStatsManager();
        mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
        mBackgroundExecutor = injector.getBackgroundExecutor();
        mOatArtifactDeletionEnabled = injector.isOatArtifactDeletionEnabled();
@@ -297,7 +302,8 @@ public final class AppHibernationService extends SystemService {

            pkgState.hibernated = isHibernating;
            if (isHibernating) {
                mBackgroundExecutor.execute(() -> hibernatePackageForUser(packageName, realUserId));
                mBackgroundExecutor.execute(
                        () -> hibernatePackageForUser(packageName, realUserId, pkgState));
            } else {
                mBackgroundExecutor.execute(
                        () -> unhibernatePackageForUser(packageName, realUserId));
@@ -415,8 +421,9 @@ public final class AppHibernationService extends SystemService {
                                    + "the package was uninstalled? ", pkgName, userId));
                    continue;
                }
                HibernationStats stats = new HibernationStats(
                        mGlobalHibernationStates.get(pkgName).savedByte);
                long diskBytesSaved = mGlobalHibernationStates.get(pkgName).savedByte
                        + userPackageStates.get(pkgName).savedByte;
                HibernationStats stats = new HibernationStats(diskBytesSaved);
                statsMap.put(pkgName, stats);
            }
        }
@@ -427,16 +434,28 @@ public final class AppHibernationService extends SystemService {
     * Put an app into hibernation for a given user, allowing user-level optimizations to occur. Do
     * not hold {@link #mLock} while calling this to avoid deadlock scenarios.
     */
    private void hibernatePackageForUser(@NonNull String packageName, int userId) {
    private void hibernatePackageForUser(@NonNull String packageName, int userId,
            UserLevelState state) {
        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
        final long caller = Binder.clearCallingIdentity();
        try {
            ApplicationInfo info = mIPackageManager.getApplicationInfo(
                    packageName, PACKAGE_MATCH_FLAGS, userId);
            StorageStats stats = mStorageStatsManager.queryStatsForPackage(
                    info.storageUuid, packageName, new UserHandle(userId));
            mIActivityManager.forceStopPackage(packageName, userId);
            mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
                    null /* observer */);
            synchronized (mLock) {
                state.savedByte = stats.getCacheBytes();
            }
        } catch (RemoteException e) {
            throw new IllegalStateException(
                    "Failed to hibernate due to manager not being available", e);
        } catch (PackageManager.NameNotFoundException e) {
            Slog.e(TAG, "Package name not found when querying storage stats", e);
        } catch (IOException e) {
            Slog.e(TAG, "Storage device not found", e);
        } finally {
            Binder.restoreCallingIdentity(caller);
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -920,6 +939,8 @@ public final class AppHibernationService extends SystemService {

        UserManager getUserManager();

        StorageStatsManager getStorageStatsManager();

        Executor getBackgroundExecutor();

        UsageStatsManagerInternal getUsageStatsManagerInternal();
@@ -968,6 +989,11 @@ public final class AppHibernationService extends SystemService {
            return mContext.getSystemService(UserManager.class);
        }

        @Override
        public StorageStatsManager getStorageStatsManager() {
            return mContext.getSystemService(StorageStatsManager.class);
        }

        @Override
        public Executor getBackgroundExecutor() {
            return mScheduledExecutorService;
+4 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ final class UserLevelState {

    public String packageName;
    public boolean hibernated;
    // Saved bytes from user level hibernation.
    public long savedByte;
    @CurrentTimeMillisLong
    public long lastUnhibernatedMs;

@@ -36,6 +38,7 @@ final class UserLevelState {
    UserLevelState(UserLevelState state) {
        packageName = state.packageName;
        hibernated = state.hibernated;
        savedByte = state.savedByte;
        lastUnhibernatedMs = state.lastUnhibernatedMs;
    }

@@ -44,6 +47,7 @@ final class UserLevelState {
        return "UserLevelState{"
                + "packageName='" + packageName + '\''
                + ", hibernated=" + hibernated + '\''
                + ", savedByte=" + savedByte + '\''
                + ", lastUnhibernated=" + DATE_FORMAT.format(lastUnhibernatedMs)
                + '}';
    }
+38 −6
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ 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;
@@ -38,6 +40,8 @@ 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;
@@ -48,6 +52,7 @@ 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;
@@ -68,10 +73,12 @@ 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;

/**
@@ -104,6 +111,8 @@ public final class AppHibernationServiceTest {
    @Mock
    private UserManager mUserManager;
    @Mock
    private StorageStatsManager mStorageStatsManager;
    @Mock
    private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore;
    @Mock
    private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -115,7 +124,7 @@ public final class AppHibernationServiceTest {
    private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;

    @Before
    public void setUp() throws RemoteException {
    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);
@@ -140,6 +149,11 @@ public final class AppHibernationServiceTest {
        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);
@@ -381,18 +395,31 @@ public final class AppHibernationServiceTest {
    }

    @Test
    public void testGetHibernationStatsForUser_getsStatsForPackage() {
        // GIVEN a package is hibernating globally and for a user
    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> stats =
        Map<String, HibernationStats> statsMap =
                mAppHibernationService.getHibernationStatsForUser(
                        Set.of(PACKAGE_NAME_1), USER_ID_1);

        // THEN the stats exist for the package
        assertTrue(stats.containsKey(PACKAGE_NAME_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
@@ -518,6 +545,11 @@ public final class AppHibernationServiceTest {
            return mUserManager;
        }

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

        @Override
        public UsageStatsManagerInternal getUsageStatsManagerInternal() {
            return mUsageStatsManagerInternal;