File: IpcDataCache.java

package info (click to toggle)
android-platform-frameworks-base 1%3A14~beta1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 326,084 kB
  • sloc: java: 2,032,373; xml: 343,016; cpp: 304,181; python: 3,683; ansic: 2,090; sh: 1,871; makefile: 120; sed: 19
file content (590 lines) | stat: -rw-r--r-- 22,994 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.PropertyInvalidatedCache;
import android.text.TextUtils;
import android.util.ArraySet;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FastPrintWriter;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
 * but doesn't hold a lock across data fetches on query misses.
 *
 * The intended use case is caching frequently-read, seldom-changed information normally retrieved
 * across interprocess communication. Imagine that you've written a user birthday information
 * daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface over
 * binder. That binder interface looks something like this:
 *
 * <pre>
 * parcelable Birthday {
 *   int month;
 *   int day;
 * }
 * interface IUserBirthdayService {
 *   Birthday getUserBirthday(int userId);
 * }
 * </pre>
 *
 * Suppose the service implementation itself looks like this...
 *
 * <pre>
 * public class UserBirthdayServiceImpl implements IUserBirthdayService {
 *   private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
 *   {@literal @}Override
 *   public synchronized Birthday getUserBirthday(int userId) {
 *     return mUidToBirthday.get(userId);
 *   }
 *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
 *     mUidToBirthday.clear();
 *     mUidToBirthday.putAll(uidToBirthday);
 *   }
 * }
 * </pre>
 *
 * ... and we have a client in frameworks (loaded into every app process) that looks like this:
 *
 * <pre>
 * public class ActivityThread {
 *   ...
 *   public Birthday getUserBirthday(int userId) {
 *     return GetService("birthdayd").getUserBirthday(userId);
 *   }
 *   ...
 * }
 * </pre>
 *
 * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call to
 * the birthdayd process and consult its database of birthdays. If we query user birthdays
 * frequently, we do a lot of work that we don't have to do, since user birthdays change
 * infrequently.
 *
 * IpcDataCache is part of a pattern for optimizing this kind of information-querying code. Using
 * {@code IpcDataCache}, you'd write the client this way:
 *
 * <pre>
 * public class ActivityThread {
 *   ...
 *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
 *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
 *           {@literal @}Override
 *           public Birthday apply(Integer) {
 *              return GetService("birthdayd").getUserBirthday(userId);
 *           }
 *       };
 *   private static final int BDAY_CACHE_MAX = 8;  // Maximum birthdays to cache
 *   private static final String BDAY_API = "getUserBirthday";
 *   private final IpcDataCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
 *     IpcDataCache&lt;Integer, Birthday%&gt;(
 *             BDAY_CACHE_MAX, MODULE_SYSTEM, BDAY_API,  BDAY_API, mBirthdayQuery);
 *
 *   public void disableUserBirthdayCache() {
 *     mBirthdayCache.disableForCurrentProcess();
 *   }
 *   public void invalidateUserBirthdayCache() {
 *     mBirthdayCache.invalidateCache();
 *   }
 *   public Birthday getUserBirthday(int userId) {
 *     return mBirthdayCache.query(userId);
 *   }
 *   ...
 * }
 * </pre>
 *
 * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
 * for the first time; on subsequent queries, we return the already-known Birthday object.
 *
 * The second parameter to the IpcDataCache constructor is a string that identifies the "module"
 * that owns the cache. There are some well-known modules (such as {@code MODULE_SYSTEM} but any
 * string is permitted.  The third parameters is the name of the API being cached; this, too, can
 * any value.  The fourth is the name of the cache.  The cache is usually named after th API.
 * Some things you must know about the three strings:
 * <list>
 * <ul> The system property that controls the cache is named {@code cache_key.<module>.<api>}.
 * Usually, the SELinux rules permit a process to write a system property (and therefore
 * invalidate a cache) based on the wildcard {@code cache_key.<module>.*}.  This means that
 * although the cache can be constructed with any module string, whatever string is chosen must be
 * consistent with the SELinux configuration.
 * <ul> The API name can be any string of alphanumeric characters.  All caches with the same API
 * are invalidated at the same time.  If a server supports several caches and all are invalidated
 * in common, then it is most efficient to assign the same API string to every cache.
 * <ul> The cache name can be any string.  In debug output, the name is used to distiguish between
 * caches with the same API name.  The cache name is also used when disabling caches in the
 * current process.  So, invalidation is based on the module+api but disabling (which is generally
 * a once-per-process operation) is based on the cache name.
 * </list>
 *
 * User birthdays do occasionally change, so we have to modify the server to invalidate this
 * cache when necessary. That invalidation code looks like this:
 *
 * <pre>
 * public class UserBirthdayServiceImpl {
 *   ...
 *   public UserBirthdayServiceImpl() {
 *     ...
 *     ActivityThread.currentActivityThread().disableUserBirthdayCache();
 *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
 *   }
 *
 *   private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
 *     mUidToBirthday.clear();
 *     mUidToBirthday.putAll(uidToBirthday);
 *     ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
 *   }
 *   ...
 * }
 * </pre>
 *
 * The call to {@code IpcDataCache.invalidateCache()} guarantees that all clients will re-fetch
 * birthdays from binder during consequent calls to
 * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
 * held, we maintain consistency between different client views of the birthday state. The use of
 * IpcDataCache in this idiomatic way introduces no new race conditions.
 *
 * IpcDataCache has a few other features for doing things like incremental enhancement of cached
 * values and invalidation of multiple caches (that all share the same property key) at once.
 *
 * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
 * time we update the cache. SELinux configuration must allow everyone to read this property
 * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
 * the property. (These properties conventionally begin with the "cache_key." prefix.)
 *
 * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
 * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In this
 * local case, there's no IPC, so use of the cache is (depending on exact circumstance)
 * unnecessary.
 *
 * There may be queries for which it is more efficient to bypass the cache than to cache the
 * result.  This would be true, for example, if some queries would require frequent cache
 * invalidation while other queries require infrequent invalidation.  To expand on the birthday
 * example, suppose that there is a userId that signifies "the next birthday".  When passed this
 * userId, the server returns the next birthday among all users - this value changes as time
 * advances.  The userId value can be cached, but the cache must be invalidated whenever a
 * birthday occurs, and this invalidates all birthdays.  If there is a large number of users,
 * invalidation will happen so often that the cache provides no value.
 *
 * The class provides a bypass mechanism to handle this situation.
 * <pre>
 * public class ActivityThread {
 *   ...
 *   private final IpcDataCache.QueryHandler&lt;Integer, Birthday&gt; mBirthdayQuery =
 *       new IpcDataCache.QueryHandler&lt;Integer, Birthday&gt;() {
 *           {@literal @}Override
 *           public Birthday apply(Integer) {
 *              return GetService("birthdayd").getUserBirthday(userId);
 *           }
 *           {@literal @}Override
 *           public boolean shouldBypassQuery(Integer userId) {
 *               return userId == NEXT_BIRTHDAY;
 *           }
 *       };
 *   ...
 * }
 * </pre>
 *
 * If the {@code shouldBypassQuery()} method returns true then the cache is not used for that
 * particular query.  The {@code shouldBypassQuery()} method is not abstract and the default
 * implementation returns false.
 *
 * For security, there is a allowlist of processes that are allowed to invalidate a cache.  The
 * allowlist includes normal runtime processes but does not include test processes.  Test
 * processes must call {@code IpcDataCache.disableForTestMode()} to disable all cache activity in
 * that process.
 *
 * Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
 *
 * To test a binder cache, create one or more tests that exercise the binder method.  This should
 * be done twice: once with production code and once with a special image that sets {@code DEBUG}
 * and {@code VERIFY} true.  In the latter case, verify that no cache inconsistencies are
 * reported.  If a cache inconsistency is reported, however, it might be a false positive.  This
 * happens if the server side data can be read and written non-atomically with respect to cache
 * invalidation.
 *
 * @param <Query> The class used to index cache entries: must be hashable and comparable
 * @param <Result> The class holding cache entries; use a boxed primitive if possible
 * @hide
 */
