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

Commit cce1ef45 authored by Lee Shombert's avatar Lee Shombert Committed by Android (Google) Code Review
Browse files

Merge "Cleanup PIC flags" into main

parents 3525c956 77d90690
Loading
Loading
Loading
Loading
+4 −217
Original line number Diff line number Diff line
@@ -23,24 +23,17 @@ import static com.android.internal.util.Preconditions.checkArgumentPositive;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SystemPropertySetter;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ApplicationSharedMemory;
import com.android.internal.os.BackgroundThread;

import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -80,13 +73,11 @@ import java.util.concurrent.atomic.AtomicLong;
public class PropertyInvalidatedCache<Query, Result> {
    /**
     * A method to report if the PermissionManager notifications can be separated from cache
     * invalidation.  The feature relies on a series of flags; the dependency is captured in this
     * method.
     * invalidation.  The feature relies on a series of flags, all of which are committed.
     * @hide
     */
    public static boolean separatePermissionNotificationsEnabled() {
        return isSharedMemoryAvailable()
                && Flags.picSeparatePermissionNotifications();
        return true;
    }

    /**
@@ -496,25 +487,6 @@ public class PropertyInvalidatedCache<Query, Result> {
        // entries to be combined in a single hash map.
        private final boolean mIsolated;

        // Collect statistics.
        private final boolean mStatistics;

        // An array of booleans to indicate if a UID has been involved in a map access.  A value
        // exists for every UID that was ever involved during cache access. This is updated only
        // if statistics are being collected.
        private final SparseBooleanArray mUidSeen;

        // A hash map that ignores the UID.  This is used in look-aside fashion just for hit/miss
        // statistics.  This is updated only if statistics are being collected.
        private final ArraySet<Query> mShadowCache;

        // Shadow statistics.  Only hits and misses need to be recorded.  These are updated only
        // if statistics are being collected.  The "SelfHits" records hits when the UID is the
        // process uid.
        private int mShadowHits;
        private int mShadowMisses;
        private int mShadowSelfHits;

        // The process UID.
        private final int mSelfUid;

@@ -526,15 +498,7 @@ public class PropertyInvalidatedCache<Query, Result> {
         * isolation feature is enabled.
         */
        CacheMap(boolean isolate, boolean testMode) {
            mIsolated = Flags.picIsolateCacheByUid() && isolate;
            mStatistics = Flags.picIsolatedCacheStatistics() && mIsolated;
            if (mStatistics) {
                mUidSeen = new SparseBooleanArray();
                mShadowCache = new ArraySet<>();
            } else {
                mUidSeen = null;
                mShadowCache = null;
            }
            mIsolated = isolate;
            mSelfUid = Process.myUid();
            mTestMode = testMode;
        }
@@ -556,19 +520,6 @@ public class PropertyInvalidatedCache<Query, Result> {
         */
        Result get(Query query) {
            final int uid = callerUid();

            // Shadow statistics
            if (mStatistics) {
                if (mShadowCache.contains(query)) {
                    mShadowHits++;
                    if (uid == mSelfUid) {
                        mShadowSelfHits++;
                    }
                } else {
                    mShadowMisses++;
                }
            }

            var map = mCache.get(uid);
            if (map != null) {
                return map.get(query);
@@ -595,10 +546,6 @@ public class PropertyInvalidatedCache<Query, Result> {
         */
        void remove(Query query) {
            final int uid = callerUid();
            if (mStatistics) {
                mShadowCache.remove(query);
            }

            var map = mCache.get(uid);
            if (map != null) {
                map.remove(query);
@@ -610,11 +557,6 @@ public class PropertyInvalidatedCache<Query, Result> {
         */
        void put(Query query, Result result) {
            final int uid = callerUid();
            if (mStatistics) {
                mShadowCache.add(query);
                mUidSeen.put(uid, true);
            }

            var map = mCache.get(uid);
            if (map == null) {
                map = createMap();
@@ -639,23 +581,9 @@ public class PropertyInvalidatedCache<Query, Result> {
         * Clear the entries in the cache.  Update the shadow statistics.
         */
        void clear() {
            if (mStatistics) {
                mShadowCache.clear();
            }

            mCache.clear();
        }

        // Dump basic statistics, if any are collected.  Do nothing if statistics are not enabled.
        void dump(PrintWriter pw) {
            if (mStatistics) {
                pw.println(formatSimple("    ShadowHits: %d, ShadowMisses: %d, ShadowSize: %d",
                                mShadowHits, mShadowMisses, mShadowCache.size()));
                pw.println(formatSimple("    ShadowUids: %d, SelfUid: %d",
                                mUidSeen.size(), mShadowSelfHits));
            }
        }

        // Dump detailed statistics
        void dumpDetailed(PrintWriter pw) {
            for (int i = 0; i < mCache.size(); i++) {
@@ -1304,19 +1232,10 @@ public class PropertyInvalidatedCache<Query, Result> {
        return false; // Always disable shared memory on Ravenwood. (for now)
    }

    /**
     * Keys that cannot be put in shared memory yet.
     */
    private static boolean inSharedMemoryDenyList(@NonNull String name) {
        final String pkginfo = PREFIX_SYSTEM + "package_info";
        return name.equals(pkginfo);
    }

    // Return true if this cache can use shared memory for its nonce.  Shared memory may be used
    // if the module is the system.
    private static boolean sharedMemoryOkay(@NonNull CacheKey id) {
        return sSharedMemoryAvailable && sNamespaceSystem.equals(id.namespace)
                && !inSharedMemoryDenyList(id.key);
        return sSharedMemoryAvailable && sNamespaceSystem.equals(id.namespace);
    }

    /**
@@ -1936,137 +1855,6 @@ public class PropertyInvalidatedCache<Query, Result> {
        getNonceHandler(name).uncork();
    }

    /**
     * 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.
     *
     * The auto-cork delay is configurable but it should not be too long.  The purpose of
     * the delay is to minimize the number of times a server writes to the system property
     * when invalidating the cache.  One write every 50ms does not hurt system performance.
     * @hide
     */
    public static final class AutoCorker {
        public static final int DEFAULT_AUTO_CORK_DELAY_MS = 50;

        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;

        @GuardedBy("mLock")
        private NonceHandler mNonce;

        public AutoCorker(@NonNull String propertyName) {
            this(propertyName, DEFAULT_AUTO_CORK_DELAY_MS);
        }

        public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) {
            if (separatePermissionNotificationsEnabled()) {
                throw new IllegalStateException("AutoCorking is unavailable");
            }

            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() {
            synchronized (mLock) {
                if (mNonce == null) {
                    mNonce = getNonceHandler(mPropertyName);
                }
            }

            if (getLooper() == 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);
                }
                mNonce.invalidate();
                return;
            }
            synchronized (mLock) {
                boolean alreadyQueued = mUncorkDeadlineMs >= 0;
                if (DEBUG) {
                    Log.d(TAG, formatSimple(
                            "autoCork %s mUncorkDeadlineMs=%s", mPropertyName,
                            mUncorkDeadlineMs));
                }
                mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs;
                if (!alreadyQueued) {
                    getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
                    mNonce.cork();
                } else {
                    // Count this as a corked invalidation.
                    mNonce.invalidate();
                }
            }
        }

        private void handleMessage(Message msg) {
            synchronized (mLock) {
                if (DEBUG) {
                    Log.d(TAG, formatSimple(
                            "handleMsesage %s mUncorkDeadlineMs=%s",
                            mPropertyName, mUncorkDeadlineMs));
                }

                if (mUncorkDeadlineMs < 0) {
                    return;  // ???
                }
                long nowMs = SystemClock.uptimeMillis();
                if (mUncorkDeadlineMs > nowMs) {
                    mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs;
                    if (DEBUG) {
                        Log.d(TAG, formatSimple(
                                        "scheduling uncork at %s",
                                        mUncorkDeadlineMs));
                    }
                    getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
                    return;
                }
                if (DEBUG) {
                    Log.d(TAG, "automatic uncorking " + mPropertyName);
                }
                mUncorkDeadlineMs = -1;
                mNonce.uncork();
            }
        }

        @GuardedBy("mLock")
        private Handler getHandlerLocked() {
            if (mHandler == null) {
                mHandler = new Handler(getLooper()) {
                        @Override
                        public void handleMessage(Message msg) {
                            AutoCorker.this.handleMessage(msg);
                        }
                    };
            }
            return mHandler;
        }

        /**
         * Return a looper for auto-uncork messages.  Messages should be processed on the
         * background thread, not on the main thread.
         */
        private static Looper getLooper() {
            return BackgroundThread.getHandler().getLooper();
        }
    }

    /**
     * Return the name of the cache, to be used in debug messages.  This is exposed
     * primarily for testing.
@@ -2243,7 +2031,6 @@ public class PropertyInvalidatedCache<Query, Result> {
            pw.println(formatSimple(
                "    Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
                mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
            mCache.dump(pw);

            if (!brief) {
                if (isReservedNonce(mLastSeenNonce)) {
+0 −32
Original line number Diff line number Diff line
@@ -10,38 +10,6 @@ flag {
     bug: "360897450"
}

flag {
     namespace: "system_performance"
     name: "pic_isolate_cache_by_uid"
     is_fixed_read_only: true
     description: "Ensure that different UIDs use different caches"
     bug: "373752556"
}

flag {
     namespace: "system_performance"
     name: "pic_isolated_cache_statistics"
     is_fixed_read_only: true
     description: "Collects statistics for cache UID isolation strategies"
     bug: "379098894"
}

flag {
     namespace: "system_performance"
     name: "pic_separate_permission_notifications"
     is_fixed_read_only: true
     description: "Seperate PermissionManager notifications from cache udpates"
     bug: "379699402"
}

flag {
     namespace: "system_performance"
     name: "pic_cache_nulls"
     is_fixed_read_only: true
     description: "Cache null returns from binder calls"
     bug: "372923336"
}

flag {
     namespace: "system_performance"
     name: "pic_test_mode"
+1 −13
Original line number Diff line number Diff line
@@ -11888,25 +11888,13 @@ public abstract class PackageManager {
        sPackageInfoCache.uncorkInvalidations();
    }

    // This auto-corker is obsolete once the separate permission notifications feature is
    // committed.
    private static final PropertyInvalidatedCache.AutoCorker sCacheAutoCorker =
            PropertyInvalidatedCache.separatePermissionNotificationsEnabled()
            ? null
            : new PropertyInvalidatedCache
                    .AutoCorker(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);

    /**
     * Invalidate caches of package and permission information system-wide.
     *
     * @hide
     */
    public static void invalidatePackageInfoCache() {
        if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
        sPackageInfoCache.invalidateCache();
        } else {
            sCacheAutoCorker.autoCork();
        }
    }

    /**
+0 −3
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.app;

import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
import static android.app.PropertyInvalidatedCache.MODULE_ADSERVICES;
import static android.app.PropertyInvalidatedCache.MODULE_BLUETOOTH;
@@ -39,7 +38,6 @@ import android.app.PropertyInvalidatedCache.NonceStore;
import android.app.PropertyInvalidatedCache.NonceWatcher;
import android.os.Binder;
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;

@@ -667,7 +665,6 @@ public class PropertyInvalidatedCacheTests {
    }

    // Verify that a cache created with isolatedUids(true) separates out the results.
    @RequiresFlagsEnabled(FLAG_PIC_ISOLATE_CACHE_BY_UID)
    @Test
    public void testIsolatedUids() {
        TestCache cache = new TestCache(new Args(MODULE_TEST)
+0 −14
Original line number Diff line number Diff line
@@ -16,27 +16,17 @@

package android.os;

import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.app.PropertyInvalidatedCache;
import android.app.PropertyInvalidatedCache.Args;
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import android.os.IpcDataCache;

import androidx.test.filters.SmallTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

/**
@@ -51,10 +41,6 @@ import org.junit.Test;
@SmallTest
public class IpcDataCacheTest {

    @Rule
    public final CheckFlagsRule mCheckFlagsRule =
            DeviceFlagsValueProvider.createCheckFlagsRule();

    // Configuration for creating caches
    private static final String MODULE = IpcDataCache.MODULE_TEST;
    private static final String API = "testApi";