From 84737b417b394a01d99edfefdef6c194a785cf83 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Wed, 12 May 2021 08:25:47 -0700 Subject: [PATCH 1/3] Correct snapshot races Bug: 185481065 This fixes the race conditions that could theoretically cause PackageManager snapshots to fail, especially after package installation. No failures have actually been observed. 1. Three arrays in Settings are now watched. Other attributes that are included in the snapshot are annotated @Watched(manual = true). untrackedStorage() is used for these arrays during XML read/rwrite operations. Also, KeySetManagerService now takes a WatchedArrayMap from Settings, instead of untrackedStorage(). KeySetManagerService should not alter the array, but if it does, Settings will learn about it. 2. Key attributes in SnapshotCache are volatile. This should not be necessary, since snapshots are only modified under PM mLock, but the change is made just in case. 3. PackageManagerService.sSnapshotInvalid is now an atomic. This should eliminate narrow race conditions around the variable. 4. A corking facility is part of PackageManagerService. Corking is not currently used but it could be valuable to suppress snapshot creation during very busy periods. This is the same reason for corking that PropertyInvalidatedCache uses. SnapshotStatistics can track corking. 5. SnapshotStatistics has a "brief" mode that can be invoked from the command line. Brief output provides a good overview of snapshot behavior. Full output (which is the default) is good for more detailed analysis. Test: atest * CtsRoleTestCases * CtsContentTestCases:IntentFilterTest * CtsDynamicMimeHostTestCases * FrameworksServicesTests:UserSystemPackageInstallerTest * FrameworksServicesTests:PackageManagerSettingsTests * FrameworksServicesTests:PackageManagerServiceTest * FrameworksServicesTests:AppsFilterTest * FrameworksServicesTests:PackageInstallerSessionTest * FrameworksServicesTests:ScanTests * UserLifecycleTests#startUser * UserLifecycleTests#stopUser * UserLifecycleTests#switchUser * FrameworksServicesTests:WatcherTest * android.appsecurity.cts.EphemeralTest * android.appsecurity.cts.InstantAppUserTest Change-Id: I111cc4256cbd795fc2aba287f57cfcfd5f9cd7ab --- .../java/com/android/server/pm/DumpState.java | 9 ++ .../server/pm/KeySetManagerService.java | 5 +- .../server/pm/PackageManagerService.java | 97 +++++++++++++---- .../java/com/android/server/pm/Settings.java | 102 +++++++++--------- .../android/server/pm/SnapshotStatistics.java | 42 ++++++-- .../android/server/utils/SnapshotCache.java | 10 +- .../server/pm/KeySetManagerServiceTest.java | 8 +- .../pm/PackageManagerSettingsTests.java | 7 +- 8 files changed, 186 insertions(+), 94 deletions(-) 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..882d8e64c8cc4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1309,14 +1309,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(); } } @@ -1588,6 +1591,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; @@ -4874,12 +4878,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 @@ -4899,6 +4907,9 @@ public class PackageManagerService extends IPackageManager.Stub // and an image with the flag set false does not use snapshots. private static final boolean SNAPSHOT_ENABLED = false; + // 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. private final boolean mSnapshotEnabled; @@ -4922,15 +4933,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 +4950,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 +4968,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 +4980,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 +5019,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 +5400,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 +6423,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 +18562,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 +24059,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 +24403,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..e14c9871ea853 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; @@ -435,6 +436,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 +464,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 +499,22 @@ 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 = + new KeySetManagerService(mPackages); /** 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,6 +540,27 @@ public final class Settings implements Watchable, Snappable { }}; } + private void registerObservers() { + 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); + mPendingPackages.registerObserver(mObserver); + mPastSignatures.registerObserver(mObserver); + mKeySetRefs.registerObserver(mObserver); + } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public Settings(Map pkgSettings) { mLock = new PackageManagerTracedLock(); @@ -549,22 +578,8 @@ public final class Settings implements Watchable, Snappable { mBackupStoppedPackagesFilename = null; mKernelMappingFilename = null; mDomainVerificationManager = null; - 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(); @@ -602,22 +617,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,7 +629,6 @@ 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); // The following assignments satisfy Java requirements but are not @@ -662,20 +661,23 @@ public final class Settings implements Watchable, Snappable { mSharedUsers.putAll(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 +2328,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 +2738,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 +2911,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 +3699,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 +3730,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 +3925,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..68eb4b09b3ab5 100644 --- a/services/core/java/com/android/server/utils/SnapshotCache.java +++ b/services/core/java/com/android/server/utils/SnapshotCache.java @@ -40,10 +40,10 @@ public abstract class SnapshotCache extends Watcher{ 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; /** * Create a cache with a source object for rebuilding snapshots and a @@ -69,7 +69,7 @@ public abstract class SnapshotCache extends Watcher{ * 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 +79,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,7 +88,7 @@ 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(); 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)); From 99173ce6b915645d946191324e70cc08a81ffdc3 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Wed, 12 May 2021 08:25:50 -0700 Subject: [PATCH 2/3] Improve snapshot performance Bug: 185481065 Use caches to improve snapshot performance. SnapshotCache.Auto is a concrete SnapshotCache that can be applied to a Snappable. All the Watched* classes are Snappable. The cache improves performance and reduces memory churn. Test: atest * CtsContentTestCases:IntentFilterTest * CtsDynamicMimeHostTestCases * CtsRoleTestCases * FrameworksServicesTests:UserSystemPackageInstallerTest * FrameworksServicesTests:PackageManagerSettingsTests * FrameworksServicesTests:PackageManagerServiceTest * FrameworksServicesTests:AppsFilterTest * FrameworksServicesTests:PackageInstallerSessionTest * FrameworksServicesTests:ScanTests * UserLifecycleTests#startUser * UserLifecycleTests#stopUser * UserLifecycleTests#switchUser * FrameworksServicesTests:WatcherTest * android.appsecurity.cts.EphemeralTest * android.appsecurity.cts.InstantAppUserTest Change-Id: Ia993613f566cb86b145ddf1d5280e8780252a951 --- .../server/pm/PackageManagerService.java | 29 ++++++-- .../java/com/android/server/pm/Settings.java | 54 +++++++++++--- .../android/server/utils/SnapshotCache.java | 74 ++++++++++++++++++- 3 files changed, 137 insertions(+), 20 deletions(-) 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(); + } + } + } From f75c4a4477bbb7dc4a18479ff150193aba6bcede Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Wed, 12 May 2021 08:25:53 -0700 Subject: [PATCH 3/3] Enable PackageManagerService snapshots Bug: 179388643 Throw the switch that enables PackageManagerService snapshots. Revert this CL if the snapshots introduce field failures. This improves performance on the multiUser tests startUser and stopUser (switchUser is broken for different reasons right now). The "disabled" values are taken with baseline code and snapshots disabled. The "baseline" values are taken with baseline code and snapshots enabled. The "proposed" values are taken with the current CL active. disabled baseline (delta) intent (delta) boottime 28948 29169 221 28396 -552 pss 294300 334060 39760 299919 5619 rss 561201 614756 53555 566610 5409 start 4287 3894 -393 2461 -1826 stop 2847 2093 -754 1223 -1624 Test: atest * CtsContentTestCases:IntentFilterTest * CtsDynamicMimeHostTestCases * CtsRoleTestCases * FrameworksServicesTests:UserSystemPackageInstallerTest * FrameworksServicesTests:PackageManagerSettingsTests * FrameworksServicesTests:PackageManagerServiceTest * FrameworksServicesTests:AppsFilterTest * FrameworksServicesTests:PackageInstallerSessionTest * FrameworksServicesTests:ScanTests * UserLifecycleTests#startUser * UserLifecycleTests#stopUser * UserLifecycleTests#switchUser * FrameworksServicesTests:WatcherTest * android.appsecurity.cts.EphemeralTest * android.appsecurity.cts.InstantAppUserTest Change-Id: I8dc579301d11c29f94a1861694be3c39f6ebf1ca --- .../core/java/com/android/server/pm/PackageManagerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 36923fe70a28e..427bb2d9bd34d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4924,7 +4924,7 @@ 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);