xref: /aosp_15_r20/external/leakcanary2/shark-android/src/main/java/shark/AndroidReferenceMatchers.kt (revision d9e8da70d8c9df9a41d7848ae506fb3115cae6e6)
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