diff options
4 files changed, 130 insertions, 56 deletions
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 9bb0ba4cf8fa..1e971a5c736a 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -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); } /** @@ -1857,6 +1866,14 @@ public class PropertyInvalidatedCache<Query, Result> { } /** + * 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. * diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java index 8db1567336d3..a2e9314f6436 100644 --- a/core/java/android/os/IpcDataCache.java +++ b/core/java/android/os/IpcDataCache.java @@ -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); } /** diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index 177c7f0b2f27..bd273377984d 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -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()); } } diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java index e14608ac503d..523b1b075a7c 100644 --- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java +++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java @@ -16,13 +16,19 @@ 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.ravenwood.RavenwoodRule; +import android.os.IpcDataCache; import androidx.test.filters.SmallTest; @@ -287,7 +293,12 @@ public class IpcDataCacheTest { @Override public String apply(Integer qv) { mRecomputeCount += 1; - return "foo" + qv.toString(); + // 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() { @@ -406,31 +417,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 +445,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 +504,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()); + } } |