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 Original line Diff line number Diff line
@@ -19,6 +19,9 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.UserHandle;
import android.text.TextUtils;

import java.util.Objects;


/**
/**
 * implementation of PackageStats associated with a
 * implementation of PackageStats associated with a
@@ -173,4 +176,31 @@ public class PackageStats implements Parcelable {
        dest.writeLong(externalMediaSize);
        dest.writeLong(externalMediaSize);
        dest.writeLong(externalObbSize);
        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 Original line Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.server.storage;
package com.android.server.storage;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.IPackageStatsObserver;
@@ -64,7 +66,8 @@ public class AppCollector {
        mBackgroundHandler = new BackgroundHandler(BackgroundThread.get().getLooper(),
        mBackgroundHandler = new BackgroundHandler(BackgroundThread.get().getLooper(),
                volume,
                volume,
                context.getPackageManager(),
                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;
        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 {
    private class BackgroundHandler extends Handler {
        static final int MSG_START_LOADING_SIZES = 0;
        static final int MSG_START_LOADING_SIZES = 0;
        private final VolumeInfo mVolume;
        private final VolumeInfo mVolume;
        private final PackageManager mPm;
        private final PackageManager mPm;
        private final UserManager mUm;
        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);
            super(looper);
            mVolume = volume;
            mVolume = volume;
            mPm = pm;
            mPm = pm;
            mUm = um;
            mUm = um;
            mStorageStatsManager = storageStatsManager;
        }
        }


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


                    // Kick off the async package size query for all apps.
                    List<PackageStats> stats = new ArrayList<>();
                    final StatsObserver observer = new StatsObserver(count);
                    for (UserInfo user : users) {
                    for (UserInfo user : users) {
                        for (ApplicationInfo app : volumeApps) {
                        for (ApplicationInfo app : volumeApps) {
                            mPm.getPackageSizeInfoAsUser(app.packageName, user.id,
                            PackageStats packageStats = new PackageStats(app.packageName, user.id);
                                    observer);
                            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 Original line Diff line number Diff line
@@ -16,12 +16,15 @@


package com.android.server.storage;
package com.android.server.storage;


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


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


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


        // Set up this to handle the asynchronous call to the PackageManager. This returns the
        when(mSsm.queryStatsForPackage(eq("testuuid"),
        // package info for the specified package.
                eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
        doAnswer(new Answer<Void>() {
        assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats);
             @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);
    }
    }


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


        // Set up this to handle the asynchronous call to the PackageManager. This returns the
        when(mSsm.queryStatsForPackage(eq("testuuid"),
        // package info for our packages.
                eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
        doAnswer(new Answer<Void>() {
        when(mSsm.queryStatsForPackage(eq("testuuid"),
             @Override
                eq("com.test.app"), eq(UserHandle.of(1)))).thenReturn(new StorageStats());
             public Void answer(InvocationOnMock invocation) {
        assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats, otherStats);
                 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);
    }
    }


    @Test(expected=NullPointerException.class)
    @Test(expected=NullPointerException.class)