diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index ec79483e0f34b..ed00609cd8abb 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -58,6 +58,7 @@ public final class DumpState { private boolean mTitlePrinted; private boolean mFullPreferred; private boolean mCheckIn; + private boolean mBrief; private String mTargetPackageName; @@ -128,4 +129,12 @@ public final class DumpState { public void setCheckIn(boolean checkIn) { mCheckIn = checkIn; } + + public boolean isBrief() { + return mBrief; + } + + public void setBrief(boolean brief) { + mBrief = brief; + } } diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java index 2015c78e0817e..34caaf52b2154 100644 --- a/services/core/java/com/android/server/pm/KeySetManagerService.java +++ b/services/core/java/com/android/server/pm/KeySetManagerService.java @@ -30,6 +30,7 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.utils.WatchedArrayMap; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -65,7 +66,7 @@ public class KeySetManagerService { protected final LongSparseArray> mKeySetMapping; - private final ArrayMap mPackages; + private final WatchedArrayMap mPackages; private long lastIssuedKeySetId = 0; @@ -114,7 +115,7 @@ public class KeySetManagerService { } } - public KeySetManagerService(ArrayMap packages) { + public KeySetManagerService(WatchedArrayMap packages) { mKeySets = new LongSparseArray(); mPublicKeys = new LongSparseArray(); mKeySetMapping = new LongSparseArray>(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 679042ff40c9d..427bb2d9bd34d 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 @@ -1309,14 +1315,17 @@ public class PackageManagerService extends IPackageManager.Stub // Avoid invalidation-thrashing by preventing cache invalidations from causing property // writes if the cache isn't enabled yet. We re-enable writes later when we're // done initializing. - sSnapshotCorked = true; + sSnapshotCorked.incrementAndGet(); PackageManager.corkPackageInfoCache(); } @Override public void enablePackageCaches() { // Uncork cache invalidations and allow clients to cache package information. - sSnapshotCorked = false; + int corking = sSnapshotCorked.decrementAndGet(); + if (TRACE_SNAPSHOTS && corking == 0) { + Log.i(TAG, "snapshot: corking returns to 0"); + } PackageManager.uncorkPackageInfoCache(); } } @@ -1395,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. @@ -1588,6 +1610,7 @@ public class PackageManagerService extends IPackageManager.Stub static final int INTEGRITY_VERIFICATION_COMPLETE = 25; static final int CHECK_PENDING_INTEGRITY_VERIFICATION = 26; static final int DOMAIN_VERIFICATION = 27; + static final int SNAPSHOT_UNCORK = 28; static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000; static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500; @@ -1834,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 = @@ -4874,12 +4897,16 @@ public class PackageManagerService extends IPackageManager.Stub // A lock-free cache for frequently called functions. private volatile Computer mSnapshotComputer; // If true, the snapshot is invalid (stale). The attribute is static since it may be - // set from outside classes. - private static volatile boolean sSnapshotInvalid = true; + // set from outside classes. The attribute may be set to true anywhere, although it + // should only be set true while holding mLock. However, the attribute id guaranteed + // to be set false only while mLock and mSnapshotLock are both held. + private static AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true); + // The package manager that is using snapshots. + private static PackageManagerService sSnapshotConsumer = null; // If true, the snapshot is corked. Do not create a new snapshot but use the live // computer. This throttles snapshot creation during periods of churn in Package // Manager. - private static volatile boolean sSnapshotCorked = false; + private static AtomicInteger sSnapshotCorked = new AtomicInteger(0); /** * This lock is used to make reads from {@link #sSnapshotInvalid} and @@ -4897,7 +4924,10 @@ public class PackageManagerService extends IPackageManager.Stub // The snapshot disable/enable switch. An image with the flag set true uses snapshots // and an image with the flag set false does not use snapshots. - private static final boolean SNAPSHOT_ENABLED = false; + private static final boolean SNAPSHOT_ENABLED = true; + + // The default auto-cork delay for snapshots. This is 1s. + private static final long SNAPSHOT_AUTOCORK_DELAY_MS = TimeUnit.SECONDS.toMillis(1); // The per-instance snapshot disable/enable flag. This is generally set to false in // test instances and set to SNAPSHOT_ENABLED in operational instances. @@ -4922,15 +4952,16 @@ public class PackageManagerService extends IPackageManager.Stub // If the current thread holds mLock then it may have modified state but not // yet invalidated the snapshot. Always give the thread the live computer. return mLiveComputer; + } else if (sSnapshotCorked.get() > 0) { + // Snapshots are corked, which means new ones should not be built right now. + mSnapshotStatistics.corked(); + return mLiveComputer; } synchronized (mSnapshotLock) { + // This synchronization block serializes access to the snapshot computer and + // to the code that samples mSnapshotInvalid. Computer c = mSnapshotComputer; - if (sSnapshotCorked && (c != null)) { - // Snapshots are corked, which means new ones should not be built right now. - c.use(); - return c; - } - if (sSnapshotInvalid || (c == null)) { + if (sSnapshotInvalid.getAndSet(false) || (c == null)) { // The snapshot is invalid if it is marked as invalid or if it is null. If it // is null, then it is currently being rebuilt by rebuildSnapshot(). synchronized (mLock) { @@ -4938,9 +4969,7 @@ public class PackageManagerService extends IPackageManager.Stub // invalidated as it is rebuilt. However, the snapshot is still // self-consistent (the lock is being held) and is current as of the time // this function is entered. - if (sSnapshotInvalid) { - rebuildSnapshot(); - } + rebuildSnapshot(); // Guaranteed to be non-null. mSnapshotComputer is only be set to null // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since @@ -4958,12 +4987,11 @@ public class PackageManagerService extends IPackageManager.Stub * Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block other * threads from using the invalid computer until it is rebuilt. */ - @GuardedBy("mLock") + @GuardedBy({ "mLock", "mSnapshotLock"}) private void rebuildSnapshot() { final long now = SystemClock.currentTimeMicro(); final int hits = mSnapshotComputer == null ? -1 : mSnapshotComputer.getUsed(); mSnapshotComputer = null; - sSnapshotInvalid = false; final Snapshot args = new Snapshot(Snapshot.SNAPPED); mSnapshotComputer = new ComputerEngine(args); final long done = SystemClock.currentTimeMicro(); @@ -4971,6 +4999,30 @@ public class PackageManagerService extends IPackageManager.Stub mSnapshotStatistics.rebuild(now, done, hits); } + /** + * Create a new snapshot. Used for testing only. This does collect statistics or + * update the snapshot used by other actors. It does not alter the invalidation + * flag. This method takes the mLock internally. + */ + private Computer createNewSnapshot() { + synchronized (mLock) { + final Snapshot args = new Snapshot(Snapshot.SNAPPED); + return new ComputerEngine(args); + } + } + + /** + * Cork snapshots. This times out after the programmed delay. + */ + private void corkSnapshots(int multiplier) { + int corking = sSnapshotCorked.getAndIncrement(); + if (TRACE_SNAPSHOTS && corking == 0) { + Log.i(TAG, "snapshot: corking goes positive"); + } + Message message = mHandler.obtainMessage(SNAPSHOT_UNCORK); + mHandler.sendMessageDelayed(message, SNAPSHOT_AUTOCORK_DELAY_MS * multiplier); + } + /** * Create a live computer */ @@ -4986,9 +5038,9 @@ public class PackageManagerService extends IPackageManager.Stub */ public static void onChange(@Nullable Watchable what) { if (TRACE_SNAPSHOTS) { - Log.e(TAG, "snapshot: onChange(" + what + ")"); + Log.i(TAG, "snapshot: onChange(" + what + ")"); } - sSnapshotInvalid = true; + sSnapshotInvalid.set(true); } /** @@ -5367,6 +5419,13 @@ public class PackageManagerService extends IPackageManager.Stub mDomainVerificationManager.runMessage(messageCode, object); break; } + case SNAPSHOT_UNCORK: { + int corking = sSnapshotCorked.decrementAndGet(); + if (TRACE_SNAPSHOTS && corking == 0) { + Log.e(TAG, "snapshot: corking goes to zero in message handler"); + } + break; + } } } } @@ -6383,12 +6442,13 @@ public class PackageManagerService extends IPackageManager.Stub // constructor, at which time the invalidation method updates it. The cache is // corked initially to ensure a cached computer is not built until the end of the // constructor. - mSnapshotEnabled = SNAPSHOT_ENABLED; - sSnapshotCorked = true; - sSnapshotInvalid = true; mSnapshotStatistics = new SnapshotStatistics(); + sSnapshotConsumer = this; + sSnapshotCorked.set(1); + sSnapshotInvalid.set(true); mLiveComputer = createLiveComputer(); mSnapshotComputer = null; + mSnapshotEnabled = SNAPSHOT_ENABLED; registerObserver(); } @@ -18521,7 +18581,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy({"mInstallLock", "mLock"}) + @GuardedBy("mInstallLock") private void installPackagesTracedLI(List requests) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages"); @@ -24018,6 +24078,15 @@ public class PackageManagerService extends IPackageManager.Stub dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS); } else if ("snapshot".equals(cmd)) { dumpState.setDump(DumpState.DUMP_SNAPSHOT_STATISTICS); + if (opti < args.length) { + if ("--full".equals(args[opti])) { + dumpState.setBrief(false); + opti++; + } else if ("--brief".equals(args[opti])) { + dumpState.setBrief(true); + opti++; + } + } } else if ("write".equals(cmd)) { synchronized (mLock) { writeSettingsLPrTEMP(); @@ -24353,13 +24422,14 @@ public class PackageManagerService extends IPackageManager.Stub pw.println(" Snapshots disabled"); } else { int hits = 0; + int level = sSnapshotCorked.get(); synchronized (mSnapshotLock) { if (mSnapshotComputer != null) { hits = mSnapshotComputer.getUsed(); } } final long now = SystemClock.currentTimeMicro(); - mSnapshotStatistics.dump(pw, " ", now, hits, true); + mSnapshotStatistics.dump(pw, " ", now, hits, level, dumpState.isBrief()); } } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 1b8eee3925a5f..f5a13d5781ad1 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -351,6 +351,7 @@ public final class Settings implements Watchable, Snappable { private final PackageManagerTracedLock mLock; + @Watched(manual = true) private final RuntimePermissionPersistence mRuntimePermissionsPersistence; private final File mSettingsFilename; @@ -364,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 @@ -397,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. @@ -435,6 +438,7 @@ public final class Settings implements Watchable, Snappable { } /** Device identity for the purpose of package verification. */ + @Watched(manual = true) private VerifierDeviceIdentity mVerifierDeviceIdentity; // The user's preferred activities associated with particular intent @@ -462,10 +466,12 @@ public final class Settings implements Watchable, Snappable { private final WatchedSparseArray mOtherAppIds; // For reading/writing settings file. - private final ArrayList mPastSignatures = - new ArrayList(); - private final ArrayMap mKeySetRefs = - new ArrayMap(); + @Watched + private final WatchedArrayList mPastSignatures = + new WatchedArrayList(); + @Watched + private final WatchedArrayMap mKeySetRefs = + new WatchedArrayMap(); // Packages that have been renamed since they were first installed. // Keys are the new names of the packages, values are the original @@ -495,18 +501,21 @@ public final class Settings implements Watchable, Snappable { * TODO: make this just a local variable that is passed in during package * scanning to make it less confusing. */ - private final ArrayList mPendingPackages = new ArrayList<>(); + @Watched + private final WatchedArrayList mPendingPackages = new WatchedArrayList<>(); private final File mSystemDir; - public final KeySetManagerService mKeySetManagerService = - new KeySetManagerService(mPackages.untrackedStorage()); + private final KeySetManagerService mKeySetManagerService; /** Settings and other information about permissions */ + @Watched(manual = true) final LegacyPermissionSettings mPermissions; + @Watched(manual = true) private final LegacyPermissionDataProvider mPermissionDataProvider; + @Watched(manual = true) private final DomainVerificationManagerInternal mDomainVerificationManager; /** @@ -532,23 +541,7 @@ public final class Settings implements Watchable, Snappable { }}; } - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public Settings(Map pkgSettings) { - mLock = new PackageManagerTracedLock(); - mPackages.putAll(pkgSettings); - mAppIds = new WatchedArrayList<>(); - mOtherAppIds = new WatchedSparseArray<>(); - mSystemDir = null; - mPermissions = null; - mRuntimePermissionsPersistence = null; - mPermissionDataProvider = null; - mSettingsFilename = null; - mBackupSettingsFilename = null; - mPackageListFilename = null; - mStoppedPackagesFilename = null; - mBackupStoppedPackagesFilename = null; - mKernelMappingFilename = null; - mDomainVerificationManager = null; + private void registerObservers() { mPackages.registerObserver(mObserver); mInstallerPackages.registerObserver(mObserver); mKernelMapping.registerObserver(mObserver); @@ -564,7 +557,43 @@ public final class Settings implements Watchable, Snappable { mRenamedPackages.registerObserver(mObserver); mNextAppLinkGeneration.registerObserver(mObserver); mDefaultBrowserApp.registerObserver(mObserver); + mPendingPackages.registerObserver(mObserver); + mPastSignatures.registerObserver(mObserver); + 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<>(); + mOtherAppIds = new WatchedSparseArray<>(); + mSystemDir = null; + mPermissions = null; + mRuntimePermissionsPersistence = null; + mPermissionDataProvider = null; + mSettingsFilename = null; + mBackupSettingsFilename = null; + mPackageListFilename = null; + mStoppedPackagesFilename = null; + mBackupStoppedPackagesFilename = null; + mKernelMappingFilename = null; + mDomainVerificationManager = null; + + registerObservers(); Watchable.verifyWatchedAttributes(this, mObserver); mSnapshot = makeCache(); @@ -574,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<>(); @@ -602,22 +643,7 @@ public final class Settings implements Watchable, Snappable { mDomainVerificationManager = domainVerificationManager; - mPackages.registerObserver(mObserver); - mInstallerPackages.registerObserver(mObserver); - mKernelMapping.registerObserver(mObserver); - mDisabledSysPackages.registerObserver(mObserver); - mBlockUninstallPackages.registerObserver(mObserver); - mVersion.registerObserver(mObserver); - mPreferredActivities.registerObserver(mObserver); - mPersistentPreferredActivities.registerObserver(mObserver); - mCrossProfileIntentResolvers.registerObserver(mObserver); - mSharedUsers.registerObserver(mObserver); - mAppIds.registerObserver(mObserver); - mOtherAppIds.registerObserver(mObserver); - mRenamedPackages.registerObserver(mObserver); - mNextAppLinkGeneration.registerObserver(mObserver); - mDefaultBrowserApp.registerObserver(mObserver); - + registerObservers(); Watchable.verifyWatchedAttributes(this, mObserver); mSnapshot = makeCache(); @@ -629,8 +655,13 @@ public final class Settings implements Watchable, Snappable { * are changed by PackageManagerService APIs are deep-copied */ private Settings(Settings r) { - final int mPackagesSize = r.mPackages.size(); - 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 @@ -647,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; @@ -659,23 +688,26 @@ 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(); - mPastSignatures.addAll(r.mPastSignatures); - mKeySetRefs.putAll(r.mKeySetRefs); + WatchedArrayList.snapshot( + mPastSignatures, r.mPastSignatures); + WatchedArrayMap.snapshot( + mKeySetRefs, r.mKeySetRefs); mRenamedPackages.snapshot(r.mRenamedPackages); mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp); // mReadMessages - mPendingPackages.addAll(r.mPendingPackages); + WatchedArrayList.snapshot( + mPendingPackages, r.mPendingPackages); mSystemDir = null; // mKeySetManagerService; mPermissions = r.mPermissions; mPermissionDataProvider = r.mPermissionDataProvider; // Do not register any Watchables and do not create a snapshot cache. - mSnapshot = null; + mSnapshot = new SnapshotCache.Sealed(); } /** @@ -2326,7 +2358,7 @@ public final class Settings implements Watchable, Snappable { serializer.startTag(null, "shared-user"); serializer.attribute(null, ATTR_NAME, usr.name); serializer.attributeInt(null, "userId", usr.userId); - usr.signatures.writeXml(serializer, "sigs", mPastSignatures); + usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage()); serializer.endTag(null, "shared-user"); } @@ -2736,11 +2768,11 @@ public final class Settings implements Watchable, Snappable { writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions); - pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); + pkg.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage()); if (installSource.initiatingPackageSignatures != null) { installSource.initiatingPackageSignatures.writeXml( - serializer, "install-initiator-sigs", mPastSignatures); + serializer, "install-initiator-sigs", mPastSignatures.untrackedStorage()); } writeSigningKeySetLPr(serializer, pkg.keySetData); @@ -2909,7 +2941,7 @@ public final class Settings implements Watchable, Snappable { } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { // No longer used. } else if (tagName.equals("keyset-settings")) { - mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs); + mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs.untrackedStorage()); } else if (TAG_VERSION.equals(tagName)) { final String volumeUuid = XmlUtils.readStringAttribute(parser, ATTR_VOLUME_UUID); @@ -3697,7 +3729,7 @@ public final class Settings implements Watchable, Snappable { } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) { readEnabledComponentsLPw(packageSetting, parser, 0); } else if (tagName.equals("sigs")) { - packageSetting.signatures.readXml(parser, mPastSignatures); + packageSetting.signatures.readXml(parser, mPastSignatures.untrackedStorage()); } else if (tagName.equals(TAG_PERMISSIONS)) { readInstallPermissionsLPr(parser, packageSetting.getLegacyPermissionState(), users); @@ -3728,7 +3760,7 @@ public final class Settings implements Watchable, Snappable { packageSetting.keySetData.addDefinedKeySet(id, alias); } else if (tagName.equals("install-initiator-sigs")) { final PackageSignatures signatures = new PackageSignatures(); - signatures.readXml(parser, mPastSignatures); + signatures.readXml(parser, mPastSignatures.untrackedStorage()); packageSetting.installSource = packageSetting.installSource.setInitiatingPackageSignatures(signatures); } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { @@ -3923,7 +3955,7 @@ public final class Settings implements Watchable, Snappable { String tagName = parser.getName(); if (tagName.equals("sigs")) { - su.signatures.readXml(parser, mPastSignatures); + su.signatures.readXml(parser, mPastSignatures.untrackedStorage()); } else if (tagName.equals("perms")) { readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users); } else { diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java index c425bad50ae87..7bf00603f1321 100644 --- a/services/core/java/com/android/server/pm/SnapshotStatistics.java +++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java @@ -23,6 +23,7 @@ import android.os.Message; import android.os.SystemClock; import android.text.TextUtils; +import com.android.internal.annotations.GuardedBy; import com.android.server.EventLogTags; import java.io.PrintWriter; @@ -238,6 +239,11 @@ public class SnapshotStatistics { */ public int mTotalUsed = 0; + /** + * The total number of times a snapshot was bypassed because corking was in effect. + */ + public int mTotalCorked = 0; + /** * The total number of builds that count as big, which means they took longer than * SNAPSHOT_BIG_BUILD_TIME_NS. @@ -291,6 +297,13 @@ public class SnapshotStatistics { } } + /** + * Record a cork. + */ + private void corked() { + mTotalCorked++; + } + private Stats(long now) { mStartTimeUs = now; mTimes = new int[mTimeBins.count()]; @@ -308,6 +321,7 @@ public class SnapshotStatistics { mUsed = Arrays.copyOf(orig.mUsed, orig.mUsed.length); mTotalBuilds = orig.mTotalBuilds; mTotalUsed = orig.mTotalUsed; + mTotalCorked = orig.mTotalCorked; mBigBuilds = orig.mBigBuilds; mShortLived = orig.mShortLived; mTotalTimeUs = orig.mTotalTimeUs; @@ -365,6 +379,7 @@ public class SnapshotStatistics { * Dump the summary statistics record. Choose the header or the data. * number of builds * number of uses + * number of corks * number of big builds * number of short lifetimes * cumulative build time, in seconds @@ -373,13 +388,13 @@ public class SnapshotStatistics { private void dumpStats(PrintWriter pw, String indent, long now, boolean header) { dumpPrefix(pw, indent, now, header, "Summary stats"); if (header) { - pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s", - "TotBlds", "TotUsed", "BigBlds", "ShortLvd", + pw.format(Locale.US, " %10s %10s %10s %10s %10s %10s %10s", + "TotBlds", "TotUsed", "TotCork", "BigBlds", "ShortLvd", "TotTime", "MaxTime"); } else { pw.format(Locale.US, - " %10d %10d %10d %10d %10d %10d", - mTotalBuilds, mTotalUsed, mBigBuilds, mShortLived, + " %10d %10d %10d %10d %10d %10d %10d", + mTotalBuilds, mTotalUsed, mTotalCorked, mBigBuilds, mShortLived, mTotalTimeUs / 1000, mMaxBuildTimeUs / 1000); } pw.println(); @@ -516,7 +531,7 @@ public class SnapshotStatistics { * @param done The time at which the snapshot rebuild completed, in ns. * @param hits The number of times the previous snapshot was used. */ - public void rebuild(long now, long done, int hits) { + public final void rebuild(long now, long done, int hits) { // The duration has a span of about 2000s final int duration = (int) (done - now); boolean reportEvent = false; @@ -543,10 +558,21 @@ public class SnapshotStatistics { } } + /** + * Record a corked snapshot request. + */ + public final void corked() { + synchronized (mLock) { + mShort[0].corked(); + mLong[0].corked(); + } + } + /** * Roll a stats array. Shift the elements up an index and create a new element at * index zero. The old element zero is completed with the specified time. */ + @GuardedBy("mLock") private void shift(Stats[] s, long now) { s[0].complete(now); for (int i = s.length - 1; i > 0; i--) { @@ -598,7 +624,8 @@ public class SnapshotStatistics { * Dump the statistics. The format is compatible with the PackageManager dumpsys * output. */ - public void dump(PrintWriter pw, String indent, long now, int unrecorded, boolean full) { + public void dump(PrintWriter pw, String indent, long now, int unrecorded, + int corkLevel, boolean full) { // Grab the raw statistics under lock, but print them outside of the lock. Stats[] l; Stats[] s; @@ -608,7 +635,8 @@ public class SnapshotStatistics { s = Arrays.copyOf(mShort, mShort.length); s[0] = new Stats(s[0]); } - pw.format(Locale.US, "%s Unrecorded hits %d", indent, unrecorded); + pw.format(Locale.US, "%s Unrecorded-hits: %d Cork-level: %d", indent, + unrecorded, corkLevel); pw.println(); dump(pw, indent, now, l, s, "stats"); if (!full) { diff --git a/services/core/java/com/android/server/utils/SnapshotCache.java b/services/core/java/com/android/server/utils/SnapshotCache.java index b4b8835ac0269..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,25 +38,65 @@ 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; // The cached snapshot - private T mSnapshot = null; + private volatile T mSnapshot = null; // True if the snapshot is sealed and may not be modified. - private boolean mSealed = false; + 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,13 +106,14 @@ public abstract class SnapshotCache extends Watcher{ public SnapshotCache() { mSource = null; mSealed = true; + mStatistics = null; } /** * Notify the object that the source object has changed. If the local object is sealed then * IllegalStateException is thrown. Otherwise, the cache is cleared. */ - public void onChange(@Nullable Watchable what) { + public final void onChange(@Nullable Watchable what) { if (mSealed) { throw new IllegalStateException("attempt to change a sealed object"); } @@ -79,7 +123,7 @@ public abstract class SnapshotCache extends Watcher{ /** * Seal the cache. Attempts to modify the cache will generate an exception. */ - public void seal() { + public final void seal() { mSealed = true; } @@ -88,11 +132,14 @@ public abstract class SnapshotCache extends Watcher{ * new snapshot and saves it in the cache. * @return A snapshot as returned by createSnapshot() and possibly cached. */ - public T snapshot() { + public final T snapshot() { T s = mSnapshot; 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(); + } + } + } diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index 709b009c2feb8..1b6bddc158b8b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java @@ -25,6 +25,7 @@ import android.util.ArraySet; import android.util.LongSparseArray; import com.android.internal.util.ArrayUtils; +import com.android.server.utils.WatchedArrayMap; import java.io.File; import java.io.IOException; @@ -33,7 +34,7 @@ import java.security.cert.CertificateException; public class KeySetManagerServiceTest extends AndroidTestCase { - private ArrayMap mPackagesMap; + private WatchedArrayMap mPackagesMap; private KeySetManagerService mKsms; public PackageSetting generateFakePackageSetting(String name) { @@ -46,7 +47,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase { @Override public void setUp() throws Exception { super.setUp(); - mPackagesMap = new ArrayMap(); + mPackagesMap = new WatchedArrayMap(); mKsms = new KeySetManagerService(mPackagesMap); } @@ -94,7 +95,8 @@ public class KeySetManagerServiceTest extends AndroidTestCase { } public void testEncodePublicKey() throws IOException { - ArrayMap packagesMap = new ArrayMap(); + WatchedArrayMap packagesMap = + new WatchedArrayMap(); KeySetManagerService ksms = new KeySetManagerService(packagesMap); PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index a2311690744e3..29f4aa976ef61 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -47,7 +47,6 @@ import android.os.BaseBundle; import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; @@ -64,6 +63,7 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import com.android.server.utils.WatchableTester; +import com.android.server.utils.WatchedArrayMap; import com.google.common.truth.Truth; @@ -1202,9 +1202,8 @@ public class PackageManagerSettingsTests { private void verifyKeySetMetaData(Settings settings) throws ReflectiveOperationException, IllegalAccessException { - ArrayMap packages = - settings.mPackages.untrackedStorage(); - KeySetManagerService ksms = settings.mKeySetManagerService; + WatchedArrayMap packages = settings.mPackages; + KeySetManagerService ksms = settings.getKeySetManagerService(); /* verify keyset and public key ref counts */ assertThat(KeySetUtils.getKeySetRefCount(ksms, 1), is(2));