@TestApi
@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> {
    /**
     * {@inheritDoc}
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public static abstract class QueryHandler<Q,R>
            extends PropertyInvalidatedCache.QueryHandler<Q,R> {
        /**
         * Compute a result given a query.  The semantics are those of Functor.
         */
        public abstract @Nullable R apply(@NonNull Q query);

        /**
         * Return true if a query should not use the cache.  The default implementation
         * always uses the cache.
         */
        public boolean shouldBypassCache(@NonNull Q query) {
            return false;
        }
    };

    /**
     * The list of cache namespaces.  Each namespace corresponds to an sepolicy domain.  A
     * namespace is owned by a single process, although a single process can have more
     * than one namespace (system_server, as an example).
     * @hide
     */
    @StringDef(
        prefix = { "MODULE_"
        },
        value = {
            MODULE_TEST,
            MODULE_SYSTEM,
            MODULE_BLUETOOTH,
            MODULE_TELEPHONY
        }
    )
    @Retention(RetentionPolicy.SOURCE)
    public @interface IpcDataCacheModule { }

    /**
     * The module used for unit tests and cts tests.  It is expected that no process in
     * the system has permissions to write properties with this module.
     * @hide
     */
    @TestApi
    public static final String MODULE_TEST = PropertyInvalidatedCache.MODULE_TEST;

    /**
     * The module used for system server/framework caches.  This is not visible outside
     * the system processes.
     * @hide
     */
    @TestApi
    public static final String MODULE_SYSTEM = PropertyInvalidatedCache.MODULE_SYSTEM;

    /**
     * The module used for bluetooth caches.
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public static final String MODULE_BLUETOOTH = PropertyInvalidatedCache.MODULE_BLUETOOTH;

    /**
     * Make a new property invalidated cache.  The key is computed from the module and api
     * parameters.
     *
     * @param maxEntries Maximum number of entries to cache; LRU discard
     * @param module The module under which the cache key should be placed.
     * @param api The api this cache front-ends.  The api must be a Java identifier but
     * need not be an actual api.
     * @param cacheName Name of this cache in debug and dumpsys
     * @param computer The code to compute values that are not in the cache.
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module,
            @NonNull String api, @NonNull String cacheName,
            @NonNull QueryHandler<Query, Result> computer) {
        super(maxEntries, module, api, cacheName, computer);
    }

    /**
     * {@inheritDoc}
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    @Override
    public void disableForCurrentProcess() {
        super.disableForCurrentProcess();
    }

    /**
     * {@inheritDoc}
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public static void disableForCurrentProcess(@NonNull String cacheName) {
        PropertyInvalidatedCache.disableForCurrentProcess(cacheName);
    }

    /**
     * {@inheritDoc}
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    @Override
    public @Nullable Result query(@NonNull Query query) {
        return super.query(query);
    }

    /**
     * {@inheritDoc}
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    @Override
    public void invalidateCache() {
        super.invalidateCache();
    }

    /**
     * Invalidate caches in all processes that are keyed for the module and api.
     * @hide
     */
    @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
    @TestApi
    public static void invalidateCache(@NonNull @IpcDataCacheModule String module,
            @NonNull String api) {
        PropertyInvalidatedCache.invalidateCache(module, api);
    }

    /**
     * This is a convenience class that encapsulates configuration information for a
     * cache.  It may be supplied to the cache constructors in lieu of the other
     * parameters.  The class captures maximum entry count, the module, the key, and the
     * api.
     *
     * There are three specific use cases supported by this class.
     *
     * 1. Instance-per-cache: create a static instance of this class using the same
     *    parameters as would have been given to IpcDataCache (or
     *    PropertyInvalidatedCache).  This static instance provides a hook for the
     *    invalidateCache() and disableForLocalProcess() calls, which, generally, must
     *    also be static.
     *
     * 2. Short-hand for shared configuration parameters: create an instance of this class
     *    to capture the maximum number of entries and the module to be used by more than
     *    one cache in the class.  Refer to this instance when creating new configs.  Only
     *    the api and (optionally key) for the new cache must be supplied.
     *
     * 3. Tied caches: create a static instance of this class to capture the maximum
     *    number of entries, the module, and the key.  Refer to this instance when
     *    creating a new config that differs in only the api.  The new config can be
     *    created as part of the cache constructor.  All caches that trace back to the
     *    root config share the same key and are invalidated by the invalidateCache()
     *    method of the root config.  All caches that trace back to the root config can be
     *    disabled in the local process by the disableAllForCurrentProcess() method of the
     *    root config.
     *
     * @hide
     */
    public static class Config {
        private final int mMaxEntries;
        @IpcDataCacheModule
        private final String mModule;
        private final String mApi;
        private final String mName;

        /**
         * The list of cache names that were created extending this Config.  If
         * disableForCurrentProcess() is invoked on this config then all children will be
         * disabled.  Furthermore, any new children based off of this config will be
         * disabled.  The construction order guarantees that new caches will be disabled
         * before they are created (the Config must be created before the IpcDataCache is
         * created).
         */
        private ArraySet<String> mChildren;

        /**
         * True if registered children are disabled in the current process.  If this is
         * true then all new children are disabled as they are registered.
         */
        private boolean mDisabled = false;

        public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
                @NonNull String api, @NonNull String name) {
            mMaxEntries = maxEntries;
            mModule = module;
            mApi = api;
            mName = name;
        }

        /**
         * A short-hand constructor that makes the name the same as the api.
         */
        public Config(int maxEntries, @NonNull @IpcDataCacheModule String module,
                @NonNull String api) {
            this(maxEntries, module, api, api);
        }

        /**
         * Copy the module and max entries from the Config and take the api and name from
         * the parameter list.
         */
        public Config(@NonNull Config root, @NonNull String api, @NonNull String name) {
            this(root.maxEntries(), root.module(), api, name);
        }

        /**
         * Copy the module and max entries from the Config and take the api and name from
         * the parameter list.
         */
        public Config(@NonNull Config root, @NonNull String api) {
            this(root.maxEntries(), root.module(), api, api);
        }

        /**
         * Fetch a config that is a child of <this>.  The child shares the same api as the
         * parent and is registered with the parent for the purposes of disabling in the
         * current process.
         */
        public Config child(@NonNull String name) {
            final Config result = new Config(this, api(), name);
            registerChild(name);
            return result;
        }

        public final int maxEntries() {
            return mMaxEntries;
        }

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

        public final @NonNull String api() {
            return mApi;
        }

        public final @NonNull String name() {
            return mName;
        }

        /**
         * Register a child cache name.  If disableForCurrentProcess() has been called
         * against this cache, disable th new child.
         */
        private final void registerChild(String name) {
            synchronized (this) {
                if (mChildren == null) {
                    mChildren = new ArraySet<>();
                }
                mChildren.add(name);
                if (mDisabled) {
                    IpcDataCache.disableForCurrentProcess(name);
                }
            }
        }

        /**
         * Invalidate all caches that share this Config's module and api.
         */
        public void invalidateCache() {
            IpcDataCache.invalidateCache(mModule, mApi);
        }

        /**
         * Disable all caches that share this Config's name.
         */
        public void disableForCurrentProcess() {
            IpcDataCache.disableForCurrentProcess(mName);
        }

        /**
         * Disable this cache and all children.  Any child that is added in the future
         * will alwo be disabled.
         */
        public void disableAllForCurrentProcess() {
            synchronized (this) {
                mDisabled = true;
                disableForCurrentProcess();
                if (mChildren != null) {
                    for (String c : mChildren) {
                        IpcDataCache.disableForCurrentProcess(c);
                    }
                }
            }
        }
    }

    /**
     * Create a new cache using a config.
     * @hide
     */
    public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) {
        super(config.maxEntries(), config.module(), config.api(), config.name(), computer);
    }

    /**
     * An interface suitable for a lambda expression instead of a QueryHandler.
     * @hide
     */
    public interface RemoteCall<Query, Result> {
        Result apply(Query query) throws RemoteException;
    }

    /**
     * This is a query handler that is created with a lambda expression that is invoked
     * every time the handler is called.  The handler is specifically meant for services
     * hosted by system_server; the handler automatically rethrows RemoteException as a
     * RuntimeException, which is the usual handling for failed binder calls.
     */
    private static class SystemServerCallHandler<Query, Result>
            extends IpcDataCache.QueryHandler<Query, Result> {
        private final RemoteCall<Query, Result> mHandler;
        public SystemServerCallHandler(RemoteCall handler) {
            mHandler = handler;
        }
        @Override
        public Result apply(Query query) {
            try {
                return mHandler.apply(query);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /**
     * Create a cache using a config and a lambda expression.
     * @hide
     */
    public IpcDataCache(@NonNull Config config, @NonNull RemoteCall<Query, Result> computer) {
        this(config, new SystemServerCallHandler<>(computer));
    }
}