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<Integer, Birthday%> mUidToBirthday;
* {@literal @}Override
* public synchronized Birthday getUserBirthday(int userId) {
* return mUidToBirthday.get(userId);
* }
* private synchronized void updateBirthdays(Map<Integer, Birthday%> 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<Integer, Birthday> mBirthdayQuery =
* new IpcDataCache.QueryHandler<Integer, Birthday>() {
* {@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<Integer, Birthday%> mBirthdayCache = new
* IpcDataCache<Integer, Birthday%>(
* 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<Integer, Birthday%> 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<Integer, Birthday> mBirthdayQuery =
* new IpcDataCache.QueryHandler<Integer, Birthday>() {
* {@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));
}
}
|