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

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

Merge "PIC does not cache nulls by default" into main

parents c9974ad9 675732af
Loading
Loading
Loading
Loading
+23 −6
Original line number Diff line number Diff line
@@ -1294,6 +1294,13 @@ public class PropertyInvalidatedCache<Query, Result> {
    public static record Args(@NonNull String mModule, @Nullable String mApi,
            int mMaxEntries, boolean mIsolateUids, boolean mTestMode, boolean mCacheNulls) {

        /**
         * Default values for fields.
         */
        public static final int DEFAULT_MAX_ENTRIES = 32;
        public static final boolean DEFAULT_ISOLATE_UIDS = true;
        public static final boolean DEFAULT_CACHE_NULLS = false;

        // Validation: the module must be one of the known module strings and the maxEntries must
        // be positive.
        public Args {
@@ -1308,10 +1315,10 @@ public class PropertyInvalidatedCache<Query, Result> {
        public Args(@NonNull String module) {
            this(module,
                    null,       // api
                    32,         // maxEntries
                    true,       // isolateUids
                    DEFAULT_MAX_ENTRIES,
                    DEFAULT_ISOLATE_UIDS,
                    false,      // testMode
                    true        // allowNulls
                    DEFAULT_CACHE_NULLS
                 );
        }

@@ -1361,7 +1368,7 @@ public class PropertyInvalidatedCache<Query, Result> {
     * Burst a property name into module and api.  Throw if the key is invalid.  This method is
     * used in to transition legacy cache constructors to the args constructor.
     */
    private static Args parseProperty(@NonNull String name) {
    private static Args argsFromProperty(@NonNull String name) {
        throwIfInvalidCacheKey(name);
        // Strip off the leading well-known prefix.
        String base = name.substring(CACHE_KEY_PREFIX.length() + 1);
@@ -1384,8 +1391,9 @@ public class PropertyInvalidatedCache<Query, Result> {
     *
     * @hide
     */
    @Deprecated
    public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
        this(parseProperty(propertyName).maxEntries(maxEntries), propertyName, null);
        this(argsFromProperty(propertyName).maxEntries(maxEntries), propertyName, null);
    }

    /**
@@ -1399,9 +1407,10 @@ public class PropertyInvalidatedCache<Query, Result> {
     * @param cacheName Name of this cache in debug and dumpsys
     * @hide
     */
    @Deprecated
    public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
            @NonNull String cacheName) {
        this(parseProperty(propertyName).maxEntries(maxEntries), cacheName, null);
        this(argsFromProperty(propertyName).maxEntries(maxEntries), cacheName, null);
    }

    /**
@@ -1856,6 +1865,14 @@ public class PropertyInvalidatedCache<Query, Result> {
        invalidateCache(createPropertyName(module, api));
    }

    /**
     * Invalidate caches in all processes that have the module and api specified in the args.
     * @hide
     */
    public static void invalidateCache(@NonNull Args args) {
        invalidateCache(createPropertyName(args.mModule, args.mApi));
    }

    /**
     * Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
     * {@var name}. This function is synchronous: caches are invalidated upon return.
+34 −32
Original line number Diff line number Diff line
@@ -400,10 +400,11 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
    }

    /**
     * This is a convenience class that encapsulates configuration information for a
     * cache.  It may be supplied to the cache constructors in lieu of the other
     * parameters.  The class captures maximum entry count, the module, the key, and the
     * api.
     * This is a convenience class that encapsulates configuration information for a cache.  It
     * may be supplied to the cache constructors in lieu of the other parameters.  The class
     * captures maximum entry count, the module, the key, and the api.  The key is used to
     * invalidate the cache and may be shared by different caches.  The api is a user-visible (in
     * debug) name for the cache.
     *
     * There are three specific use cases supported by this class.
     *
@@ -430,11 +431,8 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
     * @hide
     */
    public static class Config {
        private final int mMaxEntries;
        @IpcDataCacheModule
        private final String mModule;
        private final String mApi;
        private final String mName;
        final Args mArgs;
        final String mName;

        /**
         * The list of cache names that were created extending this Config.  If
@@ -452,12 +450,20 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
         */
        private boolean mDisabled = false;

        /**
         * Fully construct a config.
         */
        private Config(@NonNull Args args, @NonNull String name) {
            mArgs = args;
            mName = name;
        }

        /**
         *
         */
        public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
                @NonNull String api, @NonNull String name) {
            mMaxEntries = maxEntries;
            mModule = module;
            mApi = api;
            mName = name;
            this(new Args(module).api(api).maxEntries(maxEntries), name);
        }

        /**
@@ -473,7 +479,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
         * the parameter list.
         */
        public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
            this(root.maxEntries(), root.module(), api, name);
            this(root.mArgs.api(api), name);
        }

        /**
@@ -481,7 +487,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
         * the parameter list.
         */
        public Config(@NonNull Config root, @NonNull String api) {
            this(root.maxEntries(), root.module(), api, api);
            this(root.mArgs.api(api), api);
        }

        /**
@@ -490,26 +496,23 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
         * current process.
         */
        public Config child(@NonNull String name) {
            final Config result = new Config(this, api(), name);
            final Config result = new Config(mArgs, name);
            registerChild(name);
            return result;
        }

        public final int maxEntries() {
            return mMaxEntries;
        }

        @IpcDataCacheModule
        public final @NonNull String module() {
            return mModule;
        }

        public final @NonNull String api() {
            return mApi;
        /**
         * Set the cacheNull behavior.
         */
        public Config cacheNulls(boolean enable) {
            return new Config(mArgs.cacheNulls(enable), mName);
        }

        public final @NonNull String name() {
            return mName;
        /**
         * Set the isolateUidss behavior.
         */
        public Config isolateUids(boolean enable) {
            return new Config(mArgs.isolateUids(enable), mName);
        }

        /**
@@ -532,7 +535,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
         * Invalidate all caches that share this Config's module and api.
         */
        public void invalidateCache() {
            IpcDataCache.invalidateCache(mModule, mApi);
            IpcDataCache.invalidateCache(mArgs);
        }

        /**
@@ -564,8 +567,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
     * @hide
     */
    public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
      super(new Args(config.module()).maxEntries(config.maxEntries()).api(config.api()),
          config.name(), computer);
        super(config.mArgs, config.mName, computer);
    }

    /**
+15 −0
Original line number Diff line number Diff line
@@ -740,5 +740,20 @@ public class PropertyInvalidatedCacheTests {
        assertEquals(null, cache.query(30));
        // The recompute is 4 because nulls were not cached.
        assertEquals(4, cache.getRecomputeCount());

        // Verify that the default is not to cache nulls.
        cache = new TestCache(new Args(MODULE_TEST)
                .maxEntries(4).api("testCachingNulls"),
                new TestQuery());
        cache.invalidateCache();
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        assertEquals(3, cache.getRecomputeCount());
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        // The recompute is 4 because nulls were not cached.
        assertEquals(4, cache.getRecomputeCount());
    }
}
+64 −18
Original line number Diff line number Diff line
@@ -16,13 +16,21 @@

package android.os;

import static android.app.Flags.FLAG_PIC_CACHE_NULLS;
import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;

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

import android.app.PropertyInvalidatedCache;
import android.app.PropertyInvalidatedCache.Args;
import android.multiuser.Flags;
import android.platform.test.annotations.IgnoreUnderRavenwood;
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;

@@ -43,6 +51,10 @@ 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";
@@ -287,8 +299,13 @@ public class IpcDataCacheTest {
        @Override
        public String apply(Integer qv) {
            mRecomputeCount += 1;
            // Special case for testing caches of nulls.  Integers in the range 30-40 return null.
            if (qv >= 30 && qv < 40) {
                return null;
            } else {
                return "foo" + qv.toString();
            }
        }

        int getRecomputeCount() {
            return mRecomputeCount;
@@ -406,31 +423,16 @@ public class IpcDataCacheTest {
    }

    @Test
    public void testConfig() {
    public void testConfigDisable() {
        // Create a set of caches based on a set of chained configs.
        IpcDataCache.Config a = new IpcDataCache.Config(8, MODULE, "apiA");
        TestCache ac = new TestCache(a);
        assertEquals(8, a.maxEntries());
        assertEquals(MODULE, a.module());
        assertEquals("apiA", a.api());
        assertEquals("apiA", a.name());
        IpcDataCache.Config b = new IpcDataCache.Config(a, "apiB");
        TestCache bc = new TestCache(b);
        assertEquals(8, b.maxEntries());
        assertEquals(MODULE, b.module());
        assertEquals("apiB", b.api());
        assertEquals("apiB", b.name());
        IpcDataCache.Config c = new IpcDataCache.Config(a, "apiC", "nameC");
        TestCache cc = new TestCache(c);
        assertEquals(8, c.maxEntries());
        assertEquals(MODULE, c.module());
        assertEquals("apiC", c.api());
        assertEquals("nameC", c.name());
        IpcDataCache.Config d = a.child("nameD");
        TestCache dc = new TestCache(d);
        assertEquals(8, d.maxEntries());
        assertEquals(MODULE, d.module());
        assertEquals("apiA", d.api());
        assertEquals("nameD", d.name());

        a.disableForCurrentProcess();
        assertEquals(ac.isDisabled(), true);
@@ -449,6 +451,7 @@ public class IpcDataCacheTest {
        assertEquals(ec.isDisabled(), true);
    }


    // Verify that invalidating the cache from an app process would fail due to lack of permissions.
    @Test
    @android.platform.test.annotations.DisabledOnRavenwood(
@@ -507,4 +510,47 @@ public class IpcDataCacheTest {
        // Re-enable test mode (so that the cleanup for the test does not throw).
        IpcDataCache.setTestMode(true);
    }

    @RequiresFlagsEnabled(FLAG_PIC_CACHE_NULLS)
    @Test
    public void testCachingNulls() {
        IpcDataCache.Config c =
                new IpcDataCache.Config(4, IpcDataCache.MODULE_TEST, "testCachingNulls");
        TestCache cache;
        cache = new TestCache(c.cacheNulls(true));
        cache.invalidateCache();
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        assertEquals(3, cache.getRecomputeCount());
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        assertEquals(3, cache.getRecomputeCount());

        cache = new TestCache(c.cacheNulls(false));
        cache.invalidateCache();
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        assertEquals(3, cache.getRecomputeCount());
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        // The recompute is 4 because nulls were not cached.
        assertEquals(4, cache.getRecomputeCount());

        // Verify that the default is not to cache nulls.
        cache = new TestCache(c);
        cache.invalidateCache();
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        assertEquals(3, cache.getRecomputeCount());
        assertEquals("foo1", cache.query(1));
        assertEquals("foo2", cache.query(2));
        assertEquals(null, cache.query(30));
        // The recompute is 4 because nulls were not cached.
        assertEquals(4, cache.getRecomputeCount());
    }
}