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