Package 

Enum AndroidLeakFixes

  • 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.

    • Enum Constant Summary

      Enum Constants 
      Enum Constant Description
      PERMISSION_CONTROLLER_MANAGER

      PermissionControllerManager 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_CHECKER

      Every 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_VIEW

      When 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_VIEW

      Fix 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_HOLDER

      In 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_MANAGER

      Samsung 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_VIEW

      mLastHoveredView is a static field in TextView that leaks the last hovered view.

      This fix clears it when the activity is destroyed.

      BUBBLE_POPUP

      A 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_MANAGER

      ClipboardUIManager is a static singleton that leaks an activity context. This fix makes sure the manager is called with an application context.

      CONNECTIVITY_MANAGER

      ConnectivityManager 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_INFO

      Until 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_THREADS

      HandlerThread 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_MANAGER

      Obtaining 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_POOL

      This 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_HELPER

      MediaSessionLegacyHelper 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 String getName()
      final Integer getOrdinal()
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait