• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.TestApi;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.os.ParcelFileDescriptor;
26 import android.os.SystemClock;
27 import android.os.SystemProperties;
28 import android.text.TextUtils;
29 import android.util.Log;
30 
31 import com.android.internal.annotations.GuardedBy;
32 import com.android.internal.util.FastPrintWriter;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.io.ByteArrayOutputStream;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.PrintWriter;
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.LinkedHashMap;
44 import java.util.Map;
45 import java.util.Objects;
46 import java.util.Random;
47 import java.util.Set;
48 import java.util.WeakHashMap;
49 import java.util.concurrent.atomic.AtomicLong;
50 
51 /**
52  * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
53  * but doesn't hold a lock across data fetches on query misses.
54  *
55  * The intended use case is caching frequently-read, seldom-changed information normally
56  * retrieved across interprocess communication. Imagine that you've written a user birthday
57  * information daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface
58  * over binder. That binder interface looks something like this:
59  *
60  * <pre>
61  * parcelable Birthday {
62  *   int month;
63  *   int day;
64  * }
65  * interface IUserBirthdayService {
66  *   Birthday getUserBirthday(int userId);
67  * }
68  * </pre>
69  *
70  * Suppose the service implementation itself looks like this...
71  *
72  * <pre>
73  * public class UserBirthdayServiceImpl implements IUserBirthdayService {
74  *   private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
75  *   {@literal @}Override
76  *   public synchronized Birthday getUserBirthday(int userId) {
77  *     return mUidToBirthday.get(userId);
78  *   }
79  *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
80  *     mUidToBirthday.clear();
81  *     mUidToBirthday.putAll(uidToBirthday);
82  *   }
83  * }
84  * </pre>
85  *
86  * ... and we have a client in frameworks (loaded into every app process) that looks
87  * like this:
88  *
89  * <pre>
90  * public class ActivityThread {
91  *   ...
92  *   public Birthday getUserBirthday(int userId) {
93  *     return GetService("birthdayd").getUserBirthday(userId);
94  *   }
95  *   ...
96  * }
97  * </pre>
98  *
99  * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call
100  * to the birthdayd process and consult its database of birthdays. If we query user birthdays
101  * frequently, we do a lot of work that we don't have to do, since user birthdays
102  * change infrequently.
103  *
104  * PropertyInvalidatedCache is part of a pattern for optimizing this kind of
105  * information-querying code. Using {@code PropertyInvalidatedCache}, you'd write the client
106  * this way:
107  *
108  * <pre>
109  * public class ActivityThread {
110  *   ...
111  *   private final PropertyInvalidatedCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
112  *       new PropertyInvalidatedCache.QueryHandler&lt;Integer, Birthday&gt;() {
113  *           {@literal @}Override
114  *           public Birthday apply(Integer) {
115  *              return GetService("birthdayd").getUserBirthday(userId);
116  *           }
117  *       };
118  *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
119  *   private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
120  *   private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
121  *     PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(
122  *             BDAY_CACHE_MAX, MODULE_SYSTEM, "getUserBirthday", mBirthdayQuery);
123  *
124  *   public void disableUserBirthdayCache() {
125  *     mBirthdayCache.disableForCurrentProcess();
126  *   }
127  *   public void invalidateUserBirthdayCache() {
128  *     mBirthdayCache.invalidateCache();
129  *   }
130  *   public Birthday getUserBirthday(int userId) {
131  *     return mBirthdayCache.query(userId);
132  *   }
133  *   ...
134  * }
135  * </pre>
136  *
137  * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
138  * for the first time; on subsequent queries, we return the already-known Birthday object.
139  *
140  * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
141  * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
142  * string is permitted.  The third parameters is the name of the API being cached; this, too, can
143  * any value.  The fourth is the name of the cache.  The cache is usually named after th API.
144  * Some things you must know about the three strings:
145  * <list>
146  * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
147  * Usually, the SELinux rules permit a process to write a system property (and therefore
148  * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}.  This means that
149  * although the cache can be constructed with any module string, whatever string is chosen must be
150  * consistent with the SELinux configuration.
151  * <ul> The API name can be any string of alphanumeric characters.  All caches with the same API
152  * are invalidated at the same time.  If a server supports several caches and all are invalidated
153  * in common, then it is most efficient to assign the same API string to every cache.
154  * <ul> The cache name can be any string.  In debug output, the name is used to distiguish between
155  * caches with the same API name.  The cache name is also used when disabling caches in the
156  * current process.  So, invalidation is based on the module+api but disabling (which is generally
157  * a once-per-process operation) is based on the cache name.
158  * </list>
159  *
160  * User birthdays do occasionally change, so we have to modify the server to invalidate this
161  * cache when necessary. That invalidation code looks like this:
162  *
163  * <pre>
164  * public class UserBirthdayServiceImpl {
165  *   ...
166  *   public UserBirthdayServiceImpl() {
167  *     ...
168  *     ActivityThread.currentActivityThread().disableUserBirthdayCache();
169  *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
170  *   }
171  *
172  *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
173  *     mUidToBirthday.clear();
174  *     mUidToBirthday.putAll(uidToBirthday);
175  *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
176  *   }
177  *   ...
178  * }
179  * </pre>
180  *
181  * The call to {@code PropertyInvalidatedCache.invalidateCache()} guarantees that all clients
182  * will re-fetch birthdays from binder during consequent calls to
183  * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
184  * held, we maintain consistency between different client views of the birthday state. The use
185  * of PropertyInvalidatedCache in this idiomatic way introduces no new race conditions.
186  *
187  * PropertyInvalidatedCache has a few other features for doing things like incremental
188  * enhancement of cached values and invalidation of multiple caches (that all share the same
189  * property key) at once.
190  *
191  * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
192  * time we update the cache. SELinux configuration must allow everyone to read this property
193  * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
194  * the property. (These properties conventionally begin with the "cache_key." prefix.)
195  *
196  * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
197  * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In
198  * this local case, there's no IPC, so use of the cache is (depending on exact
199  * circumstance) unnecessary.
200  *
201  * There may be queries for which it is more efficient to bypass the cache than to cache
202  * the result.  This would be true, for example, if some queries would require frequent
203  * cache invalidation while other queries require infrequent invalidation.  To expand on
204  * the birthday example, suppose that there is a userId that signifies "the next
205  * birthday".  When passed this userId, the server returns the next birthday among all
206  * users - this value changes as time advances.  The userId value can be cached, but the
207  * cache must be invalidated whenever a birthday occurs, and this invalidates all
208  * birthdays.  If there is a large number of users, invalidation will happen so often that
209  * the cache provides no value.
210  *
211  * The class provides a bypass mechanism to handle this situation.
212  * <pre>
213  * public class ActivityThread {
214  *   ...
215  *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
216  *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
217  *           {@literal @}Override
218  *           public Birthday apply(Integer) {
219  *              return GetService("birthdayd").getUserBirthday(userId);
220  *           }
221  *           {@literal @}Override
222  *           public boolean shouldBypassQuery(Integer userId) {
223  *               return userId == NEXT_BIRTHDAY;
224  *           }
225  *       };
226  *   ...
227  * }
228  * </pre>
229  *
230  * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
231  * particular query.  The {@code shouldBypassQuery()} method is not abstract and the default
232  * implementation returns false.
233  *
234  * For security, there is a allowlist of processes that are allowed to invalidate a cache.
235  * The allowlist includes normal runtime processes but does not include test processes.
236  * Test processes must call {@code PropertyInvalidatedCache.disableForTestMode()} to disable
237  * all cache activity in that process.
238  *
239  * Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
240  *
241  * To test a binder cache, create one or more tests that exercise the binder method.  This
242  * should be done twice: once with production code and once with a special image that sets
243  * {@code DEBUG} and {@code VERIFY} true.  In the latter case, verify that no cache
244  * inconsistencies are reported.  If a cache inconsistency is reported, however, it might be a
245  * false positive.  This happens if the server side data can be read and written non-atomically
246  * with respect to cache invalidation.
247  *
248  * @param <Query> The class used to index cache entries: must be hashable and comparable
249  * @param <Result> The class holding cache entries; use a boxed primitive if possible
250  * @hide
251  */
252 @TestApi
253 public class PropertyInvalidatedCache<Query, Result> {
254     /**
255      * This is a configuration class that customizes a cache instance.
256      * @hide
257      */
258     @TestApi
259     public static abstract class QueryHandler<Q,R> {
260         /**
261          * Compute a result given a query.  The semantics are those of Functor.
262          */
apply(@onNull Q query)263         public abstract @Nullable R apply(@NonNull Q query);
264 
265         /**
266          * Return true if a query should not use the cache.  The default implementation
267          * always uses the cache.
268          */
shouldBypassCache(@onNull Q query)269         public boolean shouldBypassCache(@NonNull Q query) {
270             return false;
271         }
272     };
273 
274     /**
275      * The system properties used by caches should be of the form <prefix>.<module>.<api>,
276      * where the prefix is "cache_key", the module is one of the constants below, and the
277      * api is any string.  The ability to write the property (which happens during
278      * invalidation) depends on SELinux rules; these rules are defined against
279      * <prefix>.<module>.  Therefore, the module chosen for a cache property must match
280      * the permissions granted to the processes that contain the corresponding caches.
281      * @hide
282      */
283 
284     /**
285      * The module used for unit tests and cts tests.  It is expected that no process in
286      * the system has permissions to write properties with this module.
287      * @hide
288      */
289     @TestApi
290     public static final String MODULE_TEST = "test";
291 
292     /**
293      * The module used for system server/framework caches.  This is not visible outside
294      * the system processes.
295      * @hide
296      */
297     @TestApi
298     public static final String MODULE_SYSTEM = "system_server";
299 
300     /**
301      * The module used for bluetooth caches.
302      * @hide
303      */
304     @TestApi
305     public static final String MODULE_BLUETOOTH = "bluetooth";
306 
307     /**
308      * The module used for telephony caches.
309      */
310     public static final String MODULE_TELEPHONY = "telephony";
311 
312     /**
313      * Constants that affect retries when the process is unable to write the property.
314      * The first constant is the number of times the process will attempt to set the
315      * property.  The second constant is the delay between attempts.
316      */
317 
318     /**
319      * Wait 200ms between retry attempts and the retry limit is 5.  That gives a total possible
320      * delay of 1s, which should be less than ANR timeouts.  The goal is to have the system crash
321      * because the property could not be set (which is a condition that is easily recognized) and
322      * not crash because of an ANR (which can be confusing to debug).
323      */
324     private static final int PROPERTY_FAILURE_RETRY_DELAY_MILLIS = 200;
325     private static final int PROPERTY_FAILURE_RETRY_LIMIT = 5;
326 
327     /**
328      * Construct a system property that matches the rules described above.  The module is
329      * one of the permitted values above.  The API is a string that is a legal Java simple
330      * identifier.  The api is modified to conform to the system property style guide by
331      * replacing every upper case letter with an underscore and the lower case equivalent.
332      * (An initial upper case letter is not prefixed with an underscore).
333      * There is no requirement that the apiName be the name of an actual API.
334      *
335      * Be aware that SystemProperties has a maximum length which is private to the
336      * implementation.  The current maximum is 92 characters. If this method creates a
337      * property name that is too long, SystemProperties.set() will fail without a good
338      * error message.
339      * @hide
340      */
341     @TestApi
createPropertyName(@onNull String module, @NonNull String apiName)342     public static @NonNull String createPropertyName(@NonNull String module,
343             @NonNull String apiName) {
344         char[] api = apiName.toCharArray();
345         int upper = 0;
346         for (int i = 1; i < api.length; i++) {
347             if (Character.isUpperCase(api[i])) {
348                 upper++;
349             }
350         }
351         char[] suffix = new char[api.length + upper];
352         int j = 0;
353         for (int i = 0; i < api.length; i++) {
354             if (Character.isJavaIdentifierPart(api[i])) {
355                 if (Character.isUpperCase(api[i])) {
356                     if (i > 0) {
357                         suffix[j++] = '_';
358                     }
359                     suffix[j++] = Character.toLowerCase(api[i]);
360                 } else {
361                     suffix[j++] = api[i];
362                 }
363             } else {
364                 throw new IllegalArgumentException("invalid api name");
365             }
366         }
367 
368         return "cache_key." + module + "." + new String(suffix);
369     }
370 
371     /**
372      * Reserved nonce values.  Use isReservedNonce() to test for a reserved value.  Note
373      * that all values cause the cache to be skipped.
374      */
375     private static final int NONCE_UNSET = 0;
376     private static final int NONCE_DISABLED = 1;
377     private static final int NONCE_CORKED = 2;
378     private static final int NONCE_BYPASS = 3;
379 
isReservedNonce(long n)380     private static boolean isReservedNonce(long n) {
381         return n >= NONCE_UNSET && n <= NONCE_BYPASS;
382     }
383 
384     /**
385      * The names of the nonces
386      */
387     private static final String[] sNonceName =
388             new String[]{ "unset", "disabled", "corked", "bypass" };
389 
390     private static final String TAG = "PropertyInvalidatedCache";
391     private static final boolean DEBUG = false;
392     private static final boolean VERIFY = false;
393 
394     /**
395      * The object-private lock.
396      */
397     private final Object mLock = new Object();
398 
399     // Per-Cache performance counters.
400     @GuardedBy("mLock")
401     private long mHits = 0;
402 
403     @GuardedBy("mLock")
404     private long mMisses = 0;
405 
406     @GuardedBy("mLock")
407     private long[] mSkips = new long[]{ 0, 0, 0, 0 };
408 
409     @GuardedBy("mLock")
410     private long mMissOverflow = 0;
411 
412     @GuardedBy("mLock")
413     private long mHighWaterMark = 0;
414 
415     @GuardedBy("mLock")
416     private long mClears = 0;
417 
418     /**
419      * Protect objects that support corking.  mLock and sGlobalLock must never be taken while this
420      * is held.
421      */
422     private static final Object sCorkLock = new Object();
423 
424     /**
425      * Record the number of invalidate or cork calls that were nops because the cache was already
426      * corked.  This is static because invalidation is done in a static context.  Entries are
427      * indexed by the cache property.
428      */
429     @GuardedBy("sCorkLock")
430     private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>();
431 
432     /**
433      * A map of cache keys that we've "corked". (The values are counts.)  When a cache key is
434      * corked, we skip the cache invalidate when the cache key is in the unset state --- that
435      * is, when a cache key is corked, an invalidation does not enable the cache if somebody
436      * else hasn't disabled it.
437      */
438     @GuardedBy("sCorkLock")
439     private static final HashMap<String, Integer> sCorks = new HashMap<>();
440 
441     /**
442      * A lock for the global list of caches and cache keys.  This must never be taken inside mLock
443      * or sCorkLock.
444      */
445     private static final Object sGlobalLock = new Object();
446 
447     /**
448      * A map of cache keys that have been disabled in the local process.  When a key is
449      * disabled locally, existing caches are disabled and the key is saved in this map.
450      * Future cache instances that use the same key will be disabled in their constructor.
451      */
452     @GuardedBy("sGlobalLock")
453     private static final HashSet<String> sDisabledKeys = new HashSet<>();
454 
455     /**
456      * Weakly references all cache objects in the current process, allowing us to iterate over
457      * them all for purposes like issuing debug dumps and reacting to memory pressure.
458      */
459     @GuardedBy("sGlobalLock")
460     private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>();
461 
462     /**
463      * Counts of the number of times a cache key was invalidated.  Invalidation occurs in a static
464      * context with no cache object available, so this is a static map.  Entries are indexed by
465      * the cache property.
466      */
467     @GuardedBy("sGlobalLock")
468     private static final HashMap<String, Long> sInvalidates = new HashMap<>();
469 
470     /**
471      * If sEnabled is false then all cache operations are stubbed out.  Set
472      * it to false inside test processes.
473      */
474     private static boolean sEnabled = true;
475 
476     /**
477      * Name of the property that holds the unique value that we use to invalidate the cache.
478      */
479     private final String mPropertyName;
480 
481     /**
482      * Handle to the {@code mPropertyName} property, transitioning to non-{@code null} once the
483      * property exists on the system.
484      */
485     private volatile SystemProperties.Handle mPropertyHandle;
486 
487     /**
488      * The name by which this cache is known.  This should normally be the
489      * binder call that is being cached, but the constructors default it to
490      * the property name.
491      */
492     private final String mCacheName;
493 
494     /**
495      * The function that computes a Result, given a Query.  This function is called on a
496      * cache miss.
497      */
498     private QueryHandler<Query, Result> mComputer;
499 
500     /**
501      * A default function that delegates to the deprecated recompute() method.
502      */
503     private static class DefaultComputer<Query, Result> extends QueryHandler<Query, Result> {
504         final PropertyInvalidatedCache<Query, Result> mCache;
DefaultComputer(PropertyInvalidatedCache<Query, Result> cache)505         DefaultComputer(PropertyInvalidatedCache<Query, Result> cache) {
506             mCache = cache;
507         }
apply(Query query)508         public Result apply(Query query) {
509             return mCache.recompute(query);
510         }
511     }
512 
513     @GuardedBy("mLock")
514     private final LinkedHashMap<Query, Result> mCache;
515 
516     /**
517      * The last value of the {@code mPropertyHandle} that we observed.
518      */
519     @GuardedBy("mLock")
520     private long mLastSeenNonce = NONCE_UNSET;
521 
522     /**
523      * Whether we've disabled the cache in this process.
524      */
525     private boolean mDisabled = false;
526 
527     /**
528      * Maximum number of entries the cache will maintain.
529      */
530     private final int mMaxEntries;
531 
532     /**
533      * Make a new property invalidated cache.  This constructor names the cache after the
534      * property name.  New clients should prefer the constructor that takes an explicit
535      * cache name.
536      *
537      * TODO(216112648): deprecate this as a public interface, in favor of the four-argument
538      * constructor.
539      *
540      * @param maxEntries Maximum number of entries to cache; LRU discard
541      * @param propertyName Name of the system property holding the cache invalidation nonce.
542      *
543      * @hide
544      */
PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName)545     public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
546         this(maxEntries, propertyName, propertyName);
547     }
548 
549     /**
550      * Make a new property invalidated cache.
551      *
552      * TODO(216112648): deprecate this as a public interface, in favor of the four-argument
553      * constructor.
554      *
555      * @param maxEntries Maximum number of entries to cache; LRU discard
556      * @param propertyName Name of the system property holding the cache invalidation nonce
557      * @param cacheName Name of this cache in debug and dumpsys
558      * @hide
559      */
PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName, @NonNull String cacheName)560     public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
561             @NonNull String cacheName) {
562         mPropertyName = propertyName;
563         mCacheName = cacheName;
564         mMaxEntries = maxEntries;
565         mComputer = new DefaultComputer<>(this);
566         mCache = createMap();
567         registerCache();
568     }
569 
570     /**
571      * Make a new property invalidated cache.  The key is computed from the module and api
572      * parameters.
573      *
574      * @param maxEntries Maximum number of entries to cache; LRU discard
575      * @param module The module under which the cache key should be placed.
576      * @param api The api this cache front-ends.  The api must be a Java identifier but
577      * need not be an actual api.
578      * @param cacheName Name of this cache in debug and dumpsys
579      * @param computer The code to compute values that are not in the cache.
580      * @hide
581      */
582     @TestApi
PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api, @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer)583     public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
584             @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
585         mPropertyName = createPropertyName(module, api);
586         mCacheName = cacheName;
587         mMaxEntries = maxEntries;
588         mComputer = computer;
589         mCache = createMap();
590         registerCache();
591     }
592 
593     // Create a map.  This should be called only from the constructor.
createMap()594     private LinkedHashMap<Query, Result> createMap() {
595         return new LinkedHashMap<Query, Result>(
596             2 /* start small */,
597             0.75f /* default load factor */,
598             true /* LRU access order */) {
599                 @GuardedBy("mLock")
600                 @Override
601                 protected boolean removeEldestEntry(Map.Entry eldest) {
602                     final int size = size();
603                     if (size > mHighWaterMark) {
604                         mHighWaterMark = size;
605                     }
606                     if (size > mMaxEntries) {
607                         mMissOverflow++;
608                         return true;
609                     }
610                     return false;
611                 }
612         };
613     }
614 
615     /**
616      * Register the map in the global list.  If the cache is disabled globally, disable it
617      * now.  This method is only ever called from the constructor, which means no other thread has
618      * access to the object yet.  It can safely be modified outside any lock.
619      */
620     private void registerCache() {
621         synchronized (sGlobalLock) {
622             if (sDisabledKeys.contains(mCacheName)) {
623                 disableInstance();
624             }
625             sCaches.put(this, null);
626         }
627     }
628 
629     /**
630      * SystemProperties are protected and cannot be written (or read, usually) by random
631      * processes.  So, for testing purposes, the methods have a bypass mode that reads and
632      * writes to a HashMap and does not go out to the SystemProperties at all.
633      */
634 
635     // If true, the cache might be under test.  If false, there is no testing in progress.
636     private static volatile boolean sTesting = false;
637 
638     // If sTesting is true then keys that are under test are in this map.
639     private static final HashMap<String, Long> sTestingPropertyMap = new HashMap<>();
640 
641     /**
642      * Enable or disable testing.  The testing property map is cleared every time this
643      * method is called.
644      * @hide
645      */
646     @TestApi
647     public static void setTestMode(boolean mode) {
648         sTesting = mode;
649         synchronized (sTestingPropertyMap) {
650             sTestingPropertyMap.clear();
651         }
652     }
653 
654     /**
655      * Enable testing the specific cache key.  Only keys in the map are subject to testing.
656      * There is no method to stop testing a property name.  Just disable the test mode.
657      */
658     private static void testPropertyName(@NonNull String name) {
659         synchronized (sTestingPropertyMap) {
660             sTestingPropertyMap.put(name, (long) NONCE_UNSET);
661         }
662     }
663 
664     /**
665      * Enable testing the specific cache key.  Only keys in the map are subject to testing.
666      * There is no method to stop testing a property name.  Just disable the test mode.
667      * @hide
668      */
669     @TestApi
670     public void testPropertyName() {
671         testPropertyName(mPropertyName);
672     }
673 
674     // Read the system property associated with the current cache.  This method uses the
675     // handle for faster reading.
676     private long getCurrentNonce() {
677         if (sTesting) {
678             synchronized (sTestingPropertyMap) {
679                 Long n = sTestingPropertyMap.get(mPropertyName);
680                 if (n != null) {
681                     return n;
682                 }
683             }
684         }
685 
686         SystemProperties.Handle handle = mPropertyHandle;
687         if (handle == null) {
688             handle = SystemProperties.find(mPropertyName);
689             if (handle == null) {
690                 return NONCE_UNSET;
691             }
692             mPropertyHandle = handle;
693         }
694         return handle.getLong(NONCE_UNSET);
695     }
696 
697     // Write the nonce in a static context.  No handle is available.
698     private static void setNonce(String name, long val) {
699         if (sTesting) {
700             synchronized (sTestingPropertyMap) {
701                 Long n = sTestingPropertyMap.get(name);
702                 if (n != null) {
703                     sTestingPropertyMap.put(name, val);
704                     return;
705                 }
706             }
707         }
708         RuntimeException failure = null;
709         for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) {
710             try {
711                 SystemProperties.set(name, Long.toString(val));
712                 if (attempt > 0) {
713                     // This log is not guarded.  Based on known bug reports, it should
714                     // occur once a week or less.  The purpose of the log message is to
715                     // identify the retries as a source of delay that might be otherwise
716                     // be attributed to the cache itself.
717                     Log.w(TAG, "Nonce set after " + attempt + " tries");
718                 }
719                 return;
720             } catch (RuntimeException e) {
721                 if (failure == null) {
722                     failure = e;
723                 }
724                 try {
725                     Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS);
726                 } catch (InterruptedException x) {
727                     // Ignore this exception.  The desired delay is only approximate and
728                     // there is no issue if the sleep sometimes terminates early.
729                 }
730             }
731         }
732         // This point is reached only if SystemProperties.set() fails at least once.
733         // Rethrow the first exception that was received.
734         throw failure;
735     }
736 
737     // Set the nonce in a static context.  No handle is available.
738     private static long getNonce(String name) {
739         if (sTesting) {
740             synchronized (sTestingPropertyMap) {
741                 Long n = sTestingPropertyMap.get(name);
742                 if (n != null) {
743                     return n;
744                 }
745             }
746         }
747         return SystemProperties.getLong(name, NONCE_UNSET);
748     }
749 
750     /**
751      * Forget all cached values.
752      * TODO(216112648) remove this as a public API.  Clients should invalidate caches, not clear
753      * them.
754      * @hide
755      */
756     public final void clear() {
757         synchronized (mLock) {
758             if (DEBUG) {
759                 Log.d(TAG, "clearing cache for " + mPropertyName);
760             }
761             mCache.clear();
762             mClears++;
763         }
764     }
765 
766     /**
767      * Fetch a result from scratch in case it's not in the cache at all.  Called unlocked: may
768      * block. If this function returns null, the result of the cache query is null. There is no
769      * "negative cache" in the query: we don't cache null results at all.
770      * TODO(216112648): deprecate this as a public interface, in favor of an instance of
771      * QueryHandler.
772      * @hide
773      */
774     public Result recompute(@NonNull Query query) {
775         return mComputer.apply(query);
776     }
777 
778     /**
779      * Return true if the query should bypass the cache.  The default behavior is to
780      * always use the cache but the method can be overridden for a specific class.
781      * TODO(216112648): deprecate this as a public interface, in favor of an instance of
782      * QueryHandler.
783      * @hide
784      */
785     public boolean bypass(@NonNull Query query) {
786         return mComputer.shouldBypassCache(query);
787     }
788 
789     /**
790      * Determines if a pair of responses are considered equal. Used to determine whether
791      * a cache is inadvertently returning stale results when VERIFY is set to true.
792      * @hide
793      */
794     public boolean resultEquals(Result cachedResult, Result fetchedResult) {
795         // If a service crashes and returns a null result, the cached value remains valid.
796         if (fetchedResult != null) {
797             return Objects.equals(cachedResult, fetchedResult);
798         }
799         return true;
800     }
801 
802     /**
803      * Make result up-to-date on a cache hit.  Called unlocked;
804      * may block.
805      *
806      * Return either 1) oldResult itself (the same object, by reference equality), in which
807      * case we just return oldResult as the result of the cache query, 2) a new object, which
808      * replaces oldResult in the cache and which we return as the result of the cache query
809      * after performing another property read to make sure that the result hasn't changed in
810      * the meantime (if the nonce has changed in the meantime, we drop the cache and try the
811      * whole query again), or 3) null, which causes the old value to be removed from the cache
812      * and null to be returned as the result of the cache query.
813      * @hide
814      */
815     protected Result refresh(Result oldResult, Query query) {
816         return oldResult;
817     }
818 
819     /**
820      * Disable the use of this cache in this process.  This method is using internally and during
821      * testing.  To disable a cache in normal code, use disableLocal().  A disabled cache cannot
822      * be re-enabled.
823      * @hide
824      */
825     @TestApi
826     public final void disableInstance() {
827         synchronized (mLock) {
828             mDisabled = true;
829             clear();
830         }
831     }
832 
833     /**
834      * Disable the local use of all caches with the same name.  All currently registered caches
835      * with the name will be disabled now, and all future cache instances that use the name will
836      * be disabled in their constructor.
837      */
838     private static final void disableLocal(@NonNull String name) {
839         synchronized (sGlobalLock) {
840             if (sDisabledKeys.contains(name)) {
841                 // The key is already in recorded so there is no further work to be done.
842                 return;
843             }
844             for (PropertyInvalidatedCache cache : sCaches.keySet()) {
845                 if (name.equals(cache.mCacheName)) {
846                     cache.disableInstance();
847                 }
848             }
849             // Record the disabled key after the iteration.  If an exception occurs during the
850             // iteration above, and the code is retried, the function should not exit early.
851             sDisabledKeys.add(name);
852         }
853     }
854 
855     /**
856      * Stop disabling local caches with a particular name.  Any caches that are currently
857      * disabled remain disabled (the "disabled" setting is sticky).  However, new caches
858      * with this name will not be disabled.  It is not an error if the cache name is not
859      * found in the list of disabled caches.
860      * @hide
861      */
862     @TestApi
863     public final void forgetDisableLocal() {
864         synchronized (sGlobalLock) {
865             sDisabledKeys.remove(mCacheName);
866         }
867     }
868 
869     /**
870      * Disable this cache in the current process, and all other caches that use the same
871      * name.  This does not affect caches that have a different name but use the same
872      * property.
873      * TODO(216112648) Remove this in favor of disableForCurrentProcess().
874      * @hide
875      */
876     public void disableLocal() {
877         disableForCurrentProcess();
878     }
879 
880     /**
881      * Disable this cache in the current process, and all other present and future caches that use
882      * the same name.  This does not affect caches that have a different name but use the same
883      * property.  Once disabled, a cache cannot be reenabled.
884      * @hide
885      */
886     @TestApi
887     public void disableForCurrentProcess() {
888         disableLocal(mCacheName);
889     }
890 
891     /** @hide */
892     @TestApi
893     public static void disableForCurrentProcess(@NonNull String cacheName) {
894         disableLocal(cacheName);
895     }
896 
897     /**
898      * Return whether a cache instance is disabled.
899      * @hide
900      */
901     @TestApi
902     public final boolean isDisabled() {
903         return mDisabled || !sEnabled;
904     }
905 
906     /**
907      * Get a value from the cache or recompute it.
908      * @hide
909      */
910     @TestApi
911     public @Nullable Result query(@NonNull Query query) {
912         // Let access to mDisabled race: it's atomic anyway.
913         long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED;
914         if (bypass(query)) {
915             currentNonce = NONCE_BYPASS;
916         }
917         for (;;) {
918             if (isReservedNonce(currentNonce)) {
919                 if (!mDisabled) {
920                     // Do not bother collecting statistics if the cache is
921                     // locally disabled.
922                     synchronized (mLock) {
923                         mSkips[(int) currentNonce]++;
924                     }
925                 }
926 
927                 if (DEBUG) {
928                     if (!mDisabled) {
929                         Log.d(TAG, TextUtils.formatSimple(
930                             "cache %s %s for %s",
931                             cacheName(), sNonceName[(int) currentNonce], queryToString(query)));
932                     }
933                 }
934                 return recompute(query);
935             }
936             final Result cachedResult;
937             synchronized (mLock) {
938                 if (currentNonce == mLastSeenNonce) {
939                     cachedResult = mCache.get(query);
940 
941                     if (cachedResult != null) mHits++;
942                 } else {
943                     if (DEBUG) {
944                         Log.d(TAG, TextUtils.formatSimple(
945                             "clearing cache %s of %d entries because nonce changed [%s] -> [%s]",
946                             cacheName(), mCache.size(),
947                             mLastSeenNonce, currentNonce));
948                     }
949                     clear();
950                     mLastSeenNonce = currentNonce;
951                     cachedResult = null;
952                 }
953             }
954             // Cache hit --- but we're not quite done yet.  A value in the cache might need to
955             // be augmented in a "refresh" operation.  The refresh operation can combine the
956             // old and the new nonce values.  In order to make sure the new parts of the value
957             // are consistent with the old, possibly-reused parts, we check the property value
958             // again after the refresh and do the whole fetch again if the property invalidated
959             // us while we were refreshing.
960             if (cachedResult != null) {
961                 final Result refreshedResult = refresh(cachedResult, query);
962                 if (refreshedResult != cachedResult) {
963                     if (DEBUG) {
964                         Log.d(TAG, "cache refresh for " + cacheName() + " " + queryToString(query));
965                     }
966                     final long afterRefreshNonce = getCurrentNonce();
967                     if (currentNonce != afterRefreshNonce) {
968                         currentNonce = afterRefreshNonce;
969                         if (DEBUG) {
970                             Log.d(TAG, TextUtils.formatSimple(
971                                     "restarting %s %s because nonce changed in refresh",
972                                     cacheName(),
973                                     queryToString(query)));
974                         }
975                         continue;
976                     }
977                     synchronized (mLock) {
978                         if (currentNonce != mLastSeenNonce) {
979                             // Do nothing: cache is already out of date. Just return the value
980                             // we already have: there's no guarantee that the contents of mCache
981                             // won't become invalid as soon as we return.
982                         } else if (refreshedResult == null) {
983                             mCache.remove(query);
984                         } else {
985                             mCache.put(query, refreshedResult);
986                         }
987                     }
988                     return maybeCheckConsistency(query, refreshedResult);
989                 }
990                 if (DEBUG) {
991                     Log.d(TAG, "cache hit for " + cacheName() + " " + queryToString(query));
992                 }
993                 return maybeCheckConsistency(query, cachedResult);
994             }
995             // Cache miss: make the value from scratch.
996             if (DEBUG) {
997                 Log.d(TAG, "cache miss for " + cacheName() + " " + queryToString(query));
998             }
999             final Result result = recompute(query);
1000             synchronized (mLock) {
1001                 // If someone else invalidated the cache while we did the recomputation, don't
1002                 // update the cache with a potentially stale result.
1003                 if (mLastSeenNonce == currentNonce && result != null) {
1004                     mCache.put(query, result);
1005                 }
1006                 mMisses++;
1007             }
1008             return maybeCheckConsistency(query, result);
1009         }
1010     }
1011 
1012     // Inner class avoids initialization in processes that don't do any invalidation
1013     private static final class NoPreloadHolder {
1014         private static final AtomicLong sNextNonce = new AtomicLong((new Random()).nextLong());
1015         public static long next() {
1016             return sNextNonce.getAndIncrement();
1017         }
1018     }
1019 
1020     /**
1021      * Non-static convenience version of disableSystemWide() for situations in which only a
1022      * single PropertyInvalidatedCache is keyed on a particular property value.
1023      *
1024      * When multiple caches share a single property value, using an instance method on one of
1025      * the cache objects to invalidate all of the cache objects becomes confusing and you should
1026      * just use the static version of this function.
1027      * @hide
1028      */
1029     @TestApi
1030     public final void disableSystemWide() {
1031         disableSystemWide(mPropertyName);
1032     }
1033 
1034     /**
1035      * Disable all caches system-wide that are keyed on {@var name}. This
1036      * function is synchronous: caches are invalidated and disabled upon return.
1037      *
1038      * @param name Name of the cache-key property to invalidate
1039      */
1040     private static void disableSystemWide(@NonNull String name) {
1041         if (!sEnabled) {
1042             return;
1043         }
1044         setNonce(name, NONCE_DISABLED);
1045     }
1046 
1047     /**
1048      * Non-static convenience version of invalidateCache() for situations in which only a single
1049      * PropertyInvalidatedCache is keyed on a particular property value.
1050      * @hide
1051      */
1052     @TestApi
1053     public void invalidateCache() {
1054         invalidateCache(mPropertyName);
1055     }
1056 
1057     /**
1058      * Invalidate caches in all processes that are keyed for the module and api.
1059      * @hide
1060      */
1061     @TestApi
1062     public static void invalidateCache(@NonNull String module, @NonNull String api) {
1063         invalidateCache(createPropertyName(module, api));
1064     }
1065 
1066     /**
1067      * Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
1068      * {@var name}. This function is synchronous: caches are invalidated upon return.
1069      *
1070      * TODO(216112648) make this method private in favor of the two-argument (module, api)
1071      * override.
1072      *
1073      * @param name Name of the cache-key property to invalidate
1074      * @hide
1075      */
1076     public static void invalidateCache(@NonNull String name) {
1077         if (!sEnabled) {
1078             if (DEBUG) {
1079                 Log.w(TAG, TextUtils.formatSimple(
1080                     "cache invalidate %s suppressed", name));
1081             }
1082             return;
1083         }
1084 
1085         // Take the cork lock so invalidateCache() racing against corkInvalidations() doesn't
1086         // clobber a cork-written NONCE_UNSET with a cache key we compute before the cork.
1087         // The property service is single-threaded anyway, so we don't lose any concurrency by
1088         // taking the cork lock around cache invalidations.  If we see contention on this lock,
1089         // we're invalidating too often.
1090         synchronized (sCorkLock) {
1091             Integer numberCorks = sCorks.get(name);
1092             if (numberCorks != null && numberCorks > 0) {
1093                 if (DEBUG) {
1094                     Log.d(TAG, "ignoring invalidation due to cork: " + name);
1095                 }
1096                 final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
1097                 sCorkedInvalidates.put(name, count + 1);
1098                 return;
1099             }
1100             invalidateCacheLocked(name);
1101         }
1102     }
1103 
1104     @GuardedBy("sCorkLock")
1105     private static void invalidateCacheLocked(@NonNull String name) {
1106         // There's no race here: we don't require that values strictly increase, but instead
1107         // only that each is unique in a single runtime-restart session.
1108         final long nonce = getNonce(name);
1109         if (nonce == NONCE_DISABLED) {
1110             if (DEBUG) {
1111                 Log.d(TAG, "refusing to invalidate disabled cache: " + name);
1112             }
1113             return;
1114         }
1115 
1116         long newValue;
1117         do {
1118             newValue = NoPreloadHolder.next();
1119         } while (isReservedNonce(newValue));
1120         if (DEBUG) {
1121             Log.d(TAG, TextUtils.formatSimple(
1122                     "invalidating cache [%s]: [%s] -> [%s]",
1123                     name, nonce, Long.toString(newValue)));
1124         }
1125         // There is a small race with concurrent disables here.  A compare-and-exchange
1126         // property operation would be required to eliminate the race condition.
1127         setNonce(name, newValue);
1128         long invalidateCount = sInvalidates.getOrDefault(name, (long) 0);
1129         sInvalidates.put(name, ++invalidateCount);
1130     }
1131 
1132     /**
1133      * Temporarily put the cache in the uninitialized state and prevent invalidations from
1134      * moving it out of that state: useful in cases where we want to avoid the overhead of a
1135      * large number of cache invalidations in a short time.  While the cache is corked, clients
1136      * bypass the cache and talk to backing services directly.  This property makes corking
1137      * correctness-preserving even if corked outside the lock that controls access to the
1138      * cache's backing service.
1139      *
1140      * corkInvalidations() and uncorkInvalidations() must be called in pairs.
1141      *
1142      * @param name Name of the cache-key property to cork
1143      * @hide
1144      */
1145     public static void corkInvalidations(@NonNull String name) {
1146         if (!sEnabled) {
1147             if (DEBUG) {
1148                 Log.w(TAG, TextUtils.formatSimple(
1149                     "cache cork %s suppressed", name));
1150             }
1151             return;
1152         }
1153 
1154         synchronized (sCorkLock) {
1155             int numberCorks = sCorks.getOrDefault(name, 0);
1156             if (DEBUG) {
1157                 Log.d(TAG, TextUtils.formatSimple(
1158                         "corking %s: numberCorks=%s", name, numberCorks));
1159             }
1160 
1161             // If we're the first ones to cork this cache, set the cache to the corked state so
1162             // existing caches talk directly to their services while we've corked updates.
1163             // Make sure we don't clobber a disabled cache value.
1164 
1165             // TODO(dancol): we can skip this property write and leave the cache enabled if the
1166             // caller promises not to make observable changes to the cache backing state before
1167             // uncorking the cache, e.g., by holding a read lock across the cork-uncork pair.
1168             // Implement this more dangerous mode of operation if necessary.
1169             if (numberCorks == 0) {
1170                 final long nonce = getNonce(name);
1171                 if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
1172                     setNonce(name, NONCE_CORKED);
1173                 }
1174             } else {
1175                 final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
1176                 sCorkedInvalidates.put(name, count + 1);
1177             }
1178             sCorks.put(name, numberCorks + 1);
1179             if (DEBUG) {
1180                 Log.d(TAG, "corked: " + name);
1181             }
1182         }
1183     }
1184 
1185     /**
1186      * Undo the effect of a cork, allowing cache invalidations to proceed normally.
1187      * Removing the last cork on a cache name invalidates the cache by side effect,
1188      * transitioning it to normal operation (unless explicitly disabled system-wide).
1189      *
1190      * @param name Name of the cache-key property to uncork
1191      * @hide
1192      */
1193     public static void uncorkInvalidations(@NonNull String name) {
1194         if (!sEnabled) {
1195             if (DEBUG) {
1196                 Log.w(TAG, TextUtils.formatSimple(
1197                         "cache uncork %s suppressed", name));
1198             }
1199             return;
1200         }
1201 
1202         synchronized (sCorkLock) {
1203             int numberCorks = sCorks.getOrDefault(name, 0);
1204             if (DEBUG) {
1205                 Log.d(TAG, TextUtils.formatSimple(
1206                         "uncorking %s: numberCorks=%s", name, numberCorks));
1207             }
1208 
1209             if (numberCorks < 1) {
1210                 throw new AssertionError("cork underflow: " + name);
1211             }
1212             if (numberCorks == 1) {
1213                 sCorks.remove(name);
1214                 invalidateCacheLocked(name);
1215                 if (DEBUG) {
1216                     Log.d(TAG, "uncorked: " + name);
1217                 }
1218             } else {
1219                 sCorks.put(name, numberCorks - 1);
1220             }
1221         }
1222     }
1223 
1224     /**
1225      * Time-based automatic corking helper. This class allows providers of cached data to
1226      * amortize the cost of cache invalidations by corking the cache immediately after a
1227      * modification (instructing clients to bypass the cache temporarily) and automatically
1228      * uncork after some period of time has elapsed.
1229      *
1230      * It's better to use explicit cork and uncork pairs that tighly surround big batches of
1231      * invalidations, but it's not always practical to tell where these invalidation batches
1232      * might occur. AutoCorker's time-based corking is a decent alternative.
1233      *
1234      * The auto-cork delay is configurable but it should not be too long.  The purpose of
1235      * the delay is to minimize the number of times a server writes to the system property
1236      * when invalidating the cache.  One write every 50ms does not hurt system performance.
1237      * @hide
1238      */
1239     public static final class AutoCorker {
1240         public static final int DEFAULT_AUTO_CORK_DELAY_MS = 50;
1241 
1242         private final String mPropertyName;
1243         private final int mAutoCorkDelayMs;
1244         private final Object mLock = new Object();
1245         @GuardedBy("mLock")
1246         private long mUncorkDeadlineMs = -1;  // SystemClock.uptimeMillis()
1247         @GuardedBy("mLock")
1248         private Handler mHandler;
1249 
1250         public AutoCorker(@NonNull String propertyName) {
1251             this(propertyName, DEFAULT_AUTO_CORK_DELAY_MS);
1252         }
1253 
1254         public AutoCorker(@NonNull String propertyName, int autoCorkDelayMs) {
1255             mPropertyName = propertyName;
1256             mAutoCorkDelayMs = autoCorkDelayMs;
1257             // We can't initialize mHandler here: when we're created, the main loop might not
1258             // be set up yet! Wait until we have a main loop to initialize our
1259             // corking callback.
1260         }
1261 
1262         public void autoCork() {
1263             if (Looper.getMainLooper() == null) {
1264                 // We're not ready to auto-cork yet, so just invalidate the cache immediately.
1265                 if (DEBUG) {
1266                     Log.w(TAG, "invalidating instead of autocorking early in init: "
1267                             + mPropertyName);
1268                 }
1269                 PropertyInvalidatedCache.invalidateCache(mPropertyName);
1270                 return;
1271             }
1272             synchronized (mLock) {
1273                 boolean alreadyQueued = mUncorkDeadlineMs >= 0;
1274                 if (DEBUG) {
1275                     Log.w(TAG, TextUtils.formatSimple(
1276                             "autoCork %s mUncorkDeadlineMs=%s", mPropertyName,
1277                             mUncorkDeadlineMs));
1278                 }
1279                 mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs;
1280                 if (!alreadyQueued) {
1281                     getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
1282                     PropertyInvalidatedCache.corkInvalidations(mPropertyName);
1283                 } else {
1284                     synchronized (sCorkLock) {
1285                         final long count = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0);
1286                         sCorkedInvalidates.put(mPropertyName, count + 1);
1287                     }
1288                 }
1289             }
1290         }
1291 
1292         private void handleMessage(Message msg) {
1293             synchronized (mLock) {
1294                 if (DEBUG) {
1295                     Log.w(TAG, TextUtils.formatSimple(
1296                             "handleMsesage %s mUncorkDeadlineMs=%s",
1297                             mPropertyName, mUncorkDeadlineMs));
1298                 }
1299 
1300                 if (mUncorkDeadlineMs < 0) {
1301                     return;  // ???
1302                 }
1303                 long nowMs = SystemClock.uptimeMillis();
1304                 if (mUncorkDeadlineMs > nowMs) {
1305                     mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs;
1306                     if (DEBUG) {
1307                         Log.w(TAG, TextUtils.formatSimple(
1308                                         "scheduling uncork at %s",
1309                                         mUncorkDeadlineMs));
1310                     }
1311                     getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs);
1312                     return;
1313                 }
1314                 if (DEBUG) {
1315                     Log.w(TAG, "automatic uncorking " + mPropertyName);
1316                 }
1317                 mUncorkDeadlineMs = -1;
1318                 PropertyInvalidatedCache.uncorkInvalidations(mPropertyName);
1319             }
1320         }
1321 
1322         @GuardedBy("mLock")
1323         private Handler getHandlerLocked() {
1324             if (mHandler == null) {
1325                 mHandler = new Handler(Looper.getMainLooper()) {
1326                         @Override
1327                         public void handleMessage(Message msg) {
1328                             AutoCorker.this.handleMessage(msg);
1329                         }
1330                     };
1331             }
1332             return mHandler;
1333         }
1334     }
1335 
1336     /**
1337      * Return the result generated by a given query to the cache, performing debugging checks when
1338      * enabled.
1339      */
1340     private Result maybeCheckConsistency(Query query, Result proposedResult) {
1341         if (VERIFY) {
1342             Result resultToCompare = recompute(query);
1343             boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
1344             if (!nonceChanged && !resultEquals(proposedResult, resultToCompare)) {
1345                 Log.e(TAG, TextUtils.formatSimple(
1346                         "cache %s inconsistent for %s is %s should be %s",
1347                         cacheName(), queryToString(query),
1348                         proposedResult, resultToCompare));
1349             }
1350             // Always return the "true" result in verification mode.
1351             return resultToCompare;
1352         }
1353         return proposedResult;
1354     }
1355 
1356     /**
1357      * Return the name of the cache, to be used in debug messages.  This is exposed
1358      * primarily for testing.
1359      * @hide
1360      */
1361     public final @NonNull String cacheName() {
1362         return mCacheName;
1363     }
1364 
1365     /**
1366      * Return the property used by the cache.  This is primarily for test purposes.
1367      * @hide
1368      */
1369     public final @NonNull String propertyName() {
1370         return mPropertyName;
1371     }
1372 
1373     /**
1374      * Return the query as a string, to be used in debug messages.  New clients should not
1375      * override this, but should instead add the necessary toString() method to the Query
1376      * class.
1377      * TODO(216112648) add a method in the QueryHandler and deprecate this API.
1378      * @hide
1379      */
1380     protected @NonNull String queryToString(@NonNull Query query) {
1381         return Objects.toString(query);
1382     }
1383 
1384     /**
1385      * Disable all caches in the local process.  This is primarily useful for testing when
1386      * the test needs to bypass the cache or when the test is for a server, and the test
1387      * process does not have privileges to write SystemProperties. Once disabled it is not
1388      * possible to re-enable caching in the current process.  If a client wants to
1389      * temporarily disable caching, use the corking mechanism.
1390      * @hide
1391      */
1392     @TestApi
1393     public static void disableForTestMode() {
1394         Log.d(TAG, "disabling all caches in the process");
1395         sEnabled = false;
1396     }
1397 
1398     /**
1399      * Report the disabled status of this cache instance.  The return value does not
1400      * reflect status of the property key.
1401      * @hide
1402      */
1403     @TestApi
1404     public boolean getDisabledState() {
1405         return isDisabled();
1406     }
1407 
1408     /**
1409      * Return the number of entries in the cache.  This is used for testing and has package-only
1410      * visibility.
1411      * @hide
1412      */
1413     public int size() {
1414         synchronized (mLock) {
1415             return mCache.size();
1416         }
1417     }
1418 
1419     /**
1420      * Returns a list of caches alive at the current time.
1421      */
1422     @GuardedBy("sGlobalLock")
1423     private static @NonNull ArrayList<PropertyInvalidatedCache> getActiveCaches() {
1424         return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet());
1425     }
1426 
1427     /**
1428      * Returns a list of the active corks in a process.
1429      */
1430     private static @NonNull ArrayList<Map.Entry<String, Integer>> getActiveCorks() {
1431         synchronized (sCorkLock) {
1432             return new ArrayList<Map.Entry<String, Integer>>(sCorks.entrySet());
1433         }
1434     }
1435 
1436     /**
1437      * Switches that can be used to control the detail emitted by a cache dump.  The
1438      * "CONTAINS" switches match if the cache (property) name contains the switch
1439      * argument.  The "LIKE" switches match if the cache (property) name matches the
1440      * switch argument as a regex.  The regular expression must match the entire name,
1441      * which generally means it may need leading/trailing "." expressions.
1442      */
1443     final static String NAME_CONTAINS = "-name-has=";
1444     final static String NAME_LIKE = "-name-like=";
1445     final static String PROPERTY_CONTAINS = "-property-has=";
1446     final static String PROPERTY_LIKE = "-property-like=";
1447 
1448     /**
1449      * Return true if any argument is a detailed specification switch.
1450      */
1451     private static boolean anyDetailed(String[] args) {
1452         for (String a : args) {
1453             if (a.startsWith(NAME_CONTAINS) || a.startsWith(NAME_LIKE)
1454                 || a.startsWith(PROPERTY_CONTAINS) || a.startsWith(PROPERTY_LIKE)) {
1455                 return true;
1456             }
1457         }
1458         return false;
1459     }
1460 
1461     /**
1462      * A helper method to determine if a string matches a switch.
1463      */
1464     private static boolean chooses(String arg, String key, String reference, boolean contains) {
1465         if (arg.startsWith(key)) {
1466             final String value = arg.substring(key.length());
1467             if (contains) {
1468                 return reference.contains(value);
1469             } else {
1470                 return reference.matches(value);
1471             }
1472         }
1473         return false;
1474     }
1475 
1476     /**
1477      * Return true if this cache should be dumped in detail.  This method is not called
1478      * unless it has already been determined that there is at least one match requested.
1479      */
1480     private boolean showDetailed(String[] args) {
1481         for (String a : args) {
1482             if (chooses(a, NAME_CONTAINS, cacheName(), true)
1483                 || chooses(a, NAME_LIKE, cacheName(), false)
1484                 || chooses(a, PROPERTY_CONTAINS, mPropertyName, true)
1485                 || chooses(a, PROPERTY_LIKE, mPropertyName, false)) {
1486                 return true;
1487             }
1488         }
1489         return false;
1490     }
1491 
1492     private void dumpContents(PrintWriter pw, boolean detailed, String[] args) {
1493         // If the user has requested specific caches and this is not one of them, return
1494         // immediately.
1495         if (detailed && !showDetailed(args)) {
1496             return;
1497         }
1498 
1499         long invalidateCount;
1500         long corkedInvalidates;
1501         synchronized (sCorkLock) {
1502             invalidateCount = sInvalidates.getOrDefault(mPropertyName, (long) 0);
1503             corkedInvalidates = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0);
1504         }
1505 
1506         synchronized (mLock) {
1507             pw.println(TextUtils.formatSimple("  Cache Name: %s", cacheName()));
1508             pw.println(TextUtils.formatSimple("    Property: %s", mPropertyName));
1509             final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]
1510                     + mSkips[NONCE_BYPASS];
1511             pw.println(TextUtils.formatSimple(
1512                     "    Hits: %d, Misses: %d, Skips: %d, Clears: %d",
1513                     mHits, mMisses, skips, mClears));
1514             pw.println(TextUtils.formatSimple(
1515                     "    Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d",
1516                     mSkips[NONCE_CORKED], mSkips[NONCE_UNSET],
1517                     mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED]));
1518             pw.println(TextUtils.formatSimple(
1519                     "    Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
1520                     mLastSeenNonce, invalidateCount, corkedInvalidates));
1521             pw.println(TextUtils.formatSimple(
1522                     "    Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
1523                     mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
1524             pw.println(TextUtils.formatSimple("    Enabled: %s", mDisabled ? "false" : "true"));
1525             pw.println("");
1526 
1527             // No specific cache was requested.  This is the default, and no details
1528             // should be dumped.
1529             if (!detailed) {
1530                 return;
1531             }
1532             Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
1533             if (cacheEntries.size() == 0) {
1534                 return;
1535             }
1536 
1537             pw.println("    Contents:");
1538             for (Map.Entry<Query, Result> entry : cacheEntries) {
1539                 String key = Objects.toString(entry.getKey());
1540                 String value = Objects.toString(entry.getValue());
1541 
1542                 pw.println(TextUtils.formatSimple("      Key: %s\n      Value: %s\n", key, value));
1543             }
1544         }
1545     }
1546 
1547     /**
1548      * Dump the corking status.
1549      */
1550     @GuardedBy("sCorkLock")
1551     private static void dumpCorkInfo(PrintWriter pw) {
1552         ArrayList<Map.Entry<String, Integer>> activeCorks = getActiveCorks();
1553         if (activeCorks.size() > 0) {
1554             pw.println("  Corking Status:");
1555             for (int i = 0; i < activeCorks.size(); i++) {
1556                 Map.Entry<String, Integer> entry = activeCorks.get(i);
1557                 pw.println(TextUtils.formatSimple("    Property Name: %s Count: %d",
1558                                 entry.getKey(), entry.getValue()));
1559             }
1560         }
1561     }
1562 
1563     /**
1564      * Without arguments, this dumps statistics from every cache in the process to the
1565      * provided ParcelFileDescriptor.  Optional switches allow the caller to choose
1566      * specific caches (selection is by cache name or property name); if these switches
1567      * are used then the output includes both cache statistics and cache entries.
1568      */
1569     private static void dumpCacheInfo(@NonNull PrintWriter pw, @NonNull String[] args) {
1570         if (!sEnabled) {
1571             pw.println("  Caching is disabled in this process.");
1572             return;
1573         }
1574 
1575         // See if detailed is requested for any cache.  If there is a specific detailed request,
1576         // then only that cache is reported.
1577         boolean detail = anyDetailed(args);
1578 
1579         ArrayList<PropertyInvalidatedCache> activeCaches;
1580         synchronized (sGlobalLock) {
1581             activeCaches = getActiveCaches();
1582             if (!detail) {
1583                 dumpCorkInfo(pw);
1584             }
1585         }
1586 
1587         for (int i = 0; i < activeCaches.size(); i++) {
1588             PropertyInvalidatedCache currentCache = activeCaches.get(i);
1589             currentCache.dumpContents(pw, detail, args);
1590         }
1591     }
1592 
1593     /**
1594      * Without arguments, this dumps statistics from every cache in the process to the
1595      * provided ParcelFileDescriptor.  Optional switches allow the caller to choose
1596      * specific caches (selection is by cache name or property name); if these switches
1597      * are used then the output includes both cache statistics and cache entries.
1598      * @hide
1599      */
1600     public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) {
1601         // Create a PrintWriter that uses a byte array.  The code can safely write to
1602         // this array without fear of blocking.  The completed byte array will be sent
1603         // to the caller after all the data has been collected and all locks have been
1604         // released.
1605         ByteArrayOutputStream barray = new ByteArrayOutputStream();
1606         PrintWriter bout = new PrintWriter(barray);
1607         dumpCacheInfo(bout, args);
1608         bout.close();
1609 
1610         try {
1611             // Send the final byte array to the output.  This happens outside of all locks.
1612             var out = new FileOutputStream(pfd.getFileDescriptor());
1613             barray.writeTo(out);
1614             out.close();
1615             barray.close();
1616         } catch (IOException e) {
1617             Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
1618         }
1619     }
1620 
1621     /**
1622      * Trim memory by clearing all the caches.
1623      * @hide
1624      */
1625     public static void onTrimMemory() {
1626         ArrayList<PropertyInvalidatedCache> activeCaches;
1627         synchronized (sGlobalLock) {
1628             activeCaches = getActiveCaches();
1629         }
1630         for (int i = 0; i < activeCaches.size(); i++) {
1631             activeCaches.get(i).clear();
1632         }
1633     }
1634 }
1635