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

Commit 84737b41 authored by Lee Shombert's avatar Lee Shombert
Browse files

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
parent b854b5ea
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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;
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -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<ArraySet<Long>> mKeySetMapping;

    private final ArrayMap<String, PackageSetting> mPackages;
    private final WatchedArrayMap<String, PackageSetting> mPackages;

    private long lastIssuedKeySetId = 0;

@@ -114,7 +115,7 @@ public class KeySetManagerService {
        }
    }

    public KeySetManagerService(ArrayMap<String, PackageSetting> packages) {
    public KeySetManagerService(WatchedArrayMap<String, PackageSetting> packages) {
        mKeySets = new LongSparseArray<KeySetHandle>();
        mPublicKeys = new LongSparseArray<PublicKeyHandle>();
        mKeySetMapping = new LongSparseArray<ArraySet<Long>>();
+74 −23
Original line number Diff line number Diff line
@@ -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();
                    }
                    // 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<InstallRequest> 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());
            }
        }
    }
+52 −50
Original line number Diff line number Diff line
@@ -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<SettingBase> mOtherAppIds;

    // For reading/writing settings file.
    private final ArrayList<Signature> mPastSignatures =
            new ArrayList<Signature>();
    private final ArrayMap<Long, Integer> mKeySetRefs =
            new ArrayMap<Long, Integer>();
    @Watched
    private final WatchedArrayList<Signature> mPastSignatures =
            new WatchedArrayList<Signature>();
    @Watched
    private final WatchedArrayMap<Long, Integer> mKeySetRefs =
            new WatchedArrayMap<Long, Integer>();

    // 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<PackageSetting> mPendingPackages = new ArrayList<>();
    @Watched
    private final WatchedArrayList<PackageSetting> 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<String, PackageSetting> 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 {
+35 −7
Original line number Diff line number Diff line
@@ -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) {
Loading