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

Commit d7b0329a authored by Daniel Nishi's avatar Daniel Nishi
Browse files

Use the fast track for the AppCollector.

This replaces some of the threading shenanigans done to get this
working with the regular PackageManager call. By swapping this
out, we can get results faster, using less power, and with a
simpler implementation and testing strategy.

Bug: 35807386
Test: FrameworkServicesTest
Change-Id: Ib94fd7eba838b5e728f8f2615bcb4d9c82f21885
parent 910ac520
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.TextUtils;

import java.util.Objects;

/**
 * implementation of PackageStats associated with a
@@ -173,4 +176,31 @@ public class PackageStats implements Parcelable {
        dest.writeLong(externalMediaSize);
        dest.writeLong(externalObbSize);
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof PackageStats)) {
            return false;
        }

        final PackageStats otherStats = (PackageStats) obj;
        return ((TextUtils.equals(packageName, otherStats.packageName))
                && userHandle == otherStats.userHandle
                && codeSize == otherStats.codeSize
                && dataSize == otherStats.dataSize
                && cacheSize == otherStats.cacheSize
                && externalCodeSize == otherStats.externalCodeSize
                && externalDataSize == otherStats.externalDataSize
                && externalCacheSize == otherStats.externalCacheSize
                && externalMediaSize == otherStats.externalMediaSize
                && externalObbSize == otherStats.externalObbSize);
    }

    @Override
    public int hashCode() {
        return Objects.hash(packageName, userHandle, codeSize, dataSize,
                cacheSize, externalCodeSize, externalDataSize, externalCacheSize, externalMediaSize,
                externalObbSize);
    }

}
+18 −28
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server.storage;

import android.annotation.NonNull;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
@@ -64,7 +66,8 @@ public class AppCollector {
        mBackgroundHandler = new BackgroundHandler(BackgroundThread.get().getLooper(),
                volume,
                context.getPackageManager(),
                (UserManager) context.getSystemService(Context.USER_SERVICE));
                (UserManager) context.getSystemService(Context.USER_SERVICE),
                (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE));
    }

    /**
@@ -93,39 +96,20 @@ public class AppCollector {
        return value;
    }

    private class StatsObserver extends IPackageStatsObserver.Stub {
        private AtomicInteger mCount;
        private final ArrayList<PackageStats> mPackageStats;

        public StatsObserver(int count) {
            mCount = new AtomicInteger(count);
            mPackageStats = new ArrayList<>(count);
        }

        @Override
        public void onGetStatsCompleted(PackageStats packageStats, boolean succeeded)
                throws RemoteException {
            if (succeeded) {
                mPackageStats.add(packageStats);
            }

            if (mCount.decrementAndGet() == 0) {
                mStats.complete(mPackageStats);
            }
        }
    }

    private class BackgroundHandler extends Handler {
        static final int MSG_START_LOADING_SIZES = 0;
        private final VolumeInfo mVolume;
        private final PackageManager mPm;
        private final UserManager mUm;
        private final StorageStatsManager mStorageStatsManager;

        BackgroundHandler(Looper looper, @NonNull VolumeInfo volume, PackageManager pm, UserManager um) {
        BackgroundHandler(Looper looper, @NonNull VolumeInfo volume,
                PackageManager pm, UserManager um, StorageStatsManager storageStatsManager) {
            super(looper);
            mVolume = volume;
            mPm = pm;
            mUm = um;
            mStorageStatsManager = storageStatsManager;
        }

        @Override
@@ -149,14 +133,20 @@ public class AppCollector {
                        mStats.complete(new ArrayList<>());
                    }

                    // Kick off the async package size query for all apps.
                    final StatsObserver observer = new StatsObserver(count);
                    List<PackageStats> stats = new ArrayList<>();
                    for (UserInfo user : users) {
                        for (ApplicationInfo app : volumeApps) {
                            mPm.getPackageSizeInfoAsUser(app.packageName, user.id,
                                    observer);
                            PackageStats packageStats = new PackageStats(app.packageName, user.id);
                            StorageStats storageStats = mStorageStatsManager.queryStatsForPackage(
                                    app.volumeUuid, app.packageName, user.getUserHandle());
                            packageStats.cacheSize = storageStats.getCacheBytes();
                            packageStats.codeSize = storageStats.getCodeBytes();
                            packageStats.dataSize = storageStats.getDataBytes();
                            stats.add(packageStats);
                        }
                    }

                    mStats.complete(stats);
                }
            }
        }
+13 −70
Original line number Diff line number Diff line
@@ -16,12 +16,15 @@

package com.android.server.storage;

import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.pm.UserInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.VolumeInfo;
import android.test.AndroidTestCase;
@@ -53,6 +56,7 @@ public class AppCollectorTest extends AndroidTestCase {
    @Mock private Context mContext;
    @Mock private PackageManager mPm;
    @Mock private UserManager mUm;
    @Mock private StorageStatsManager mSsm;
    private List<ApplicationInfo> mApps;
    private List<UserInfo> mUsers;

@@ -63,6 +67,7 @@ public class AppCollectorTest extends AndroidTestCase {
        mApps = new ArrayList<>();
        when(mContext.getPackageManager()).thenReturn(mPm);
        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUm);
        when(mContext.getSystemService(Context.STORAGE_STATS_SERVICE)).thenReturn(mSsm);

        // Set up the app list.
        when(mPm.getInstalledApplications(anyInt())).thenReturn(mApps);
@@ -100,39 +105,9 @@ public class AppCollectorTest extends AndroidTestCase {
        AppCollector collector = new AppCollector(mContext, volume);
        PackageStats stats = new PackageStats("com.test.app");

        // Set up this to handle the asynchronous call to the PackageManager. This returns the
        // package info for the specified package.
        doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) {
                 try {
                     ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
                             .onGetStatsCompleted(stats, true);
                 } catch (Exception e) {
                     // We fail instead of just letting the exception fly because throwing
                     // out of the callback like this on the background thread causes the test
                     // runner to crash, rather than reporting the failure.
                     fail();
                 }
                 return null;
             }
        }).when(mPm).getPackageSizeInfoAsUser(eq("com.test.app"), eq(0), any());


        // Because getPackageStats is a blocking call, we block execution of the test until the
        // call finishes. In order to finish the call, we need the above answer to execute.
        List<PackageStats> myStats = new ArrayList<>();
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                myStats.addAll(collector.getPackageStats(TIMEOUT));
                latch.countDown();
            }
        }).start();
        latch.await();

        assertThat(myStats).containsExactly(stats);
        when(mSsm.queryStatsForPackage(eq("testuuid"),
                eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
        assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats);
    }

    @Test
@@ -151,43 +126,11 @@ public class AppCollectorTest extends AndroidTestCase {
        PackageStats otherStats = new PackageStats("com.test.app");
        otherStats.userHandle = 1;

        // Set up this to handle the asynchronous call to the PackageManager. This returns the
        // package info for our packages.
        doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) {
                 try {
                     ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
                             .onGetStatsCompleted(stats, true);

                     // Now callback for the other uid.
                     ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
                             .onGetStatsCompleted(otherStats, true);
                 } catch (Exception e) {
                     // We fail instead of just letting the exception fly because throwing
                     // out of the callback like this on the background thread causes the test
                     // runner to crash, rather than reporting the failure.
                     fail();
                 }
                 return null;
             }
        }).when(mPm).getPackageSizeInfoAsUser(eq("com.test.app"), eq(0), any());


        // Because getPackageStats is a blocking call, we block execution of the test until the
        // call finishes. In order to finish the call, we need the above answer to execute.
        List<PackageStats> myStats = new ArrayList<>();
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                myStats.addAll(collector.getPackageStats(TIMEOUT));
                latch.countDown();
            }
        }).start();
        latch.await();

        assertThat(myStats).containsAllOf(stats, otherStats);
        when(mSsm.queryStatsForPackage(eq("testuuid"),
                eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
        when(mSsm.queryStatsForPackage(eq("testuuid"),
                eq("com.test.app"), eq(UserHandle.of(1)))).thenReturn(new StorageStats());
        assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats, otherStats);
    }

    @Test(expected=NullPointerException.class)