1 /* 2 * Copyright (C) 2010 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 package android.os; 17 18 import android.animation.ValueAnimator; 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.app.ActivityManager; 24 import android.app.ActivityThread; 25 import android.app.IActivityManager; 26 import android.compat.annotation.UnsupportedAppUsage; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.ServiceConnection; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageManager; 33 import android.net.TrafficStats; 34 import android.net.Uri; 35 import android.os.storage.IStorageManager; 36 import android.os.strictmode.CleartextNetworkViolation; 37 import android.os.strictmode.ContentUriWithoutPermissionViolation; 38 import android.os.strictmode.CredentialProtectedWhileLockedViolation; 39 import android.os.strictmode.CustomViolation; 40 import android.os.strictmode.DiskReadViolation; 41 import android.os.strictmode.DiskWriteViolation; 42 import android.os.strictmode.ExplicitGcViolation; 43 import android.os.strictmode.FileUriExposedViolation; 44 import android.os.strictmode.ImplicitDirectBootViolation; 45 import android.os.strictmode.IncorrectContextUseViolation; 46 import android.os.strictmode.InstanceCountViolation; 47 import android.os.strictmode.IntentReceiverLeakedViolation; 48 import android.os.strictmode.LeakedClosableViolation; 49 import android.os.strictmode.NetworkViolation; 50 import android.os.strictmode.NonSdkApiUsedViolation; 51 import android.os.strictmode.ResourceMismatchViolation; 52 import android.os.strictmode.ServiceConnectionLeakedViolation; 53 import android.os.strictmode.SqliteObjectLeakedViolation; 54 import android.os.strictmode.UnbufferedIoViolation; 55 import android.os.strictmode.UntaggedSocketViolation; 56 import android.os.strictmode.Violation; 57 import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation; 58 import android.util.ArrayMap; 59 import android.util.Log; 60 import android.util.Printer; 61 import android.util.Singleton; 62 import android.util.Slog; 63 import android.view.IWindowManager; 64 65 import com.android.internal.annotations.GuardedBy; 66 import com.android.internal.os.BackgroundThread; 67 import com.android.internal.os.RuntimeInit; 68 import com.android.internal.util.FastPrintWriter; 69 import com.android.internal.util.HexDump; 70 71 import dalvik.system.BlockGuard; 72 import dalvik.system.CloseGuard; 73 import dalvik.system.VMDebug; 74 import dalvik.system.VMRuntime; 75 76 import java.io.PrintWriter; 77 import java.io.StringWriter; 78 import java.lang.annotation.Retention; 79 import java.lang.annotation.RetentionPolicy; 80 import java.net.InetAddress; 81 import java.net.UnknownHostException; 82 import java.util.ArrayDeque; 83 import java.util.ArrayList; 84 import java.util.Arrays; 85 import java.util.Deque; 86 import java.util.HashMap; 87 import java.util.Iterator; 88 import java.util.Map; 89 import java.util.concurrent.Executor; 90 import java.util.concurrent.RejectedExecutionException; 91 import java.util.concurrent.atomic.AtomicInteger; 92 import java.util.function.Consumer; 93 94 /** 95 * StrictMode is a developer tool which detects things you might be doing by accident and brings 96 * them to your attention so you can fix them. 97 * 98 * <p>StrictMode is most commonly used to catch accidental disk or network access on the 99 * application's main thread, where UI operations are received and animations take place. Keeping 100 * disk and network operations off the main thread makes for much smoother, more responsive 101 * applications. By keeping your application's main thread responsive, you also prevent <a 102 * href="{@docRoot}guide/practices/design/responsiveness.html">ANR dialogs</a> from being shown to 103 * users. 104 * 105 * <p class="note">Note that even though an Android device's disk is often on flash memory, many 106 * devices run a filesystem on top of that memory with very limited concurrency. It's often the case 107 * that almost all disk accesses are fast, but may in individual cases be dramatically slower when 108 * certain I/O is happening in the background from other processes. If possible, it's best to assume 109 * that such things are not fast. 110 * 111 * <p>Example code to enable from early in your {@link android.app.Application}, {@link 112 * android.app.Activity}, or other application component's {@link android.app.Application#onCreate} 113 * method: 114 * 115 * <pre> 116 * public void onCreate() { 117 * if (DEVELOPER_MODE) { 118 * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}() 119 * .detectDiskReads() 120 * .detectDiskWrites() 121 * .detectNetwork() // or .detectAll() for all detectable problems 122 * .penaltyLog() 123 * .build()); 124 * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}() 125 * .detectLeakedSqlLiteObjects() 126 * .detectLeakedClosableObjects() 127 * .penaltyLog() 128 * .penaltyDeath() 129 * .build()); 130 * } 131 * super.onCreate(); 132 * } 133 * </pre> 134 * 135 * <p>You can decide what should happen when a violation is detected. For example, using {@link 136 * ThreadPolicy.Builder#penaltyLog} you can watch the output of <code>adb logcat</code> while you 137 * use your application to see the violations as they happen. 138 * 139 * <p>If you find violations that you feel are problematic, there are a variety of tools to help 140 * solve them: threads, {@link android.os.Handler}, {@link android.os.AsyncTask}, {@link 141 * android.app.IntentService}, etc. But don't feel compelled to fix everything that StrictMode 142 * finds. In particular, many cases of disk access are often necessary during the normal activity 143 * lifecycle. Use StrictMode to find things you did by accident. Network requests on the UI thread 144 * are almost always a problem, though. 145 * 146 * <p class="note">StrictMode is not a security mechanism and is not guaranteed to find all disk or 147 * network accesses. While it does propagate its state across process boundaries when doing {@link 148 * android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network 149 * access from JNI calls won't necessarily trigger it. Future versions of Android may catch more (or 150 * fewer) operations, so you should never leave StrictMode enabled in applications distributed on 151 * Google Play. 152 */ 153 public final class StrictMode { 154 private static final String TAG = "StrictMode"; 155 private static final boolean LOG_V = Log.isLoggable(TAG, Log.VERBOSE); 156 157 /** 158 * Boolean system property to disable strict mode checks outright. Set this to 'true' to force 159 * disable; 'false' has no effect on other enable/disable policy. 160 * 161 * @hide 162 */ 163 public static final String DISABLE_PROPERTY = "persist.sys.strictmode.disable"; 164 165 /** 166 * The boolean system property to control screen flashes on violations. 167 * 168 * @hide 169 */ 170 public static final String VISUAL_PROPERTY = "persist.sys.strictmode.visual"; 171 172 /** 173 * Temporary property used to include {@link #DETECT_VM_CLEARTEXT_NETWORK} in {@link 174 * VmPolicy.Builder#detectAll()}. Apps can still always opt-into detection using {@link 175 * VmPolicy.Builder#detectCleartextNetwork()}. 176 */ 177 private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear"; 178 179 /** 180 * Quick feature-flag that can be used to disable the defaults provided by {@link 181 * #initThreadDefaults(ApplicationInfo)} and {@link #initVmDefaults(ApplicationInfo)}. 182 */ 183 private static final boolean DISABLE = false; 184 185 // Only apply VM penalties for the same violation at this interval. 186 private static final long MIN_VM_INTERVAL_MS = 1000; 187 188 // Only log a duplicate stack trace to the logs every second. 189 private static final long MIN_LOG_INTERVAL_MS = 1000; 190 191 // Only show an annoying dialog at most every 30 seconds 192 private static final long MIN_DIALOG_INTERVAL_MS = 30000; 193 194 // Only log a dropbox entry at most every 30 seconds 195 private static final long MIN_DROPBOX_INTERVAL_MS = 3000; 196 197 // How many Span tags (e.g. animations) to report. 198 private static final int MAX_SPAN_TAGS = 20; 199 200 // How many offending stacks to keep track of (and time) per loop 201 // of the Looper. 202 private static final int MAX_OFFENSES_PER_LOOP = 10; 203 204 /** @hide */ 205 @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = { 206 DETECT_THREAD_DISK_WRITE, 207 DETECT_THREAD_DISK_READ, 208 DETECT_THREAD_NETWORK, 209 DETECT_THREAD_CUSTOM, 210 DETECT_THREAD_RESOURCE_MISMATCH, 211 DETECT_THREAD_UNBUFFERED_IO, 212 DETECT_THREAD_EXPLICIT_GC, 213 PENALTY_GATHER, 214 PENALTY_LOG, 215 PENALTY_DIALOG, 216 PENALTY_DEATH, 217 PENALTY_FLASH, 218 PENALTY_DROPBOX, 219 PENALTY_DEATH_ON_NETWORK, 220 PENALTY_DEATH_ON_CLEARTEXT_NETWORK, 221 PENALTY_DEATH_ON_FILE_URI_EXPOSURE, 222 }) 223 @Retention(RetentionPolicy.SOURCE) 224 public @interface ThreadPolicyMask {} 225 226 // Thread policy: bits 0-15 227 228 /** @hide */ 229 private static final int DETECT_THREAD_DISK_WRITE = 1 << 0; 230 /** @hide */ 231 private static final int DETECT_THREAD_DISK_READ = 1 << 1; 232 /** @hide */ 233 private static final int DETECT_THREAD_NETWORK = 1 << 2; 234 /** @hide */ 235 private static final int DETECT_THREAD_CUSTOM = 1 << 3; 236 /** @hide */ 237 private static final int DETECT_THREAD_RESOURCE_MISMATCH = 1 << 4; 238 /** @hide */ 239 private static final int DETECT_THREAD_UNBUFFERED_IO = 1 << 5; 240 /** @hide */ 241 private static final int DETECT_THREAD_EXPLICIT_GC = 1 << 6; 242 243 /** @hide */ 244 private static final int DETECT_THREAD_ALL = 0x0000ffff; 245 246 /** @hide */ 247 @IntDef(flag = true, prefix = { "DETECT_THREAD_", "PENALTY_" }, value = { 248 DETECT_VM_CURSOR_LEAKS, 249 DETECT_VM_CLOSABLE_LEAKS, 250 DETECT_VM_ACTIVITY_LEAKS, 251 DETECT_VM_INSTANCE_LEAKS, 252 DETECT_VM_REGISTRATION_LEAKS, 253 DETECT_VM_FILE_URI_EXPOSURE, 254 DETECT_VM_CLEARTEXT_NETWORK, 255 DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION, 256 DETECT_VM_UNTAGGED_SOCKET, 257 DETECT_VM_NON_SDK_API_USAGE, 258 DETECT_VM_IMPLICIT_DIRECT_BOOT, 259 DETECT_VM_INCORRECT_CONTEXT_USE, 260 PENALTY_GATHER, 261 PENALTY_LOG, 262 PENALTY_DIALOG, 263 PENALTY_DEATH, 264 PENALTY_FLASH, 265 PENALTY_DROPBOX, 266 PENALTY_DEATH_ON_NETWORK, 267 PENALTY_DEATH_ON_CLEARTEXT_NETWORK, 268 PENALTY_DEATH_ON_FILE_URI_EXPOSURE, 269 }) 270 @Retention(RetentionPolicy.SOURCE) 271 public @interface VmPolicyMask {} 272 273 // VM policy: bits 0-15 274 275 /** @hide */ 276 private static final int DETECT_VM_CURSOR_LEAKS = 1 << 0; 277 /** @hide */ 278 private static final int DETECT_VM_CLOSABLE_LEAKS = 1 << 1; 279 /** @hide */ 280 private static final int DETECT_VM_ACTIVITY_LEAKS = 1 << 2; 281 /** @hide */ 282 private static final int DETECT_VM_INSTANCE_LEAKS = 1 << 3; 283 /** @hide */ 284 private static final int DETECT_VM_REGISTRATION_LEAKS = 1 << 4; 285 /** @hide */ 286 private static final int DETECT_VM_FILE_URI_EXPOSURE = 1 << 5; 287 /** @hide */ 288 private static final int DETECT_VM_CLEARTEXT_NETWORK = 1 << 6; 289 /** @hide */ 290 private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 1 << 7; 291 /** @hide */ 292 private static final int DETECT_VM_UNTAGGED_SOCKET = 1 << 8; 293 /** @hide */ 294 private static final int DETECT_VM_NON_SDK_API_USAGE = 1 << 9; 295 /** @hide */ 296 private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10; 297 /** @hide */ 298 private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11; 299 /** @hide */ 300 private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12; 301 302 /** @hide */ 303 private static final int DETECT_VM_ALL = 0x0000ffff; 304 305 // Penalty policy: bits 16-31 306 307 /** 308 * Non-public penalty mode which overrides all the other penalty bits and signals that we're in 309 * a Binder call and we should ignore the other penalty bits and instead serialize back all our 310 * offending stack traces to the caller to ultimately handle in the originating process. 311 * 312 * <p>This must be kept in sync with the constant in libs/binder/Parcel.cpp 313 * 314 * @hide 315 */ 316 public static final int PENALTY_GATHER = 1 << 31; 317 318 /** {@hide} */ 319 public static final int PENALTY_LOG = 1 << 30; 320 /** {@hide} */ 321 public static final int PENALTY_DIALOG = 1 << 29; 322 /** {@hide} */ 323 public static final int PENALTY_DEATH = 1 << 28; 324 /** {@hide} */ 325 public static final int PENALTY_FLASH = 1 << 27; 326 /** {@hide} */ 327 public static final int PENALTY_DROPBOX = 1 << 26; 328 /** {@hide} */ 329 public static final int PENALTY_DEATH_ON_NETWORK = 1 << 25; 330 /** {@hide} */ 331 public static final int PENALTY_DEATH_ON_CLEARTEXT_NETWORK = 1 << 24; 332 /** {@hide} */ 333 public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 1 << 23; 334 335 /** @hide */ 336 public static final int PENALTY_ALL = 0xffff0000; 337 338 /** {@hide} */ 339 public static final int NETWORK_POLICY_ACCEPT = 0; 340 /** {@hide} */ 341 public static final int NETWORK_POLICY_LOG = 1; 342 /** {@hide} */ 343 public static final int NETWORK_POLICY_REJECT = 2; 344 345 // TODO: wrap in some ImmutableHashMap thing. 346 // Note: must be before static initialization of sVmPolicy. 347 private static final HashMap<Class, Integer> EMPTY_CLASS_LIMIT_MAP = 348 new HashMap<Class, Integer>(); 349 350 /** The current VmPolicy in effect. */ 351 private static volatile VmPolicy sVmPolicy = VmPolicy.LAX; 352 353 /** {@hide} */ 354 @TestApi 355 public interface ViolationLogger { 356 357 /** Called when penaltyLog is enabled and a violation needs logging. */ log(ViolationInfo info)358 void log(ViolationInfo info); 359 } 360 361 private static final ViolationLogger LOGCAT_LOGGER = 362 info -> { 363 String msg; 364 if (info.durationMillis != -1) { 365 msg = "StrictMode policy violation; ~duration=" + info.durationMillis + " ms:"; 366 } else { 367 msg = "StrictMode policy violation:"; 368 } 369 Log.d(TAG, msg + " " + info.getStackTrace()); 370 }; 371 372 private static volatile ViolationLogger sLogger = LOGCAT_LOGGER; 373 374 private static final ThreadLocal<OnThreadViolationListener> sThreadViolationListener = 375 new ThreadLocal<>(); 376 private static final ThreadLocal<Executor> sThreadViolationExecutor = new ThreadLocal<>(); 377 378 /** 379 * When #{@link ThreadPolicy.Builder#penaltyListener} is enabled, the listener is called on the 380 * provided executor when a Thread violation occurs. 381 */ 382 public interface OnThreadViolationListener { 383 /** Called on a thread policy violation. */ onThreadViolation(Violation v)384 void onThreadViolation(Violation v); 385 } 386 387 /** 388 * When #{@link VmPolicy.Builder#penaltyListener} is enabled, the listener is called on the 389 * provided executor when a VM violation occurs. 390 */ 391 public interface OnVmViolationListener { 392 /** Called on a VM policy violation. */ onVmViolation(Violation v)393 void onVmViolation(Violation v); 394 } 395 396 /** {@hide} */ 397 @TestApi setViolationLogger(ViolationLogger listener)398 public static void setViolationLogger(ViolationLogger listener) { 399 if (listener == null) { 400 listener = LOGCAT_LOGGER; 401 } 402 sLogger = listener; 403 } 404 405 /** 406 * The number of threads trying to do an async dropbox write. Just to limit ourselves out of 407 * paranoia. 408 */ 409 private static final AtomicInteger sDropboxCallsInFlight = new AtomicInteger(0); 410 411 /** 412 * Callback supplied to dalvik / libcore to get informed of usages of java API that are not 413 * a part of the public SDK. 414 */ 415 private static final Consumer<String> sNonSdkApiUsageConsumer = 416 message -> onVmPolicyViolation(new NonSdkApiUsedViolation(message)); 417 StrictMode()418 private StrictMode() {} 419 420 /** 421 * {@link StrictMode} policy applied to a certain thread. 422 * 423 * <p>The policy is enabled by {@link #setThreadPolicy}. The current policy can be retrieved 424 * with {@link #getThreadPolicy}. 425 * 426 * <p>Note that multiple penalties may be provided and they're run in order from least to most 427 * severe (logging before process death, for example). There's currently no mechanism to choose 428 * different penalties for different detected actions. 429 */ 430 public static final class ThreadPolicy { 431 /** The default, lax policy which doesn't catch anything. */ 432 public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null); 433 434 @UnsupportedAppUsage 435 final @ThreadPolicyMask int mask; 436 final OnThreadViolationListener mListener; 437 final Executor mCallbackExecutor; 438 ThreadPolicy(@hreadPolicyMask int mask, OnThreadViolationListener listener, Executor executor)439 private ThreadPolicy(@ThreadPolicyMask int mask, OnThreadViolationListener listener, 440 Executor executor) { 441 this.mask = mask; 442 mListener = listener; 443 mCallbackExecutor = executor; 444 } 445 446 @Override toString()447 public String toString() { 448 return "[StrictMode.ThreadPolicy; mask=" + mask + "]"; 449 } 450 451 /** 452 * Creates {@link ThreadPolicy} instances. Methods whose names start with {@code detect} 453 * specify what problems we should look for. Methods whose names start with {@code penalty} 454 * specify what we should do when we detect a problem. 455 * 456 * <p>You can call as many {@code detect} and {@code penalty} methods as you like. Currently 457 * order is insignificant: all penalties apply to all detected problems. 458 * 459 * <p>For example, detect everything and log anything that's found: 460 * 461 * <pre> 462 * StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder() 463 * .detectAll() 464 * .penaltyLog() 465 * .build(); 466 * StrictMode.setThreadPolicy(policy); 467 * </pre> 468 */ 469 public static final class Builder { 470 private @ThreadPolicyMask int mMask = 0; 471 private OnThreadViolationListener mListener; 472 private Executor mExecutor; 473 474 /** 475 * Create a Builder that detects nothing and has no violations. (but note that {@link 476 * #build} will default to enabling {@link #penaltyLog} if no other penalties are 477 * specified) 478 */ Builder()479 public Builder() { 480 mMask = 0; 481 } 482 483 /** Initialize a Builder from an existing ThreadPolicy. */ Builder(ThreadPolicy policy)484 public Builder(ThreadPolicy policy) { 485 mMask = policy.mask; 486 mListener = policy.mListener; 487 mExecutor = policy.mCallbackExecutor; 488 } 489 490 /** 491 * Detect everything that's potentially suspect. 492 * 493 * <p>As of the Gingerbread release this includes network and disk operations but will 494 * likely expand in future releases. 495 */ detectAll()496 public @NonNull Builder detectAll() { 497 detectDiskReads(); 498 detectDiskWrites(); 499 detectNetwork(); 500 501 final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion(); 502 if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) { 503 detectCustomSlowCalls(); 504 } 505 if (targetSdk >= Build.VERSION_CODES.M) { 506 detectResourceMismatches(); 507 } 508 if (targetSdk >= Build.VERSION_CODES.O) { 509 detectUnbufferedIo(); 510 } 511 return this; 512 } 513 514 /** Disable the detection of everything. */ permitAll()515 public @NonNull Builder permitAll() { 516 return disable(DETECT_THREAD_ALL); 517 } 518 519 /** Enable detection of network operations. */ detectNetwork()520 public @NonNull Builder detectNetwork() { 521 return enable(DETECT_THREAD_NETWORK); 522 } 523 524 /** Disable detection of network operations. */ permitNetwork()525 public @NonNull Builder permitNetwork() { 526 return disable(DETECT_THREAD_NETWORK); 527 } 528 529 /** Enable detection of disk reads. */ detectDiskReads()530 public @NonNull Builder detectDiskReads() { 531 return enable(DETECT_THREAD_DISK_READ); 532 } 533 534 /** Disable detection of disk reads. */ permitDiskReads()535 public @NonNull Builder permitDiskReads() { 536 return disable(DETECT_THREAD_DISK_READ); 537 } 538 539 /** Enable detection of slow calls. */ detectCustomSlowCalls()540 public @NonNull Builder detectCustomSlowCalls() { 541 return enable(DETECT_THREAD_CUSTOM); 542 } 543 544 /** Disable detection of slow calls. */ permitCustomSlowCalls()545 public @NonNull Builder permitCustomSlowCalls() { 546 return disable(DETECT_THREAD_CUSTOM); 547 } 548 549 /** Disable detection of mismatches between defined resource types and getter calls. */ permitResourceMismatches()550 public @NonNull Builder permitResourceMismatches() { 551 return disable(DETECT_THREAD_RESOURCE_MISMATCH); 552 } 553 554 /** Detect unbuffered input/output operations. */ detectUnbufferedIo()555 public @NonNull Builder detectUnbufferedIo() { 556 return enable(DETECT_THREAD_UNBUFFERED_IO); 557 } 558 559 /** Disable detection of unbuffered input/output operations. */ permitUnbufferedIo()560 public @NonNull Builder permitUnbufferedIo() { 561 return disable(DETECT_THREAD_UNBUFFERED_IO); 562 } 563 564 /** 565 * Enables detection of mismatches between defined resource types and getter calls. 566 * 567 * <p>This helps detect accidental type mismatches and potentially expensive type 568 * conversions when obtaining typed resources. 569 * 570 * <p>For example, a strict mode violation would be thrown when calling {@link 571 * android.content.res.TypedArray#getInt(int, int)} on an index that contains a 572 * String-type resource. If the string value can be parsed as an integer, this method 573 * call will return a value without crashing; however, the developer should format the 574 * resource as an integer to avoid unnecessary type conversion. 575 */ detectResourceMismatches()576 public @NonNull Builder detectResourceMismatches() { 577 return enable(DETECT_THREAD_RESOURCE_MISMATCH); 578 } 579 580 /** Enable detection of disk writes. */ detectDiskWrites()581 public @NonNull Builder detectDiskWrites() { 582 return enable(DETECT_THREAD_DISK_WRITE); 583 } 584 585 /** Disable detection of disk writes. */ permitDiskWrites()586 public @NonNull Builder permitDiskWrites() { 587 return disable(DETECT_THREAD_DISK_WRITE); 588 } 589 590 /** 591 * Detect explicit GC requests, i.e. calls to Runtime.gc(). 592 * 593 * @hide 594 */ 595 @TestApi detectExplicitGc()596 public @NonNull Builder detectExplicitGc() { 597 // TODO(b/3400644): Un-hide this for next API update 598 // TODO(b/3400644): Un-hide ExplicitGcViolation for next API update 599 // TODO(b/3400644): Make DETECT_EXPLICIT_GC a @TestApi for next API update 600 // TODO(b/3400644): Call this from detectAll in next API update 601 return enable(DETECT_THREAD_EXPLICIT_GC); 602 } 603 604 /** 605 * Disable detection of explicit GC requests, i.e. calls to Runtime.gc(). 606 * 607 * @hide 608 */ permitExplicitGc()609 public @NonNull Builder permitExplicitGc() { 610 // TODO(b/3400644): Un-hide this for next API update 611 return disable(DETECT_THREAD_EXPLICIT_GC); 612 } 613 614 /** 615 * Show an annoying dialog to the developer on detected violations, rate-limited to be 616 * only a little annoying. 617 */ penaltyDialog()618 public @NonNull Builder penaltyDialog() { 619 return enable(PENALTY_DIALOG); 620 } 621 622 /** 623 * Crash the whole process on violation. This penalty runs at the end of all enabled 624 * penalties so you'll still get see logging or other violations before the process 625 * dies. 626 * 627 * <p>Unlike {@link #penaltyDeathOnNetwork}, this applies to disk reads, disk writes, 628 * and network usage if their corresponding detect flags are set. 629 */ penaltyDeath()630 public @NonNull Builder penaltyDeath() { 631 return enable(PENALTY_DEATH); 632 } 633 634 /** 635 * Crash the whole process on any network usage. Unlike {@link #penaltyDeath}, this 636 * penalty runs <em>before</em> anything else. You must still have called {@link 637 * #detectNetwork} to enable this. 638 * 639 * <p>In the Honeycomb or later SDKs, this is on by default. 640 */ penaltyDeathOnNetwork()641 public @NonNull Builder penaltyDeathOnNetwork() { 642 return enable(PENALTY_DEATH_ON_NETWORK); 643 } 644 645 /** Flash the screen during a violation. */ penaltyFlashScreen()646 public @NonNull Builder penaltyFlashScreen() { 647 return enable(PENALTY_FLASH); 648 } 649 650 /** Log detected violations to the system log. */ penaltyLog()651 public @NonNull Builder penaltyLog() { 652 return enable(PENALTY_LOG); 653 } 654 655 /** 656 * Enable detected violations log a stacktrace and timing data to the {@link 657 * android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform 658 * integrators doing beta user field data collection. 659 */ penaltyDropBox()660 public @NonNull Builder penaltyDropBox() { 661 return enable(PENALTY_DROPBOX); 662 } 663 664 /** 665 * Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified 666 * executor every violation. 667 */ penaltyListener( @onNull Executor executor, @NonNull OnThreadViolationListener listener)668 public @NonNull Builder penaltyListener( 669 @NonNull Executor executor, @NonNull OnThreadViolationListener listener) { 670 if (executor == null) { 671 throw new NullPointerException("executor must not be null"); 672 } 673 mListener = listener; 674 mExecutor = executor; 675 return this; 676 } 677 678 /** @removed */ penaltyListener( @onNull OnThreadViolationListener listener, @NonNull Executor executor)679 public @NonNull Builder penaltyListener( 680 @NonNull OnThreadViolationListener listener, @NonNull Executor executor) { 681 return penaltyListener(executor, listener); 682 } 683 enable(@hreadPolicyMask int mask)684 private Builder enable(@ThreadPolicyMask int mask) { 685 mMask |= mask; 686 return this; 687 } 688 disable(@hreadPolicyMask int mask)689 private Builder disable(@ThreadPolicyMask int mask) { 690 mMask &= ~mask; 691 return this; 692 } 693 694 /** 695 * Construct the ThreadPolicy instance. 696 * 697 * <p>Note: if no penalties are enabled before calling <code>build</code>, {@link 698 * #penaltyLog} is implicitly set. 699 */ build()700 public ThreadPolicy build() { 701 // If there are detection bits set but no violation bits 702 // set, enable simple logging. 703 if (mListener == null 704 && mMask != 0 705 && (mMask 706 & (PENALTY_DEATH 707 | PENALTY_LOG 708 | PENALTY_DROPBOX 709 | PENALTY_DIALOG)) 710 == 0) { 711 penaltyLog(); 712 } 713 return new ThreadPolicy(mMask, mListener, mExecutor); 714 } 715 } 716 } 717 718 /** 719 * {@link StrictMode} policy applied to all threads in the virtual machine's process. 720 * 721 * <p>The policy is enabled by {@link #setVmPolicy}. 722 */ 723 public static final class VmPolicy { 724 /** The default, lax policy which doesn't catch anything. */ 725 public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null); 726 727 @UnsupportedAppUsage 728 final @VmPolicyMask int mask; 729 final OnVmViolationListener mListener; 730 final Executor mCallbackExecutor; 731 732 // Map from class to max number of allowed instances in memory. 733 final HashMap<Class, Integer> classInstanceLimit; 734 VmPolicy( @mPolicyMask int mask, HashMap<Class, Integer> classInstanceLimit, OnVmViolationListener listener, Executor executor)735 private VmPolicy( 736 @VmPolicyMask int mask, 737 HashMap<Class, Integer> classInstanceLimit, 738 OnVmViolationListener listener, 739 Executor executor) { 740 if (classInstanceLimit == null) { 741 throw new NullPointerException("classInstanceLimit == null"); 742 } 743 this.mask = mask; 744 this.classInstanceLimit = classInstanceLimit; 745 mListener = listener; 746 mCallbackExecutor = executor; 747 } 748 749 @Override toString()750 public String toString() { 751 return "[StrictMode.VmPolicy; mask=" + mask + "]"; 752 } 753 754 /** 755 * Creates {@link VmPolicy} instances. Methods whose names start with {@code detect} specify 756 * what problems we should look for. Methods whose names start with {@code penalty} specify 757 * what we should do when we detect a problem. 758 * 759 * <p>You can call as many {@code detect} and {@code penalty} methods as you like. Currently 760 * order is insignificant: all penalties apply to all detected problems. 761 * 762 * <p>For example, detect everything and log anything that's found: 763 * 764 * <pre> 765 * StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder() 766 * .detectAll() 767 * .penaltyLog() 768 * .build(); 769 * StrictMode.setVmPolicy(policy); 770 * </pre> 771 */ 772 public static final class Builder { 773 @UnsupportedAppUsage 774 private @VmPolicyMask int mMask; 775 private OnVmViolationListener mListener; 776 private Executor mExecutor; 777 778 private HashMap<Class, Integer> mClassInstanceLimit; // null until needed 779 private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write 780 Builder()781 public Builder() { 782 mMask = 0; 783 } 784 785 /** Build upon an existing VmPolicy. */ Builder(VmPolicy base)786 public Builder(VmPolicy base) { 787 mMask = base.mask; 788 mClassInstanceLimitNeedCow = true; 789 mClassInstanceLimit = base.classInstanceLimit; 790 mListener = base.mListener; 791 mExecutor = base.mCallbackExecutor; 792 } 793 794 /** 795 * Set an upper bound on how many instances of a class can be in memory at once. Helps 796 * to prevent object leaks. 797 */ setClassInstanceLimit(Class klass, int instanceLimit)798 public @NonNull Builder setClassInstanceLimit(Class klass, int instanceLimit) { 799 if (klass == null) { 800 throw new NullPointerException("klass == null"); 801 } 802 if (mClassInstanceLimitNeedCow) { 803 if (mClassInstanceLimit.containsKey(klass) 804 && mClassInstanceLimit.get(klass) == instanceLimit) { 805 // no-op; don't break COW 806 return this; 807 } 808 mClassInstanceLimitNeedCow = false; 809 mClassInstanceLimit = (HashMap<Class, Integer>) mClassInstanceLimit.clone(); 810 } else if (mClassInstanceLimit == null) { 811 mClassInstanceLimit = new HashMap<Class, Integer>(); 812 } 813 mMask |= DETECT_VM_INSTANCE_LEAKS; 814 mClassInstanceLimit.put(klass, instanceLimit); 815 return this; 816 } 817 818 /** Detect leaks of {@link android.app.Activity} subclasses. */ detectActivityLeaks()819 public @NonNull Builder detectActivityLeaks() { 820 return enable(DETECT_VM_ACTIVITY_LEAKS); 821 } 822 823 /** @hide */ permitActivityLeaks()824 public @NonNull Builder permitActivityLeaks() { 825 synchronized (StrictMode.class) { 826 sExpectedActivityInstanceCount.clear(); 827 } 828 return disable(DETECT_VM_ACTIVITY_LEAKS); 829 } 830 831 /** 832 * Detect reflective usage of APIs that are not part of the public Android SDK. 833 * 834 * <p>Note that any non-SDK APIs that this processes accesses before this detection is 835 * enabled may not be detected. To ensure that all such API accesses are detected, 836 * you should apply this policy as early as possible after process creation. 837 */ detectNonSdkApiUsage()838 public @NonNull Builder detectNonSdkApiUsage() { 839 return enable(DETECT_VM_NON_SDK_API_USAGE); 840 } 841 842 /** 843 * Permit reflective usage of APIs that are not part of the public Android SDK. Note 844 * that this <b>only</b> affects {@code StrictMode}, the underlying runtime may 845 * continue to restrict or warn on access to methods that are not part of the 846 * public SDK. 847 */ permitNonSdkApiUsage()848 public @NonNull Builder permitNonSdkApiUsage() { 849 return disable(DETECT_VM_NON_SDK_API_USAGE); 850 } 851 852 /** 853 * Detect everything that's potentially suspect. 854 * 855 * <p>In the Honeycomb release this includes leaks of SQLite cursors, Activities, and 856 * other closable objects but will likely expand in future releases. 857 */ detectAll()858 public @NonNull Builder detectAll() { 859 detectLeakedSqlLiteObjects(); 860 861 final int targetSdk = VMRuntime.getRuntime().getTargetSdkVersion(); 862 if (targetSdk >= Build.VERSION_CODES.HONEYCOMB) { 863 detectActivityLeaks(); 864 detectLeakedClosableObjects(); 865 } 866 if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN) { 867 detectLeakedRegistrationObjects(); 868 } 869 if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 870 detectFileUriExposure(); 871 } 872 if (targetSdk >= Build.VERSION_CODES.M) { 873 // TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have 874 // facility for apps to mark sockets that should be ignored 875 if (SystemProperties.getBoolean(CLEARTEXT_PROPERTY, false)) { 876 detectCleartextNetwork(); 877 } 878 } 879 if (targetSdk >= Build.VERSION_CODES.O) { 880 detectContentUriWithoutPermission(); 881 detectUntaggedSockets(); 882 } 883 if (targetSdk >= Build.VERSION_CODES.Q) { 884 detectCredentialProtectedWhileLocked(); 885 } 886 if (targetSdk >= Build.VERSION_CODES.R) { 887 detectIncorrectContextUse(); 888 } 889 890 // TODO: Decide whether to detect non SDK API usage beyond a certain API level. 891 // TODO: enable detectImplicitDirectBoot() once system is less noisy 892 893 return this; 894 } 895 896 /** 897 * Detect when an {@link android.database.sqlite.SQLiteCursor} or other SQLite object is 898 * finalized without having been closed. 899 * 900 * <p>You always want to explicitly close your SQLite cursors to avoid unnecessary 901 * database contention and temporary memory leaks. 902 */ detectLeakedSqlLiteObjects()903 public @NonNull Builder detectLeakedSqlLiteObjects() { 904 return enable(DETECT_VM_CURSOR_LEAKS); 905 } 906 907 /** 908 * Detect when an {@link java.io.Closeable} or other object with an explicit termination 909 * method is finalized without having been closed. 910 * 911 * <p>You always want to explicitly close such objects to avoid unnecessary resources 912 * leaks. 913 */ detectLeakedClosableObjects()914 public @NonNull Builder detectLeakedClosableObjects() { 915 return enable(DETECT_VM_CLOSABLE_LEAKS); 916 } 917 918 /** 919 * Detect when a {@link BroadcastReceiver} or {@link ServiceConnection} is leaked during 920 * {@link Context} teardown. 921 */ detectLeakedRegistrationObjects()922 public @NonNull Builder detectLeakedRegistrationObjects() { 923 return enable(DETECT_VM_REGISTRATION_LEAKS); 924 } 925 926 /** 927 * Detect when the calling application exposes a {@code file://} {@link android.net.Uri} 928 * to another app. 929 * 930 * <p>This exposure is discouraged since the receiving app may not have access to the 931 * shared path. For example, the receiving app may not have requested the {@link 932 * android.Manifest.permission#READ_EXTERNAL_STORAGE} runtime permission, or the 933 * platform may be sharing the {@link android.net.Uri} across user profile boundaries. 934 * 935 * <p>Instead, apps should use {@code content://} Uris so the platform can extend 936 * temporary permission for the receiving app to access the resource. 937 * 938 * @see android.support.v4.content.FileProvider 939 * @see Intent#FLAG_GRANT_READ_URI_PERMISSION 940 */ detectFileUriExposure()941 public @NonNull Builder detectFileUriExposure() { 942 return enable(DETECT_VM_FILE_URI_EXPOSURE); 943 } 944 945 /** 946 * Detect any network traffic from the calling app which is not wrapped in SSL/TLS. This 947 * can help you detect places that your app is inadvertently sending cleartext data 948 * across the network. 949 * 950 * <p>Using {@link #penaltyDeath()} or {@link #penaltyDeathOnCleartextNetwork()} will 951 * block further traffic on that socket to prevent accidental data leakage, in addition 952 * to crashing your process. 953 * 954 * <p>Using {@link #penaltyDropBox()} will log the raw contents of the packet that 955 * triggered the violation. 956 * 957 * <p>This inspects both IPv4/IPv6 and TCP/UDP network traffic, but it may be subject to 958 * false positives, such as when STARTTLS protocols or HTTP proxies are used. 959 */ detectCleartextNetwork()960 public @NonNull Builder detectCleartextNetwork() { 961 return enable(DETECT_VM_CLEARTEXT_NETWORK); 962 } 963 964 /** 965 * Detect when the calling application sends a {@code content://} {@link 966 * android.net.Uri} to another app without setting {@link 967 * Intent#FLAG_GRANT_READ_URI_PERMISSION} or {@link 968 * Intent#FLAG_GRANT_WRITE_URI_PERMISSION}. 969 * 970 * <p>Forgetting to include one or more of these flags when sending an intent is 971 * typically an app bug. 972 * 973 * @see Intent#FLAG_GRANT_READ_URI_PERMISSION 974 * @see Intent#FLAG_GRANT_WRITE_URI_PERMISSION 975 */ detectContentUriWithoutPermission()976 public @NonNull Builder detectContentUriWithoutPermission() { 977 return enable(DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION); 978 } 979 980 /** 981 * Detect any sockets in the calling app which have not been tagged using {@link 982 * TrafficStats}. Tagging sockets can help you investigate network usage inside your 983 * app, such as a narrowing down heavy usage to a specific library or component. 984 * 985 * <p>This currently does not detect sockets created in native code. 986 * 987 * @see TrafficStats#setThreadStatsTag(int) 988 * @see TrafficStats#tagSocket(java.net.Socket) 989 * @see TrafficStats#tagDatagramSocket(java.net.DatagramSocket) 990 */ detectUntaggedSockets()991 public @NonNull Builder detectUntaggedSockets() { 992 return enable(DETECT_VM_UNTAGGED_SOCKET); 993 } 994 995 /** @hide */ permitUntaggedSockets()996 public @NonNull Builder permitUntaggedSockets() { 997 return disable(DETECT_VM_UNTAGGED_SOCKET); 998 } 999 1000 /** 1001 * Detect any implicit reliance on Direct Boot automatic filtering 1002 * of {@link PackageManager} values. Violations are only triggered 1003 * when implicit calls are made while the user is locked. 1004 * <p> 1005 * Apps becoming Direct Boot aware need to carefully inspect each 1006 * query site and explicitly decide which combination of flags they 1007 * want to use: 1008 * <ul> 1009 * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AWARE} 1010 * <li>{@link PackageManager#MATCH_DIRECT_BOOT_UNAWARE} 1011 * <li>{@link PackageManager#MATCH_DIRECT_BOOT_AUTO} 1012 * </ul> 1013 */ detectImplicitDirectBoot()1014 public @NonNull Builder detectImplicitDirectBoot() { 1015 return enable(DETECT_VM_IMPLICIT_DIRECT_BOOT); 1016 } 1017 1018 /** @hide */ permitImplicitDirectBoot()1019 public @NonNull Builder permitImplicitDirectBoot() { 1020 return disable(DETECT_VM_IMPLICIT_DIRECT_BOOT); 1021 } 1022 1023 /** 1024 * Detect access to filesystem paths stored in credential protected 1025 * storage areas while the user is locked. 1026 * <p> 1027 * When a user is locked, credential protected storage is 1028 * unavailable, and files stored in these locations appear to not 1029 * exist, which can result in subtle app bugs if they assume default 1030 * behaviors or empty states. Instead, apps should store data needed 1031 * while a user is locked under device protected storage areas. 1032 * 1033 * @see Context#createDeviceProtectedStorageContext() 1034 */ detectCredentialProtectedWhileLocked()1035 public @NonNull Builder detectCredentialProtectedWhileLocked() { 1036 return enable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED); 1037 } 1038 1039 /** @hide */ permitCredentialProtectedWhileLocked()1040 public @NonNull Builder permitCredentialProtectedWhileLocked() { 1041 return disable(DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED); 1042 } 1043 1044 /** 1045 * Detect attempts to invoke a method on a {@link Context} that is not suited for such 1046 * operation. 1047 * <p>An example of this is trying to obtain an instance of visual service (e.g. 1048 * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not 1049 * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and 1050 * therefore can report incorrect metrics or resources. 1051 * @see Context#getDisplay() 1052 * @see Context#getSystemService(String) 1053 * @hide 1054 */ 1055 @TestApi detectIncorrectContextUse()1056 public @NonNull Builder detectIncorrectContextUse() { 1057 return enable(DETECT_VM_INCORRECT_CONTEXT_USE); 1058 } 1059 1060 /** 1061 * Disable detection of incorrect context use. 1062 * TODO(b/149790106): Fix usages and remove. 1063 * @hide 1064 */ 1065 @TestApi permitIncorrectContextUse()1066 public @NonNull Builder permitIncorrectContextUse() { 1067 return disable(DETECT_VM_INCORRECT_CONTEXT_USE); 1068 } 1069 1070 /** 1071 * Crashes the whole process on violation. This penalty runs at the end of all enabled 1072 * penalties so you'll still get your logging or other violations before the process 1073 * dies. 1074 */ penaltyDeath()1075 public @NonNull Builder penaltyDeath() { 1076 return enable(PENALTY_DEATH); 1077 } 1078 1079 /** 1080 * Crashes the whole process when cleartext network traffic is detected. 1081 * 1082 * @see #detectCleartextNetwork() 1083 */ penaltyDeathOnCleartextNetwork()1084 public @NonNull Builder penaltyDeathOnCleartextNetwork() { 1085 return enable(PENALTY_DEATH_ON_CLEARTEXT_NETWORK); 1086 } 1087 1088 /** 1089 * Crashes the whole process when a {@code file://} {@link android.net.Uri} is exposed 1090 * beyond this app. 1091 * 1092 * @see #detectFileUriExposure() 1093 */ penaltyDeathOnFileUriExposure()1094 public @NonNull Builder penaltyDeathOnFileUriExposure() { 1095 return enable(PENALTY_DEATH_ON_FILE_URI_EXPOSURE); 1096 } 1097 1098 /** Log detected violations to the system log. */ penaltyLog()1099 public @NonNull Builder penaltyLog() { 1100 return enable(PENALTY_LOG); 1101 } 1102 1103 /** 1104 * Enable detected violations log a stacktrace and timing data to the {@link 1105 * android.os.DropBoxManager DropBox} on policy violation. Intended mostly for platform 1106 * integrators doing beta user field data collection. 1107 */ penaltyDropBox()1108 public @NonNull Builder penaltyDropBox() { 1109 return enable(PENALTY_DROPBOX); 1110 } 1111 1112 /** 1113 * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation. 1114 */ penaltyListener( @onNull Executor executor, @NonNull OnVmViolationListener listener)1115 public @NonNull Builder penaltyListener( 1116 @NonNull Executor executor, @NonNull OnVmViolationListener listener) { 1117 if (executor == null) { 1118 throw new NullPointerException("executor must not be null"); 1119 } 1120 mListener = listener; 1121 mExecutor = executor; 1122 return this; 1123 } 1124 1125 /** @removed */ penaltyListener( @onNull OnVmViolationListener listener, @NonNull Executor executor)1126 public @NonNull Builder penaltyListener( 1127 @NonNull OnVmViolationListener listener, @NonNull Executor executor) { 1128 return penaltyListener(executor, listener); 1129 } 1130 enable(@mPolicyMask int mask)1131 private Builder enable(@VmPolicyMask int mask) { 1132 mMask |= mask; 1133 return this; 1134 } 1135 disable(@mPolicyMask int mask)1136 Builder disable(@VmPolicyMask int mask) { 1137 mMask &= ~mask; 1138 return this; 1139 } 1140 1141 /** 1142 * Construct the VmPolicy instance. 1143 * 1144 * <p>Note: if no penalties are enabled before calling <code>build</code>, {@link 1145 * #penaltyLog} is implicitly set. 1146 */ build()1147 public VmPolicy build() { 1148 // If there are detection bits set but no violation bits 1149 // set, enable simple logging. 1150 if (mListener == null 1151 && mMask != 0 1152 && (mMask 1153 & (PENALTY_DEATH 1154 | PENALTY_LOG 1155 | PENALTY_DROPBOX 1156 | PENALTY_DIALOG)) 1157 == 0) { 1158 penaltyLog(); 1159 } 1160 return new VmPolicy( 1161 mMask, 1162 mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP, 1163 mListener, 1164 mExecutor); 1165 } 1166 } 1167 } 1168 1169 /** 1170 * Log of strict mode violation stack traces that have occurred during a Binder call, to be 1171 * serialized back later to the caller via Parcel.writeNoException() (amusingly) where the 1172 * caller can choose how to react. 1173 */ 1174 private static final ThreadLocal<ArrayList<ViolationInfo>> gatheredViolations = 1175 new ThreadLocal<ArrayList<ViolationInfo>>() { 1176 @Override 1177 protected ArrayList<ViolationInfo> initialValue() { 1178 // Starts null to avoid unnecessary allocations when 1179 // checking whether there are any violations or not in 1180 // hasGatheredViolations() below. 1181 return null; 1182 } 1183 }; 1184 1185 /** 1186 * Sets the policy for what actions on the current thread should be detected, as well as the 1187 * penalty if such actions occur. 1188 * 1189 * <p>Internally this sets a thread-local variable which is propagated across cross-process IPC 1190 * calls, meaning you can catch violations when a system service or another process accesses the 1191 * disk or network on your behalf. 1192 * 1193 * @param policy the policy to put into place 1194 */ setThreadPolicy(final ThreadPolicy policy)1195 public static void setThreadPolicy(final ThreadPolicy policy) { 1196 setThreadPolicyMask(policy.mask); 1197 sThreadViolationListener.set(policy.mListener); 1198 sThreadViolationExecutor.set(policy.mCallbackExecutor); 1199 } 1200 1201 /** @hide */ setThreadPolicyMask(@hreadPolicyMask int threadPolicyMask)1202 public static void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) { 1203 // In addition to the Java-level thread-local in Dalvik's 1204 // BlockGuard, we also need to keep a native thread-local in 1205 // Binder in order to propagate the value across Binder calls, 1206 // even across native-only processes. The two are kept in 1207 // sync via the callback to onStrictModePolicyChange, below. 1208 setBlockGuardPolicy(threadPolicyMask); 1209 1210 // And set the Android native version... 1211 Binder.setThreadStrictModePolicy(threadPolicyMask); 1212 } 1213 1214 // Sets the policy in Dalvik/libcore (BlockGuard) setBlockGuardPolicy(@hreadPolicyMask int threadPolicyMask)1215 private static void setBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) { 1216 if (threadPolicyMask == 0) { 1217 BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY); 1218 return; 1219 } 1220 final BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 1221 final AndroidBlockGuardPolicy androidPolicy; 1222 if (policy instanceof AndroidBlockGuardPolicy) { 1223 androidPolicy = (AndroidBlockGuardPolicy) policy; 1224 } else { 1225 androidPolicy = THREAD_ANDROID_POLICY.get(); 1226 BlockGuard.setThreadPolicy(androidPolicy); 1227 } 1228 androidPolicy.setThreadPolicyMask(threadPolicyMask); 1229 } 1230 setBlockGuardVmPolicy(@mPolicyMask int vmPolicyMask)1231 private static void setBlockGuardVmPolicy(@VmPolicyMask int vmPolicyMask) { 1232 // We only need to install BlockGuard for a small subset of VM policies 1233 vmPolicyMask &= DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED; 1234 if (vmPolicyMask != 0) { 1235 BlockGuard.setVmPolicy(VM_ANDROID_POLICY); 1236 } else { 1237 BlockGuard.setVmPolicy(BlockGuard.LAX_VM_POLICY); 1238 } 1239 } 1240 1241 // Sets up CloseGuard in Dalvik/libcore setCloseGuardEnabled(boolean enabled)1242 private static void setCloseGuardEnabled(boolean enabled) { 1243 if (!(CloseGuard.getReporter() instanceof AndroidCloseGuardReporter)) { 1244 CloseGuard.setReporter(new AndroidCloseGuardReporter()); 1245 } 1246 CloseGuard.setEnabled(enabled); 1247 } 1248 1249 /** 1250 * Returns the bitmask of the current thread's policy. 1251 * 1252 * @return the bitmask of all the DETECT_* and PENALTY_* bits currently enabled 1253 * @hide 1254 */ 1255 @UnsupportedAppUsage getThreadPolicyMask()1256 public static @ThreadPolicyMask int getThreadPolicyMask() { 1257 final BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 1258 if (policy instanceof AndroidBlockGuardPolicy) { 1259 return ((AndroidBlockGuardPolicy) policy).getThreadPolicyMask(); 1260 } else { 1261 return 0; 1262 } 1263 } 1264 1265 /** Returns the current thread's policy. */ getThreadPolicy()1266 public static ThreadPolicy getThreadPolicy() { 1267 // TODO: this was a last minute Gingerbread API change (to 1268 // introduce VmPolicy cleanly) but this isn't particularly 1269 // optimal for users who might call this method often. This 1270 // should be in a thread-local and not allocate on each call. 1271 return new ThreadPolicy( 1272 getThreadPolicyMask(), 1273 sThreadViolationListener.get(), 1274 sThreadViolationExecutor.get()); 1275 } 1276 1277 /** 1278 * A convenience wrapper that takes the current {@link ThreadPolicy} from {@link 1279 * #getThreadPolicy}, modifies it to permit both disk reads & writes, and sets the new 1280 * policy with {@link #setThreadPolicy}, returning the old policy so you can restore it at the 1281 * end of a block. 1282 * 1283 * @return the old policy, to be passed to {@link #setThreadPolicy} to restore the policy at the 1284 * end of a block 1285 */ allowThreadDiskWrites()1286 public static ThreadPolicy allowThreadDiskWrites() { 1287 return new ThreadPolicy( 1288 allowThreadDiskWritesMask(), 1289 sThreadViolationListener.get(), 1290 sThreadViolationExecutor.get()); 1291 } 1292 1293 /** @hide */ allowThreadDiskWritesMask()1294 public static @ThreadPolicyMask int allowThreadDiskWritesMask() { 1295 int oldPolicyMask = getThreadPolicyMask(); 1296 int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_WRITE | DETECT_THREAD_DISK_READ); 1297 if (newPolicyMask != oldPolicyMask) { 1298 setThreadPolicyMask(newPolicyMask); 1299 } 1300 return oldPolicyMask; 1301 } 1302 1303 /** 1304 * A convenience wrapper that takes the current {@link ThreadPolicy} from {@link 1305 * #getThreadPolicy}, modifies it to permit disk reads, and sets the new policy with {@link 1306 * #setThreadPolicy}, returning the old policy so you can restore it at the end of a block. 1307 * 1308 * @return the old policy, to be passed to setThreadPolicy to restore the policy. 1309 */ allowThreadDiskReads()1310 public static ThreadPolicy allowThreadDiskReads() { 1311 return new ThreadPolicy( 1312 allowThreadDiskReadsMask(), 1313 sThreadViolationListener.get(), 1314 sThreadViolationExecutor.get()); 1315 } 1316 1317 /** @hide */ allowThreadDiskReadsMask()1318 public static @ThreadPolicyMask int allowThreadDiskReadsMask() { 1319 int oldPolicyMask = getThreadPolicyMask(); 1320 int newPolicyMask = oldPolicyMask & ~(DETECT_THREAD_DISK_READ); 1321 if (newPolicyMask != oldPolicyMask) { 1322 setThreadPolicyMask(newPolicyMask); 1323 } 1324 return oldPolicyMask; 1325 } 1326 1327 /** @hide */ allowThreadViolations()1328 public static ThreadPolicy allowThreadViolations() { 1329 ThreadPolicy oldPolicy = getThreadPolicy(); 1330 setThreadPolicyMask(0); 1331 return oldPolicy; 1332 } 1333 1334 /** @hide */ allowVmViolations()1335 public static VmPolicy allowVmViolations() { 1336 VmPolicy oldPolicy = getVmPolicy(); 1337 sVmPolicy = VmPolicy.LAX; 1338 return oldPolicy; 1339 } 1340 1341 /** 1342 * Determine if the given app is "bundled" as part of the system image. These bundled apps are 1343 * developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to 1344 * chase any {@link StrictMode} regressions by enabling detection when running on {@link 1345 * Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds. 1346 * 1347 * <p>Unbundled apps included in the system image are expected to detect and triage their own 1348 * {@link StrictMode} issues separate from the OS release process, which is why we don't enable 1349 * them here. 1350 * 1351 * @hide 1352 */ isBundledSystemApp(ApplicationInfo ai)1353 public static boolean isBundledSystemApp(ApplicationInfo ai) { 1354 if (ai == null || ai.packageName == null) { 1355 // Probably system server 1356 return true; 1357 } else if (ai.isSystemApp()) { 1358 // Ignore unbundled apps living in the wrong namespace 1359 if (ai.packageName.equals("com.android.vending") 1360 || ai.packageName.equals("com.android.chrome")) { 1361 return false; 1362 } 1363 1364 // Ignore bundled apps that are way too spammy 1365 // STOPSHIP: burn this list down to zero 1366 if (ai.packageName.equals("com.android.phone")) { 1367 return false; 1368 } 1369 1370 if (ai.packageName.equals("android") 1371 || ai.packageName.startsWith("android.") 1372 || ai.packageName.startsWith("com.android.")) { 1373 return true; 1374 } 1375 } 1376 return false; 1377 } 1378 1379 /** 1380 * Initialize default {@link ThreadPolicy} for the current thread. 1381 * 1382 * @hide 1383 */ initThreadDefaults(ApplicationInfo ai)1384 public static void initThreadDefaults(ApplicationInfo ai) { 1385 final ThreadPolicy.Builder builder = new ThreadPolicy.Builder(); 1386 final int targetSdkVersion = 1387 (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT; 1388 1389 // Starting in HC, we don't allow network usage on the main thread 1390 if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { 1391 builder.detectNetwork(); 1392 builder.penaltyDeathOnNetwork(); 1393 } 1394 1395 if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { 1396 // Detect nothing extra 1397 } else if (Build.IS_USERDEBUG) { 1398 // Detect everything in bundled apps 1399 if (isBundledSystemApp(ai)) { 1400 builder.detectAll(); 1401 builder.penaltyDropBox(); 1402 if (SystemProperties.getBoolean(VISUAL_PROPERTY, false)) { 1403 builder.penaltyFlashScreen(); 1404 } 1405 } 1406 } else if (Build.IS_ENG) { 1407 // Detect everything in bundled apps 1408 if (isBundledSystemApp(ai)) { 1409 builder.detectAll(); 1410 builder.penaltyDropBox(); 1411 builder.penaltyLog(); 1412 builder.penaltyFlashScreen(); 1413 } 1414 } 1415 1416 setThreadPolicy(builder.build()); 1417 } 1418 1419 /** 1420 * Initialize default {@link VmPolicy} for the current VM. 1421 * 1422 * @hide 1423 */ initVmDefaults(ApplicationInfo ai)1424 public static void initVmDefaults(ApplicationInfo ai) { 1425 final VmPolicy.Builder builder = new VmPolicy.Builder(); 1426 final int targetSdkVersion = 1427 (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT; 1428 1429 // Starting in N, we don't allow file:// Uri exposure 1430 if (targetSdkVersion >= Build.VERSION_CODES.N) { 1431 builder.detectFileUriExposure(); 1432 builder.penaltyDeathOnFileUriExposure(); 1433 } 1434 1435 if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { 1436 // Detect nothing extra 1437 } else if (Build.IS_USERDEBUG) { 1438 // Detect everything in bundled apps (except activity leaks, which 1439 // are expensive to track) 1440 if (isBundledSystemApp(ai)) { 1441 builder.detectAll(); 1442 builder.permitActivityLeaks(); 1443 builder.penaltyDropBox(); 1444 } 1445 } else if (Build.IS_ENG) { 1446 // Detect everything in bundled apps 1447 if (isBundledSystemApp(ai)) { 1448 builder.detectAll(); 1449 builder.penaltyDropBox(); 1450 builder.penaltyLog(); 1451 } 1452 } 1453 1454 setVmPolicy(builder.build()); 1455 } 1456 1457 /** 1458 * Used by the framework to make file usage a fatal error. 1459 * 1460 * @hide 1461 */ 1462 @UnsupportedAppUsage enableDeathOnFileUriExposure()1463 public static void enableDeathOnFileUriExposure() { 1464 sVmPolicy = 1465 new VmPolicy( 1466 sVmPolicy.mask 1467 | DETECT_VM_FILE_URI_EXPOSURE 1468 | PENALTY_DEATH_ON_FILE_URI_EXPOSURE, 1469 sVmPolicy.classInstanceLimit, 1470 sVmPolicy.mListener, 1471 sVmPolicy.mCallbackExecutor); 1472 } 1473 1474 /** 1475 * Used by lame internal apps that haven't done the hard work to get themselves off file:// Uris 1476 * yet. 1477 * 1478 * @hide 1479 */ 1480 @UnsupportedAppUsage disableDeathOnFileUriExposure()1481 public static void disableDeathOnFileUriExposure() { 1482 sVmPolicy = 1483 new VmPolicy( 1484 sVmPolicy.mask 1485 & ~(DETECT_VM_FILE_URI_EXPOSURE 1486 | PENALTY_DEATH_ON_FILE_URI_EXPOSURE), 1487 sVmPolicy.classInstanceLimit, 1488 sVmPolicy.mListener, 1489 sVmPolicy.mCallbackExecutor); 1490 } 1491 1492 @UnsupportedAppUsage 1493 private static final ThreadLocal<ArrayList<ViolationInfo>> violationsBeingTimed = 1494 new ThreadLocal<ArrayList<ViolationInfo>>() { 1495 @Override 1496 protected ArrayList<ViolationInfo> initialValue() { 1497 return new ArrayList<ViolationInfo>(); 1498 } 1499 }; 1500 1501 // Note: only access this once verifying the thread has a Looper. 1502 private static final ThreadLocal<Handler> THREAD_HANDLER = 1503 new ThreadLocal<Handler>() { 1504 @Override 1505 protected Handler initialValue() { 1506 return new Handler(); 1507 } 1508 }; 1509 1510 private static final ThreadLocal<AndroidBlockGuardPolicy> THREAD_ANDROID_POLICY = 1511 new ThreadLocal<AndroidBlockGuardPolicy>() { 1512 @Override 1513 protected AndroidBlockGuardPolicy initialValue() { 1514 return new AndroidBlockGuardPolicy(0); 1515 } 1516 }; 1517 tooManyViolationsThisLoop()1518 private static boolean tooManyViolationsThisLoop() { 1519 return violationsBeingTimed.get().size() >= MAX_OFFENSES_PER_LOOP; 1520 } 1521 1522 private static class AndroidBlockGuardPolicy implements BlockGuard.Policy { 1523 private @ThreadPolicyMask int mThreadPolicyMask; 1524 1525 // Map from violation stacktrace hashcode -> uptimeMillis of 1526 // last violation. No locking needed, as this is only 1527 // accessed by the same thread. 1528 private ArrayMap<Integer, Long> mLastViolationTime; 1529 AndroidBlockGuardPolicy(@hreadPolicyMask int threadPolicyMask)1530 public AndroidBlockGuardPolicy(@ThreadPolicyMask int threadPolicyMask) { 1531 mThreadPolicyMask = threadPolicyMask; 1532 } 1533 1534 @Override toString()1535 public String toString() { 1536 return "AndroidBlockGuardPolicy; mPolicyMask=" + mThreadPolicyMask; 1537 } 1538 1539 // Part of BlockGuard.Policy interface: getPolicyMask()1540 public int getPolicyMask() { 1541 return mThreadPolicyMask; 1542 } 1543 1544 // Part of BlockGuard.Policy interface: onWriteToDisk()1545 public void onWriteToDisk() { 1546 if ((mThreadPolicyMask & DETECT_THREAD_DISK_WRITE) == 0) { 1547 return; 1548 } 1549 if (tooManyViolationsThisLoop()) { 1550 return; 1551 } 1552 startHandlingViolationException(new DiskWriteViolation()); 1553 } 1554 1555 // Not part of BlockGuard.Policy; just part of StrictMode: onCustomSlowCall(String name)1556 void onCustomSlowCall(String name) { 1557 if ((mThreadPolicyMask & DETECT_THREAD_CUSTOM) == 0) { 1558 return; 1559 } 1560 if (tooManyViolationsThisLoop()) { 1561 return; 1562 } 1563 startHandlingViolationException(new CustomViolation(name)); 1564 } 1565 1566 // Not part of BlockGuard.Policy; just part of StrictMode: onResourceMismatch(Object tag)1567 void onResourceMismatch(Object tag) { 1568 if ((mThreadPolicyMask & DETECT_THREAD_RESOURCE_MISMATCH) == 0) { 1569 return; 1570 } 1571 if (tooManyViolationsThisLoop()) { 1572 return; 1573 } 1574 startHandlingViolationException(new ResourceMismatchViolation(tag)); 1575 } 1576 1577 // Not part of BlockGuard.Policy; just part of StrictMode: onUnbufferedIO()1578 public void onUnbufferedIO() { 1579 if ((mThreadPolicyMask & DETECT_THREAD_UNBUFFERED_IO) == 0) { 1580 return; 1581 } 1582 if (tooManyViolationsThisLoop()) { 1583 return; 1584 } 1585 startHandlingViolationException(new UnbufferedIoViolation()); 1586 } 1587 1588 // Part of BlockGuard.Policy interface: onReadFromDisk()1589 public void onReadFromDisk() { 1590 if ((mThreadPolicyMask & DETECT_THREAD_DISK_READ) == 0) { 1591 return; 1592 } 1593 if (tooManyViolationsThisLoop()) { 1594 return; 1595 } 1596 startHandlingViolationException(new DiskReadViolation()); 1597 } 1598 1599 // Part of BlockGuard.Policy interface: onNetwork()1600 public void onNetwork() { 1601 if ((mThreadPolicyMask & DETECT_THREAD_NETWORK) == 0) { 1602 return; 1603 } 1604 if ((mThreadPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) { 1605 throw new NetworkOnMainThreadException(); 1606 } 1607 if (tooManyViolationsThisLoop()) { 1608 return; 1609 } 1610 startHandlingViolationException(new NetworkViolation()); 1611 } 1612 1613 // Part of BlockGuard.Policy interface: onExplicitGc()1614 public void onExplicitGc() { 1615 if ((mThreadPolicyMask & DETECT_THREAD_EXPLICIT_GC) == 0) { 1616 return; 1617 } 1618 if (tooManyViolationsThisLoop()) { 1619 return; 1620 } 1621 startHandlingViolationException(new ExplicitGcViolation()); 1622 } 1623 getThreadPolicyMask()1624 public @ThreadPolicyMask int getThreadPolicyMask() { 1625 return mThreadPolicyMask; 1626 } 1627 setThreadPolicyMask(@hreadPolicyMask int threadPolicyMask)1628 public void setThreadPolicyMask(@ThreadPolicyMask int threadPolicyMask) { 1629 mThreadPolicyMask = threadPolicyMask; 1630 } 1631 1632 // Start handling a violation that just started and hasn't 1633 // actually run yet (e.g. no disk write or network operation 1634 // has yet occurred). This sees if we're in an event loop 1635 // thread and, if so, uses it to roughly measure how long the 1636 // violation took. startHandlingViolationException(Violation e)1637 void startHandlingViolationException(Violation e) { 1638 final int penaltyMask = (mThreadPolicyMask & PENALTY_ALL); 1639 final ViolationInfo info = new ViolationInfo(e, penaltyMask); 1640 info.violationUptimeMillis = SystemClock.uptimeMillis(); 1641 handleViolationWithTimingAttempt(info); 1642 } 1643 1644 // Attempts to fill in the provided ViolationInfo's 1645 // durationMillis field if this thread has a Looper we can use 1646 // to measure with. We measure from the time of violation 1647 // until the time the looper is idle again (right before 1648 // the next epoll_wait) handleViolationWithTimingAttempt(final ViolationInfo info)1649 void handleViolationWithTimingAttempt(final ViolationInfo info) { 1650 Looper looper = Looper.myLooper(); 1651 1652 // Without a Looper, we're unable to time how long the 1653 // violation takes place. This case should be rare, as 1654 // most users will care about timing violations that 1655 // happen on their main UI thread. Note that this case is 1656 // also hit when a violation takes place in a Binder 1657 // thread, in "gather" mode. In this case, the duration 1658 // of the violation is computed by the ultimate caller and 1659 // its Looper, if any. 1660 // 1661 // Also, as a special short-cut case when the only penalty 1662 // bit is death, we die immediately, rather than timing 1663 // the violation's duration. This makes it convenient to 1664 // use in unit tests too, rather than waiting on a Looper. 1665 // 1666 // TODO: if in gather mode, ignore Looper.myLooper() and always 1667 // go into this immediate mode? 1668 if (looper == null || (info.mPenaltyMask == PENALTY_DEATH)) { 1669 info.durationMillis = -1; // unknown (redundant, already set) 1670 onThreadPolicyViolation(info); 1671 return; 1672 } 1673 1674 final ArrayList<ViolationInfo> records = violationsBeingTimed.get(); 1675 if (records.size() >= MAX_OFFENSES_PER_LOOP) { 1676 // Not worth measuring. Too many offenses in one loop. 1677 return; 1678 } 1679 records.add(info); 1680 if (records.size() > 1) { 1681 // There's already been a violation this loop, so we've already 1682 // registered an idle handler to process the list of violations 1683 // at the end of this Looper's loop. 1684 return; 1685 } 1686 1687 final IWindowManager windowManager = 1688 info.penaltyEnabled(PENALTY_FLASH) ? sWindowManager.get() : null; 1689 if (windowManager != null) { 1690 try { 1691 windowManager.showStrictModeViolation(true); 1692 } catch (RemoteException unused) { 1693 } 1694 } 1695 1696 // We post a runnable to a Handler (== delay 0 ms) for 1697 // measuring the end time of a violation instead of using 1698 // an IdleHandler (as was previously used) because an 1699 // IdleHandler may not run for quite a long period of time 1700 // if an ongoing animation is happening and continually 1701 // posting ASAP (0 ms) animation steps. Animations are 1702 // throttled back to 60fps via SurfaceFlinger/View 1703 // invalidates, _not_ by posting frame updates every 16 1704 // milliseconds. 1705 THREAD_HANDLER 1706 .get() 1707 .postAtFrontOfQueue( 1708 () -> { 1709 long loopFinishTime = SystemClock.uptimeMillis(); 1710 1711 // Note: we do this early, before handling the 1712 // violation below, as handling the violation 1713 // may include PENALTY_DEATH and we don't want 1714 // to keep the red border on. 1715 if (windowManager != null) { 1716 try { 1717 windowManager.showStrictModeViolation(false); 1718 } catch (RemoteException unused) { 1719 } 1720 } 1721 1722 for (int n = 0; n < records.size(); ++n) { 1723 ViolationInfo v = records.get(n); 1724 v.violationNumThisLoop = n + 1; 1725 v.durationMillis = 1726 (int) (loopFinishTime - v.violationUptimeMillis); 1727 onThreadPolicyViolation(v); 1728 } 1729 records.clear(); 1730 }); 1731 } 1732 1733 // Note: It's possible (even quite likely) that the 1734 // thread-local policy mask has changed from the time the 1735 // violation fired and now (after the violating code ran) due 1736 // to people who push/pop temporary policy in regions of code, 1737 // hence the policy being passed around. onThreadPolicyViolation(final ViolationInfo info)1738 void onThreadPolicyViolation(final ViolationInfo info) { 1739 if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; penalty=" + info.mPenaltyMask); 1740 1741 if (info.penaltyEnabled(PENALTY_GATHER)) { 1742 ArrayList<ViolationInfo> violations = gatheredViolations.get(); 1743 if (violations == null) { 1744 violations = new ArrayList<>(1); 1745 gatheredViolations.set(violations); 1746 } 1747 for (ViolationInfo previous : violations) { 1748 if (info.getStackTrace().equals(previous.getStackTrace())) { 1749 // Duplicate. Don't log. 1750 return; 1751 } 1752 } 1753 violations.add(info); 1754 return; 1755 } 1756 1757 // Not perfect, but fast and good enough for dup suppression. 1758 Integer crashFingerprint = info.hashCode(); 1759 long lastViolationTime = 0; 1760 long now = SystemClock.uptimeMillis(); 1761 if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger 1762 if (mLastViolationTime != null) { 1763 Long vtime = mLastViolationTime.get(crashFingerprint); 1764 if (vtime != null) { 1765 lastViolationTime = vtime; 1766 } 1767 clampViolationTimeMap(mLastViolationTime, Math.max(MIN_LOG_INTERVAL_MS, 1768 Math.max(MIN_DIALOG_INTERVAL_MS, MIN_DROPBOX_INTERVAL_MS))); 1769 } else { 1770 mLastViolationTime = new ArrayMap<>(1); 1771 } 1772 mLastViolationTime.put(crashFingerprint, now); 1773 } 1774 long timeSinceLastViolationMillis = 1775 lastViolationTime == 0 ? Long.MAX_VALUE : (now - lastViolationTime); 1776 1777 if (info.penaltyEnabled(PENALTY_LOG) 1778 && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { 1779 sLogger.log(info); 1780 } 1781 1782 final Violation violation = info.mViolation; 1783 1784 // Penalties that ActivityManager should execute on our behalf. 1785 int penaltyMask = 0; 1786 1787 if (info.penaltyEnabled(PENALTY_DIALOG) 1788 && timeSinceLastViolationMillis > MIN_DIALOG_INTERVAL_MS) { 1789 penaltyMask |= PENALTY_DIALOG; 1790 } 1791 1792 if (info.penaltyEnabled(PENALTY_DROPBOX) 1793 && timeSinceLastViolationMillis > MIN_DROPBOX_INTERVAL_MS) { 1794 penaltyMask |= PENALTY_DROPBOX; 1795 } 1796 1797 if (penaltyMask != 0) { 1798 final boolean justDropBox = (info.mPenaltyMask == PENALTY_DROPBOX); 1799 if (justDropBox) { 1800 // If all we're going to ask the activity manager 1801 // to do is dropbox it (the common case during 1802 // platform development), we can avoid doing this 1803 // call synchronously which Binder data suggests 1804 // isn't always super fast, despite the implementation 1805 // in the ActivityManager trying to be mostly async. 1806 dropboxViolationAsync(penaltyMask, info); 1807 } else { 1808 handleApplicationStrictModeViolation(penaltyMask, info); 1809 } 1810 } 1811 1812 if (info.penaltyEnabled(PENALTY_DEATH)) { 1813 throw new RuntimeException("StrictMode ThreadPolicy violation", violation); 1814 } 1815 1816 // penaltyDeath will cause penaltyCallback to no-op since we cannot guarantee the 1817 // executor finishes before crashing. 1818 final OnThreadViolationListener listener = sThreadViolationListener.get(); 1819 final Executor executor = sThreadViolationExecutor.get(); 1820 if (listener != null && executor != null) { 1821 try { 1822 executor.execute( 1823 () -> { 1824 // Lift violated policy to prevent infinite recursion. 1825 ThreadPolicy oldPolicy = StrictMode.allowThreadViolations(); 1826 try { 1827 listener.onThreadViolation(violation); 1828 } finally { 1829 StrictMode.setThreadPolicy(oldPolicy); 1830 } 1831 }); 1832 } catch (RejectedExecutionException e) { 1833 Log.e(TAG, "ThreadPolicy penaltyCallback failed", e); 1834 } 1835 } 1836 } 1837 } 1838 1839 private static final BlockGuard.VmPolicy VM_ANDROID_POLICY = new BlockGuard.VmPolicy() { 1840 @Override 1841 public void onPathAccess(String path) { 1842 if (path == null) return; 1843 1844 // NOTE: keep credential-protected paths in sync with Environment.java 1845 if (path.startsWith("/data/user/") 1846 || path.startsWith("/data/media/") 1847 || path.startsWith("/data/system_ce/") 1848 || path.startsWith("/data/misc_ce/") 1849 || path.startsWith("/data/vendor_ce/") 1850 || path.startsWith("/storage/emulated/")) { 1851 final int second = path.indexOf('/', 1); 1852 final int third = path.indexOf('/', second + 1); 1853 final int fourth = path.indexOf('/', third + 1); 1854 if (fourth == -1) return; 1855 1856 try { 1857 final int userId = Integer.parseInt(path.substring(third + 1, fourth)); 1858 onCredentialProtectedPathAccess(path, userId); 1859 } catch (NumberFormatException ignored) { 1860 } 1861 } else if (path.startsWith("/data/data/")) { 1862 onCredentialProtectedPathAccess(path, UserHandle.USER_SYSTEM); 1863 } 1864 } 1865 }; 1866 1867 /** 1868 * In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any 1869 * violations but not showing a dialog, not loggging, and not killing the process. In these 1870 * cases we don't need to do a synchronous call to the ActivityManager. This is used by both 1871 * per-thread and vm-wide violations when applicable. 1872 */ dropboxViolationAsync( final int penaltyMask, final ViolationInfo info)1873 private static void dropboxViolationAsync( 1874 final int penaltyMask, final ViolationInfo info) { 1875 int outstanding = sDropboxCallsInFlight.incrementAndGet(); 1876 if (outstanding > 20) { 1877 // What's going on? Let's not make make the situation 1878 // worse and just not log. 1879 sDropboxCallsInFlight.decrementAndGet(); 1880 return; 1881 } 1882 1883 if (LOG_V) Log.d(TAG, "Dropboxing async; in-flight=" + outstanding); 1884 1885 BackgroundThread.getHandler().post(() -> { 1886 handleApplicationStrictModeViolation(penaltyMask, info); 1887 int outstandingInner = sDropboxCallsInFlight.decrementAndGet(); 1888 if (LOG_V) Log.d(TAG, "Dropbox complete; in-flight=" + outstandingInner); 1889 }); 1890 } 1891 handleApplicationStrictModeViolation(int penaltyMask, ViolationInfo info)1892 private static void handleApplicationStrictModeViolation(int penaltyMask, 1893 ViolationInfo info) { 1894 final int oldMask = getThreadPolicyMask(); 1895 try { 1896 // First, remove any policy before we call into the Activity Manager, 1897 // otherwise we'll infinite recurse as we try to log policy violations 1898 // to disk, thus violating policy, thus requiring logging, etc... 1899 // We restore the current policy below, in the finally block. 1900 setThreadPolicyMask(0); 1901 1902 IActivityManager am = ActivityManager.getService(); 1903 if (am == null) { 1904 Log.w(TAG, "No activity manager; failed to Dropbox violation."); 1905 } else { 1906 am.handleApplicationStrictModeViolation( 1907 RuntimeInit.getApplicationObject(), penaltyMask, info); 1908 } 1909 } catch (RemoteException e) { 1910 if (e instanceof DeadObjectException) { 1911 // System process is dead; ignore 1912 } else { 1913 Log.e(TAG, "RemoteException handling StrictMode violation", e); 1914 } 1915 } finally { 1916 setThreadPolicyMask(oldMask); 1917 } 1918 } 1919 1920 private static class AndroidCloseGuardReporter implements CloseGuard.Reporter { 1921 1922 @Override report(String message, Throwable allocationSite)1923 public void report(String message, Throwable allocationSite) { 1924 onVmPolicyViolation(new LeakedClosableViolation(message, allocationSite)); 1925 } 1926 1927 @Override report(String message)1928 public void report(String message) { 1929 onVmPolicyViolation(new LeakedClosableViolation(message)); 1930 } 1931 } 1932 1933 /** Called from Parcel.writeNoException() */ hasGatheredViolations()1934 /* package */ static boolean hasGatheredViolations() { 1935 return gatheredViolations.get() != null; 1936 } 1937 1938 /** 1939 * Called from Parcel.writeException(), so we drop this memory and don't incorrectly attribute 1940 * it to the wrong caller on the next Binder call on this thread. 1941 */ clearGatheredViolations()1942 /* package */ static void clearGatheredViolations() { 1943 gatheredViolations.set(null); 1944 } 1945 1946 /** @hide */ 1947 @UnsupportedAppUsage 1948 @TestApi conditionallyCheckInstanceCounts()1949 public static void conditionallyCheckInstanceCounts() { 1950 VmPolicy policy = getVmPolicy(); 1951 int policySize = policy.classInstanceLimit.size(); 1952 if (policySize == 0) { 1953 return; 1954 } 1955 1956 System.gc(); 1957 System.runFinalization(); 1958 System.gc(); 1959 1960 // Note: classInstanceLimit is immutable, so this is lock-free 1961 // Create the classes array. 1962 Class[] classes = policy.classInstanceLimit.keySet().toArray(new Class[policySize]); 1963 long[] instanceCounts = VMDebug.countInstancesOfClasses(classes, false); 1964 for (int i = 0; i < classes.length; ++i) { 1965 Class klass = classes[i]; 1966 int limit = policy.classInstanceLimit.get(klass); 1967 long instances = instanceCounts[i]; 1968 if (instances > limit) { 1969 onVmPolicyViolation(new InstanceCountViolation(klass, instances, limit)); 1970 } 1971 } 1972 } 1973 1974 private static long sLastInstanceCountCheckMillis = 0; 1975 private static boolean sIsIdlerRegistered = false; // guarded by StrictMode.class 1976 private static final MessageQueue.IdleHandler sProcessIdleHandler = 1977 new MessageQueue.IdleHandler() { 1978 public boolean queueIdle() { 1979 long now = SystemClock.uptimeMillis(); 1980 if (now - sLastInstanceCountCheckMillis > 30 * 1000) { 1981 sLastInstanceCountCheckMillis = now; 1982 conditionallyCheckInstanceCounts(); 1983 } 1984 return true; 1985 } 1986 }; 1987 1988 /** 1989 * Sets the policy for what actions in the VM process (on any thread) should be detected, as 1990 * well as the penalty if such actions occur. 1991 * 1992 * @param policy the policy to put into place 1993 */ setVmPolicy(final VmPolicy policy)1994 public static void setVmPolicy(final VmPolicy policy) { 1995 synchronized (StrictMode.class) { 1996 sVmPolicy = policy; 1997 setCloseGuardEnabled(vmClosableObjectLeaksEnabled()); 1998 1999 Looper looper = Looper.getMainLooper(); 2000 if (looper != null) { 2001 MessageQueue mq = looper.mQueue; 2002 if (policy.classInstanceLimit.size() == 0 2003 || (sVmPolicy.mask & PENALTY_ALL) == 0) { 2004 mq.removeIdleHandler(sProcessIdleHandler); 2005 sIsIdlerRegistered = false; 2006 } else if (!sIsIdlerRegistered) { 2007 mq.addIdleHandler(sProcessIdleHandler); 2008 sIsIdlerRegistered = true; 2009 } 2010 } 2011 2012 int networkPolicy = NETWORK_POLICY_ACCEPT; 2013 if ((sVmPolicy.mask & DETECT_VM_CLEARTEXT_NETWORK) != 0) { 2014 if ((sVmPolicy.mask & PENALTY_DEATH) != 0 2015 || (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0) { 2016 networkPolicy = NETWORK_POLICY_REJECT; 2017 } else { 2018 networkPolicy = NETWORK_POLICY_LOG; 2019 } 2020 } 2021 2022 final INetworkManagementService netd = 2023 INetworkManagementService.Stub.asInterface( 2024 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); 2025 if (netd != null) { 2026 try { 2027 netd.setUidCleartextNetworkPolicy(android.os.Process.myUid(), networkPolicy); 2028 } catch (RemoteException ignored) { 2029 } 2030 } else if (networkPolicy != NETWORK_POLICY_ACCEPT) { 2031 Log.w(TAG, "Dropping requested network policy due to missing service!"); 2032 } 2033 2034 2035 if ((sVmPolicy.mask & DETECT_VM_NON_SDK_API_USAGE) != 0) { 2036 VMRuntime.setNonSdkApiUsageConsumer(sNonSdkApiUsageConsumer); 2037 VMRuntime.setDedupeHiddenApiWarnings(false); 2038 } else { 2039 VMRuntime.setNonSdkApiUsageConsumer(null); 2040 VMRuntime.setDedupeHiddenApiWarnings(true); 2041 } 2042 2043 setBlockGuardVmPolicy(sVmPolicy.mask); 2044 } 2045 } 2046 2047 /** Gets the current VM policy. */ getVmPolicy()2048 public static VmPolicy getVmPolicy() { 2049 synchronized (StrictMode.class) { 2050 return sVmPolicy; 2051 } 2052 } 2053 2054 /** 2055 * Enable the recommended StrictMode defaults, with violations just being logged. 2056 * 2057 * <p>This catches disk and network access on the main thread, as well as leaked SQLite cursors 2058 * and unclosed resources. This is simply a wrapper around {@link #setVmPolicy} and {@link 2059 * #setThreadPolicy}. 2060 */ enableDefaults()2061 public static void enableDefaults() { 2062 setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); 2063 setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); 2064 } 2065 2066 /** @hide */ vmSqliteObjectLeaksEnabled()2067 public static boolean vmSqliteObjectLeaksEnabled() { 2068 return (sVmPolicy.mask & DETECT_VM_CURSOR_LEAKS) != 0; 2069 } 2070 2071 /** @hide */ vmClosableObjectLeaksEnabled()2072 public static boolean vmClosableObjectLeaksEnabled() { 2073 return (sVmPolicy.mask & DETECT_VM_CLOSABLE_LEAKS) != 0; 2074 } 2075 2076 /** @hide */ vmRegistrationLeaksEnabled()2077 public static boolean vmRegistrationLeaksEnabled() { 2078 return (sVmPolicy.mask & DETECT_VM_REGISTRATION_LEAKS) != 0; 2079 } 2080 2081 /** @hide */ vmFileUriExposureEnabled()2082 public static boolean vmFileUriExposureEnabled() { 2083 return (sVmPolicy.mask & DETECT_VM_FILE_URI_EXPOSURE) != 0; 2084 } 2085 2086 /** @hide */ vmCleartextNetworkEnabled()2087 public static boolean vmCleartextNetworkEnabled() { 2088 return (sVmPolicy.mask & DETECT_VM_CLEARTEXT_NETWORK) != 0; 2089 } 2090 2091 /** @hide */ vmContentUriWithoutPermissionEnabled()2092 public static boolean vmContentUriWithoutPermissionEnabled() { 2093 return (sVmPolicy.mask & DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION) != 0; 2094 } 2095 2096 /** @hide */ vmUntaggedSocketEnabled()2097 public static boolean vmUntaggedSocketEnabled() { 2098 return (sVmPolicy.mask & DETECT_VM_UNTAGGED_SOCKET) != 0; 2099 } 2100 2101 /** @hide */ vmImplicitDirectBootEnabled()2102 public static boolean vmImplicitDirectBootEnabled() { 2103 return (sVmPolicy.mask & DETECT_VM_IMPLICIT_DIRECT_BOOT) != 0; 2104 } 2105 2106 /** @hide */ vmCredentialProtectedWhileLockedEnabled()2107 public static boolean vmCredentialProtectedWhileLockedEnabled() { 2108 return (sVmPolicy.mask & DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED) != 0; 2109 } 2110 2111 /** @hide */ vmIncorrectContextUseEnabled()2112 public static boolean vmIncorrectContextUseEnabled() { 2113 return (sVmPolicy.mask & DETECT_VM_INCORRECT_CONTEXT_USE) != 0; 2114 } 2115 2116 /** @hide */ onSqliteObjectLeaked(String message, Throwable originStack)2117 public static void onSqliteObjectLeaked(String message, Throwable originStack) { 2118 onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack)); 2119 } 2120 2121 /** @hide */ 2122 @UnsupportedAppUsage onWebViewMethodCalledOnWrongThread(Throwable originStack)2123 public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) { 2124 onVmPolicyViolation(new WebViewMethodCalledOnWrongThreadViolation(originStack)); 2125 } 2126 2127 /** @hide */ onIntentReceiverLeaked(Throwable originStack)2128 public static void onIntentReceiverLeaked(Throwable originStack) { 2129 onVmPolicyViolation(new IntentReceiverLeakedViolation(originStack)); 2130 } 2131 2132 /** @hide */ onServiceConnectionLeaked(Throwable originStack)2133 public static void onServiceConnectionLeaked(Throwable originStack) { 2134 onVmPolicyViolation(new ServiceConnectionLeakedViolation(originStack)); 2135 } 2136 2137 /** @hide */ onFileUriExposed(Uri uri, String location)2138 public static void onFileUriExposed(Uri uri, String location) { 2139 final String message = uri + " exposed beyond app through " + location; 2140 if ((sVmPolicy.mask & PENALTY_DEATH_ON_FILE_URI_EXPOSURE) != 0) { 2141 throw new FileUriExposedException(message); 2142 } else { 2143 onVmPolicyViolation(new FileUriExposedViolation(message)); 2144 } 2145 } 2146 2147 /** @hide */ onContentUriWithoutPermission(Uri uri, String location)2148 public static void onContentUriWithoutPermission(Uri uri, String location) { 2149 onVmPolicyViolation(new ContentUriWithoutPermissionViolation(uri, location)); 2150 } 2151 2152 /** @hide */ onCleartextNetworkDetected(byte[] firstPacket)2153 public static void onCleartextNetworkDetected(byte[] firstPacket) { 2154 byte[] rawAddr = null; 2155 if (firstPacket != null) { 2156 if (firstPacket.length >= 20 && (firstPacket[0] & 0xf0) == 0x40) { 2157 // IPv4 2158 rawAddr = new byte[4]; 2159 System.arraycopy(firstPacket, 16, rawAddr, 0, 4); 2160 } else if (firstPacket.length >= 40 && (firstPacket[0] & 0xf0) == 0x60) { 2161 // IPv6 2162 rawAddr = new byte[16]; 2163 System.arraycopy(firstPacket, 24, rawAddr, 0, 16); 2164 } 2165 } 2166 2167 final int uid = android.os.Process.myUid(); 2168 String msg = "Detected cleartext network traffic from UID " + uid; 2169 if (rawAddr != null) { 2170 try { 2171 msg += " to " + InetAddress.getByAddress(rawAddr); 2172 } catch (UnknownHostException ignored) { 2173 } 2174 } 2175 msg += HexDump.dumpHexString(firstPacket).trim() + " "; 2176 final boolean forceDeath = (sVmPolicy.mask & PENALTY_DEATH_ON_CLEARTEXT_NETWORK) != 0; 2177 onVmPolicyViolation(new CleartextNetworkViolation(msg), forceDeath); 2178 } 2179 2180 /** @hide */ onUntaggedSocket()2181 public static void onUntaggedSocket() { 2182 onVmPolicyViolation(new UntaggedSocketViolation()); 2183 } 2184 2185 /** @hide */ onImplicitDirectBoot()2186 public static void onImplicitDirectBoot() { 2187 onVmPolicyViolation(new ImplicitDirectBootViolation()); 2188 } 2189 2190 /** @hide */ onIncorrectContextUsed(String message, Throwable originStack)2191 public static void onIncorrectContextUsed(String message, Throwable originStack) { 2192 onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack)); 2193 } 2194 2195 /** Assume locked until we hear otherwise */ 2196 private static volatile boolean sUserKeyUnlocked = false; 2197 isUserKeyUnlocked(int userId)2198 private static boolean isUserKeyUnlocked(int userId) { 2199 final IStorageManager storage = IStorageManager.Stub 2200 .asInterface(ServiceManager.getService("mount")); 2201 if (storage != null) { 2202 try { 2203 return storage.isUserKeyUnlocked(userId); 2204 } catch (RemoteException ignored) { 2205 } 2206 } 2207 return false; 2208 } 2209 2210 /** @hide */ onCredentialProtectedPathAccess(String path, int userId)2211 private static void onCredentialProtectedPathAccess(String path, int userId) { 2212 // We can cache the unlocked state for the userId we're running as, 2213 // since any relocking of that user will always result in our 2214 // process being killed to release any CE FDs we're holding onto. 2215 if (userId == UserHandle.myUserId()) { 2216 if (sUserKeyUnlocked) { 2217 return; 2218 } else if (isUserKeyUnlocked(userId)) { 2219 sUserKeyUnlocked = true; 2220 return; 2221 } 2222 } else if (isUserKeyUnlocked(userId)) { 2223 return; 2224 } 2225 2226 onVmPolicyViolation(new CredentialProtectedWhileLockedViolation( 2227 "Accessed credential protected path " + path + " while user " + userId 2228 + " was locked")); 2229 } 2230 2231 // Map from VM violation fingerprint to uptime millis. 2232 @UnsupportedAppUsage 2233 private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<>(); 2234 2235 /** 2236 * Clamp the given map by removing elements with timestamp older than the given retainSince. 2237 */ clampViolationTimeMap(final @NonNull Map<Integer, Long> violationTime, final long retainSince)2238 private static void clampViolationTimeMap(final @NonNull Map<Integer, Long> violationTime, 2239 final long retainSince) { 2240 final Iterator<Map.Entry<Integer, Long>> iterator = violationTime.entrySet().iterator(); 2241 while (iterator.hasNext()) { 2242 Map.Entry<Integer, Long> e = iterator.next(); 2243 if (e.getValue() < retainSince) { 2244 // Remove stale entries 2245 iterator.remove(); 2246 } 2247 } 2248 // Ideally we'd cap the total size of the map, though it'll involve quickselect of topK, 2249 // seems not worth it (saving some space immediately but they will be obsoleted soon anyway) 2250 } 2251 2252 /** @hide */ onVmPolicyViolation(Violation originStack)2253 public static void onVmPolicyViolation(Violation originStack) { 2254 onVmPolicyViolation(originStack, false); 2255 } 2256 2257 /** @hide */ onVmPolicyViolation(Violation violation, boolean forceDeath)2258 public static void onVmPolicyViolation(Violation violation, boolean forceDeath) { 2259 final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0; 2260 final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath; 2261 final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0; 2262 2263 final int penaltyMask = (sVmPolicy.mask & PENALTY_ALL); 2264 final ViolationInfo info = new ViolationInfo(violation, penaltyMask); 2265 2266 // Erase stuff not relevant for process-wide violations 2267 info.numAnimationsRunning = 0; 2268 info.tags = null; 2269 info.broadcastIntentAction = null; 2270 2271 final Integer fingerprint = info.hashCode(); 2272 final long now = SystemClock.uptimeMillis(); 2273 long lastViolationTime; 2274 long timeSinceLastViolationMillis = Long.MAX_VALUE; 2275 if (sLogger == LOGCAT_LOGGER) { // Don't throttle it if there is a non-default logger 2276 synchronized (sLastVmViolationTime) { 2277 if (sLastVmViolationTime.containsKey(fingerprint)) { 2278 lastViolationTime = sLastVmViolationTime.get(fingerprint); 2279 timeSinceLastViolationMillis = now - lastViolationTime; 2280 } 2281 if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) { 2282 sLastVmViolationTime.put(fingerprint, now); 2283 } 2284 clampViolationTimeMap(sLastVmViolationTime, 2285 now - Math.max(MIN_VM_INTERVAL_MS, MIN_LOG_INTERVAL_MS)); 2286 } 2287 } 2288 if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) { 2289 // Rate limit all penalties. 2290 return; 2291 } 2292 2293 if (penaltyLog && sLogger != null && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { 2294 sLogger.log(info); 2295 } 2296 2297 if (penaltyDropbox) { 2298 if (penaltyDeath) { 2299 handleApplicationStrictModeViolation(PENALTY_DROPBOX, info); 2300 } else { 2301 // Common case for userdebug/eng builds. If no death and 2302 // just dropboxing, we can do the ActivityManager call 2303 // asynchronously. 2304 dropboxViolationAsync(PENALTY_DROPBOX, info); 2305 } 2306 } 2307 2308 if (penaltyDeath) { 2309 System.err.println("StrictMode VmPolicy violation with POLICY_DEATH; shutting down."); 2310 Process.killProcess(Process.myPid()); 2311 System.exit(10); 2312 } 2313 2314 // If penaltyDeath, we can't guarantee this callback finishes before the process dies for 2315 // all executors. penaltyDeath supersedes penaltyCallback. 2316 if (sVmPolicy.mListener != null && sVmPolicy.mCallbackExecutor != null) { 2317 final OnVmViolationListener listener = sVmPolicy.mListener; 2318 try { 2319 sVmPolicy.mCallbackExecutor.execute( 2320 () -> { 2321 // Lift violated policy to prevent infinite recursion. 2322 VmPolicy oldPolicy = allowVmViolations(); 2323 try { 2324 listener.onVmViolation(violation); 2325 } finally { 2326 setVmPolicy(oldPolicy); 2327 } 2328 }); 2329 } catch (RejectedExecutionException e) { 2330 Log.e(TAG, "VmPolicy penaltyCallback failed", e); 2331 } 2332 } 2333 } 2334 2335 /** Called from Parcel.writeNoException() */ writeGatheredViolationsToParcel(Parcel p)2336 /* package */ static void writeGatheredViolationsToParcel(Parcel p) { 2337 ArrayList<ViolationInfo> violations = gatheredViolations.get(); 2338 if (violations == null) { 2339 p.writeInt(0); 2340 } else { 2341 // To avoid taking up too much transaction space, only include 2342 // details for the first 3 violations. Deep inside, CrashInfo 2343 // will truncate each stack trace to ~20kB. 2344 final int size = Math.min(violations.size(), 3); 2345 p.writeInt(size); 2346 for (int i = 0; i < size; i++) { 2347 violations.get(i).writeToParcel(p, 0); 2348 } 2349 } 2350 gatheredViolations.set(null); 2351 } 2352 2353 /** 2354 * Called from Parcel.readException() when the exception is EX_STRICT_MODE_VIOLATIONS, we here 2355 * read back all the encoded violations. 2356 */ readAndHandleBinderCallViolations(Parcel p)2357 /* package */ static void readAndHandleBinderCallViolations(Parcel p) { 2358 Throwable localCallSite = new Throwable(); 2359 final int policyMask = getThreadPolicyMask(); 2360 final boolean currentlyGathering = (policyMask & PENALTY_GATHER) != 0; 2361 2362 final int size = p.readInt(); 2363 for (int i = 0; i < size; i++) { 2364 final ViolationInfo info = new ViolationInfo(p, !currentlyGathering); 2365 info.addLocalStack(localCallSite); 2366 BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 2367 if (policy instanceof AndroidBlockGuardPolicy) { 2368 ((AndroidBlockGuardPolicy) policy).handleViolationWithTimingAttempt(info); 2369 } 2370 } 2371 } 2372 2373 /** 2374 * Called from android_util_Binder.cpp's android_os_Parcel_enforceInterface when an incoming 2375 * Binder call requires changing the StrictMode policy mask. The role of this function is to ask 2376 * Binder for its current (native) thread-local policy value and synchronize it to libcore's 2377 * (Java) thread-local policy value. 2378 */ 2379 @UnsupportedAppUsage onBinderStrictModePolicyChange(@hreadPolicyMask int newPolicy)2380 private static void onBinderStrictModePolicyChange(@ThreadPolicyMask int newPolicy) { 2381 setBlockGuardPolicy(newPolicy); 2382 } 2383 2384 /** 2385 * A tracked, critical time span. (e.g. during an animation.) 2386 * 2387 * <p>The object itself is a linked list node, to avoid any allocations during rapid span 2388 * entries and exits. 2389 * 2390 * @hide 2391 */ 2392 public static class Span { 2393 private String mName; 2394 private long mCreateMillis; 2395 private Span mNext; 2396 private Span mPrev; // not used when in freeList, only active 2397 private final ThreadSpanState mContainerState; 2398 Span(ThreadSpanState threadState)2399 Span(ThreadSpanState threadState) { 2400 mContainerState = threadState; 2401 } 2402 2403 // Empty constructor for the NO_OP_SPAN Span()2404 protected Span() { 2405 mContainerState = null; 2406 } 2407 2408 /** 2409 * To be called when the critical span is complete (i.e. the animation is done animating). 2410 * This can be called on any thread (even a different one from where the animation was 2411 * taking place), but that's only a defensive implementation measure. It really makes no 2412 * sense for you to call this on thread other than that where you created it. 2413 * 2414 * @hide 2415 */ 2416 @UnsupportedAppUsage finish()2417 public void finish() { 2418 ThreadSpanState state = mContainerState; 2419 synchronized (state) { 2420 if (mName == null) { 2421 // Duplicate finish call. Ignore. 2422 return; 2423 } 2424 2425 // Remove ourselves from the active list. 2426 if (mPrev != null) { 2427 mPrev.mNext = mNext; 2428 } 2429 if (mNext != null) { 2430 mNext.mPrev = mPrev; 2431 } 2432 if (state.mActiveHead == this) { 2433 state.mActiveHead = mNext; 2434 } 2435 2436 state.mActiveSize--; 2437 2438 if (LOG_V) Log.d(TAG, "Span finished=" + mName + "; size=" + state.mActiveSize); 2439 2440 this.mCreateMillis = -1; 2441 this.mName = null; 2442 this.mPrev = null; 2443 this.mNext = null; 2444 2445 // Add ourselves to the freeList, if it's not already 2446 // too big. 2447 if (state.mFreeListSize < 5) { 2448 this.mNext = state.mFreeListHead; 2449 state.mFreeListHead = this; 2450 state.mFreeListSize++; 2451 } 2452 } 2453 } 2454 } 2455 2456 // The no-op span that's used in user builds. 2457 private static final Span NO_OP_SPAN = 2458 new Span() { 2459 public void finish() { 2460 // Do nothing. 2461 } 2462 }; 2463 2464 /** 2465 * Linked lists of active spans and a freelist. 2466 * 2467 * <p>Locking notes: there's one of these structures per thread and all members of this 2468 * structure (as well as the Span nodes under it) are guarded by the ThreadSpanState object 2469 * instance. While in theory there'd be no locking required because it's all local per-thread, 2470 * the finish() method above is defensive against people calling it on a different thread from 2471 * where they created the Span, hence the locking. 2472 */ 2473 private static class ThreadSpanState { 2474 public Span mActiveHead; // doubly-linked list. 2475 public int mActiveSize; 2476 public Span mFreeListHead; // singly-linked list. only changes at head. 2477 public int mFreeListSize; 2478 } 2479 2480 private static final ThreadLocal<ThreadSpanState> sThisThreadSpanState = 2481 new ThreadLocal<ThreadSpanState>() { 2482 @Override 2483 protected ThreadSpanState initialValue() { 2484 return new ThreadSpanState(); 2485 } 2486 }; 2487 2488 @UnsupportedAppUsage 2489 private static Singleton<IWindowManager> sWindowManager = 2490 new Singleton<IWindowManager>() { 2491 protected IWindowManager create() { 2492 return IWindowManager.Stub.asInterface(ServiceManager.getService("window")); 2493 } 2494 }; 2495 2496 /** 2497 * Enter a named critical span (e.g. an animation) 2498 * 2499 * <p>The name is an arbitary label (or tag) that will be applied to any strictmode violation 2500 * that happens while this span is active. You must call finish() on the span when done. 2501 * 2502 * <p>This will never return null, but on devices without debugging enabled, this may return a 2503 * dummy object on which the finish() method is a no-op. 2504 * 2505 * <p>TODO: add CloseGuard to this, verifying callers call finish. 2506 * 2507 * @hide 2508 */ 2509 @UnsupportedAppUsage enterCriticalSpan(String name)2510 public static Span enterCriticalSpan(String name) { 2511 if (Build.IS_USER) { 2512 return NO_OP_SPAN; 2513 } 2514 if (name == null || name.isEmpty()) { 2515 throw new IllegalArgumentException("name must be non-null and non-empty"); 2516 } 2517 ThreadSpanState state = sThisThreadSpanState.get(); 2518 Span span = null; 2519 synchronized (state) { 2520 if (state.mFreeListHead != null) { 2521 span = state.mFreeListHead; 2522 state.mFreeListHead = span.mNext; 2523 state.mFreeListSize--; 2524 } else { 2525 // Shouldn't have to do this often. 2526 span = new Span(state); 2527 } 2528 span.mName = name; 2529 span.mCreateMillis = SystemClock.uptimeMillis(); 2530 span.mNext = state.mActiveHead; 2531 span.mPrev = null; 2532 state.mActiveHead = span; 2533 state.mActiveSize++; 2534 if (span.mNext != null) { 2535 span.mNext.mPrev = span; 2536 } 2537 if (LOG_V) Log.d(TAG, "Span enter=" + name + "; size=" + state.mActiveSize); 2538 } 2539 return span; 2540 } 2541 2542 /** 2543 * For code to note that it's slow. This is a no-op unless the current thread's {@link 2544 * android.os.StrictMode.ThreadPolicy} has {@link 2545 * android.os.StrictMode.ThreadPolicy.Builder#detectCustomSlowCalls} enabled. 2546 * 2547 * @param name a short string for the exception stack trace that's built if when this fires. 2548 */ noteSlowCall(String name)2549 public static void noteSlowCall(String name) { 2550 BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 2551 if (!(policy instanceof AndroidBlockGuardPolicy)) { 2552 // StrictMode not enabled. 2553 return; 2554 } 2555 ((AndroidBlockGuardPolicy) policy).onCustomSlowCall(name); 2556 } 2557 2558 /** 2559 * For code to note that a resource was obtained using a type other than its defined type. This 2560 * is a no-op unless the current thread's {@link android.os.StrictMode.ThreadPolicy} has {@link 2561 * android.os.StrictMode.ThreadPolicy.Builder#detectResourceMismatches()} enabled. 2562 * 2563 * @param tag an object for the exception stack trace that's built if when this fires. 2564 * @hide 2565 */ noteResourceMismatch(Object tag)2566 public static void noteResourceMismatch(Object tag) { 2567 BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 2568 if (!(policy instanceof AndroidBlockGuardPolicy)) { 2569 // StrictMode not enabled. 2570 return; 2571 } 2572 ((AndroidBlockGuardPolicy) policy).onResourceMismatch(tag); 2573 } 2574 2575 /** @hide */ noteUnbufferedIO()2576 public static void noteUnbufferedIO() { 2577 BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 2578 if (!(policy instanceof AndroidBlockGuardPolicy)) { 2579 // StrictMode not enabled. 2580 return; 2581 } 2582 policy.onUnbufferedIO(); 2583 } 2584 2585 /** @hide */ noteDiskRead()2586 public static void noteDiskRead() { 2587 BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 2588 if (!(policy instanceof AndroidBlockGuardPolicy)) { 2589 // StrictMode not enabled. 2590 return; 2591 } 2592 policy.onReadFromDisk(); 2593 } 2594 2595 /** @hide */ noteDiskWrite()2596 public static void noteDiskWrite() { 2597 BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 2598 if (!(policy instanceof AndroidBlockGuardPolicy)) { 2599 // StrictMode not enabled. 2600 return; 2601 } 2602 policy.onWriteToDisk(); 2603 } 2604 2605 @GuardedBy("StrictMode.class") 2606 private static final HashMap<Class, Integer> sExpectedActivityInstanceCount = new HashMap<>(); 2607 2608 /** 2609 * Returns an object that is used to track instances of activites. The activity should store a 2610 * reference to the tracker object in one of its fields. 2611 * 2612 * @hide 2613 */ trackActivity(Object instance)2614 public static Object trackActivity(Object instance) { 2615 return new InstanceTracker(instance); 2616 } 2617 2618 /** @hide */ 2619 @UnsupportedAppUsage incrementExpectedActivityCount(Class klass)2620 public static void incrementExpectedActivityCount(Class klass) { 2621 if (klass == null) { 2622 return; 2623 } 2624 2625 synchronized (StrictMode.class) { 2626 if ((sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) { 2627 return; 2628 } 2629 2630 // Use the instance count from InstanceTracker as initial value. 2631 Integer expected = sExpectedActivityInstanceCount.get(klass); 2632 Integer newExpected = 2633 expected == null ? InstanceTracker.getInstanceCount(klass) + 1 : expected + 1; 2634 sExpectedActivityInstanceCount.put(klass, newExpected); 2635 } 2636 } 2637 2638 /** @hide */ decrementExpectedActivityCount(Class klass)2639 public static void decrementExpectedActivityCount(Class klass) { 2640 if (klass == null) { 2641 return; 2642 } 2643 2644 final int limit; 2645 synchronized (StrictMode.class) { 2646 if ((sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) { 2647 return; 2648 } 2649 2650 Integer expected = sExpectedActivityInstanceCount.get(klass); 2651 int newExpected = (expected == null || expected == 0) ? 0 : expected - 1; 2652 if (newExpected == 0) { 2653 sExpectedActivityInstanceCount.remove(klass); 2654 } else { 2655 sExpectedActivityInstanceCount.put(klass, newExpected); 2656 } 2657 2658 // Note: adding 1 here to give some breathing room during 2659 // orientation changes. (shouldn't be necessary, though?) 2660 limit = newExpected + 1; 2661 } 2662 2663 // Quick check. 2664 int actual = InstanceTracker.getInstanceCount(klass); 2665 if (actual <= limit) { 2666 return; 2667 } 2668 2669 // Do a GC and explicit count to double-check. 2670 // This is the work that we are trying to avoid by tracking the object instances 2671 // explicity. Running an explicit GC can be expensive (80ms) and so can walking 2672 // the heap to count instance (30ms). This extra work can make the system feel 2673 // noticeably less responsive during orientation changes when activities are 2674 // being restarted. Granted, it is only a problem when StrictMode is enabled 2675 // but it is annoying. 2676 2677 System.gc(); 2678 System.runFinalization(); 2679 System.gc(); 2680 2681 long instances = VMDebug.countInstancesOfClass(klass, false); 2682 if (instances > limit) { 2683 onVmPolicyViolation(new InstanceCountViolation(klass, instances, limit)); 2684 } 2685 } 2686 2687 /** 2688 * Parcelable that gets sent in Binder call headers back to callers to report violations that 2689 * happened during a cross-process call. 2690 * 2691 * @hide 2692 */ 2693 @TestApi 2694 public static final class ViolationInfo implements Parcelable { 2695 /** Stack and violation details. */ 2696 private final Violation mViolation; 2697 2698 /** Path leading to a violation that occurred across binder. */ 2699 private final Deque<StackTraceElement[]> mBinderStack = new ArrayDeque<>(); 2700 2701 /** Memoized stack trace of full violation. */ 2702 @Nullable private String mStackTrace; 2703 2704 /** The strict mode penalty mask at the time of violation. */ 2705 private final int mPenaltyMask; 2706 2707 /** The wall time duration of the violation, when known. -1 when not known. */ 2708 public int durationMillis = -1; 2709 2710 /** The number of animations currently running. */ 2711 public int numAnimationsRunning = 0; 2712 2713 /** List of tags from active Span instances during this violation, or null for none. */ 2714 public String[] tags; 2715 2716 /** 2717 * Which violation number this was (1-based) since the last Looper loop, from the 2718 * perspective of the root caller (if it crossed any processes via Binder calls). The value 2719 * is 0 if the root caller wasn't on a Looper thread. 2720 */ 2721 public int violationNumThisLoop; 2722 2723 /** The time (in terms of SystemClock.uptimeMillis()) that the violation occurred. */ 2724 public long violationUptimeMillis; 2725 2726 /** 2727 * The action of the Intent being broadcast to somebody's onReceive on this thread right 2728 * now, or null. 2729 */ 2730 public String broadcastIntentAction; 2731 2732 /** If this is a instance count violation, the number of instances in memory, else -1. */ 2733 public long numInstances = -1; 2734 2735 /** Create an instance of ViolationInfo initialized from an exception. */ ViolationInfo(Violation tr, int penaltyMask)2736 ViolationInfo(Violation tr, int penaltyMask) { 2737 this.mViolation = tr; 2738 this.mPenaltyMask = penaltyMask; 2739 violationUptimeMillis = SystemClock.uptimeMillis(); 2740 this.numAnimationsRunning = ValueAnimator.getCurrentAnimationsCount(); 2741 Intent broadcastIntent = ActivityThread.getIntentBeingBroadcast(); 2742 if (broadcastIntent != null) { 2743 broadcastIntentAction = broadcastIntent.getAction(); 2744 } 2745 ThreadSpanState state = sThisThreadSpanState.get(); 2746 if (tr instanceof InstanceCountViolation) { 2747 this.numInstances = ((InstanceCountViolation) tr).getNumberOfInstances(); 2748 } 2749 synchronized (state) { 2750 int spanActiveCount = state.mActiveSize; 2751 if (spanActiveCount > MAX_SPAN_TAGS) { 2752 spanActiveCount = MAX_SPAN_TAGS; 2753 } 2754 if (spanActiveCount != 0) { 2755 this.tags = new String[spanActiveCount]; 2756 Span iter = state.mActiveHead; 2757 int index = 0; 2758 while (iter != null && index < spanActiveCount) { 2759 this.tags[index] = iter.mName; 2760 index++; 2761 iter = iter.mNext; 2762 } 2763 } 2764 } 2765 } 2766 2767 /** 2768 * Equivalent output to 2769 * {@link android.app.ApplicationErrorReport.CrashInfo#stackTrace}. 2770 */ getStackTrace()2771 public String getStackTrace() { 2772 if (mStackTrace == null) { 2773 StringWriter sw = new StringWriter(); 2774 PrintWriter pw = new FastPrintWriter(sw, false, 256); 2775 mViolation.printStackTrace(pw); 2776 for (StackTraceElement[] traces : mBinderStack) { 2777 pw.append("# via Binder call with stack:\n"); 2778 for (StackTraceElement traceElement : traces) { 2779 pw.append("\tat "); 2780 pw.append(traceElement.toString()); 2781 pw.append('\n'); 2782 } 2783 } 2784 pw.flush(); 2785 pw.close(); 2786 mStackTrace = sw.toString(); 2787 } 2788 return mStackTrace; 2789 } 2790 getViolationClass()2791 public Class<? extends Violation> getViolationClass() { 2792 return mViolation.getClass(); 2793 } 2794 2795 /** 2796 * Optional message describing this violation. 2797 * 2798 * @hide 2799 */ 2800 @TestApi getViolationDetails()2801 public String getViolationDetails() { 2802 return mViolation.getMessage(); 2803 } 2804 penaltyEnabled(int p)2805 boolean penaltyEnabled(int p) { 2806 return (mPenaltyMask & p) != 0; 2807 } 2808 2809 /** 2810 * Add a {@link Throwable} from the current process that caused the underlying violation. We 2811 * only preserve the stack trace elements. 2812 * 2813 * @hide 2814 */ addLocalStack(Throwable t)2815 void addLocalStack(Throwable t) { 2816 mBinderStack.addFirst(t.getStackTrace()); 2817 } 2818 2819 @Override hashCode()2820 public int hashCode() { 2821 int result = 17; 2822 if (mViolation != null) { 2823 result = 37 * result + mViolation.hashCode(); 2824 } 2825 if (numAnimationsRunning != 0) { 2826 result *= 37; 2827 } 2828 if (broadcastIntentAction != null) { 2829 result = 37 * result + broadcastIntentAction.hashCode(); 2830 } 2831 if (tags != null) { 2832 for (String tag : tags) { 2833 result = 37 * result + tag.hashCode(); 2834 } 2835 } 2836 return result; 2837 } 2838 2839 /** Create an instance of ViolationInfo initialized from a Parcel. */ 2840 @UnsupportedAppUsage ViolationInfo(Parcel in)2841 public ViolationInfo(Parcel in) { 2842 this(in, false); 2843 } 2844 2845 /** 2846 * Create an instance of ViolationInfo initialized from a Parcel. 2847 * 2848 * @param unsetGatheringBit if true, the caller is the root caller and the gathering penalty 2849 * should be removed. 2850 */ ViolationInfo(Parcel in, boolean unsetGatheringBit)2851 public ViolationInfo(Parcel in, boolean unsetGatheringBit) { 2852 mViolation = (Violation) in.readSerializable(); 2853 int binderStackSize = in.readInt(); 2854 for (int i = 0; i < binderStackSize; i++) { 2855 StackTraceElement[] traceElements = new StackTraceElement[in.readInt()]; 2856 for (int j = 0; j < traceElements.length; j++) { 2857 StackTraceElement element = 2858 new StackTraceElement( 2859 in.readString(), 2860 in.readString(), 2861 in.readString(), 2862 in.readInt()); 2863 traceElements[j] = element; 2864 } 2865 mBinderStack.add(traceElements); 2866 } 2867 int rawPenaltyMask = in.readInt(); 2868 if (unsetGatheringBit) { 2869 mPenaltyMask = rawPenaltyMask & ~PENALTY_GATHER; 2870 } else { 2871 mPenaltyMask = rawPenaltyMask; 2872 } 2873 durationMillis = in.readInt(); 2874 violationNumThisLoop = in.readInt(); 2875 numAnimationsRunning = in.readInt(); 2876 violationUptimeMillis = in.readLong(); 2877 numInstances = in.readLong(); 2878 broadcastIntentAction = in.readString(); 2879 tags = in.readStringArray(); 2880 } 2881 2882 /** Save a ViolationInfo instance to a parcel. */ 2883 @Override writeToParcel(Parcel dest, int flags)2884 public void writeToParcel(Parcel dest, int flags) { 2885 dest.writeSerializable(mViolation); 2886 dest.writeInt(mBinderStack.size()); 2887 for (StackTraceElement[] traceElements : mBinderStack) { 2888 dest.writeInt(traceElements.length); 2889 for (StackTraceElement element : traceElements) { 2890 dest.writeString(element.getClassName()); 2891 dest.writeString(element.getMethodName()); 2892 dest.writeString(element.getFileName()); 2893 dest.writeInt(element.getLineNumber()); 2894 } 2895 } 2896 int start = dest.dataPosition(); 2897 dest.writeInt(mPenaltyMask); 2898 dest.writeInt(durationMillis); 2899 dest.writeInt(violationNumThisLoop); 2900 dest.writeInt(numAnimationsRunning); 2901 dest.writeLong(violationUptimeMillis); 2902 dest.writeLong(numInstances); 2903 dest.writeString(broadcastIntentAction); 2904 dest.writeStringArray(tags); 2905 int total = dest.dataPosition() - start; 2906 if (Binder.CHECK_PARCEL_SIZE && total > 10 * 1024) { 2907 Slog.d( 2908 TAG, 2909 "VIO: penalty=" 2910 + mPenaltyMask 2911 + " dur=" 2912 + durationMillis 2913 + " numLoop=" 2914 + violationNumThisLoop 2915 + " anim=" 2916 + numAnimationsRunning 2917 + " uptime=" 2918 + violationUptimeMillis 2919 + " numInst=" 2920 + numInstances); 2921 Slog.d(TAG, "VIO: action=" + broadcastIntentAction); 2922 Slog.d(TAG, "VIO: tags=" + Arrays.toString(tags)); 2923 Slog.d(TAG, "VIO: TOTAL BYTES WRITTEN: " + (dest.dataPosition() - start)); 2924 } 2925 } 2926 2927 /** Dump a ViolationInfo instance to a Printer. */ dump(Printer pw, String prefix)2928 public void dump(Printer pw, String prefix) { 2929 pw.println(prefix + "stackTrace: " + getStackTrace()); 2930 pw.println(prefix + "penalty: " + mPenaltyMask); 2931 if (durationMillis != -1) { 2932 pw.println(prefix + "durationMillis: " + durationMillis); 2933 } 2934 if (numInstances != -1) { 2935 pw.println(prefix + "numInstances: " + numInstances); 2936 } 2937 if (violationNumThisLoop != 0) { 2938 pw.println(prefix + "violationNumThisLoop: " + violationNumThisLoop); 2939 } 2940 if (numAnimationsRunning != 0) { 2941 pw.println(prefix + "numAnimationsRunning: " + numAnimationsRunning); 2942 } 2943 pw.println(prefix + "violationUptimeMillis: " + violationUptimeMillis); 2944 if (broadcastIntentAction != null) { 2945 pw.println(prefix + "broadcastIntentAction: " + broadcastIntentAction); 2946 } 2947 if (tags != null) { 2948 int index = 0; 2949 for (String tag : tags) { 2950 pw.println(prefix + "tag[" + (index++) + "]: " + tag); 2951 } 2952 } 2953 } 2954 2955 @Override describeContents()2956 public int describeContents() { 2957 return 0; 2958 } 2959 2960 public static final @android.annotation.NonNull Parcelable.Creator<ViolationInfo> CREATOR = 2961 new Parcelable.Creator<ViolationInfo>() { 2962 @Override 2963 public ViolationInfo createFromParcel(Parcel in) { 2964 return new ViolationInfo(in); 2965 } 2966 2967 @Override 2968 public ViolationInfo[] newArray(int size) { 2969 return new ViolationInfo[size]; 2970 } 2971 }; 2972 } 2973 2974 private static final class InstanceTracker { 2975 private static final HashMap<Class<?>, Integer> sInstanceCounts = 2976 new HashMap<Class<?>, Integer>(); 2977 2978 private final Class<?> mKlass; 2979 InstanceTracker(Object instance)2980 public InstanceTracker(Object instance) { 2981 mKlass = instance.getClass(); 2982 2983 synchronized (sInstanceCounts) { 2984 final Integer value = sInstanceCounts.get(mKlass); 2985 final int newValue = value != null ? value + 1 : 1; 2986 sInstanceCounts.put(mKlass, newValue); 2987 } 2988 } 2989 2990 @Override finalize()2991 protected void finalize() throws Throwable { 2992 try { 2993 synchronized (sInstanceCounts) { 2994 final Integer value = sInstanceCounts.get(mKlass); 2995 if (value != null) { 2996 final int newValue = value - 1; 2997 if (newValue > 0) { 2998 sInstanceCounts.put(mKlass, newValue); 2999 } else { 3000 sInstanceCounts.remove(mKlass); 3001 } 3002 } 3003 } 3004 } finally { 3005 super.finalize(); 3006 } 3007 } 3008 getInstanceCount(Class<?> klass)3009 public static int getInstanceCount(Class<?> klass) { 3010 synchronized (sInstanceCounts) { 3011 final Integer value = sInstanceCounts.get(klass); 3012 return value != null ? value : 0; 3013 } 3014 } 3015 } 3016 } 3017