diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 882d8e64c8cc4..36923fe70a28e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -404,6 +404,7 @@ import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.server.utils.SnapshotCache; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.utils.Watchable; import com.android.server.utils.Watched; @@ -871,12 +872,17 @@ public class PackageManagerService extends IPackageManager.Stub @Watched @GuardedBy("mLock") final WatchedArrayMap mPackages = new WatchedArrayMap<>(); + private final SnapshotCache> mPackagesSnapshot = + new SnapshotCache.Auto(mPackages, mPackages, "PackageManagerService.mPackages"); // Keys are isolated uids and values are the uid of the application // that created the isolated process. @Watched @GuardedBy("mLock") final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray(); + private final SnapshotCache mIsolatedOwnersSnapshot = + new SnapshotCache.Auto(mIsolatedOwners, mIsolatedOwners, + "PackageManagerService.mIsolatedOwners"); /** * Tracks new system packages [received in an OTA] that we expect to @@ -1398,14 +1404,27 @@ public class PackageManagerService extends IPackageManager.Stub @Watched final WatchedArrayMap> mSharedLibraries = new WatchedArrayMap<>(); + private final SnapshotCache>> + mSharedLibrariesSnapshot = + new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries, + "PackageManagerService.mSharedLibraries"); @Watched final WatchedArrayMap> mStaticLibsByDeclaringPackage = new WatchedArrayMap<>(); + private final SnapshotCache>> + mStaticLibsByDeclaringPackageSnapshot = + new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries, + "PackageManagerService.mSharedLibraries"); // Mapping from instrumentation class names to info about them. @Watched final WatchedArrayMap mInstrumentation = new WatchedArrayMap<>(); + private final SnapshotCache> + mInstrumentationSnapshot = + new SnapshotCache.Auto<>(mInstrumentation, mInstrumentation, + "PackageManagerService.mInstrumentation"); + // Packages whose data we have transfered into another package, thus // should no longer exist. @@ -1838,11 +1857,11 @@ public class PackageManagerService extends IPackageManager.Stub Snapshot(int type) { if (type == Snapshot.SNAPPED) { settings = mSettings.snapshot(); - isolatedOwners = mIsolatedOwners.snapshot(); - packages = mPackages.snapshot(); - sharedLibs = mSharedLibraries.snapshot(); - staticLibs = mStaticLibsByDeclaringPackage.snapshot(); - instrumentation = mInstrumentation.snapshot(); + isolatedOwners = mIsolatedOwnersSnapshot.snapshot(); + packages = mPackagesSnapshot.snapshot(); + sharedLibs = mSharedLibrariesSnapshot.snapshot(); + staticLibs = mStaticLibsByDeclaringPackageSnapshot.snapshot(); + instrumentation = mInstrumentationSnapshot.snapshot(); resolveComponentName = mResolveComponentName.clone(); resolveActivity = new ActivityInfo(mResolveActivity); instantAppInstallerActivity = diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index e14c9871ea853..f5a13d5781ad1 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -365,19 +365,21 @@ public final class Settings implements Watchable, Snappable { /** Map from package name to settings */ @Watched @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - final WatchedArrayMap mPackages = new WatchedArrayMap<>(); + final WatchedArrayMap mPackages; + private final SnapshotCache> mPackagesSnapshot; /** * List of packages that were involved in installing other packages, i.e. are listed * in at least one app's InstallSource. */ @Watched - private final WatchedArraySet mInstallerPackages = new WatchedArraySet<>(); + private final WatchedArraySet mInstallerPackages; + private final SnapshotCache> mInstallerPackagesSnapshot; /** Map from package name to appId and excluded userids */ @Watched - private final WatchedArrayMap mKernelMapping = - new WatchedArrayMap<>(); + private final WatchedArrayMap mKernelMapping; + private final SnapshotCache> mKernelMappingSnapshot; // List of replaced system applications @Watched @@ -398,7 +400,7 @@ public final class Settings implements Watchable, Snappable { /** Map from volume UUID to {@link VersionInfo} */ @Watched - private WatchedArrayMap mVersion = new WatchedArrayMap<>(); + private final WatchedArrayMap mVersion = new WatchedArrayMap<>(); /** * Version details for a storage volume that may hold apps. @@ -504,8 +506,7 @@ public final class Settings implements Watchable, Snappable { private final File mSystemDir; - private final KeySetManagerService mKeySetManagerService = - new KeySetManagerService(mPackages); + private final KeySetManagerService mKeySetManagerService; /** Settings and other information about permissions */ @Watched(manual = true) @@ -561,8 +562,21 @@ public final class Settings implements Watchable, Snappable { mKeySetRefs.registerObserver(mObserver); } + // CONSTRUCTOR @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public Settings(Map pkgSettings) { + mPackages = new WatchedArrayMap<>(); + mPackagesSnapshot = + new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages"); + mKernelMapping = new WatchedArrayMap<>(); + mKernelMappingSnapshot = + new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping"); + mInstallerPackages = new WatchedArraySet<>(); + mInstallerPackagesSnapshot = + new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages, + "Settings.mInstallerPackages"); + mKeySetManagerService = new KeySetManagerService(mPackages); + mLock = new PackageManagerTracedLock(); mPackages.putAll(pkgSettings); mAppIds = new WatchedArrayList<>(); @@ -589,6 +603,18 @@ public final class Settings implements Watchable, Snappable { LegacyPermissionDataProvider permissionDataProvider, @NonNull DomainVerificationManagerInternal domainVerificationManager, @NonNull PackageManagerTracedLock lock) { + mPackages = new WatchedArrayMap<>(); + mPackagesSnapshot = + new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages"); + mKernelMapping = new WatchedArrayMap<>(); + mKernelMappingSnapshot = + new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping"); + mInstallerPackages = new WatchedArraySet<>(); + mInstallerPackagesSnapshot = + new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages, + "Settings.mInstallerPackages"); + mKeySetManagerService = new KeySetManagerService(mPackages); + mLock = lock; mAppIds = new WatchedArrayList<>(); mOtherAppIds = new WatchedSparseArray<>(); @@ -629,7 +655,13 @@ public final class Settings implements Watchable, Snappable { * are changed by PackageManagerService APIs are deep-copied */ private Settings(Settings r) { - mPackages.putAll(r.mPackages); + mPackages = r.mPackagesSnapshot.snapshot(); + mPackagesSnapshot = new SnapshotCache.Sealed<>(); + mKernelMapping = r.mKernelMappingSnapshot.snapshot(); + mKernelMappingSnapshot = new SnapshotCache.Sealed<>(); + mInstallerPackages = r.mInstallerPackagesSnapshot.snapshot(); + mInstallerPackagesSnapshot = new SnapshotCache.Sealed<>(); + mKeySetManagerService = new KeySetManagerService(mPackages); // The following assignments satisfy Java requirements but are not // needed by the read-only methods. Note especially that the lock @@ -646,9 +678,7 @@ public final class Settings implements Watchable, Snappable { mDomainVerificationManager = r.mDomainVerificationManager; - mInstallerPackages.addAll(r.mInstallerPackages); - mKernelMapping.putAll(r.mKernelMapping); - mDisabledSysPackages.putAll(r.mDisabledSysPackages); + mDisabledSysPackages.snapshot(r.mDisabledSysPackages); mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages); mVersion.putAll(r.mVersion); mVerifierDeviceIdentity = r.mVerifierDeviceIdentity; @@ -658,7 +688,7 @@ public final class Settings implements Watchable, Snappable { mPersistentPreferredActivities, r.mPersistentPreferredActivities); WatchedSparseArray.snapshot( mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers); - mSharedUsers.putAll(r.mSharedUsers); + mSharedUsers.snapshot(r.mSharedUsers); mAppIds = r.mAppIds.snapshot(); mOtherAppIds = r.mOtherAppIds.snapshot(); WatchedArrayList.snapshot( diff --git a/services/core/java/com/android/server/utils/SnapshotCache.java b/services/core/java/com/android/server/utils/SnapshotCache.java index 68eb4b09b3ab5..42b9b23c1ab23 100644 --- a/services/core/java/com/android/server/utils/SnapshotCache.java +++ b/services/core/java/com/android/server/utils/SnapshotCache.java @@ -19,6 +19,9 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicInteger; + /** * A class that caches snapshots. Instances are instantiated on a {@link Watchable}; when the * {@link Watchable} reports a change, the cache is cleared. The snapshot() method fetches the @@ -35,6 +38,19 @@ public abstract class SnapshotCache extends Watcher{ */ private static final boolean ENABLED = true; + /** + * The statistics for a single cache. The object records the number of times a + * snapshot was reused and the number of times a snapshot was rebuilt. + */ + private static class Statistics { + final String mName; + private final AtomicInteger mReused = new AtomicInteger(0); + private final AtomicInteger mRebuilt = new AtomicInteger(0); + Statistics(@NonNull String n) { + mName = n; + } + } + // The source object from which snapshots are created. This may be null if createSnapshot() // does not require it. protected final T mSource; @@ -45,15 +61,42 @@ public abstract class SnapshotCache extends Watcher{ // True if the snapshot is sealed and may not be modified. private volatile boolean mSealed = false; + // The statistics for this cache. This may be null. + private final Statistics mStatistics; + + /** + * The global list of caches. + */ + private static final WeakHashMap sCaches = new WeakHashMap<>(); + /** * Create a cache with a source object for rebuilding snapshots and a - * {@link Watchable} that notifies when the cache is invalid. + * {@link Watchable} that notifies when the cache is invalid. If the name is null + * then statistics are not collected for this cache. + * @param source Source data for rebuilding snapshots. + * @param watchable The object that notifies when the cache is invalid. + * @param name The name of the cache, for statistics reporting. + */ + public SnapshotCache(@Nullable T source, @NonNull Watchable watchable, @Nullable String name) { + mSource = source; + watchable.registerObserver(this); + if (name != null) { + mStatistics = new Statistics(name); + sCaches.put(this, null); + } else { + mStatistics = null; + } + } + + /** + * Create a cache with a source object for rebuilding snapshots and a + * {@link Watchable} that notifies when the cache is invalid. The name is null in + * this API. * @param source Source data for rebuilding snapshots. * @param watchable The object that notifies when the cache is invalid. */ public SnapshotCache(@Nullable T source, @NonNull Watchable watchable) { - mSource = source; - watchable.registerObserver(this); + this(source, watchable, null); } /** @@ -63,6 +106,7 @@ public abstract class SnapshotCache extends Watcher{ public SnapshotCache() { mSource = null; mSealed = true; + mStatistics = null; } /** @@ -93,6 +137,9 @@ public abstract class SnapshotCache extends Watcher{ if (s == null || !ENABLED) { s = createSnapshot(); mSnapshot = s; + if (mStatistics != null) mStatistics.mRebuilt.incrementAndGet(); + } else { + if (mStatistics != null) mStatistics.mReused.incrementAndGet(); } return s; } @@ -123,4 +170,25 @@ public abstract class SnapshotCache extends Watcher{ throw new UnsupportedOperationException("cannot snapshot a sealed snaphot"); } } + + /** + * A snapshot cache suitable for Snappable types. The key is that Snappable types + * have a known implementation of createSnapshot() so that this class is concrete. + * @param The class whose snapshot is being cached. + */ + public static class Auto> extends SnapshotCache { + public Auto(@NonNull T source, @NonNull Watchable watchable, @Nullable String name) { + super(source, watchable, name); + } + public Auto(@NonNull T source, @NonNull Watchable watchable) { + this(source, watchable, null); + } + /** + * Concrete createSnapshot() using the snapshot() method of . + */ + public T createSnapshot() { + return mSource.snapshot(); + } + } + }