Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7029cc3d authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Add an empty IMM#windowDismissed(IBinder) for app compat

It seems that there are several applications that call
InputMethodManager#windowDismissed(IBinder), which was recently
removed [1], then crash due to unhandled NoSuchMethodException
exception.

The most probable hypothesis that explains why these apps ended up
calling this method via reflection is trying to address object leaks
from InputMethodManager, which we have received reports then fixed all
the known issues [2][3][4][5][6].  There are several Internet articles
that claim calling InputMethodManager#windowDismissed(IBinder) can
*fix* such object leaks, which is in fact no longer necessary in
recent versions of Android.  However, it seems that some of such apps
didn't gracefully take care of cases where IMM#windowDismissed()
doesn't exist then ended up crashing due to unhandled
NoSuchMethodException.

Note also that AndroidX Activity (androidx.activity.ComponentActivity)
already implements a similar workaround [7] hence app developers no
longer need to work around by themselves.

Anyways, in order to avoid unnecessary crashes from apps that call
IMM#windowDismissed() via reflection, this CL re-introduce it as an
empty method.  As their goal is supposed to be clearing internal
fields within InputMethodManager to avoid object leaks, an empty
method is believed to be sufficient.

 [1]: Ib455704fe1e9d243f93190a84f230210dbceac2a
      970d9d2e
 [2]: Iad09cf5dbb7f6f156fd39ed243431432e00f8945
      4478de3c
 [3]: Iaf3fe2c065b5bf91e49a729ba46262114bb6da88
      b13f015a
 [4]: I219394178e4172bc47864297f1418e677dba25e5
      5f05965f
 [5]: Id6afc8fc64512225578c62557b96c7dc2e969adf
      0f3a99d8
 [6]: I8fabb30f14bcb2cd7019e29b6642b4562d49d248
      dff365ef
 [7]: I615e92f0c64b6d668b31f2c83527b7409a7bef6f
      b1bf8502e0574a4bfcf450235595372b7cb3778a

Fix: 152261618
Test: manually verified with apps in question
Change-Id: I599896a96267fc60a738eac31be02b770e10dff3
parent e6897b19
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -1968,6 +1968,38 @@ public final class InputMethodManager {
        return true;
    }

    /**
     * An empty method only to avoid crashes of apps that call this method via reflection and do not
     * handle {@link NoSuchMethodException} in a graceful manner.
     *
     * @deprecated This is an empty method.  No framework method must call this method.
     * @hide
     */
    @Deprecated
    @UnsupportedAppUsage(trackingBug = 37122102, maxTargetSdk = Build.VERSION_CODES.Q,
            publicAlternatives = "{@code androidx.activity.ComponentActivity}")
    public void windowDismissed(IBinder appWindowToken) {
        // Intentionally empty.
        //
        // It seems that some applications call this method via reflection to null clear the
        // following fields that used to exist in InputMethodManager:
        //  * InputMethodManager#mCurRootView
        //  * InputMethodManager#mServedView
        //  * InputMethodManager#mNextServedView
        // so that these objects can be garbage-collected when an Activity gets dismissed.
        //
        // It is indeed true that older versions of InputMethodManager had issues that prevented
        // these fields from being null-cleared when it should have been, but the understanding of
        // the engineering team is that all known issues have already been fixed as of Android 10.
        //
        // For older devices, developers can work around the object leaks by using
        // androidx.activity.ComponentActivity.
        // See https://issuetracker.google.com/u/1/issues/37122102 for details.
        //
        // If you believe InputMethodManager is leaking objects in API 24 or any later version,
        // please file a bug at https://issuetracker.google.com/issues/new?component=192705.
    }

    private int getStartInputFlags(View focusedView, int startInputFlags) {
        startInputFlags |= StartInputFlags.VIEW_HAS_FOCUS;
        if (focusedView.onCheckIsTextEditor()) {