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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>>();
|
||||
|
||||
@@ -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<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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -40,10 +40,10 @@ public abstract class SnapshotCache<T> 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<T> 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<T> 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<T> 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();
|
||||
|
||||
@@ -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<String, PackageSetting> mPackagesMap;
|
||||
private WatchedArrayMap<String, PackageSetting> 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<String, PackageSetting>();
|
||||
mPackagesMap = new WatchedArrayMap<String, PackageSetting>();
|
||||
mKsms = new KeySetManagerService(mPackagesMap);
|
||||
}
|
||||
|
||||
@@ -94,7 +95,8 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
public void testEncodePublicKey() throws IOException {
|
||||
ArrayMap<String, PackageSetting> packagesMap = new ArrayMap<String, PackageSetting>();
|
||||
WatchedArrayMap<String, PackageSetting> packagesMap =
|
||||
new WatchedArrayMap<String, PackageSetting>();
|
||||
KeySetManagerService ksms = new KeySetManagerService(packagesMap);
|
||||
|
||||
PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
|
||||
|
||||
@@ -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<String, PackageSetting> packages =
|
||||
settings.mPackages.untrackedStorage();
|
||||
KeySetManagerService ksms = settings.mKeySetManagerService;
|
||||
WatchedArrayMap<String, PackageSetting> packages = settings.mPackages;
|
||||
KeySetManagerService ksms = settings.getKeySetManagerService();
|
||||
|
||||
/* verify keyset and public key ref counts */
|
||||
assertThat(KeySetUtils.getKeySetRefCount(ksms, 1), is(2));
|
||||
|
||||
Reference in New Issue
Block a user