Merge changes If07d6938,I32f9b89a,Ife4f8c4e into rvc-dev
* changes: RESTRICT AUTOMERGE: Add a facility for time-based cache corking RESTRICT AUTOMERGE: Cork permission and package cache around bulk permission update RESTRICT AUTOMERGE: Cork package information cache invalidations during boot
This commit is contained in:
committed by
Android (Google) Code Review
commit
e185aca339
@@ -17,6 +17,10 @@
|
|||||||
package android.app;
|
package android.app;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
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.os.SystemProperties;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -492,6 +496,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
|
|||||||
public static void corkInvalidations(@NonNull String name) {
|
public static void corkInvalidations(@NonNull String name) {
|
||||||
synchronized (sCorkLock) {
|
synchronized (sCorkLock) {
|
||||||
int numberCorks = sCorks.getOrDefault(name, 0);
|
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
|
// 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.
|
// existing caches talk directly to their services while we've corked updates.
|
||||||
// Make sure we don't clobber a disabled cache value.
|
// Make sure we don't clobber a disabled cache value.
|
||||||
@@ -523,6 +531,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
|
|||||||
public static void uncorkInvalidations(@NonNull String name) {
|
public static void uncorkInvalidations(@NonNull String name) {
|
||||||
synchronized (sCorkLock) {
|
synchronized (sCorkLock) {
|
||||||
int numberCorks = sCorks.getOrDefault(name, 0);
|
int numberCorks = sCorks.getOrDefault(name, 0);
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, String.format("uncorking %s: numberCorks=%s", name, numberCorks));
|
||||||
|
}
|
||||||
|
|
||||||
if (numberCorks < 1) {
|
if (numberCorks < 1) {
|
||||||
throw new AssertionError("cork underflow: " + name);
|
throw new AssertionError("cork underflow: " + name);
|
||||||
}
|
}
|
||||||
@@ -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) {
|
protected Result maybeCheckConsistency(Query query, Result proposedResult) {
|
||||||
if (VERIFY) {
|
if (VERIFY) {
|
||||||
Result resultToCompare = recompute(query);
|
Result resultToCompare = recompute(query);
|
||||||
|
|||||||
@@ -8167,4 +8167,19 @@ public abstract class PackageManager {
|
|||||||
sPackageInfoCache.disableLocal();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2817,10 +2817,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
|
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
|
||||||
PackageManager.invalidatePackageInfoCache();
|
|
||||||
PackageManager.disableApplicationInfoCache();
|
PackageManager.disableApplicationInfoCache();
|
||||||
PackageManager.disablePackageInfoCache();
|
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",
|
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
|
||||||
Trace.TRACE_TAG_PACKAGE_MANAGER);
|
Trace.TRACE_TAG_PACKAGE_MANAGER);
|
||||||
mPendingBroadcasts = new PendingPackageBroadcasts();
|
mPendingBroadcasts = new PendingPackageBroadcasts();
|
||||||
@@ -3626,6 +3630,9 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
|
|
||||||
mModuleInfoProvider = new ModuleInfoProvider(mContext, this);
|
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
|
// Now after opening every single application zip, make sure they
|
||||||
// are all flushed. Not really needed, but keeps things nice and
|
// are all flushed. Not really needed, but keeps things nice and
|
||||||
// tidy.
|
// tidy.
|
||||||
|
|||||||
@@ -3908,11 +3908,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
|
|||||||
*/
|
*/
|
||||||
private void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated,
|
private void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated,
|
||||||
@NonNull PermissionCallback callback) {
|
@NonNull PermissionCallback callback) {
|
||||||
|
PackageManager.corkPackageInfoCache(); // Prevent invalidation storm
|
||||||
|
try {
|
||||||
final int flags = UPDATE_PERMISSIONS_ALL |
|
final int flags = UPDATE_PERMISSIONS_ALL |
|
||||||
(sdkUpdated
|
(sdkUpdated
|
||||||
? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
|
? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
|
||||||
: 0);
|
: 0);
|
||||||
updatePermissions(null, null, volumeUuid, flags, callback);
|
updatePermissions(null, null, volumeUuid, flags, callback);
|
||||||
|
} finally {
|
||||||
|
PackageManager.uncorkPackageInfoCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user