Loading core/java/android/app/PropertyInvalidatedCache.java +112 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ package android.app; import android.annotation.NonNull; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; Loading Loading @@ -492,6 +496,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> { public static void corkInvalidations(@NonNull String name) { synchronized (sCorkLock) { int numberCorks = sCorks.getOrDefault(name, 0); if (DEBUG) { Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks)); } // If we're the first ones to cork this cache, set the cache to the unset state so // existing caches talk directly to their services while we've corked updates. // Make sure we don't clobber a disabled cache value. Loading Loading @@ -523,6 +531,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> { public static void uncorkInvalidations(@NonNull String name) { synchronized (sCorkLock) { int numberCorks = sCorks.getOrDefault(name, 0); if (DEBUG) { Log.d(TAG, String.format("uncorking %s: numberCorks=%s", name, numberCorks)); } if (numberCorks < 1) { throw new AssertionError("cork underflow: " + name); } Loading @@ -538,6 +550,106 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } } /** * Time-based automatic corking helper. This class allows providers of cached data to * amortize the cost of cache invalidations by corking the cache immediately after a * modification (instructing clients to bypass the cache temporarily) and automatically * uncork after some period of time has elapsed. * * It's better to use explicit cork and uncork pairs that tighly surround big batches of * invalidations, but it's not always practical to tell where these invalidation batches * might occur. AutoCorker's time-based corking is a decent alternative. */ public static final class AutoCorker { public static final int DEFAULT_AUTO_CORK_DELAY_MS = 2000; private final String mPropertyName; private final int mAutoCorkDelayMs; private final Object mLock = new Object(); @GuardedBy("mLock") private long mUncorkDeadlineMs = -1; // SystemClock.uptimeMillis() @GuardedBy("mLock") private Handler mHandler; public AutoCorker(@NonNull String propertyName) { this(propertyName, DEFAULT_AUTO_CORK_DELAY_MS); } public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) { mPropertyName = propertyName; mAutoCorkDelayMs = autoCorkDelayMs; // We can't initialize mHandler here: when we're created, the main loop might not // be set up yet! Wait until we have a main loop to initialize our // corking callback. } public void autoCork() { if (Looper.getMainLooper() == null) { // We're not ready to auto-cork yet, so just invalidate the cache immediately. if (DEBUG) { Log.w(TAG, "invalidating instead of autocorking early in init: " + mPropertyName); } PropertyInvalidatedCache.invalidateCache(mPropertyName); return; } synchronized (mLock) { boolean alreadyQueued = mUncorkDeadlineMs >= 0; if (DEBUG) { Log.w(TAG, String.format( "autoCork mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); } mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs; if (!alreadyQueued) { getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs); PropertyInvalidatedCache.corkInvalidations(mPropertyName); } } } private void handleMessage(Message msg) { synchronized (mLock) { if (DEBUG) { Log.w(TAG, String.format( "handleMsesage mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); } if (mUncorkDeadlineMs < 0) { return; // ??? } long nowMs = SystemClock.uptimeMillis(); if (mUncorkDeadlineMs > nowMs) { mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs; if (DEBUG) { Log.w(TAG, String.format( "scheduling uncork at %s", mUncorkDeadlineMs)); } getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs); return; } if (DEBUG) { Log.w(TAG, "automatic uncorking " + mPropertyName); } mUncorkDeadlineMs = -1; PropertyInvalidatedCache.uncorkInvalidations(mPropertyName); } } @GuardedBy("mLock") private Handler getHandlerLocked() { if (mHandler == null) { mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { AutoCorker.this.handleMessage(msg); } }; } return mHandler; } } protected Result maybeCheckConsistency(Query query, Result proposedResult) { if (VERIFY) { Result resultToCompare = recompute(query); Loading core/java/android/content/pm/PackageManager.java +15 −0 Original line number Diff line number Diff line Loading @@ -8167,4 +8167,19 @@ public abstract class PackageManager { sPackageInfoCache.disableLocal(); } /** * Inhibit package info cache invalidations when correct. * * @hide */ public static void corkPackageInfoCache() { PropertyInvalidatedCache.corkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO); } /** * Enable package info cache invalidations. * * @hide */ public static void uncorkPackageInfoCache() { PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO); } } services/core/java/com/android/server/pm/PackageManagerService.java +8 −1 Original line number Diff line number Diff line Loading @@ -2817,10 +2817,14 @@ public class PackageManagerService extends IPackageManager.Stub } public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) { PackageManager.invalidatePackageInfoCache(); PackageManager.disableApplicationInfoCache(); PackageManager.disablePackageInfoCache(); // 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. PackageManager.corkPackageInfoCache(); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); mPendingBroadcasts = new PendingPackageBroadcasts(); Loading Loading @@ -3626,6 +3630,9 @@ public class PackageManagerService extends IPackageManager.Stub mModuleInfoProvider = new ModuleInfoProvider(mContext, this); // Uncork cache invalidations and allow clients to cache package information. PackageManager.uncorkPackageInfoCache(); // Now after opening every single application zip, make sure they // are all flushed. Not really needed, but keeps things nice and // tidy. services/core/java/com/android/server/pm/permission/PermissionManagerService.java +10 −5 Original line number Diff line number Diff line Loading @@ -3908,11 +3908,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { */ private void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated, @NonNull PermissionCallback callback) { PackageManager.corkPackageInfoCache(); // Prevent invalidation storm try { final int flags = UPDATE_PERMISSIONS_ALL | (sdkUpdated ? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL : 0); updatePermissions(null, null, volumeUuid, flags, callback); } finally { PackageManager.uncorkPackageInfoCache(); } } /** Loading Loading
core/java/android/app/PropertyInvalidatedCache.java +112 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ package android.app; import android.annotation.NonNull; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; Loading Loading @@ -492,6 +496,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> { public static void corkInvalidations(@NonNull String name) { synchronized (sCorkLock) { int numberCorks = sCorks.getOrDefault(name, 0); if (DEBUG) { Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks)); } // If we're the first ones to cork this cache, set the cache to the unset state so // existing caches talk directly to their services while we've corked updates. // Make sure we don't clobber a disabled cache value. Loading Loading @@ -523,6 +531,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> { public static void uncorkInvalidations(@NonNull String name) { synchronized (sCorkLock) { int numberCorks = sCorks.getOrDefault(name, 0); if (DEBUG) { Log.d(TAG, String.format("uncorking %s: numberCorks=%s", name, numberCorks)); } if (numberCorks < 1) { throw new AssertionError("cork underflow: " + name); } Loading @@ -538,6 +550,106 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } } /** * Time-based automatic corking helper. This class allows providers of cached data to * amortize the cost of cache invalidations by corking the cache immediately after a * modification (instructing clients to bypass the cache temporarily) and automatically * uncork after some period of time has elapsed. * * It's better to use explicit cork and uncork pairs that tighly surround big batches of * invalidations, but it's not always practical to tell where these invalidation batches * might occur. AutoCorker's time-based corking is a decent alternative. */ public static final class AutoCorker { public static final int DEFAULT_AUTO_CORK_DELAY_MS = 2000; private final String mPropertyName; private final int mAutoCorkDelayMs; private final Object mLock = new Object(); @GuardedBy("mLock") private long mUncorkDeadlineMs = -1; // SystemClock.uptimeMillis() @GuardedBy("mLock") private Handler mHandler; public AutoCorker(@NonNull String propertyName) { this(propertyName, DEFAULT_AUTO_CORK_DELAY_MS); } public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) { mPropertyName = propertyName; mAutoCorkDelayMs = autoCorkDelayMs; // We can't initialize mHandler here: when we're created, the main loop might not // be set up yet! Wait until we have a main loop to initialize our // corking callback. } public void autoCork() { if (Looper.getMainLooper() == null) { // We're not ready to auto-cork yet, so just invalidate the cache immediately. if (DEBUG) { Log.w(TAG, "invalidating instead of autocorking early in init: " + mPropertyName); } PropertyInvalidatedCache.invalidateCache(mPropertyName); return; } synchronized (mLock) { boolean alreadyQueued = mUncorkDeadlineMs >= 0; if (DEBUG) { Log.w(TAG, String.format( "autoCork mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); } mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs; if (!alreadyQueued) { getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs); PropertyInvalidatedCache.corkInvalidations(mPropertyName); } } } private void handleMessage(Message msg) { synchronized (mLock) { if (DEBUG) { Log.w(TAG, String.format( "handleMsesage mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); } if (mUncorkDeadlineMs < 0) { return; // ??? } long nowMs = SystemClock.uptimeMillis(); if (mUncorkDeadlineMs > nowMs) { mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs; if (DEBUG) { Log.w(TAG, String.format( "scheduling uncork at %s", mUncorkDeadlineMs)); } getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs); return; } if (DEBUG) { Log.w(TAG, "automatic uncorking " + mPropertyName); } mUncorkDeadlineMs = -1; PropertyInvalidatedCache.uncorkInvalidations(mPropertyName); } } @GuardedBy("mLock") private Handler getHandlerLocked() { if (mHandler == null) { mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { AutoCorker.this.handleMessage(msg); } }; } return mHandler; } } protected Result maybeCheckConsistency(Query query, Result proposedResult) { if (VERIFY) { Result resultToCompare = recompute(query); Loading
core/java/android/content/pm/PackageManager.java +15 −0 Original line number Diff line number Diff line Loading @@ -8167,4 +8167,19 @@ public abstract class PackageManager { sPackageInfoCache.disableLocal(); } /** * Inhibit package info cache invalidations when correct. * * @hide */ public static void corkPackageInfoCache() { PropertyInvalidatedCache.corkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO); } /** * Enable package info cache invalidations. * * @hide */ public static void uncorkPackageInfoCache() { PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO); } }
services/core/java/com/android/server/pm/PackageManagerService.java +8 −1 Original line number Diff line number Diff line Loading @@ -2817,10 +2817,14 @@ public class PackageManagerService extends IPackageManager.Stub } public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) { PackageManager.invalidatePackageInfoCache(); PackageManager.disableApplicationInfoCache(); PackageManager.disablePackageInfoCache(); // 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. PackageManager.corkPackageInfoCache(); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); mPendingBroadcasts = new PendingPackageBroadcasts(); Loading Loading @@ -3626,6 +3630,9 @@ public class PackageManagerService extends IPackageManager.Stub mModuleInfoProvider = new ModuleInfoProvider(mContext, this); // Uncork cache invalidations and allow clients to cache package information. PackageManager.uncorkPackageInfoCache(); // Now after opening every single application zip, make sure they // are all flushed. Not really needed, but keeps things nice and // tidy.
services/core/java/com/android/server/pm/permission/PermissionManagerService.java +10 −5 Original line number Diff line number Diff line Loading @@ -3908,11 +3908,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { */ private void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated, @NonNull PermissionCallback callback) { PackageManager.corkPackageInfoCache(); // Prevent invalidation storm try { final int flags = UPDATE_PERMISSIONS_ALL | (sdkUpdated ? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL : 0); updatePermissions(null, null, volumeUuid, flags, callback); } finally { PackageManager.uncorkPackageInfoCache(); } } /** Loading