1 /* <lambda>null2 * Copyright (C) 2015 Square, Inc. 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 shark 17 18 import java.lang.ref.PhantomReference 19 import java.lang.ref.SoftReference 20 import java.lang.ref.WeakReference 21 import java.util.EnumSet 22 import shark.AndroidReferenceMatchers.Companion.appDefaults 23 import shark.AndroidReferenceMatchers.Companion.buildKnownReferences 24 import shark.ReferencePattern.InstanceFieldPattern 25 import shark.ReferencePattern.JavaLocalPattern 26 import shark.ReferencePattern.NativeGlobalVariablePattern 27 import shark.ReferencePattern.StaticFieldPattern 28 29 /** 30 * [AndroidReferenceMatchers] values add [ReferenceMatcher] instances to a global list via their 31 * [add] method. A [ReferenceMatcher] is either a [IgnoredReferenceMatcher] or 32 * a [LibraryLeakReferenceMatcher]. 33 * 34 * [AndroidReferenceMatchers] is used to build the list of known references that cannot ever create 35 * leaks (via [IgnoredReferenceMatcher]) as well as the list of known leaks in the Android Framework 36 * and in manufacturer specific Android implementations. 37 * 38 * This class is a work in progress. You can help by reporting leak traces that seem to be caused 39 * by the Android SDK, here: https://github.com/square/leakcanary/issues/new 40 * 41 * We filter on SDK versions and Manufacturers because many of those leaks are specific to a given 42 * manufacturer implementation, they usually share their builds across multiple models, and the 43 * leaks eventually get fixed in newer versions. 44 * 45 * Most app developers should use [appDefaults]. However, you can also use a subset of 46 * [AndroidReferenceMatchers] by creating an [EnumSet] that matches your needs and calling 47 * [buildKnownReferences]. 48 */ 49 enum class AndroidReferenceMatchers { 50 51 // ######## Android Framework known leaks ######## 52 53 IREQUEST_FINISH_CALLBACK { 54 override fun add( 55 references: MutableList<ReferenceMatcher> 56 ) { 57 references += instanceFieldLeak( 58 "android.app.Activity\$1", "this\$0", 59 description = "Android Q added a new android.app.IRequestFinishCallback\$Stub " + 60 "class. android.app.Activity creates an implementation of that interface as an " + 61 "anonymous subclass. That anonymous subclass has a reference to the activity. " + 62 "Another process is keeping the android.app.IRequestFinishCallback\$Stub " + 63 "reference alive long after Activity.onDestroyed() has been called, " + 64 "causing the activity to leak. " + 65 "Fix: You can \"fix\" this leak by overriding Activity.onBackPressed() and calling " + 66 "Activity.finishAfterTransition(); instead of super if the activity is task root and the " + 67 "fragment stack is empty. " + 68 "Tracked here: https://issuetracker.google.com/issues/139738913" 69 ) { 70 sdkInt == 29 71 } 72 } 73 }, 74 75 /** 76 * See AndroidReferenceReaders.ACTIVITY_THREAD__NEW_ACTIVITIES for more context 77 */ 78 ACTIVITY_THREAD__M_NEW_ACTIVITIES { 79 override fun add( 80 references: MutableList<ReferenceMatcher> 81 ) { 82 references += instanceFieldLeak( 83 "android.app.ActivityThread", "mNewActivities", 84 description = """ 85 New activities are leaked by ActivityThread until the main thread becomes idle. 86 Tracked here: https://issuetracker.google.com/issues/258390457 87 """.trimIndent() 88 ) { 89 sdkInt >= 19 90 } 91 } 92 }, 93 94 SPAN_CONTROLLER { 95 override fun add( 96 references: MutableList<ReferenceMatcher> 97 ) { 98 val description = 99 ("Editor inserts a special span, which has a reference to the EditText. That span is a" 100 + " NoCopySpan, which makes sure it gets dropped when creating a new" 101 + " SpannableStringBuilder from a given CharSequence." 102 + " TextView.onSaveInstanceState() does a copy of its mText before saving it in the" 103 + " bundle. Prior to KitKat, that copy was done using the SpannableString" 104 + " constructor, instead of SpannableStringBuilder. The SpannableString constructor" 105 + " does not drop NoCopySpan spans. So we end up with a saved state that holds a" 106 + " reference to the textview and therefore the entire view hierarchy & activity" 107 + " context. Fix: https://github.com/android/platform_frameworks_base/commit" 108 + "/af7dcdf35a37d7a7dbaad7d9869c1c91bce2272b ." 109 + " To fix this, you could override TextView.onSaveInstanceState(), and then use" 110 + " reflection to access TextView.SavedState.mText and clear the NoCopySpan spans.") 111 112 references += instanceFieldLeak( 113 "android.widget.Editor\$SpanController", "this$0", description 114 ) { 115 sdkInt <= 19 116 } 117 118 references += instanceFieldLeak( 119 "android.widget.Editor\$EasyEditSpanController", "this$0", description 120 ) { 121 sdkInt <= 19 122 } 123 } 124 }, 125 126 MEDIA_SESSION_LEGACY_HELPER__SINSTANCE { 127 override fun add( 128 references: MutableList<ReferenceMatcher> 129 ) { 130 references += 131 staticFieldLeak( 132 "android.media.session.MediaSessionLegacyHelper", "sInstance", 133 description = "MediaSessionLegacyHelper is a static singleton that is lazily instantiated and" 134 + " keeps a reference to the context it's given the first time" 135 + " MediaSessionLegacyHelper.getHelper() is called." 136 + " This leak was introduced in android-5.0.1_r1 and fixed in Android 5.1.0_r1 by" 137 + " calling context.getApplicationContext()." 138 + " Fix: https://github.com/android/platform_frameworks_base/commit" 139 + "/9b5257c9c99c4cb541d8e8e78fb04f008b1a9091" 140 + " To fix this, you could call MediaSessionLegacyHelper.getHelper() early" 141 + " in Application.onCreate() and pass it the application context." 142 ) { 143 sdkInt == 21 144 } 145 } 146 }, 147 148 TEXT_LINE__SCACHED { 149 override fun add( 150 references: MutableList<ReferenceMatcher> 151 ) { 152 references += staticFieldLeak( 153 "android.text.TextLine", "sCached", 154 description = "TextLine.sCached is a pool of 3 TextLine instances. TextLine.recycle() has had" 155 + " at least two bugs that created memory leaks by not correctly clearing the" 156 + " recycled TextLine instances. The first was fixed in android-5.1.0_r1:" 157 + " https://github.com/android/platform_frameworks_base/commit" 158 + "/893d6fe48d37f71e683f722457bea646994a10" 159 + " The second was fixed, not released yet:" 160 + " https://github.com/android/platform_frameworks_base/commit" 161 + "/b3a9bc038d3a218b1dbdf7b5668e3d6c12be5e" 162 + " To fix this, you could access TextLine.sCached and clear the pool every now" 163 + " and then (e.g. on activity destroy)." 164 ) { 165 sdkInt <= 22 166 } 167 } 168 }, 169 170 BLOCKING_QUEUE { 171 override fun add( 172 references: MutableList<ReferenceMatcher> 173 ) { 174 val description = ("A thread waiting on a blocking queue will leak the last" 175 + " dequeued object as a stack local reference. So when a HandlerThread becomes idle, it" 176 + " keeps a local reference to the last message it received. That message then gets" 177 + " recycled and can be used again. As long as all messages are recycled after being" 178 + " used, this won't be a problem, because these references are cleared when being" 179 + " recycled. However, dialogs create template Message instances to be copied when a" 180 + " message needs to be sent. These Message templates holds references to the dialog" 181 + " listeners, which most likely leads to holding a reference onto the activity in some" 182 + " way. Dialogs never recycle their template Message, assuming these Message instances" 183 + " will get GCed when the dialog is GCed." 184 + " The combination of these two things creates a high potential for memory leaks as soon" 185 + " as you use dialogs. These memory leaks might be temporary, but some handler threads" 186 + " sleep for a long time." 187 + " This leak is fixed by AndroidLeakFixes.FLUSH_HANDLER_THREADS in plumber-android." 188 + " Bug report: https://issuetracker.google.com/issues/146144484" 189 + " Fixed in Android 12: https://cs.android.com/android/_/android/platform/frameworks/base" 190 + "/+/d577e728e9bccbafc707af3060ea914caa73c14f") 191 192 references += instanceFieldLeak("android.os.Message", "obj", description) { 193 sdkInt < 31 194 } 195 } 196 }, 197 198 INPUT_METHOD_MANAGER_IS_TERRIBLE { 199 override fun add( 200 references: MutableList<ReferenceMatcher> 201 ) { 202 val description = 203 ("When we detach a view that receives keyboard input, the InputMethodManager" 204 + " leaks a reference to it until a new view asks for keyboard input." 205 + " Tracked here: https://code.google.com/p/android/issues/detail?id=171190" 206 + " Hack: https://gist.github.com/pyricau/4df64341cc978a7de414") 207 208 references += instanceFieldLeak( 209 "android.view.inputmethod.InputMethodManager", "mNextServedView", description 210 ) { 211 sdkInt in 15..33 212 } 213 214 references += instanceFieldLeak( 215 "android.view.inputmethod.InputMethodManager", "mServedView", description 216 ) { 217 sdkInt in 15..28 218 } 219 220 references += instanceFieldLeak( 221 "android.view.inputmethod.InputMethodManager", "mServedInputConnection", description 222 ) { 223 sdkInt in 15..27 224 } 225 226 references += instanceFieldLeak( 227 "android.view.inputmethod.InputMethodManager", "mLastSrvView", 228 description = 229 "HUAWEI added a mLastSrvView field to InputMethodManager" + " that leaks a reference to the last served view." 230 ) { 231 manufacturer == HUAWEI && sdkInt in 23..28 232 } 233 234 references += instanceFieldLeak( 235 "android.view.inputmethod.InputMethodManager", "mCurRootView", 236 description = "The singleton InputMethodManager is holding a reference to mCurRootView long" 237 + " after the activity has been destroyed." 238 + " Observed on ICS MR1: https://github.com/square/leakcanary/issues/1" 239 + "#issuecomment-100579429" 240 + " Hack: https://gist.github.com/pyricau/4df64341cc978a7de414" 241 ) { 242 sdkInt in 15..28 243 } 244 245 references += instanceFieldLeak( 246 "android.view.inputmethod.InputMethodManager", "mImeInsetsConsumer", 247 description = """ 248 InputMethodManager.mImeInsetsConsumer isn't set to null when the activity is destroyed. 249 """.trimIndent() 250 ) { 251 sdkInt >= 30 252 } 253 } 254 }, 255 256 INPUT_MANAGER__M_LATE_INIT_CONTEXT { 257 override fun add( 258 references: MutableList<ReferenceMatcher> 259 ) { 260 references += instanceFieldLeak( 261 "android.hardware.input.InputManager", "mLateInitContext", 262 description = "InputManager singleton leaks its init context which is an activity" 263 ) { 264 sdkInt == 33 265 } 266 } 267 }, 268 269 LAYOUT_TRANSITION { 270 override fun add( 271 references: MutableList<ReferenceMatcher> 272 ) { 273 references += instanceFieldLeak( 274 "android.animation.LayoutTransition$1", "val\$parent", 275 description = "LayoutTransition leaks parent ViewGroup through" 276 + " ViewTreeObserver.OnPreDrawListener When triggered, this leaks stays until the" 277 + " window is destroyed. Tracked here:" 278 + " https://code.google.com/p/android/issues/detail?id=171830" 279 ) { 280 sdkInt in 14..22 281 } 282 } 283 }, 284 285 SPELL_CHECKER_SESSION { 286 override fun add( 287 references: MutableList<ReferenceMatcher> 288 ) { 289 references += instanceFieldLeak( 290 "android.view.textservice.SpellCheckerSession$1", "this$0", 291 description = "SpellCheckerSessionListenerImpl.mHandler is leaking destroyed Activity when the" 292 + " SpellCheckerSession is closed before the service is connected." 293 + " Tracked here: https://code.google.com/p/android/issues/detail?id=172542" 294 ) { 295 sdkInt in 16..24 296 } 297 } 298 }, 299 300 SPELL_CHECKER { 301 override fun add( 302 references: MutableList<ReferenceMatcher> 303 ) { 304 references += instanceFieldLeak( 305 "android.widget.SpellChecker$1", "this$0", 306 description = "SpellChecker holds on to a detached view that points to a destroyed activity." 307 + " mSpellRunnable is being enqueued, and that callback should be removed when " 308 + " closeSession() is called. Maybe closeSession() wasn't called, or maybe it was " 309 + " called after the view was detached." 310 ) { 311 sdkInt == 22 312 } 313 } 314 }, 315 316 ACTIVITY_CHOOSE_MODEL { 317 override fun add( 318 references: MutableList<ReferenceMatcher> 319 ) { 320 val description = ("ActivityChooserModel holds a static reference to the last set" 321 + " ActivityChooserModelPolicy which can be an activity context." 322 + " Tracked here: https://code.google.com/p/android/issues/detail?id=172659" 323 + " Hack: https://gist.github.com/andaag/b05ab66ed0f06167d6e0") 324 325 326 references += instanceFieldLeak( 327 "android.support.v7.internal.widget.ActivityChooserModel", 328 "mActivityChoserModelPolicy", 329 description = description 330 ) { 331 sdkInt in 15..22 332 } 333 334 references += instanceFieldLeak( 335 "android.widget.ActivityChooserModel", "mActivityChoserModelPolicy", 336 description = description 337 ) 338 } 339 }, 340 341 MEDIA_PROJECTION_CALLBACK { 342 override fun add(references: MutableList<ReferenceMatcher>) { 343 references += instanceFieldLeak( 344 "android.media.projection.MediaProjection\$MediaProjectionCallback", 345 "this$0", description = """ 346 MediaProjectionCallback is held by another process, and holds on to MediaProjection 347 which has an activity as its context. 348 """.trimIndent() 349 ) { 350 sdkInt in 22..28 351 } 352 } 353 }, 354 355 SPEECH_RECOGNIZER { 356 override fun add( 357 references: MutableList<ReferenceMatcher> 358 ) { 359 references += instanceFieldLeak( 360 "android.speech.SpeechRecognizer\$InternalListener", "this$0", 361 description = "Prior to Android 5, SpeechRecognizer.InternalListener was a non static inner" 362 + " class and leaked the SpeechRecognizer which leaked an activity context." 363 + " Fixed in AOSP: https://github.com/android/platform_frameworks_base/commit" 364 + " /b37866db469e81aca534ff6186bdafd44352329b" 365 ) { 366 sdkInt < 21 367 } 368 } 369 }, 370 371 ACCOUNT_MANAGER__AMS_TASK__RESPONSE { 372 override fun add( 373 references: MutableList<ReferenceMatcher> 374 ) { 375 references += nativeGlobalVariableLeak( 376 "android.accounts.AccountManager\$AmsTask\$Response", 377 description = """ 378 AccountManager.AmsTask.Response is a stub, and as all stubs it's held in memory by a 379 native ref until the calling side gets GCed, which can happen long after the stub is no 380 longer of use. 381 https://issuetracker.google.com/issues/318303120 382 """.trimIndent() 383 ) { 384 sdkInt >= 5 385 } 386 } 387 }, 388 389 MEDIA_SCANNER_CONNECTION { 390 override fun add( 391 references: MutableList<ReferenceMatcher> 392 ) { 393 references += instanceFieldLeak( 394 "android.media.MediaScannerConnection", "mContext", 395 396 description = 397 "The static method MediaScannerConnection.scanFile() takes an activity context" 398 + " but the service might not disconnect after the activity has been destroyed." 399 + " Tracked here: https://code.google.com/p/android/issues/detail?id=173788" 400 + " Fix: Create an instance of MediaScannerConnection yourself and pass in the" 401 + " application context. Call connect() and disconnect() manually." 402 ) { 403 sdkInt <= 22 404 } 405 } 406 }, 407 408 USER_MANAGER__SINSTANCE { 409 override fun add( 410 references: MutableList<ReferenceMatcher> 411 ) { 412 references += instanceFieldLeak( 413 "android.os.UserManager", "mContext", 414 description = 415 "UserManager has a static sInstance field that creates an instance and caches it" 416 + " the first time UserManager.get() is called. This instance is created with the" 417 + " outer context (which is an activity base context)." 418 + " Tracked here: https://code.google.com/p/android/issues/detail?id=173789" 419 + " Introduced by: https://github.com/android/platform_frameworks_base/commit" 420 + "/27db46850b708070452c0ce49daf5f79503fbde6" 421 + " Fix: trigger a call to UserManager.get() in Application.onCreate(), so that the" 422 + " UserManager instance gets cached with a reference to the application context." 423 ) { 424 sdkInt in 18..25 425 } 426 } 427 }, 428 429 APP_WIDGET_HOST_CALLBACKS { 430 override fun add( 431 references: MutableList<ReferenceMatcher> 432 ) { 433 references += instanceFieldLeak( 434 "android.appwidget.AppWidgetHost\$Callbacks", "this$0", 435 description = 436 "android.appwidget.AppWidgetHost\$Callbacks is a stub and is held in memory native" 437 + " code. The reference to the `mContext` was not being cleared, which caused the" 438 + " Callbacks instance to retain this reference" 439 + " Fixed in AOSP: https://github.com/android/platform_frameworks_base/commit" 440 + "/7a96f3c917e0001ee739b65da37b2fadec7d7765" 441 ) { 442 sdkInt < 22 443 } 444 } 445 }, 446 447 AUDIO_MANAGER { 448 override fun add( 449 references: MutableList<ReferenceMatcher> 450 ) { 451 references += instanceFieldLeak( 452 "android.media.AudioManager$1", "this$0", 453 description = 454 "Prior to Android M, VideoView required audio focus from AudioManager and" 455 + " never abandoned it, which leaks the Activity context through the AudioManager." 456 + " The root of the problem is that AudioManager uses whichever" 457 + " context it receives, which in the case of the VideoView example is an Activity," 458 + " even though it only needs the application's context. The issue is fixed in" 459 + " Android M, and the AudioManager now uses the application's context." 460 + " Tracked here: https://code.google.com/p/android/issues/detail?id=152173" 461 + " Fix: https://gist.github.com/jankovd/891d96f476f7a9ce24e2" 462 ) { 463 sdkInt <= 22 464 } 465 } 466 }, 467 468 EDITTEXT_BLINK_MESSAGEQUEUE { 469 override fun add( 470 references: MutableList<ReferenceMatcher> 471 ) { 472 references += instanceFieldLeak( 473 "android.widget.Editor\$Blink", "this$0", 474 description = 475 "The EditText Blink of the Cursor is implemented using a callback and Messages," 476 + " which trigger the display of the Cursor. If an AlertDialog or DialogFragment that" 477 + " contains a blinking cursor is detached, a message is posted with a delay after the" 478 + " dialog has been closed and as a result leaks the Activity." 479 + " This can be fixed manually by calling TextView.setCursorVisible(false) in the" 480 + " dismiss() method of the dialog." 481 + " Tracked here: https://code.google.com/p/android/issues/detail?id=188551" 482 + " Fixed in AOSP: https://android.googlesource.com/platform/frameworks/base/+" 483 + "/5b734f2430e9f26c769d6af8ea5645e390fcf5af%5E%21/" 484 ) { 485 sdkInt <= 23 486 } 487 } 488 }, 489 490 CONNECTIVITY_MANAGER__SINSTANCE { 491 override fun add( 492 references: MutableList<ReferenceMatcher> 493 ) { 494 references += instanceFieldLeak( 495 "android.net.ConnectivityManager", "sInstance", 496 description = 497 "ConnectivityManager has a sInstance field that is set when the first" 498 + " ConnectivityManager instance is created. ConnectivityManager has a mContext field." 499 + " When calling activity.getSystemService(Context.CONNECTIVITY_SERVICE) , the first" 500 + " ConnectivityManager instance is created with the activity context and stored in" 501 + " sInstance. That activity context then leaks forever." 502 + " Until this is fixed, app developers can prevent this leak by making sure the" 503 + " ConnectivityManager is first created with an App Context. E.g. in some static" 504 + " init do: context.getApplicationContext()" 505 + ".getSystemService(Context.CONNECTIVITY_SERVICE)" 506 + " Tracked here: https://code.google.com/p/android/issues/detail?id=198852" 507 + " Introduced here: https://github.com/android/platform_frameworks_base/commit/" 508 + "e0bef71662d81caaaa0d7214fb0bef5d39996a69" 509 ) { 510 sdkInt <= 23 511 } 512 } 513 }, 514 515 ACCESSIBILITY_NODE_INFO__MORIGINALTEXT { 516 override fun add( 517 references: MutableList<ReferenceMatcher> 518 ) { 519 references += instanceFieldLeak( 520 "android.view.accessibility.AccessibilityNodeInfo", "mOriginalText", 521 description = 522 "AccessibilityNodeInfo has a static sPool of AccessibilityNodeInfo. When" 523 + " AccessibilityNodeInfo instances are released back in the pool," 524 + " AccessibilityNodeInfo.clear() does not clear the mOriginalText field, which" 525 + " causes spans to leak which in turns causes TextView.ChangeWatcher to leak and the" 526 + " whole view hierarchy. Introduced here: https://android.googlesource.com/platform/" 527 + "frameworks/base/+/193520e3dff5248ddcf8435203bf99d2ba667219%5E%21/core/java/" 528 + "android/view/accessibility/AccessibilityNodeInfo.java" 529 ) { 530 sdkInt in 26..27 531 } 532 } 533 }, 534 535 ASSIST_STRUCTURE { 536 override fun add(references: MutableList<ReferenceMatcher>) { 537 references += instanceFieldLeak( 538 "android.app.assist.AssistStructure\$ViewNodeText", "mText", 539 description = "AssistStructure (google assistant / autofill) holds on to text spannables" + 540 " on the screen. TextView.ChangeWatcher and android.widget.Editor end up in spans and" + 541 " typically hold on to the view hierarchy" 542 ) { 543 sdkInt >= 24 544 } 545 } 546 }, 547 548 ACCESSIBILITY_ITERATORS { 549 override fun add(references: MutableList<ReferenceMatcher>) { 550 references += instanceFieldLeak( 551 "android.widget.AccessibilityIterators\$LineTextSegmentIterator", "mLayout", 552 description = "AccessibilityIterators holds on to text layouts which can hold on to spans" + 553 " TextView.ChangeWatcher and android.widget.Editor end up in spans and" + 554 " typically hold on to the view hierarchy" 555 ) { 556 sdkInt == 27 557 } 558 } 559 }, 560 561 BIOMETRIC_PROMPT { 562 override fun add(references: MutableList<ReferenceMatcher>) { 563 references += instanceFieldLeak( 564 "android.hardware.biometrics.BiometricPrompt", "mFingerprintManager", 565 description = "BiometricPrompt holds on to a FingerprintManager which holds on to a " + 566 "destroyed activity." 567 ) { 568 sdkInt == 28 569 } 570 } 571 }, 572 573 MAGNIFIER { 574 override fun add(references: MutableList<ReferenceMatcher>) { 575 references += instanceFieldLeak( 576 "android.widget.Magnifier\$InternalPopupWindow", "mCallback", 577 description = "android.widget.Magnifier.InternalPopupWindow registers a frame callback" + 578 " on android.view.ThreadedRenderer.SimpleRenderer which holds it as a native" + 579 " reference. android.widget.Editor\$InsertionHandleView registers an" + 580 " OnOperationCompleteCallback on Magnifier.InternalPopupWindow. These references are" + 581 " held after the activity has been destroyed." 582 ) { 583 sdkInt == 28 584 } 585 } 586 }, 587 588 BACKDROP_FRAME_RENDERER__MDECORVIEW { 589 override fun add( 590 references: MutableList<ReferenceMatcher> 591 ) { 592 references += instanceFieldLeak( 593 "com.android.internal.policy.BackdropFrameRenderer", "mDecorView", 594 description = 595 "When BackdropFrameRenderer.releaseRenderer() is called, there's an unknown case" 596 + " where mRenderer becomes null but mChoreographer doesn't and the thread doesn't" 597 + " stop and ends up leaking mDecorView which itself holds on to a destroyed" 598 + " activity" 599 ) { 600 sdkInt in 24..26 601 } 602 } 603 }, 604 605 VIEWLOCATIONHOLDER_ROOT { 606 override fun add( 607 references: MutableList<ReferenceMatcher> 608 ) { 609 references += instanceFieldLeak( 610 "android.view.ViewGroup\$ViewLocationHolder", 611 "mRoot", 612 description = "In Android P, ViewLocationHolder has an mRoot field that is not cleared " + 613 "in its clear() method. Introduced in https://github.com/aosp-mirror" + 614 "/platform_frameworks_base/commit/86b326012813f09d8f1de7d6d26c986a909d Bug " + 615 "report: https://issuetracker.google.com/issues/112792715" 616 ) { 617 sdkInt == 28 618 } 619 } 620 }, 621 622 ACCESSIBILITY_NODE_ID_MANAGER { 623 override fun add(references: MutableList<ReferenceMatcher>) { 624 references += instanceFieldLeak( 625 "android.view.accessibility.AccessibilityNodeIdManager", "mIdsToViews", 626 description = """ 627 Android Q Beta added AccessibilityNodeIdManager which stores all views from their 628 onAttachedToWindow() call, until detached. Unfortunately it's possible to trigger 629 the view framework to call detach before attach (by having a view removing itself 630 from its parent in onAttach, which then causes AccessibilityNodeIdManager to keep 631 children view forever. Future releases of Q will hold weak references. 632 """.trimIndent() 633 ) { 634 sdkInt in 28..29 635 } 636 } 637 }, 638 639 TEXT_TO_SPEECH { 640 override fun add( 641 references: MutableList<ReferenceMatcher> 642 ) { 643 val description = 644 ("TextToSpeech.shutdown() does not release its references to context objects." + 645 " Furthermore, TextToSpeech instances cannot be garbage collected due to other process" + 646 " keeping the references, resulting the context objects leaked." + 647 " Developers might be able to mitigate the issue by passing application context" + 648 " to TextToSpeech constructor." + 649 " Tracked at: https://github.com/square/leakcanary/issues/1210 and" + 650 " https://issuetracker.google.com/issues/129250419") 651 references += instanceFieldLeak( 652 "android.speech.tts.TextToSpeech", "mContext", 653 description = description 654 ) { 655 sdkInt == 24 656 } 657 658 references += instanceFieldLeak( 659 "android.speech.tts.TtsEngines", "mContext", 660 description = description 661 ) { 662 sdkInt == 24 663 } 664 } 665 }, 666 667 CONTROLLED_INPUT_CONNECTION_WRAPPER { 668 override fun add(references: MutableList<ReferenceMatcher>) { 669 references += nativeGlobalVariableLeak( 670 "android.view.inputmethod.InputMethodManager\$ControlledInputConnectionWrapper", 671 description = """ 672 ControlledInputConnectionWrapper is held by a global variable in native code. 673 """.trimIndent() 674 ) 675 } 676 }, 677 678 TOAST_TN { 679 override fun add(references: MutableList<ReferenceMatcher>) { 680 references += nativeGlobalVariableLeak( 681 "android.widget.Toast\$TN", 682 description = """ 683 Toast.TN is held by a global variable in native code due to an IPC call to show the toast. 684 """.trimIndent() 685 ) 686 } 687 }, 688 689 APPLICATION_PACKAGE_MANAGER__HAS_SYSTEM_FEATURE_QUERY { 690 override fun add(references: MutableList<ReferenceMatcher>) { 691 references += instanceFieldLeak( 692 "android.app.ApplicationPackageManager\$HasSystemFeatureQuery", "this\$0", 693 description = """ 694 In Android 11 DP 2 ApplicationPackageManager.HasSystemFeatureQuery was an inner class. 695 Introduced in https://cs.android.com/android/_/android/platform/frameworks/base/+/89608118192580ffca026b5dacafa637a556d578 696 Fixed in https://cs.android.com/android/_/android/platform/frameworks/base/+/1f771846c51148b7cb6283e6dc82a216ffaa5353 697 Related blog: https://dev.to/pyricau/beware-packagemanager-leaks-223g 698 """.trimIndent() 699 ) { 700 sdkInt == 29 701 } 702 } 703 }, 704 705 COMPANION_DEVICE_SERVICE__STUB { 706 override fun add(references: MutableList<ReferenceMatcher>) { 707 references += instanceFieldLeak( 708 "android.companion.CompanionDeviceService\$Stub", "this\$0", 709 description = """ 710 Android 12 added android.companion.CompanionDeviceService, a bounded service extended by 711 applications to which the system binds. CompanionDeviceService.Stub is an inner class 712 that holds a reference to CompanionDeviceService, which itself holds a Stub instance 713 that's not nullified after the service is destroyed. 714 Introduced in https://android.googlesource.com/platform/frameworks/base/+/df69bbaf29e41d9df105612500c27be730feedfc 715 Source code: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/companion/CompanionDeviceService.java 716 """.trimIndent() 717 ) { 718 sdkInt == 31 719 } 720 } 721 }, 722 723 RENDER_NODE_ANIMATOR { 724 override fun add(references: MutableList<ReferenceMatcher>) { 725 references += nativeGlobalVariableLeak( 726 "android.graphics.animation.RenderNodeAnimator", 727 description = """ 728 When a view is detached while a ripple animation is still playing on it, the native code 729 doesn't properly end the RenderNodeAnimator, i.e. it doesn't call 730 RenderNodeAnimator.callOnFinished and doesn't let go of the native ref, leading to a 731 leak of the detached animated view. 732 Tracked at: https://issuetracker.google.com/issues/229136453 733 """.trimIndent() 734 ) { 735 sdkInt in 31..32 736 } 737 } 738 }, 739 740 PLAYER_BASE { 741 override fun add( 742 references: MutableList<ReferenceMatcher> 743 ) { 744 references += nativeGlobalVariableLeak( 745 "android.media.PlayerBase\$1", 746 description = """ 747 PlayerBase$1 implements IAppOpsCallback as an inner class and is held by a native 748 ref, preventing subclasses of PlayerBase to be GC'd. 749 Introduced in API 24: https://cs.android.com/android/_/android/platform/frameworks/base/+/3c86a343dfca1b9e2e28c240dc894f60709e392c 750 Fixed in API 28: https://cs.android.com/android/_/android/platform/frameworks/base/+/aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ff 751 """.trimIndent() 752 ) { 753 sdkInt in 24..27 754 } 755 } 756 }, 757 758 WINDOW_ON_BACK_INVOKED_DISPATCHER__STUB { 759 override fun add( 760 references: MutableList<ReferenceMatcher> 761 ) { 762 references += instanceFieldLeak( 763 "android.window.WindowOnBackInvokedDispatcher\$OnBackInvokedCallbackWrapper", "mCallback", 764 description = """ 765 WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper is an IPC stub that holds a 766 reference to a callback which itself holds a view root. Another process is keeping the 767 stub alive long after the view root has been detached. 768 Tracked here: https://issuetracker.google.com/issues/229007483 769 """.trimIndent() 770 ) { 771 // Detected in Android 13 DP2, should be fixed in the next release. 772 sdkInt == 32 && id == "TPP2.220218.008" 773 } 774 } 775 }, 776 777 CONNECTIVITY_MANAGER_CALLBACK_HANDLER { 778 override fun add( 779 references: MutableList<ReferenceMatcher> 780 ) { 781 references += instanceFieldLeak( 782 "ConnectivityManager\$CallbackHandler", "this\$0", 783 description = """ 784 ConnectivityManager.CallbackHandler instances can be held statically and hold 785 a reference to ConnectivityManager instances created with a local context (e.g. activity). 786 Filed: https://issuetracker.google.com/issues/258053962 787 Fixed in API 34. 788 """.trimIndent() 789 ) { 790 sdkInt == 33 791 } 792 } 793 }, 794 795 HOST_ADPU_SERVICE_MSG_HANDLER { 796 override fun add( 797 references: MutableList<ReferenceMatcher> 798 ) { 799 references += instanceFieldLeak( 800 "android.nfc.cardemulation.HostApduService\$MsgHandler", "this\$0", 801 description = """ 802 Destroyed HostApduService instances are held by a handler instance that lives longer 803 than the service. 804 Report: https://github.com/square/leakcanary/issues/2390 805 """.trimIndent() 806 ) { 807 sdkInt in 29..33 808 } 809 } 810 }, 811 812 APP_OPS_MANAGER__CALLBACK_STUB { 813 override fun add( 814 references: MutableList<ReferenceMatcher> 815 ) { 816 references += nativeGlobalVariableLeak( 817 "android.app.AppOpsManager\$3", 818 description = """ 819 Fix: Update androidx.core:core to 1.10.0-alpha01 or greater as it includes an Android 12 820 fix for this leak on Android 12, see https://github.com/androidx/androidx/pull/435 . 821 AppOpsManager\$3 implements IAppOpsActiveCallback.Stub and is held by a native ref long 822 until the calling side gets GCed, which can happen long after the stub is no longer of 823 use. 824 """.trimIndent() 825 ) { 826 sdkInt in 31..32 827 } 828 } 829 }, 830 831 VIEW_GROUP__M_PRE_SORTED_CHILDREN { 832 override fun add( 833 references: MutableList<ReferenceMatcher> 834 ) { 835 references += instanceFieldLeak( 836 "android.view.ViewGroup", "mPreSortedChildren", 837 description = """ 838 ViewGroup.mPreSortedChildren is used as a temporary list but not cleared after being 839 used. 840 Report: https://issuetracker.google.com/issues/178029590 841 Fix: https://cs.android.com/android/_/android/platform/frameworks/base/+/73590c7751b9185137de962ba9ad9ff5a6e11e5d 842 """.trimIndent() 843 ) { 844 sdkInt == 30 845 } 846 } 847 }, 848 849 VIEW_GROUP__M_CURRENT_DRAG_CHILD { 850 override fun add( 851 references: MutableList<ReferenceMatcher> 852 ) { 853 references += instanceFieldLeak( 854 "android.view.ViewGroup", "mCurrentDragChild", 855 description = """ 856 ViewGroup.mCurrentDragChild keeps a reference to a view that was dragged after that view 857 has been detached. 858 Report: https://issuetracker.google.com/issues/170276524 859 """.trimIndent() 860 ) { 861 sdkInt in 29..30 862 } 863 } 864 }, 865 866 VIEW_TOOLTIP_CALLBACK { 867 override fun add( 868 references: MutableList<ReferenceMatcher> 869 ) { 870 // Note: the lambda order changes every release, so ideally we'd pull the dex code for 871 // every release and look at the exact class name for the this::showHoverTooltip and the 872 // this::hideTooltip lambda references in View.java . That's too much work, so we'll just 873 // rely on reports from the field: 874 // - API 33: android.view.View$$ExternalSyntheticLambda3.f$0 875 references += instanceFieldLeak( 876 "android.view.View\$\$ExternalSyntheticLambda3", "f\$0", 877 description = """ 878 When a View has tooltip text set, every hover event will fire a callback 879 to hide the tooltip after a 15 second timeout. Since the callback holds 880 a reference to the View, it will leak the View for that duration after 881 the Activity is finished or the View is removed. 882 https://cs.android.com/android/_/android/platform/frameworks/base/+/708dbe80902b963388c412f670c56ae00953273a 883 """.trimIndent() 884 ) { 885 sdkInt in 26..34 886 } 887 } 888 }, 889 890 ACTIVITY_TRANSITION_STATE__M_EXITING_TO_VIEW { 891 override fun add( 892 references: MutableList<ReferenceMatcher> 893 ) { 894 references += instanceFieldLeak( 895 "android.app.ActivityTransitionState", "mExitingToView", 896 description = """ 897 Shared element transition leak the view that was used in the transition. 898 Report: https://issuetracker.google.com/issues/141132765 899 """.trimIndent() 900 ) { 901 sdkInt in 28..29 902 } 903 } 904 }, 905 906 ANIMATION_HANDLER__ANIMATOR_REQUESTORS { 907 override fun add( 908 references: MutableList<ReferenceMatcher> 909 ) { 910 references += instanceFieldLeak( 911 "android.animation.AnimationHandler", "mAnimatorRequestors", 912 description = """ 913 AnimationHandler is a singleton holding an activity ViewRootImpl requestor after the 914 activity has been destroyed. 915 Report: https://issuetracker.google.com/issues/258534826 916 """.trimIndent() 917 ) { 918 sdkInt == 33 919 } 920 } 921 }, 922 923 FLIPPER__APPLICATION_DESCRIPTOR { 924 override fun add( 925 references: MutableList<ReferenceMatcher> 926 ) { 927 references += staticFieldLeak( 928 "com.facebook.flipper.plugins.inspector.descriptors.ApplicationDescriptor", 929 "editedDelegates", 930 description = """ 931 Flipper's ApplicationDescriptor leaks root views after they've been detached. 932 https://github.com/facebook/flipper/issues/4270 933 """.trimIndent() 934 ) 935 } 936 }, 937 938 AW_CONTENTS__A0 { 939 override fun add(references: MutableList<ReferenceMatcher>) { 940 staticFieldLeak( 941 "org.chromium.android_webview.AwContents", 942 "A0", 943 description = """ 944 WindowAndroidWrapper has a strong ref to the context key so this breaks the WeakHashMap 945 contracts and WeakHashMap is unable to perform its job of auto cleaning. 946 https://github.com/square/leakcanary/issues/2538 947 """.trimIndent() 948 ) 949 } 950 }, 951 952 AW_CONTENTS_POSTED_CALLBACK { 953 override fun add(references: MutableList<ReferenceMatcher>) { 954 val description = "Android System WebView leak: " + 955 "https://bugs.chromium.org/p/chromium/issues/detail?id=1499154" 956 instanceFieldLeak( 957 "WV.R9", 958 "e", 959 description 960 ) 961 instanceFieldLeak( 962 "WV.a6", 963 "c", 964 description 965 ) 966 instanceFieldLeak( 967 "WV.H5", 968 "c", 969 description 970 ) 971 instanceFieldLeak( 972 "WV.Y9", 973 "e", 974 description 975 ) 976 instanceFieldLeak( 977 "WV.U4", 978 "c", 979 description 980 ) 981 } 982 }, 983 984 JOB_SERVICE { 985 override fun add( 986 references: MutableList<ReferenceMatcher> 987 ) { 988 AndroidReferenceMatchers.nativeGlobalVariableLeak( 989 className = "android.app.job.JobService\$1", 990 description = """ 991 JobService used to be leaked via a binder stub. 992 Fix: https://cs.android.com/android/_/android/platform/frameworks/base/+/0796e9fb3dc2dd03fa5ff2053c63f14861cffa2f 993 """.trimIndent() 994 ) { sdkInt < 24 } 995 } 996 }, 997 998 DREAM_SERVICE { 999 override fun add(references: MutableList<ReferenceMatcher>) { 1000 AndroidReferenceMatchers.nativeGlobalVariableLeak( 1001 className = "android.service.dreams.DreamService\$1", 1002 description = """ 1003 DreamService leaks a binder stub. 1004 https://github.com/square/leakcanary/issues/2534 1005 """.trimIndent() 1006 ) { sdkInt >= 33 } 1007 } 1008 }, 1009 1010 // ######## Manufacturer specific known leaks ######## 1011 1012 // SAMSUNG 1013 1014 SPEN_GESTURE_MANAGER { 1015 override fun add( 1016 references: MutableList<ReferenceMatcher> 1017 ) { 1018 references += staticFieldLeak( 1019 "com.samsung.android.smartclip.SpenGestureManager", "mContext", 1020 description = 1021 "SpenGestureManager has a static mContext field that leaks a reference to the" + " activity. Yes, a STATIC mContext field." 1022 ) { 1023 manufacturer == SAMSUNG && sdkInt == 19 1024 } 1025 } 1026 }, 1027 1028 CLIPBOARD_UI_MANAGER__SINSTANCE { 1029 override fun add( 1030 references: MutableList<ReferenceMatcher> 1031 ) { 1032 references += instanceFieldLeak( 1033 "android.sec.clipboard.ClipboardUIManager", "mContext", 1034 description = 1035 "ClipboardUIManager is a static singleton that leaks an activity context." 1036 + " Fix: trigger a call to ClipboardUIManager.getInstance() in Application.onCreate()" 1037 + " , so that the ClipboardUIManager instance gets cached with a reference to the" 1038 + " application context. Example: https://gist.github.com/cypressious/" 1039 + "91c4fb1455470d803a602838dfcd5774" 1040 ) { 1041 manufacturer == SAMSUNG && sdkInt in 19..21 1042 } 1043 } 1044 }, 1045 1046 SEM_CLIPBOARD_MANAGER__MCONTEXT { 1047 override fun add( 1048 references: MutableList<ReferenceMatcher> 1049 ) { 1050 val description = """ 1051 SemClipboardManager inner classes are held by native references due to IPC calls 1052 """.trimIndent() 1053 references += nativeGlobalVariableLeak( 1054 "com.samsung.android.content.clipboard.SemClipboardManager$1", description 1055 ) { 1056 manufacturer == SAMSUNG && sdkInt in 19..28 1057 } 1058 references += nativeGlobalVariableLeak( 1059 "com.samsung.android.content.clipboard.SemClipboardManager$3", description 1060 ) { 1061 manufacturer == SAMSUNG && sdkInt in 19..28 1062 } 1063 } 1064 }, 1065 1066 CLIPBOARD_EX_MANAGER { 1067 override fun add( 1068 references: MutableList<ReferenceMatcher> 1069 ) { 1070 references += instanceFieldLeak( 1071 "android.sec.clipboard.ClipboardExManager", "mContext", 1072 description = "android.sec.clipboard.ClipboardExManager\$IClipboardDataPasteEventImpl\$1" + 1073 " is a native callback that holds IClipboardDataPasteEventImpl which holds" + 1074 " ClipboardExManager which has a destroyed activity as mContext" 1075 ) { 1076 manufacturer == SAMSUNG && sdkInt == 23 1077 } 1078 references += instanceFieldLeak( 1079 "android.sec.clipboard.ClipboardExManager", "mPersonaManager", 1080 description = "android.sec.clipboard.ClipboardExManager\$IClipboardDataPasteEventImpl\$1" + 1081 " is a native callback that holds IClipboardDataPasteEventImpl which holds" + 1082 " ClipboardExManager which holds PersonaManager which has a destroyed activity as" + 1083 " mContext" 1084 ) { 1085 manufacturer == SAMSUNG && sdkInt == 23 1086 } 1087 references += instanceFieldLeak( 1088 "android.widget.TextView\$IClipboardDataPasteEventImpl", "this\$0", 1089 description = "TextView\$IClipboardDataPasteEventImpl\$1 is held by a native ref, and" + 1090 " IClipboardDataPasteEventImpl ends up leaking a detached textview" 1091 ) { 1092 manufacturer == SAMSUNG && sdkInt == 22 1093 } 1094 } 1095 }, 1096 1097 SEM_EMERGENCY_MANAGER__MCONTEXT { 1098 override fun add( 1099 references: MutableList<ReferenceMatcher> 1100 ) { 1101 references += instanceFieldLeak( 1102 "com.samsung.android.emergencymode.SemEmergencyManager", "mContext", 1103 description = 1104 "SemEmergencyManager is a static singleton that leaks a DecorContext." + 1105 " Fix: https://gist.github.com/jankovd/a210460b814c04d500eb12025902d60d" 1106 ) { 1107 manufacturer == SAMSUNG && sdkInt in 19..24 1108 } 1109 } 1110 }, 1111 1112 SEM_PERSONA_MANAGER { 1113 override fun add(references: MutableList<ReferenceMatcher>) { 1114 references += instanceFieldLeak( 1115 "com.samsung.android.knox.SemPersonaManager", "mContext" 1116 ) { 1117 manufacturer == SAMSUNG && sdkInt == 24 1118 } 1119 } 1120 }, 1121 1122 SEM_APP_ICON_SOLUTION { 1123 override fun add(references: MutableList<ReferenceMatcher>) { 1124 references += instanceFieldLeak( 1125 "android.app.SemAppIconSolution", "mContext" 1126 ) { 1127 manufacturer == SAMSUNG && sdkInt in 28..29 1128 } 1129 } 1130 }, 1131 1132 AW_RESOURCE__SRESOURCES { 1133 override fun add( 1134 references: MutableList<ReferenceMatcher> 1135 ) { 1136 // AwResource#setResources() is called with resources that hold a reference to the 1137 // activity context (instead of the application context) and doesn't clear it. 1138 // Not sure what's going on there, input welcome. 1139 references += staticFieldLeak( 1140 "com.android.org.chromium.android_webview.AwResource", "sResources" 1141 ) { 1142 manufacturer == SAMSUNG && sdkInt == 19 1143 } 1144 } 1145 }, 1146 1147 TEXT_VIEW__MLAST_HOVERED_VIEW { 1148 override fun add( 1149 references: MutableList<ReferenceMatcher> 1150 ) { 1151 references += staticFieldLeak( 1152 "android.widget.TextView", "mLastHoveredView", 1153 description = 1154 "mLastHoveredView is a static field in TextView that leaks the last hovered view." 1155 ) { 1156 manufacturer == SAMSUNG && sdkInt in 19..31 1157 } 1158 } 1159 }, 1160 1161 PERSONA_MANAGER { 1162 override fun add( 1163 references: MutableList<ReferenceMatcher> 1164 ) { 1165 references += instanceFieldLeak( 1166 "android.os.PersonaManager", "mContext", 1167 description = 1168 "android.app.LoadedApk.mResources has a reference to" 1169 + " android.content.res.Resources.mPersonaManager which has a reference to" 1170 + " android.os.PersonaManager.mContext which is an activity." 1171 ) { 1172 manufacturer == SAMSUNG && sdkInt == 19 1173 } 1174 } 1175 }, 1176 1177 RESOURCES__MCONTEXT { 1178 override fun add( 1179 references: MutableList<ReferenceMatcher> 1180 ) { 1181 references += instanceFieldLeak( 1182 "android.content.res.Resources", "mContext", 1183 description = 1184 "In AOSP the Resources class does not have a context." 1185 + " Here we have ZygoteInit.mResources (static field) holding on to a Resources" 1186 + " instance that has a context that is the activity." 1187 + " Observed here: https://github.com/square/leakcanary/issues/1#issue-74450184" 1188 ) { 1189 manufacturer == SAMSUNG && sdkInt == 19 1190 } 1191 } 1192 }, 1193 1194 VIEW_CONFIGURATION__MCONTEXT { 1195 override fun add( 1196 references: MutableList<ReferenceMatcher> 1197 ) { 1198 references += instanceFieldLeak( 1199 "android.view.ViewConfiguration", "mContext", 1200 description = 1201 "In AOSP the ViewConfiguration class does not have a context." 1202 + " Here we have ViewConfiguration.sConfigurations (static field) holding on to a" 1203 + " ViewConfiguration instance that has a context that is the activity." 1204 + " Observed here: https://github.com/square/leakcanary/issues" 1205 + "/1#issuecomment-100324683" 1206 ) { 1207 manufacturer == SAMSUNG && sdkInt == 19 1208 } 1209 } 1210 }, 1211 1212 AUDIO_MANAGER__MCONTEXT_STATIC { 1213 override fun add( 1214 references: MutableList<ReferenceMatcher> 1215 ) { 1216 references += staticFieldLeak( 1217 "android.media.AudioManager", "mContext_static", 1218 description = 1219 "Samsung added a static mContext_static field to AudioManager, holds a reference" 1220 + " to the activity." 1221 + " Observed here: https://github.com/square/leakcanary/issues/32" 1222 ) { 1223 manufacturer == SAMSUNG && sdkInt == 19 1224 } 1225 } 1226 }, 1227 1228 ACTIVITY_MANAGER_MCONTEXT { 1229 override fun add( 1230 references: MutableList<ReferenceMatcher> 1231 ) { 1232 references += staticFieldLeak( 1233 "android.app.ActivityManager", "mContext", 1234 description = 1235 "Samsung added a static mContext field to ActivityManager, holds a reference" 1236 + " to the activity." 1237 + " Observed here: https://github.com/square/leakcanary/issues/177 Fix in comment:" 1238 + " https://github.com/square/leakcanary/issues/177#issuecomment-222724283" 1239 ) { 1240 manufacturer == SAMSUNG && sdkInt in 22..23 1241 } 1242 } 1243 }, 1244 1245 STATIC_MTARGET_VIEW { 1246 override fun add( 1247 references: MutableList<ReferenceMatcher> 1248 ) { 1249 references += staticFieldLeak( 1250 "android.widget.TextView", "mTargetView", 1251 description = 1252 "Samsung added a static mTargetView field to TextView which holds on to detached views." 1253 ) { 1254 manufacturer == SAMSUNG && sdkInt == 27 1255 } 1256 } 1257 }, 1258 1259 MULTI_WINDOW_DECOR_SUPPORT__MWINDOW { 1260 override fun add( 1261 references: MutableList<ReferenceMatcher> 1262 ) { 1263 references += instanceFieldLeak( 1264 "com.android.internal.policy.MultiWindowDecorSupport", "mWindow", 1265 description = """DecorView isn't leaking but its mDecorViewSupport field holds 1266 |a MultiWindowDecorSupport which has a mWindow field which holds a leaking PhoneWindow. 1267 |DecorView.mDecorViewSupport doesn't exist in AOSP. 1268 |Filed here: https://github.com/square/leakcanary/issues/1819 1269 """.trimMargin() 1270 ) { 1271 manufacturer == SAMSUNG && sdkInt in 26..29 1272 } 1273 } 1274 }, 1275 1276 IMM_CURRENT_INPUT_CONNECTION { 1277 override fun add(references: MutableList<ReferenceMatcher>) { 1278 references += instanceFieldLeak( 1279 "android.view.inputmethod.InputMethodManager", "mCurrentInputConnection", 1280 description = """ 1281 InputMethodManager keeps its EditableInputConnection after the activity has been 1282 destroyed. 1283 Filed here: https://github.com/square/leakcanary/issues/2300 1284 """.trimIndent() 1285 ) { 1286 manufacturer == SAMSUNG && sdkInt in 28..30 1287 } 1288 } 1289 }, 1290 1291 // OTHER MANUFACTURERS 1292 1293 GESTURE_BOOST_MANAGER { 1294 override fun add( 1295 references: MutableList<ReferenceMatcher> 1296 ) { 1297 references += staticFieldLeak( 1298 "android.gestureboost.GestureBoostManager", "mContext", 1299 description = 1300 "GestureBoostManager is a static singleton that leaks an activity context." + 1301 " Fix: https://github.com/square/leakcanary/issues/696#issuecomment-296420756" 1302 ) { 1303 manufacturer == HUAWEI && sdkInt in 24..25 1304 } 1305 } 1306 }, 1307 1308 BUBBLE_POPUP_HELPER__SHELPER { 1309 override fun add( 1310 references: MutableList<ReferenceMatcher> 1311 ) { 1312 references += staticFieldLeak( 1313 "android.widget.BubblePopupHelper", "sHelper", 1314 description = 1315 "A static helper for EditText bubble popups leaks a reference to the latest" + " focused view." 1316 ) { 1317 manufacturer == LG && sdkInt in 19..22 1318 } 1319 } 1320 }, 1321 1322 LGCONTEXT__MCONTEXT { 1323 override fun add( 1324 references: MutableList<ReferenceMatcher> 1325 ) { 1326 references += instanceFieldLeak( 1327 "com.lge.systemservice.core.LGContext", "mContext", 1328 description = "LGContext is a static singleton that leaks an activity context." 1329 ) { 1330 manufacturer == LG && sdkInt == 21 1331 } 1332 } 1333 }, 1334 1335 SMART_COVER_MANAGER { 1336 override fun add( 1337 references: MutableList<ReferenceMatcher> 1338 ) { 1339 references += instanceFieldLeak( 1340 "com.lge.systemservice.core.SmartCoverManager", "mContext", 1341 description = "SmartCoverManager\$CallbackRegister is a callback held by a native ref," + 1342 " and SmartCoverManager ends up leaking an activity context." 1343 ) { 1344 manufacturer == LG && sdkInt == 27 1345 } 1346 } 1347 }, 1348 1349 IMM_LAST_FOCUS_VIEW { 1350 override fun add( 1351 references: MutableList<ReferenceMatcher> 1352 ) { 1353 references += instanceFieldLeak( 1354 "android.view.inputmethod.InputMethodManager", "mLastFocusView", 1355 description = """ 1356 InputMethodManager has a mLastFocusView field that doesn't get cleared when the last 1357 focused view becomes detached. 1358 """.trimIndent() 1359 ) { 1360 manufacturer == LG && sdkInt == 29 1361 } 1362 } 1363 }, 1364 1365 MAPPER_CLIENT { 1366 override fun add( 1367 references: MutableList<ReferenceMatcher> 1368 ) { 1369 references += instanceFieldLeak( 1370 "com.nvidia.ControllerMapper.MapperClient\$ServiceClient", "this$0", 1371 description = 1372 "Not sure exactly what ControllerMapper is about, but there is an anonymous" 1373 + " Handler in ControllerMapper.MapperClient.ServiceClient, which leaks" 1374 + " ControllerMapper.MapperClient which leaks the activity context." 1375 ) { 1376 manufacturer == NVIDIA && sdkInt == 19 1377 } 1378 } 1379 }, 1380 1381 SYSTEM_SENSOR_MANAGER__MAPPCONTEXTIMPL { 1382 override fun add( 1383 references: MutableList<ReferenceMatcher> 1384 ) { 1385 references += staticFieldLeak( 1386 "android.hardware.SystemSensorManager", "mAppContextImpl", 1387 description = 1388 "SystemSensorManager stores a reference to context" 1389 + " in a static field in its constructor." 1390 + " Fix: use application context to get SensorManager" 1391 ) { 1392 (manufacturer == LENOVO && sdkInt == 19) || (manufacturer == VIVO && sdkInt == 22) 1393 } 1394 } 1395 }, 1396 1397 INSTRUMENTATION_RECOMMEND_ACTIVITY { 1398 override fun add( 1399 references: MutableList<ReferenceMatcher> 1400 ) { 1401 references += staticFieldLeak( 1402 "android.app.Instrumentation", "mRecommendActivity", 1403 description = 1404 "Instrumentation would leak com.android.internal.app.RecommendActivity (in" 1405 + " framework.jar) in Meizu FlymeOS 4.5 and above, which is based on Android 5.0 and " 1406 + " above" 1407 ) { 1408 manufacturer == MEIZU && sdkInt in 21..22 1409 } 1410 } 1411 }, 1412 1413 DEVICE_POLICY_MANAGER__SETTINGS_OBSERVER { 1414 override fun add( 1415 references: MutableList<ReferenceMatcher> 1416 ) { 1417 references += instanceFieldLeak( 1418 "android.app.admin.DevicePolicyManager\$SettingsObserver", "this$0", 1419 description = 1420 "DevicePolicyManager keeps a reference to the context it has been created with" 1421 + " instead of extracting the application context. In this Motorola build," 1422 + " DevicePolicyManager has an inner SettingsObserver class that is a content" 1423 + " observer, which is held into memory by a binder transport object." 1424 ) { 1425 manufacturer == MOTOROLA && sdkInt in 19..22 1426 } 1427 } 1428 }, 1429 1430 EXTENDED_STATUS_BAR_MANAGER { 1431 override fun add( 1432 references: MutableList<ReferenceMatcher> 1433 ) { 1434 references += instanceFieldLeak( 1435 "android.app.ExtendedStatusBarManager", "mContext", 1436 description = 1437 """ 1438 ExtendedStatusBarManager has a mContext field which references a decor context which 1439 references a destroyed activity. 1440 """.trimIndent() 1441 ) { 1442 manufacturer == SHARP && sdkInt >= 30 1443 } 1444 } 1445 }, 1446 1447 OEM_SCENE_CALL_BLOCKER { 1448 override fun add( 1449 references: MutableList<ReferenceMatcher> 1450 ) { 1451 references += staticFieldLeak( 1452 "com.oneplus.util.OemSceneCallBlocker", "sContext", 1453 description = 1454 """ 1455 OemSceneCallBlocker has a sContext static field which holds on to an activity instance. 1456 """.trimIndent() 1457 ) { 1458 manufacturer == ONE_PLUS && sdkInt == 28 1459 } 1460 } 1461 }, 1462 1463 PERF_MONITOR_LAST_CALLBACK { 1464 override fun add( 1465 references: MutableList<ReferenceMatcher> 1466 ) { 1467 references += staticFieldLeak( 1468 "android.os.PerfMonitor", "mLastCallback", 1469 description = 1470 """ 1471 PerfMonitor has a mLastCallback static field which holds on to View.PerformClick. 1472 """.trimIndent() 1473 ) { 1474 manufacturer == ONE_PLUS && sdkInt == 30 1475 } 1476 } 1477 }, 1478 1479 RAZER_TEXT_KEY_LISTENER__MCONTEXT { 1480 override fun add( 1481 references: MutableList<ReferenceMatcher> 1482 ) { 1483 references += instanceFieldLeak( 1484 "android.text.method.TextKeyListener", "mContext", 1485 description = 1486 """ 1487 In AOSP, TextKeyListener instances are held in a TextKeyListener.sInstances static 1488 array. The Razer implementation added a mContext field, creating activity leaks. 1489 """.trimIndent() 1490 ) { 1491 manufacturer == RAZER && sdkInt == 28 1492 } 1493 } 1494 }, 1495 1496 XIAMI__RESOURCES_IMPL { 1497 override fun add(references: MutableList<ReferenceMatcher>) { 1498 references += staticFieldLeak( 1499 "android.content.res.ResourcesImpl", "mAppContext", 1500 description = """ 1501 A fork of Android added a static mAppContext field to the ResourcesImpl class 1502 and that field ends up referencing lower contexts (e.g. service). Several Android 1503 manufacturers seem to be using the same broken Android fork sources. 1504 """.trimIndent() 1505 ) { 1506 listOf( 1507 HMD_GLOBAL, 1508 INFINIX, 1509 LENOVO, 1510 XIAOMI, 1511 TES, 1512 REALME 1513 ).contains(manufacturer) && 1514 sdkInt >= 30 1515 } 1516 } 1517 }, 1518 1519 // ######## Ignored references (not leaks) ######## 1520 1521 REFERENCES { 1522 override fun add( 1523 references: MutableList<ReferenceMatcher> 1524 ) { 1525 references += ignoredInstanceField(WeakReference::class.java.name, "referent") 1526 references += ignoredInstanceField("leakcanary.KeyedWeakReference", "referent") 1527 references += ignoredInstanceField(SoftReference::class.java.name, "referent") 1528 references += ignoredInstanceField(PhantomReference::class.java.name, "referent") 1529 references += ignoredInstanceField("java.lang.ref.Finalizer", "prev") 1530 references += ignoredInstanceField("java.lang.ref.Finalizer", "element") 1531 references += ignoredInstanceField("java.lang.ref.Finalizer", "next") 1532 references += ignoredInstanceField("java.lang.ref.FinalizerReference", "prev") 1533 references += ignoredInstanceField("java.lang.ref.FinalizerReference", "element") 1534 references += ignoredInstanceField("java.lang.ref.FinalizerReference", "next") 1535 references += ignoredInstanceField("sun.misc.Cleaner", "prev") 1536 references += ignoredInstanceField("sun.misc.Cleaner", "next") 1537 } 1538 }, 1539 1540 FINALIZER_WATCHDOG_DAEMON { 1541 override fun add( 1542 references: MutableList<ReferenceMatcher> 1543 ) { 1544 // If the FinalizerWatchdogDaemon thread is on the shortest path, then there was no other 1545 // reference to the object and it was about to be GCed. 1546 references += ignoredJavaLocal("FinalizerWatchdogDaemon") 1547 } 1548 }, 1549 1550 MAIN { 1551 override fun add( 1552 references: MutableList<ReferenceMatcher> 1553 ) { 1554 // The main thread stack is ever changing so local variables aren't likely to hold references 1555 // for long. If this is on the shortest path, it's probably that there's a longer path with 1556 // a real leak. 1557 references += ignoredJavaLocal("main") 1558 } 1559 }, 1560 1561 LEAK_CANARY_THREAD { 1562 override fun add( 1563 references: MutableList<ReferenceMatcher> 1564 ) { 1565 references += ignoredJavaLocal(LEAK_CANARY_THREAD_NAME) 1566 } 1567 }, 1568 1569 LEAK_CANARY_HEAP_DUMPER { 1570 override fun add(references: MutableList<ReferenceMatcher>) { 1571 // Holds on to the resumed activity (which is never destroyed), so this will not cause leaks 1572 // but may surface on the path when a resumed activity holds on to destroyed objects. 1573 // Would have a path that doesn't include LeakCanary instead. 1574 references += ignoredInstanceField( 1575 "leakcanary.internal.InternalLeakCanary", "resumedActivity" 1576 ) 1577 } 1578 }, 1579 1580 LEAK_CANARY_INTERNAL { 1581 override fun add(references: MutableList<ReferenceMatcher>) { 1582 references += ignoredInstanceField("leakcanary.internal.InternalLeakCanary", "application") 1583 } 1584 }, 1585 1586 EVENT_RECEIVER__MMESSAGE_QUEUE { 1587 override fun add( 1588 references: MutableList<ReferenceMatcher> 1589 ) { 1590 // DisplayEventReceiver keeps a reference message queue object so that it is not GC'd while 1591 // the native peer of the receiver is using them. 1592 // The main thread message queue is held on by the main Looper, but that might be a longer 1593 // path. Let's not confuse people with a shorter path that is less meaningful. 1594 references += ignoredInstanceField( 1595 "android.view.Choreographer\$FrameDisplayEventReceiver", "mMessageQueue" 1596 ) 1597 } 1598 }, 1599 1600 ; 1601 1602 internal abstract fun add(references: MutableList<ReferenceMatcher>) 1603 1604 companion object { 1605 private const val LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump" 1606 const val SAMSUNG = "samsung" 1607 const val MOTOROLA = "motorola" 1608 const val LENOVO = "LENOVO" 1609 const val LG = "LGE" 1610 const val NVIDIA = "NVIDIA" 1611 const val MEIZU = "Meizu" 1612 const val ONE_PLUS = "OnePlus" 1613 const val HUAWEI = "HUAWEI" 1614 const val VIVO = "vivo" 1615 const val RAZER = "Razer" 1616 const val SHARP = "SHARP" 1617 const val XIAOMI = "Xiaomi" 1618 const val HMD_GLOBAL = "HMD Global" 1619 const val INFINIX = "INFINIX" 1620 const val TES = "TES" 1621 const val REALME = "realme" 1622 1623 /** 1624 * Returns a list of [ReferenceMatcher] that only contains [IgnoredReferenceMatcher] and no 1625 * [LibraryLeakReferenceMatcher]. 1626 */ 1627 @JvmStatic 1628 val ignoredReferencesOnly: List<ReferenceMatcher> 1629 get() = buildKnownReferences( 1630 EnumSet.of( 1631 REFERENCES, 1632 FINALIZER_WATCHDOG_DAEMON, 1633 MAIN, 1634 LEAK_CANARY_THREAD, 1635 EVENT_RECEIVER__MMESSAGE_QUEUE 1636 ) 1637 ) 1638 1639 /** 1640 * @see [AndroidReferenceMatchers] 1641 */ 1642 @JvmStatic 1643 val appDefaults: List<ReferenceMatcher> 1644 get() = buildKnownReferences(EnumSet.allOf(AndroidReferenceMatchers::class.java)) 1645 1646 /** 1647 * Builds a list of [ReferenceMatcher] from the [referenceMatchers] set of 1648 * [AndroidReferenceMatchers]. 1649 */ 1650 @JvmStatic 1651 fun buildKnownReferences(referenceMatchers: Set<AndroidReferenceMatchers>): List<ReferenceMatcher> { 1652 val resultSet = mutableListOf<ReferenceMatcher>() 1653 referenceMatchers.forEach { 1654 it.add(resultSet) 1655 } 1656 return resultSet 1657 } 1658 1659 private val ALWAYS: AndroidBuildMirror.() -> Boolean = { 1660 true 1661 } 1662 1663 /** 1664 * Creates a [LibraryLeakReferenceMatcher] that matches a [StaticFieldPattern]. 1665 * [description] should convey what we know about this library leak. 1666 */ 1667 @JvmStatic 1668 fun staticFieldLeak( 1669 className: String, 1670 fieldName: String, 1671 description: String = "", 1672 patternApplies: AndroidBuildMirror.() -> Boolean = ALWAYS 1673 ): LibraryLeakReferenceMatcher { 1674 return libraryLeak(StaticFieldPattern(className, fieldName), description, patternApplies) 1675 } 1676 1677 /** 1678 * Creates a [LibraryLeakReferenceMatcher] that matches a [InstanceFieldPattern]. 1679 * [description] should convey what we know about this library leak. 1680 */ 1681 @JvmStatic 1682 fun instanceFieldLeak( 1683 className: String, 1684 fieldName: String, 1685 description: String = "", 1686 patternApplies: AndroidBuildMirror.() -> Boolean = ALWAYS 1687 ): LibraryLeakReferenceMatcher { 1688 return libraryLeak(InstanceFieldPattern(className, fieldName), description, patternApplies) 1689 } 1690 1691 @JvmStatic 1692 fun nativeGlobalVariableLeak( 1693 className: String, 1694 description: String = "", 1695 patternApplies: AndroidBuildMirror.() -> Boolean = ALWAYS 1696 ): LibraryLeakReferenceMatcher { 1697 return libraryLeak(NativeGlobalVariablePattern(className), description, patternApplies) 1698 } 1699 1700 private fun libraryLeak( 1701 referencePattern: ReferencePattern, 1702 description: String, 1703 patternApplies: AndroidBuildMirror.() -> Boolean 1704 ): LibraryLeakReferenceMatcher { 1705 return LibraryLeakReferenceMatcher( 1706 pattern = referencePattern, 1707 description = description, 1708 patternApplies = { graph -> 1709 AndroidBuildMirror.fromHeapGraph(graph) 1710 .patternApplies() 1711 } 1712 ) 1713 } 1714 1715 /** 1716 * Creates a [IgnoredReferenceMatcher] that matches a [InstanceFieldPattern]. 1717 */ 1718 @JvmStatic 1719 fun ignoredInstanceField( 1720 className: String, 1721 fieldName: String 1722 ): IgnoredReferenceMatcher { 1723 return IgnoredReferenceMatcher(pattern = InstanceFieldPattern(className, fieldName)) 1724 } 1725 1726 /** 1727 * Creates a [IgnoredReferenceMatcher] that matches a [JavaLocalPattern]. 1728 */ 1729 @JvmStatic 1730 fun ignoredJavaLocal( 1731 threadName: String 1732 ): IgnoredReferenceMatcher { 1733 return IgnoredReferenceMatcher(pattern = JavaLocalPattern(threadName)) 1734 } 1735 } 1736 } 1737 1738