• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.os;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.StringDef;
22 import android.annotation.SystemApi;
23 import android.annotation.TestApi;
24 import android.app.PropertyInvalidatedCache;
25 import android.text.TextUtils;
26 import android.util.ArraySet;
27 
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.util.FastPrintWriter;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.io.PrintWriter;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.LinkedHashMap;
39 import java.util.Map;
40 import java.util.Objects;
41 import java.util.Random;
42 import java.util.Set;
43 import java.util.WeakHashMap;
44 import java.util.concurrent.atomic.AtomicLong;
45 
46 /**
47  * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
48  * but doesn't hold a lock across data fetches on query misses.
49  *
50  * The intended use case is caching frequently-read, seldom-changed information normally retrieved
51  * across interprocess communication. Imagine that you've written a user birthday information
52  * daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface over
53  * binder. That binder interface looks something like this:
54  *
55  * <pre>
56  * parcelable Birthday {
57  *   int month;
58  *   int day;
59  * }
60  * interface IUserBirthdayService {
61  *   Birthday getUserBirthday(int userId);
62  * }
63  * </pre>
64  *
65  * Suppose the service implementation itself looks like this...
66  *
67  * <pre>
68  * public class UserBirthdayServiceImpl implements IUserBirthdayService {
69  *   private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
70  *   {@literal @}Override
71  *   public synchronized Birthday getUserBirthday(int userId) {
72  *     return mUidToBirthday.get(userId);
73  *   }
74  *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
75  *     mUidToBirthday.clear();
76  *     mUidToBirthday.putAll(uidToBirthday);
77  *   }
78  * }
79  * </pre>
80  *
81  * ... and we have a client in frameworks (loaded into every app process) that looks like this:
82  *
83  * <pre>
84  * public class ActivityThread {
85  *   ...
86  *   public Birthday getUserBirthday(int userId) {
87  *     return GetService("birthdayd").getUserBirthday(userId);
88  *   }
89  *   ...
90  * }
91  * </pre>
92  *
93  * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call to
94  * the birthdayd process and consult its database of birthdays. If we query user birthdays
95  * frequently, we do a lot of work that we don't have to do, since user birthdays change
96  * infrequently.
97  *
98  * IpcDataCache is part of a pattern for optimizing this kind of information-querying code. Using
99  * {@code IpcDataCache}, you'd write the client this way:
100  *
101  * <pre>
102  * public class ActivityThread {
103  *   ...
104  *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
105  *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
106  *           {@literal @}Override
107  *           public Birthday apply(Integer) {
108  *              return GetService("birthdayd").getUserBirthday(userId);
109  *           }
110  *       };
111  *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
112  *   private static final String BDAY_API = "getUserBirthday";
113  *   private final IpcDataCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
114  *     IpcDataCache&lt;Integer, Birthday%&gt;(
115  *             BDAY_CACHE_MAX, MODULE_SYSTEM, BDAY_API,  BDAY_API, mBirthdayQuery);
116  *
117  *   public void disableUserBirthdayCache() {
118  *     mBirthdayCache.disableForCurrentProcess();
119  *   }
120  *   public void invalidateUserBirthdayCache() {
121  *     mBirthdayCache.invalidateCache();
122  *   }
123  *   public Birthday getUserBirthday(int userId) {
124  *     return mBirthdayCache.query(userId);
125  *   }
126  *   ...
127  * }
128  * </pre>
129  *
130  * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
131  * for the first time; on subsequent queries, we return the already-known Birthday object.
132  *
133  * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
134  * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
135  * string is permitted.  The third parameters is the name of the API being cached; this, too, can
136  * any value.  The fourth is the name of the cache.  The cache is usually named after th API.
137  * Some things you must know about the three strings:
138  * <list>
139  * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
140  * Usually, the SELinux rules permit a process to write a system property (and therefore
141  * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}.  This means that
142  * although the cache can be constructed with any module string, whatever string is chosen must be
143  * consistent with the SELinux configuration.
144  * <ul> The API name can be any string of alphanumeric characters.  All caches with the same API
145  * are invalidated at the same time.  If a server supports several caches and all are invalidated
146  * in common, then it is most efficient to assign the same API string to every cache.
147  * <ul> The cache name can be any string.  In debug output, the name is used to distiguish between
148  * caches with the same API name.  The cache name is also used when disabling caches in the
149  * current process.  So, invalidation is based on the module+api but disabling (which is generally
150  * a once-per-process operation) is based on the cache name.
151  * </list>
152  *
153  * User birthdays do occasionally change, so we have to modify the server to invalidate this
154  * cache when necessary. That invalidation code looks like this:
155  *
156  * <pre>
157  * public class UserBirthdayServiceImpl {
158  *   ...
159  *   public UserBirthdayServiceImpl() {
160  *     ...
161  *     ActivityThread.currentActivityThread().disableUserBirthdayCache();
162  *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
163  *   }
164  *
165  *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
166  *     mUidToBirthday.clear();
167  *     mUidToBirthday.putAll(uidToBirthday);
168  *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
169  *   }
170  *   ...
171  * }
172  * </pre>
173  *
174  * The call to {@code IpcDataCache.invalidateCache()} guarantees that all clients will re-fetch
175  * birthdays from binder during consequent calls to
176  * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
177  * held, we maintain consistency between different client views of the birthday state. The use of
178  * IpcDataCache in this idiomatic way introduces no new race conditions.
179  *
180  * IpcDataCache has a few other features for doing things like incremental enhancement of cached
181  * values and invalidation of multiple caches (that all share the same property key) at once.
182  *
183  * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
184  * time we update the cache. SELinux configuration must allow everyone to read this property
185  * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
186  * the property. (These properties conventionally begin with the "cache_key." prefix.)
187  *
188  * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
189  * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In this
190  * local case, there's no IPC, so use of the cache is (depending on exact circumstance)
191  * unnecessary.
192  *
193  * There may be queries for which it is more efficient to bypass the cache than to cache the
194  * result.  This would be true, for example, if some queries would require frequent cache
195  * invalidation while other queries require infrequent invalidation.  To expand on the birthday
196  * example, suppose that there is a userId that signifies "the next birthday".  When passed this
197  * userId, the server returns the next birthday among all users - this value changes as time
198  * advances.  The userId value can be cached, but the cache must be invalidated whenever a
199  * birthday occurs, and this invalidates all birthdays.  If there is a large number of users,
200  * invalidation will happen so often that the cache provides no value.
201  *
202  * The class provides a bypass mechanism to handle this situation.
203  * <pre>
204  * public class ActivityThread {
205  *   ...
206  *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
207  *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
208  *           {@literal @}Override
209  *           public Birthday apply(Integer) {
210  *              return GetService("birthdayd").getUserBirthday(userId);
211  *           }
212  *           {@literal @}Override
213  *           public boolean shouldBypassQuery(Integer userId) {
214  *               return userId == NEXT_BIRTHDAY;
215  *           }
216  *       };
217  *   ...
218  * }
219  * </pre>
220  *
221  * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
222  * particular query.  The {@code shouldBypassQuery()} method is not abstract and the default
223  * implementation returns false.
224  *
225  * For security, there is a allowlist of processes that are allowed to invalidate a cache.  The
226  * allowlist includes normal runtime processes but does not include test processes.  Test
227  * processes must call {@code IpcDataCache.disableForTestMode()} to disable all cache activity in
228  * that process.
229  *
230  * Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
231  *
232  * To test a binder cache, create one or more tests that exercise the binder method.  This should
233  * be done twice: once with production code and once with a special image that sets {@code DEBUG}
234  * and {@code VERIFY} true.  In the latter case, verify that no cache inconsistencies are
235  * reported.  If a cache inconsistency is reported, however, it might be a false positive.  This
236  * happens if the server side data can be read and written non-atomically with respect to cache
237  * invalidation.
238  *
239  * @param <Query> The class used to index cache entries: must be hashable and comparable
240  * @param <Result> The class holding cache entries; use a boxed primitive if possible
241  * @hide
242  */
243 @TestApi
244 @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
245 public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> {
246     /**
247      * {@inheritDoc}
248      * @hide
249      */
250     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
251     @TestApi
252     public static abstract class QueryHandler<Q,R>
253             extends PropertyInvalidatedCache.QueryHandler<Q,R> {
254         /**
255          * Compute a result given a query.  The semantics are those of Functor.
256          */
apply(@onNull Q query)257         public abstract @Nullable R apply(@NonNull Q query);
258 
259         /**
260          * Return true if a query should not use the cache.  The default implementation
261          * always uses the cache.
262          */
shouldBypassCache(@onNull Q query)263         public boolean shouldBypassCache(@NonNull Q query) {
264             return false;
265         }
266     };
267 
268     /**
269      * The list of cache namespaces.  Each namespace corresponds to an sepolicy domain.  A
270      * namespace is owned by a single process, although a single process can have more
271      * than one namespace (system_server, as an example).
272      * @hide
273      */
274     @StringDef(
275         prefix = { "MODULE_"
276         },
277         value = {
278             MODULE_TEST,
279             MODULE_SYSTEM,
280             MODULE_BLUETOOTH,
281             MODULE_TELEPHONY
282         }
283     )
284     @Retention(RetentionPolicy.SOURCE)
285     public @interface IpcDataCacheModule { }
286 
287     /**
288      * The module used for unit tests and cts tests.  It is expected that no process in
289      * the system has permissions to write properties with this module.
290      * @hide
291      */
292     @TestApi
293     public static final String MODULE_TEST = PropertyInvalidatedCache.MODULE_TEST;
294 
295     /**
296      * The module used for system server/framework caches.  This is not visible outside
297      * the system processes.
298      * @hide
299      */
300     @TestApi
301     public static final String MODULE_SYSTEM = PropertyInvalidatedCache.MODULE_SYSTEM;
302 
303     /**
304      * The module used for bluetooth caches.
305      * @hide
306      */
307     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
308     @TestApi
309     public static final String MODULE_BLUETOOTH = PropertyInvalidatedCache.MODULE_BLUETOOTH;
310 
311     /**
312      * Make a new property invalidated cache.  The key is computed from the module and api
313      * parameters.
314      *
315      * @param maxEntries Maximum number of entries to cache; LRU discard
316      * @param module The module under which the cache key should be placed.
317      * @param api The api this cache front-ends.  The api must be a Java identifier but
318      * need not be an actual api.
319      * @param cacheName Name of this cache in debug and dumpsys
320      * @param computer The code to compute values that are not in the cache.
321      * @hide
322      */
323     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
324     @TestApi
IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module, @NonNull String api, @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer)325     public IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module,
326             @NonNull String api, @NonNull String cacheName,
327             @NonNull QueryHandler<Query, Result> computer) {
328         super(maxEntries, module, api, cacheName, computer);
329     }
330 
331     /**
332      * {@inheritDoc}
333      * @hide
334      */
335     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
336     @TestApi
337     @Override
disableForCurrentProcess()338     public void disableForCurrentProcess() {
339         super.disableForCurrentProcess();
340     }
341 
342     /**
343      * {@inheritDoc}
344      * @hide
345      */
346     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
347     @TestApi
disableForCurrentProcess(@onNull String cacheName)348     public static void disableForCurrentProcess(@NonNull String cacheName) {
349         PropertyInvalidatedCache.disableForCurrentProcess(cacheName);
350     }
351 
352     /**
353      * {@inheritDoc}
354      * @hide
355      */
356     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
357     @TestApi
358     @Override
query(@onNull Query query)359     public @Nullable Result query(@NonNull Query query) {
360         return super.query(query);
361     }
362 
363     /**
364      * {@inheritDoc}
365      * @hide
366      */
367     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
368     @TestApi
369     @Override
invalidateCache()370     public void invalidateCache() {
371         super.invalidateCache();
372     }
373 
374     /**
375      * Invalidate caches in all processes that are keyed for the module and api.
376      * @hide
377      */
378     @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
379     @TestApi
invalidateCache(@onNull @pcDataCacheModule String module, @NonNull String api)380     public static void invalidateCache(@NonNull @IpcDataCacheModule String module,
381             @NonNull String api) {
382         PropertyInvalidatedCache.invalidateCache(module, api);
383     }
384 
385     /**
386      * This is a convenience class that encapsulates configuration information for a
387      * cache.  It may be supplied to the cache constructors in lieu of the other
388      * parameters.  The class captures maximum entry count, the module, the key, and the
389      * api.
390      *
391      * There are three specific use cases supported by this class.
392      *
393      * 1. Instance-per-cache: create a static instance of this class using the same
394      *    parameters as would have been given to IpcDataCache (or
395      *    PropertyInvalidatedCache).  This static instance provides a hook for the
396      *    invalidateCache() and disableForLocalProcess() calls, which, generally, must
397      *    also be static.
398      *
399      * 2. Short-hand for shared configuration parameters: create an instance of this class
400      *    to capture the maximum number of entries and the module to be used by more than
401      *    one cache in the class.  Refer to this instance when creating new configs.  Only
402      *    the api and (optionally key) for the new cache must be supplied.
403      *
404      * 3. Tied caches: create a static instance of this class to capture the maximum
405      *    number of entries, the module, and the key.  Refer to this instance when
406      *    creating a new config that differs in only the api.  The new config can be
407      *    created as part of the cache constructor.  All caches that trace back to the
408      *    root config share the same key and are invalidated by the invalidateCache()
409      *    method of the root config.  All caches that trace back to the root config can be
410      *    disabled in the local process by the disableAllForCurrentProcess() method of the
411      *    root config.
412      *
413      * @hide
414      */
415     public static class Config {
416         private final int mMaxEntries;
417         @IpcDataCacheModule
418         private final String mModule;
419         private final String mApi;
420         private final String mName;
421 
422         /**
423          * The list of cache names that were created extending this Config.  If
424          * disableForCurrentProcess() is invoked on this config then all children will be
425          * disabled.  Furthermore, any new children based off of this config will be
426          * disabled.  The construction order guarantees that new caches will be disabled
427          * before they are created (the Config must be created before the IpcDataCache is
428          * created).
429          */
430         private ArraySet<String> mChildren;
431 
432         /**
433          * True if registered children are disabled in the current process.  If this is
434          * true then all new children are disabled as they are registered.
435          */
436         private boolean mDisabled = false;
437 
Config(int maxEntries, @NonNull @IpcDataCacheModule String module, @NonNull String api, @NonNull String name)438         public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
439                 @NonNull String api, @NonNull String name) {
440             mMaxEntries = maxEntries;
441             mModule = module;
442             mApi = api;
443             mName = name;
444         }
445 
446         /**
447          * A short-hand constructor that makes the name the same as the api.
448          */
Config(int maxEntries, @NonNull @IpcDataCacheModule String module, @NonNull String api)449         public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
450                 @NonNull String api) {
451             this(maxEntries, module, api, api);
452         }
453 
454         /**
455          * Copy the module and max entries from the Config and take the api and name from
456          * the parameter list.
457          */
Config(@onNull Config root, @NonNull String api, @NonNull String name)458         public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
459             this(root.maxEntries(), root.module(), api, name);
460         }
461 
462         /**
463          * Copy the module and max entries from the Config and take the api and name from
464          * the parameter list.
465          */
Config(@onNull Config root, @NonNull String api)466         public Config(@NonNull Config root, @NonNull String api) {
467             this(root.maxEntries(), root.module(), api, api);
468         }
469 
470         /**
471          * Fetch a config that is a child of <this>.  The child shares the same api as the
472          * parent and is registered with the parent for the purposes of disabling in the
473          * current process.
474          */
child(@onNull String name)475         public Config child(@NonNull String name) {
476             final Config result = new Config(this, api(), name);
477             registerChild(name);
478             return result;
479         }
480 
maxEntries()481         public final int maxEntries() {
482             return mMaxEntries;
483         }
484 
485         @IpcDataCacheModule
module()486         public final @NonNull String module() {
487             return mModule;
488         }
489 
api()490         public final @NonNull String api() {
491             return mApi;
492         }
493 
name()494         public final @NonNull String name() {
495             return mName;
496         }
497 
498         /**
499          * Register a child cache name.  If disableForCurrentProcess() has been called
500          * against this cache, disable th new child.
501          */
registerChild(String name)502         private final void registerChild(String name) {
503             synchronized (this) {
504                 if (mChildren == null) {
505                     mChildren = new ArraySet<>();
506                 }
507                 mChildren.add(name);
508                 if (mDisabled) {
509                     IpcDataCache.disableForCurrentProcess(name);
510                 }
511             }
512         }
513 
514         /**
515          * Invalidate all caches that share this Config's module and api.
516          */
invalidateCache()517         public void invalidateCache() {
518             IpcDataCache.invalidateCache(mModule, mApi);
519         }
520 
521         /**
522          * Disable all caches that share this Config's name.
523          */
disableForCurrentProcess()524         public void disableForCurrentProcess() {
525             IpcDataCache.disableForCurrentProcess(mName);
526         }
527 
528         /**
529          * Disable this cache and all children.  Any child that is added in the future
530          * will alwo be disabled.
531          */
disableAllForCurrentProcess()532         public void disableAllForCurrentProcess() {
533             synchronized (this) {
534                 mDisabled = true;
535                 disableForCurrentProcess();
536                 if (mChildren != null) {
537                     for (String c : mChildren) {
538                         IpcDataCache.disableForCurrentProcess(c);
539                     }
540                 }
541             }
542         }
543     }
544 
545     /**
546      * Create a new cache using a config.
547      * @hide
548      */
IpcDataCache(@onNull Config config, @NonNull QueryHandler<Query, Result> computer)549     public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
550         super(config.maxEntries(), config.module(), config.api(), config.name(), computer);
551     }
552 
553     /**
554      * An interface suitable for a lambda expression instead of a QueryHandler.
555      * @hide
556      */
557     public interface RemoteCall<Query, Result> {
apply(Query query)558         Result apply(Query query) throws RemoteException;
559     }
560 
561     /**
562      * This is a query handler that is created with a lambda expression that is invoked
563      * every time the handler is called.  The handler is specifically meant for services
564      * hosted by system_server; the handler automatically rethrows RemoteException as a
565      * RuntimeException, which is the usual handling for failed binder calls.
566      */
567     private static class SystemServerCallHandler<Query, Result>
568             extends IpcDataCache.QueryHandler<Query, Result> {
569         private final RemoteCall<Query, Result> mHandler;
SystemServerCallHandler(RemoteCall handler)570         public SystemServerCallHandler(RemoteCall handler) {
571             mHandler = handler;
572         }
573         @Override
apply(Query query)574         public Result apply(Query query) {
575             try {
576                 return mHandler.apply(query);
577             } catch (RemoteException e) {
578                 throw e.rethrowFromSystemServer();
579             }
580         }
581     }
582 
583     /**
584      * Create a cache using a config and a lambda expression.
585      * @hide
586      */
IpcDataCache(@onNull Config config, @NonNull RemoteCall<Query, Result> computer)587     public IpcDataCache(@NonNull Config config, @NonNull RemoteCall<Query, Result> computer) {
588         this(config, new SystemServerCallHandler<>(computer));
589     }
590 }
591