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

Commit d746a7e8 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Let InputMethodPrivilegedOperationsRegistry deal with its singleton-ness

This is a preparation to allow InputMethodManager to have per-display
instances rather than the current per-process singleton instance.

When I introduced InputMethodPrivilegedOperationsRegistry [1], there
was an assumption that InputMethodManager was a per-process global
singleton object.

Now that we are going to break up that global per-process instance
into multiple per-display instances, having multiple
InputMethodPrivilegedOperationsRegistry instances probably does not
make much sense, because it would likely to increases the risk of
compability issues in existing IMEs.  Although IME developers soon
really need to use the right Context to obtain the right instance of
InputMethodManager anyway, unnecessarily introducing compatibility
pitfalls that can be avoided in the Framework side is not my
intention.

With this CL, following 9 methods can continue to work no matter
whether InputMethodManager is a per-process singleton or not.
This is fine because those APIs had been mistakenly exposed in
InputMethodManager and already deprecated in favor of newly added ones
in InputMethodService.

 * InputMethodManager.hideSoftInputFromInputMethod
 * InputMethodManager.hideStatusIcon
 * InputMethodManager.setInputMethod
 * InputMethodManager.setInputMethodAndSubtype
 * InputMethodManager.shouldOfferSwitchingToNextInputMethod
 * InputMethodManager.showSoftInputFromInputMethod
 * InputMethodManager.showStatusIcon
 * InputMethodManager.switchToLastInputMethod
 * InputMethodManager.switchToNextInputMethod

 [1]: If762714b2003fa6477e1318110f63e13968c1d7e
      eec552e9

Bug: 115893206
Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases
Change-Id: I4a61470f06ffac5f7a512536f8431489db0108f4
parent 7db3ae4f
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -465,7 +466,7 @@ public class InputMethodService extends AbstractInputMethodService {
        public final void initializeInternal(IBinder token, int displayId,
                IInputMethodPrivilegedOperations privilegedOperations) {
            mPrivOps.set(privilegedOperations);
            mImm.registerInputMethodPrivOps(token, mPrivOps);
            InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
            updateInputMethodDisplay(displayId);
            attachToken(token);
        }
@@ -1031,7 +1032,7 @@ public class InputMethodService extends AbstractInputMethodService {
        if (mToken != null) {
            // This is completely optional, but allows us to show more explicit error messages
            // when IME developers are doing something unsupported.
            mImm.unregisterInputMethodPrivOps(mToken);
            InputMethodPrivilegedOperationsRegistry.remove(mToken);
        }
    }

+15 −47
Original line number Diff line number Diff line
@@ -57,7 +57,6 @@ import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;

import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
@@ -387,9 +386,6 @@ public final class InputMethodManager {
    final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
    final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);

    private final InputMethodPrivilegedOperationsRegistry mPrivOpsRegistry =
            new InputMethodPrivilegedOperationsRegistry();

    // -----------------------------------------------------------

    static final int MSG_DUMP = 1;
@@ -739,7 +735,7 @@ public final class InputMethodManager {
     */
    @Deprecated
    public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
        mPrivOpsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
    }

    /**
@@ -749,7 +745,7 @@ public final class InputMethodManager {
     */
    @Deprecated
    public void hideStatusIcon(IBinder imeToken) {
        mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
        InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
    }

    /** @hide */
@@ -1790,7 +1786,7 @@ public final class InputMethodManager {
    public void setInputMethod(IBinder token, String id) {
        if (token == null) {
            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
            // TODO(Bug 114488811): Consider deprecating null token rule.
            try {
                mService.setInputMethod(token, id);
@@ -1799,7 +1795,7 @@ public final class InputMethodManager {
            }
            return;
        }
        mPrivOpsRegistry.get(token).setInputMethod(id);
        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id);
    }

    /**
@@ -1819,7 +1815,7 @@ public final class InputMethodManager {
    public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
        if (token == null) {
            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
            // TODO(Bug 114488811): Consider deprecating null token rule.
            try {
                mService.setInputMethodAndSubtype(token, id, subtype);
@@ -1828,7 +1824,7 @@ public final class InputMethodManager {
            }
            return;
        }
        mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
        InputMethodPrivilegedOperationsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
    }

    /**
@@ -1848,7 +1844,7 @@ public final class InputMethodManager {
     */
    @Deprecated
    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
        mPrivOpsRegistry.get(token).hideMySoftInput(flags);
        InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(flags);
    }

    /**
@@ -1869,7 +1865,7 @@ public final class InputMethodManager {
     */
    @Deprecated
    public void showSoftInputFromInputMethod(IBinder token, int flags) {
        mPrivOpsRegistry.get(token).showMySoftInput(flags);
        InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
    }

    /**
@@ -2229,7 +2225,7 @@ public final class InputMethodManager {
    public boolean switchToLastInputMethod(IBinder imeToken) {
        if (imeToken == null) {
            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
            // TODO(Bug 114488811): Consider deprecating null token rule.
            try {
                return mService.switchToPreviousInputMethod(imeToken);
@@ -2237,7 +2233,7 @@ public final class InputMethodManager {
                throw e.rethrowFromSystemServer();
            }
        }
        return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
        return InputMethodPrivilegedOperationsRegistry.get(imeToken).switchToPreviousInputMethod();
    }

    /**
@@ -2257,7 +2253,7 @@ public final class InputMethodManager {
    public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
        if (imeToken == null) {
            // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
            // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
            // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately.
            // TODO(Bug 114488811): Consider deprecating null token rule.
            try {
                return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
@@ -2265,7 +2261,8 @@ public final class InputMethodManager {
                throw e.rethrowFromSystemServer();
            }
        }
        return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
                .switchToNextInputMethod(onlyCurrentIme);
    }

    /**
@@ -2284,7 +2281,8 @@ public final class InputMethodManager {
     */
    @Deprecated
    public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
        return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
        return InputMethodPrivilegedOperationsRegistry.get(imeToken)
                .shouldOfferSwitchingToNextInputMethod();
    }

    /**
@@ -2420,34 +2418,4 @@ public final class InputMethodManager {
        sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
        return sb.toString();
    }

    /**
     * Called by {@link InputMethodService} so that API calls to deprecated ones defined in this
     * class can be forwarded to {@link InputMethodPrivilegedOperations}.
     *
     * <p>Note: this method does not hold strong references to {@code token} and {@code ops}. The
     * registry entry will be automatically cleared after {@code token} is garbage collected.</p>
     *
     * @param token IME token that is associated with {@code ops}
     * @param ops {@link InputMethodPrivilegedOperations} that is associated with {@code token}
     * @hide
     */
    public void registerInputMethodPrivOps(IBinder token, InputMethodPrivilegedOperations ops) {
        mPrivOpsRegistry.put(token, ops);
    }

    /**
     * Called from {@link InputMethodService#onDestroy()} to make sure that deprecated IME APIs
     * defined in this class can no longer access to {@link InputMethodPrivilegedOperations}.
     *
     * <p>Note: Calling this method is optional, but at least gives more explict error message in
     * logcat when IME developers are doing something unsupported (e.g. trying to call IME APIs
     * after {@link InputMethodService#onDestroy()}).</p>
     *
     * @param token IME token to be removed.
     * @hide
     */
    public void unregisterInputMethodPrivOps(IBinder token) {
        mPrivOpsRegistry.remove(token);
    }
}
+32 −13
Original line number Diff line number Diff line
@@ -29,12 +29,19 @@ import java.util.WeakHashMap;
/**
 * A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
 * used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
 *
 * <p>This class is designed to be used as a per-process global registry.</p>
 */
