-
- All Implemented Interfaces:
-
java.io.Serializable,kotlin.Comparable
public enum AndroidLeakFixes extends Enum<AndroidLeakFixes>
A collection of hacks to fix leaks in the Android Framework and other Google Android libraries.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description public classAndroidLeakFixes.Companion
-
Enum Constant Summary
Enum Constants Enum Constant Description PERMISSION_CONTROLLER_MANAGERPermissionControllerManager stores the first context it's initialized with forever. Sometimes it's an Activity context which then leaks after Activity is destroyed.
This fix makes sure the PermissionControllerManager is created with the application context.
For Pixel devices the issue can be tracked here https://issuetracker.google.com/issues/318415056
SPELL_CHECKEREvery editable TextView has an Editor instance which has a SpellChecker instance. SpellChecker is in charge of displaying the little squiggle spans that show typos. SpellChecker starts a SpellCheckerSession as needed and then closes it when the TextView is detached from the window. A SpellCheckerSession is in charge of communicating with the spell checker service (which lives in another process) through TextServicesManager.
The SpellChecker sends the TextView content to the spell checker service every 400ms, ie every time the service calls back with a result the SpellChecker schedules another check for 400ms later.
When the TextView is detached from the window, the spell checker closes the session. In practice, SpellCheckerSessionListenerImpl.mHandler is set to null and when the service calls SpellCheckerSessionListenerImpl.onGetSuggestions or SpellCheckerSessionListenerImpl.onGetSentenceSuggestions back from another process, there's a null check for SpellCheckerSessionListenerImpl.mHandler and the callback is dropped.
Unfortunately, on Android M there's a race condition in how that's done. When the service calls back into our app process, the IPC call is received on a binder thread. That's when the null check happens. If the session is not closed at this point (mHandler not null), the callback is then posted to the main thread. If on the main thread the session is closed after that post but prior to that post being handled, then the post will still be processed, after the session has been closed.
When the post is processed, SpellCheckerSession calls back into SpellChecker which in turns schedules a new spell check to be ran in 400ms. The check is an anonymous inner class (SpellChecker$1) stored as SpellChecker.mSpellRunnable and implementing Runnable. It is scheduled by calling View.postDelayed. As we've seen, at this point the session may be closed which means that the view has been detached. View.postDelayed behaves differently when a view is detached: instead of posting to the single Handler used by the view hierarchy, it enqueues the Runnable into ViewRootImpl.RunQueue, a static queue that holds on to "actions" to be executed. As soon as a view hierarchy is attached, the ViewRootImpl.RunQueue is processed and emptied.
Unfortunately, that means that as long as no view hierarchy is attached, ie as long as there are no activities alive, the actions stay in ViewRootImpl.RunQueue. That means SpellChecker$1 ends up being kept in memory. It holds on to SpellChecker which in turns holds on to the detached TextView and corresponding destroyed activity & view hierarchy.
We have a fix for this! When the spell check session is closed, we replace SpellCheckerSession.mSpellCheckerSessionListener (which normally is the SpellChecker) with a no-op implementation. So even if callbacks are enqueued to the main thread handler, these callbacks will call the no-op implementation and SpellChecker will not be scheduling a spell check.
Sources to corroborate:
https://android.googlesource.com/platform/frameworks/base/+/marshmallow-release/core/java/android/view/textservice/SpellCheckerSession.java https://android.googlesource.com/platform/frameworks/base/+/marshmallow-release/core/java/android/view/textservice/TextServicesManager.java https://android.googlesource.com/platform/frameworks/base/+/marshmallow-release/core/java/android/widget/SpellChecker.java https://android.googlesource.com/platform/frameworks/base/+/marshmallow-release/core/java/android/view/ViewRootImpl.java
IMM_CUR_ROOT_VIEWWhen an activity is destroyed, the corresponding ViewRootImpl instance is released and ready to be garbage collected. Some time after that, ViewRootImpl#W receives a windowfocusChanged() callback, which it normally delegates to ViewRootImpl which in turn calls InputMethodManager#onPreWindowFocus which clears InputMethodManager#mCurRootView.
Unfortunately, since the ViewRootImpl instance is garbage collectable it may be garbage collected before that happens. ViewRootImpl#W has a weak reference on ViewRootImpl, so that weak reference will then return null and the windowfocusChanged() callback will be ignored, leading to InputMethodManager#mCurRootView not being cleared.
Filed here: https://issuetracker.google.com/u/0/issues/116078227 Fixed here: https://android.googlesource.com/platform/frameworks/base/+/dff365ef4dc61239fac70953b631e92972a9f41f%5E%21/#F0 InputMethodManager.mCurRootView is part of the unrestricted grey list on Android 9: https://android.googlesource.com/platform/frameworks/base/+/pie-release/config/hiddenapi-light-greylist.txt#6057
IMM_FOCUSED_VIEWFix for https://code.google.com/p/android/issues/detail?id=171190 .
When a view that has focus gets detached, we wait for the main thread to be idle and then check if the InputMethodManager is leaking a view. If yes, we tell it that the decor view got focus, which is what happens if you press home and come back from recent apps. This replaces the reference to the detached view with a reference to the decor view.
VIEW_LOCATION_HOLDERIn Android P, ViewLocationHolder has an mRoot field that is not cleared in its clear() method. Introduced in https://github.com/aosp-mirror/platform_frameworks_base/commit/86b326012813f09d8f1de7d6d26c986a909d
This leaks triggers very often when accessibility is on. To fix this leak we need to clear the ViewGroup.ViewLocationHolder.sPool pool. Unfortunately Android P prevents accessing that field through reflection. So instead, we call ViewGroup#addChildrenForAccessibility with a view group that has 32 children (32 being the pool size), which as result fills in the pool with 32 dumb views that reference a dummy context instead of an activity context.
This fix empties the pool on every activity destroy and every AndroidX fragment view destroy. You can support other cases where views get detached by calling directly ViewLocationHolderLeakFix.clearStaticPool.
ACTIVITY_MANAGERSamsung added a static mContext field to ActivityManager, holding a reference to the activity.
This fix clears the field when an activity is destroyed if it refers to this specific activity.
Observed here: https://github.com/square/leakcanary/issues/177
LAST_HOVERED_VIEWmLastHoveredView is a static field in TextView that leaks the last hovered view.
This fix clears it when the activity is destroyed.
BUBBLE_POPUPA static helper for EditText bubble popups leaks a reference to the latest focused view.
This fix clears it when the activity is destroyed.
SAMSUNG_CLIPBOARD_MANAGERClipboardUIManager is a static singleton that leaks an activity context. This fix makes sure the manager is called with an application context.
CONNECTIVITY_MANAGERConnectivityManager has a sInstance field that is set when the first ConnectivityManager instance is created. ConnectivityManager has a mContext field. When calling activity.getSystemService(Context.CONNECTIVITY_SERVICE) , the first ConnectivityManager instance is created with the activity context and stored in sInstance. That activity context then leaks forever.
This fix makes sure the connectivity manager is created with the application context.
Tracked here: https://code.google.com/p/android/issues/detail?id=198852 Introduced here: https://github.com/android/platform_frameworks_base/commit/e0bef71662d81caaaa0d7214fb0bef5d39996a69
ACCESSIBILITY_NODE_INFOUntil API 28, AccessibilityNodeInfo has a mOriginalText field that was not properly cleared when instance were put back in the pool. Leak introduced here: https://android.googlesource.com/platform/frameworks/base/+/193520e3dff5248ddcf8435203bf99d2ba667219%5E%21/core/java/android/view/accessibility/AccessibilityNodeInfo.java
Fixed here: https://android.googlesource.com/platform/frameworks/base/+/6f8ec1fd8c159b09d617ed6d9132658051443c0c
FLUSH_HANDLER_THREADSHandlerThread instances keep local reference to their last handled message after recycling it. That message is obtained by a dialog which sets on an OnClickListener on it and then never recycles it, expecting it to be garbage collected but it ends up being held by the HandlerThread.
USER_MANAGERObtaining the UserManager service ends up calling the hidden UserManager.get() method which stores the context in a singleton UserManager instance and then stores that instance in a static field.
We obtain the user manager from an activity context, so if it hasn't been created yet it will leak that activity forever.
This fix makes sure the UserManager is created and holds on to the Application context.
Issue: https://code.google.com/p/android/issues/detail?id=173789
Fixed in https://android.googlesource.com/platform/frameworks/base/+/5200e1cb07190a1f6874d72a4561064cad3ee3e0%5E%21/#F0 (Android O)
TEXT_LINE_POOLThis flushes the TextLine pool when an activity is destroyed, to prevent memory leaks.
The first memory leak has been fixed in android-5.1.0_r1 https://github.com/android/platform_frameworks_base/commit/893d6fe48d37f71e683f722457bea646994a10bf
Second memory leak: https://github.com/android/platform_frameworks_base/commit/b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4
MEDIA_SESSION_LEGACY_HELPERMediaSessionLegacyHelper is a static singleton and did not use the application context. Introduced in android-5.0.1_r1, fixed in Android 5.1.0_r1. https://github.com/android/platform_frameworks_base/commit/9b5257c9c99c4cb541d8e8e78fb04f008b1a9091
We fix this leak by invoking MediaSessionLegacyHelper.getHelper() early in the app lifecycle.
-
Method Summary
Modifier and Type Method Description final StringgetName()final IntegergetOrdinal()-
-
Method Detail
-
getOrdinal
final Integer getOrdinal()
-
-
-
-