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

Commit e185aca3 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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
parents 65693396 f0e88b2f
Loading
Loading
Loading
Loading
+112 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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.
@@ -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);
            }
@@ -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);
+15 −0
Original line number Diff line number Diff line
@@ -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);
    }
}
+8 −1
Original line number Diff line number Diff line
@@ -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();
@@ -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.
+10 −5
Original line number Diff line number Diff line
@@ -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();
        }
    }

    /**