public final class InputMethodPrivilegedOperationsRegistry {
    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>>
            mRegistry = new WeakHashMap<>();
    private InputMethodPrivilegedOperationsRegistry() {
        // Not intended to be instantiated.
    }

    private static final Object sLock = new Object();

    @Nullable
    @GuardedBy("sLock")
    private static WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>> sRegistry;

    @Nullable
    private static InputMethodPrivilegedOperations sNop;
@@ -62,10 +69,13 @@ public final class InputMethodPrivilegedOperationsRegistry {
     * @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
     */
    @AnyThread
    public void put(IBinder token, InputMethodPrivilegedOperations ops) {
        synchronized (mLock) {
    public static void put(IBinder token, InputMethodPrivilegedOperations ops) {
        synchronized (sLock) {
            if (sRegistry == null) {
                sRegistry = new WeakHashMap<>();
            }
            final WeakReference<InputMethodPrivilegedOperations> previousOps =
                    mRegistry.put(token, new WeakReference<>(ops));
                    sRegistry.put(token, new WeakReference<>(ops));
            if (previousOps != null) {
                throw new IllegalStateException(previousOps.get() + " is already registered for "
                        + " this token=" + token + " newOps=" + ops);
@@ -84,9 +94,12 @@ public final class InputMethodPrivilegedOperationsRegistry {
     */
    @NonNull
    @AnyThread
    public InputMethodPrivilegedOperations get(IBinder token) {
        synchronized (mLock) {
            final WeakReference<InputMethodPrivilegedOperations> wrapperRef = mRegistry.get(token);
    public static InputMethodPrivilegedOperations get(IBinder token) {
        synchronized (sLock) {
            if (sRegistry == null) {
                return getNopOps();
            }
            final WeakReference<InputMethodPrivilegedOperations> wrapperRef = sRegistry.get(token);
            if (wrapperRef == null) {
                return getNopOps();
            }
@@ -108,9 +121,15 @@ public final class InputMethodPrivilegedOperationsRegistry {
     * @param token IME token to be removed.
     */
    @AnyThread
    public void remove(IBinder token) {
        synchronized (mLock) {
            mRegistry.remove(token);
    public static void remove(IBinder token) {
        synchronized (sLock) {
            if (sRegistry == null) {
                return;
            }
            sRegistry.remove(token);
            if (sRegistry.isEmpty()) {
                sRegistry = null;
            }
        }
    }
}