From c54c1171640519ae0ad8da1f32477295d96db1b8 Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Thu, 6 Sep 2018 11:39:50 -0700 Subject: [PATCH] Add a new Binder interface to allow IMS to directly talk to IMMS Historically, InputMethodService (IMS) has relied on InputMethodManager's hidden methods to communicate with InputMethodManagerService (IMMS). Because of this, InputMethodManager (IMM) has ended up being a mixture of IPC endpoint for both IME clients and IME itself. There are multiple problems. * IMM is instantiated in almost all user mode processes. This means that unnecessary IPC endpoints have been accessible to them via reflection. Even though those endpoints refuses request without a valid IME window token, and even though we have tighten up use of private APIs in the runtime level, exposing unnecessary IPC endpoints is still questionable. * Mixing multiple responsibilities has been caused unnecessary complexity in IMM. In Bug 70282603, we have moved some APIs from IMM to IMS to sort out this complexity that are surfaced in API boundary, but in the implementation level everything remained to be the same. Now that Bug 70282603 is fixed, the natural next step is to start implementing actual an IPC connection from IMS to IMMS without relying on IMM. Here is the new diagram that describes (most of) IPC interfaces around IMEs. APP---(1)---IMMS \ | \ | \ | \ | \ | (2) (3) \ | \ | \ | \ | \| IME (1): IInputMethodManager.aidl: send requests from APP to IMMS IInputMethodClient.aidl: send requests from IMMS to APP (2): IInputMethodSession.aidl: send requests from APP to IME IInputContext.aidl: send requests from IME to APP -> this is the actual interface behind InputConnection (3): IInputMethod.aidl: send requests from IMMS to IME IInputMethodPrivilegedOperations.aidl: send requests from IME to IMMS IInputMethodPrivilegedOperations.aidl is what this CL is adding. With that, this CL moves 5 IPC methods from IInputMethodManager.aidl (1) to IInputMethodPrivilegedOperations.aidl (3). There remain some IPC methods that are intended to be used only from IMEs in IInputMethodManager.aidl because those methods have been unfortunately exposed via public APIs in InputMethodmanager. Although all of those public APIs were deprecated in Android P as part of Bug 70282603, we still need to keep maintaining those APIs until (most of) IMEs migrate to APIs that are newly introduced in InputMethodService. It would take several years. IInputMethodManager#getInputMethodWindowVisibleHeight() is another method that we cannot migrate right now because some apps have already relied on its corresponding hidden method in IMM, as discussed in Bug 113914148. Fix: 113177698 Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases Change-Id: I2f3ec3c5de546fb3603275a4b64000ed3f863b65 --- Android.bp | 1 + .../IInputMethodWrapper.java | 20 ++- .../InputMethodService.java | 131 +++++++++++++++--- .../view/inputmethod/InputContentInfo.java | 10 +- .../android/view/inputmethod/InputMethod.java | 22 ++- .../view/inputmethod/InputMethodManager.java | 87 ------------ .../IInputMethodPrivilegedOperations.aidl | 33 +++++ .../android/internal/view/IInputMethod.aidl | 3 +- .../internal/view/IInputMethodManager.aidl | 11 +- .../server/InputMethodManagerService.java | 70 ++++++++-- 10 files changed, 250 insertions(+), 138 deletions(-) create mode 100644 core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl diff --git a/Android.bp b/Android.bp index dd49a67a79cf9..3dd1477f70e7b 100644 --- a/Android.bp +++ b/Android.bp @@ -381,6 +381,7 @@ java_defaults { "core/java/com/android/internal/backup/IBackupTransport.aidl", "core/java/com/android/internal/backup/IObbBackupService.aidl", "core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl", + "core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl", "core/java/com/android/internal/net/INetworkWatchlistManager.aidl", "core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl", "core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl", diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 2c7e51a1db25c..00a1f6feecc57 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -35,6 +35,7 @@ import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; import android.view.inputmethod.InputMethodSubtype; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; import com.android.internal.view.IInputContext; @@ -60,7 +61,7 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final String TAG = "InputMethodWrapper"; private static final int DO_DUMP = 1; - private static final int DO_ATTACH_TOKEN = 10; + private static final int DO_INITIALIZE_INTERNAL = 10; private static final int DO_SET_INPUT_CONTEXT = 20; private static final int DO_UNSET_INPUT_CONTEXT = 30; private static final int DO_START_INPUT = 32; @@ -159,9 +160,15 @@ class IInputMethodWrapper extends IInputMethod.Stub args.recycle(); return; } - - case DO_ATTACH_TOKEN: { - inputMethod.attachToken((IBinder)msg.obj); + + case DO_INITIALIZE_INTERNAL: { + SomeArgs args = (SomeArgs) msg.obj; + try { + inputMethod.initializeInternal((IBinder) args.arg1, + (IInputMethodPrivilegedOperations) args.arg2); + } finally { + args.recycle(); + } return; } case DO_SET_INPUT_CONTEXT: { @@ -246,8 +253,9 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override - public void attachToken(IBinder token) { - mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token)); + public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps) { + mCaller.executeOrSendMessage( + mCaller.obtainMessageOO(DO_INITIALIZE_INTERNAL, token, privOps)); } @BinderThread diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 64c934b332f24..ea7d42ee79a93 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -42,6 +42,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; import android.provider.Settings; @@ -79,6 +80,9 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.internal.inputmethod.IInputContentUriToken; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -342,7 +346,8 @@ public class InputMethodService extends AbstractInputMethodService { private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING; InputMethodManager mImm; - + private IInputMethodPrivilegedOperations mPrivOps; + @UnsupportedAppUsage int mTheme = 0; @@ -444,6 +449,22 @@ public class InputMethodService extends AbstractInputMethodService { * all of the standard behavior for an input method. */ public class InputMethodImpl extends AbstractInputMethodImpl { + /** + * {@inheritDoc} + * @hide + */ + @MainThread + @Override + public final void initializeInternal(IBinder token, + IInputMethodPrivilegedOperations privilegedOperations) { + if (mToken != null) { + throw new IllegalStateException("initializeInternal() must be called at most once." + + " privOps=" + privilegedOperations); + } + mPrivOps = privilegedOperations; + attachToken(token); + } + /** * {@inheritDoc} */ @@ -470,9 +491,7 @@ public class InputMethodService extends AbstractInputMethodService { mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding + " ic=" + mInputConnection); - if (mImm != null && mToken != null) { - mImm.reportFullscreenMode(mToken, mIsFullscreen); - } + reportFullscreenMode(); initialize(); onBindInput(); } @@ -521,7 +540,11 @@ public class InputMethodService extends AbstractInputMethodService { public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, @NonNull IBinder startInputToken) { - mImm.reportStartInput(mToken, startInputToken); + try { + mPrivOps.reportStartInput(startInputToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } // This needs to be dispatched to interface methods rather than doStartInput(). // Otherwise IME developers who have overridden those interface methods will lose @@ -563,8 +586,8 @@ public class InputMethodService extends AbstractInputMethodService { } clearInsetOfPreviousIme(); // If user uses hard keyboard, IME button should always be shown. - mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()), - mBackDisposition); + setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition); + if (resultReceiver != null) { resultReceiver.send(wasVis != isInputViewShown() ? InputMethodManager.RESULT_SHOWN @@ -583,6 +606,17 @@ public class InputMethodService extends AbstractInputMethodService { } } + private void setImeWindowStatus(int visibilityFlags, int backDisposition) { + if (mPrivOps == null) { + return; + } + try { + mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Concrete implementation of * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides @@ -1039,8 +1073,7 @@ public class InputMethodService extends AbstractInputMethodService { } // If user uses hard keyboard, IME button should always be shown. boolean showing = onEvaluateInputViewShown(); - mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0), - mBackDisposition); + setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); } } @@ -1090,7 +1123,7 @@ public class InputMethodService extends AbstractInputMethodService { return; } mBackDisposition = disposition; - mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()), mBackDisposition); + setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition); } /** @@ -1188,7 +1221,18 @@ public class InputMethodService extends AbstractInputMethodService { public EditorInfo getCurrentInputEditorInfo() { return mInputEditorInfo; } - + + private void reportFullscreenMode() { + if (mPrivOps == null) { + return; + } + try { + mPrivOps.reportFullscreenMode(mIsFullscreen); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Re-evaluate whether the input method should be running in fullscreen * mode, and update its UI if this has changed since the last time it @@ -1203,9 +1247,7 @@ public class InputMethodService extends AbstractInputMethodService { if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { changed = true; mIsFullscreen = isFullscreen; - if (mImm != null && mToken != null) { - mImm.reportFullscreenMode(mToken, mIsFullscreen); - } + reportFullscreenMode(); mFullscreenApplied = true; initialize(); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) @@ -1823,7 +1865,7 @@ public class InputMethodService extends AbstractInputMethodService { final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown()); if (previousImeWindowStatus != nextImeWindowStatus) { - mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition); + setImeWindowStatus(nextImeWindowStatus, mBackDisposition); } if ((previousImeWindowStatus & IME_ACTIVE) == 0) { if (DEBUG) Log.v(TAG, "showWindow: showing!"); @@ -1848,7 +1890,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void doHideWindow() { - mImm.setImeWindowStatus(mToken, 0, mBackDisposition); + setImeWindowStatus(0, mBackDisposition); hideWindow(); } @@ -1889,10 +1931,30 @@ public class InputMethodService extends AbstractInputMethodService { + " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme); if (!mShouldClearInsetOfPreviousIme) return; - mImm.clearLastInputMethodWindowForTransition(mToken); + clearLastInputMethodWindowForTransition(); mShouldClearInsetOfPreviousIme = false; } + /** + * Tells the system that the IME decided to not show a window and the system no longer needs to + * use the previous IME's inset. + * + *

Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()} + * is the only expected caller of this method. Do not depend on this anywhere else.

+ * + *

TODO: We probably need to reconsider how IME should be handled.

+ */ + private void clearLastInputMethodWindowForTransition() { + if (mPrivOps == null) { + return; + } + try { + mPrivOps.clearLastInputMethodWindowForTransition(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Called when a new client has bound to the input method. This * may be followed by a series of {@link #onStartInput(EditorInfo, boolean)} @@ -2806,7 +2868,40 @@ public class InputMethodService extends AbstractInputMethodService { if (getCurrentInputConnection() != inputConnection) { return; } - mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo()); + exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo()); + } + + /** + * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access + * permission to the content. + * + *

See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, + * InputConnection)} for details.

+ * + * @param inputContentInfo Content to be temporarily exposed from the input method to the + * application. + * This cannot be {@code null}. + * @param editorInfo The editor that receives {@link InputContentInfo}. + */ + private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo, + @NonNull EditorInfo editorInfo) { + if (mPrivOps == null) { + return; + } + final IInputContentUriToken uriToken; + final Uri contentUri = inputContentInfo.getContentUri(); + try { + uriToken = mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName); + if (uriToken == null) { + return; + } + } catch (RemoteException e) { + Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString() + + " packageName=" + editorInfo.packageName, e); + return; + } + inputContentInfo.setUriToken(uriToken); + return; } private static int mapToImeWindowStatus(boolean isInputViewShown) { diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java index 7104a2871f218..0d0fea6edd1fa 100644 --- a/core/java/android/view/inputmethod/InputContentInfo.java +++ b/core/java/android/view/inputmethod/InputContentInfo.java @@ -183,7 +183,15 @@ public final class InputContentInfo implements Parcelable { @Nullable public Uri getLinkUri() { return mLinkUri; } - void setUriToken(IInputContentUriToken token) { + /** + * Update the internal state of this object to be associated with the given token. + * + *

TODO(yukawa): Come up with an idea to make {@link InputContentInfo} immutable.

+ * + * @param token special URI token obtained from the system. + * @hide + */ + public void setUriToken(IInputContentUriToken token) { if (mUriToken != null) { throw new IllegalStateException("URI token is already set"); } diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index ca14ebef392b9..f45507c2b8b3f 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -25,6 +25,8 @@ import android.inputmethodservice.InputMethodService; import android.os.IBinder; import android.os.ResultReceiver; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; + /** * The InputMethod interface represents an input method which can generate key * events and text, such as digital, email addresses, CJK characters, other @@ -79,7 +81,25 @@ public interface InputMethod { public interface SessionCallback { public void sessionCreated(InputMethodSession session); } - + + /** + * Called first thing after an input method is created, this supplies a + * unique token for the session it has with the system service as well as + * IPC endpoint to do some other privileged operations. + * + * @param token special token for the system to identify + * {@link InputMethodService} + * @param privilegedOperations IPC endpoint to do some privileged + * operations that are allowed only to the + * current IME. + * @hide + */ + @MainThread + default void initializeInternal(IBinder token, + IInputMethodPrivilegedOperations privilegedOperations) { + attachToken(token); + } + /** * Called first thing after an input method is created, this supplies a * unique token for the session it has with the system service. It is diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0ecd09fa8d71d..baac4cd0de9a4 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -29,7 +29,6 @@ import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -56,7 +55,6 @@ import android.view.ViewRootImpl; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.autofill.AutofillManager; -import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.os.SomeArgs; import com.android.internal.view.IInputConnectionWrapper; import com.android.internal.view.IInputContext; @@ -809,24 +807,6 @@ public final class InputMethodManager { } } - /** @hide */ - public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) { - try { - mService.setImeWindowStatus(imeToken, vis, backDisposition); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** @hide */ - public void reportStartInput(IBinder imeToken, IBinder startInputToken) { - try { - mService.reportStartInput(imeToken, startInputToken); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** @hide */ @UnsupportedAppUsage public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) { @@ -858,17 +838,6 @@ public final class InputMethodManager { } } - /** - * @hide - */ - public void reportFullscreenMode(IBinder token, boolean fullscreen) { - try { - mService.reportFullscreenMode(token, fullscreen); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** * Return true if the given view is the currently active view for the * input method. @@ -2353,28 +2322,6 @@ public final class InputMethodManager { } } - /** - * Tells the system that the IME decided to not show a window and the system no longer needs to - * use the previous IME's inset. - * - *

Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()} - * is the only expected caller of this method. Do not depend on this anywhere else.

- * - *

TODO: We probably need to reconsider how IME should be handled.

- * @hide - * @param token Supplies the identifying token given to an input method when it was started, - * which allows it to perform this operation on itself. - */ - public void clearLastInputMethodWindowForTransition(final IBinder token) { - synchronized (mH) { - try { - mService.clearLastInputMethodWindowForTransition(token); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - /** * Force switch to the last used input method and subtype. If the last input method didn't have * any subtypes, the framework will simply switch to the last input method with no subtype @@ -2513,40 +2460,6 @@ public final class InputMethodManager { } } - /** - * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access - * permission to the content. - * - *

See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, - * InputConnection)} for details.

- * - * @param token Supplies the identifying token given to an input method when it was started, - * which allows it to perform this operation on itself. - * @param inputContentInfo Content to be temporarily exposed from the input method to the - * application. - * This cannot be {@code null}. - * @param editorInfo The editor that receives {@link InputContentInfo}. - * @hide - */ - public void exposeContent(@NonNull IBinder token, @NonNull InputContentInfo inputContentInfo, - @NonNull EditorInfo editorInfo) { - final IInputContentUriToken uriToken; - final Uri contentUri = inputContentInfo.getContentUri(); - try { - uriToken = mService.createInputContentUriToken(token, contentUri, - editorInfo.packageName); - if (uriToken == null) { - return; - } - } catch (RemoteException e) { - Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString() - + " packageName=" + editorInfo.packageName, e); - return; - } - inputContentInfo.setUriToken(uriToken); - return; - } - void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { final Printer p = new PrintWriterPrinter(fout); p.println("Input method client state for " + this + ":"); diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl new file mode 100644 index 0000000000000..449b4a560dc63 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import android.net.Uri; + +import com.android.internal.inputmethod.IInputContentUriToken; + +/** + * Defines priviledged operations that only the current IME is allowed to call. + * Actual operations are implemented and handled by InputMethodManagerService. + */ +interface IInputMethodPrivilegedOperations { + void setImeWindowStatus(int vis, int backDisposition); + void reportStartInput(in IBinder startInputToken); + void clearLastInputMethodWindowForTransition(); + IInputContentUriToken createInputContentUriToken(in Uri contentUri, in String packageName); + void reportFullscreenMode(boolean fullscreen); +} diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index 5b29f4cb9fb4b..b6a654aac563f 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -22,6 +22,7 @@ import android.view.InputChannel; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputBinding; import android.view.inputmethod.InputMethodSubtype; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.IInputSessionCallback; @@ -32,7 +33,7 @@ import com.android.internal.view.IInputSessionCallback; * {@hide} */ oneway interface IInputMethod { - void attachToken(IBinder token); + void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps); void bindInput(in InputBinding binding); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 09ccf67a775e3..c08c8718230db 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -16,13 +16,12 @@ package com.android.internal.view; -import android.net.Uri; import android.os.ResultReceiver; import android.text.style.SuggestionSpan; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.EditorInfo; -import com.android.internal.inputmethod.IInputContentUriToken; + import com.android.internal.view.InputBindResult; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -69,8 +68,6 @@ interface IInputMethodManager { void hideMySoftInput(in IBinder token, int flags); void showMySoftInput(in IBinder token, int flags); void updateStatusIcon(in IBinder token, String packageName, int iconId); - void setImeWindowStatus(in IBinder token, int vis, int backDisposition); - void reportStartInput(in IBinder token, in IBinder startInputToken); void registerSuggestionSpansForNotification(in SuggestionSpan[] spans); boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index); InputMethodSubtype getCurrentInputMethodSubtype(); @@ -82,12 +79,6 @@ interface IInputMethodManager { // This is kept due to @UnsupportedAppUsage. // TODO(Bug 113914148): Consider removing this. int getInputMethodWindowVisibleHeight(); - void clearLastInputMethodWindowForTransition(in IBinder token); - - IInputContentUriToken createInputContentUriToken(in IBinder token, in Uri contentUri, - in String packageName); - - void reportFullscreenMode(in IBinder token, boolean fullscreen); oneway void notifyUserAction(int sequenceNumber); } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 617e803e3f3b8..f981b261c5370 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -127,6 +127,7 @@ import android.widget.TextView; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.IInputContentUriToken; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem; import com.android.internal.inputmethod.InputMethodUtils; @@ -198,7 +199,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_SHOW_SOFT_INPUT = 1020; static final int MSG_HIDE_SOFT_INPUT = 1030; static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035; - static final int MSG_ATTACH_TOKEN = 1040; + static final int MSG_INITIALIZE_IME = 1040; static final int MSG_CREATE_SESSION = 1050; static final int MSG_START_INPUT = 2000; @@ -1968,7 +1969,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_ATTACH_TOKEN, mCurMethod, mCurToken)); + MSG_INITIALIZE_IME, mCurMethod, mCurToken)); if (mCurClient != null) { clearClientSessionLocked(mCurClient); requestClientSessionLocked(mCurClient); @@ -2217,8 +2218,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @SuppressWarnings("deprecation") - @Override - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + private void setImeWindowStatus(IBinder token, int vis, int backDisposition) { if (!calledWithValidToken(token)) { return; } @@ -2253,8 +2253,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - @Override - public void reportStartInput(IBinder token, IBinder startInputToken) { + private void reportStartInput(IBinder token, IBinder startInputToken) { if (!calledWithValidToken(token)) { return; } @@ -3158,8 +3157,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mWindowManagerInternal.getInputMethodWindowVisibleHeight(); } - @Override - public void clearLastInputMethodWindowForTransition(IBinder token) { + @BinderThread + private void clearLastInputMethodWindowForTransition(IBinder token) { if (!calledFromValidUser()) { return; } @@ -3354,11 +3353,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideCurrentInputLocked(0, null); } return true; - case MSG_ATTACH_TOKEN: + case MSG_INITIALIZE_IME: args = (SomeArgs)msg.obj; try { if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2); - ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); + final IBinder token = (IBinder) args.arg2; + ((IInputMethod) args.arg1).initializeInternal(token, + new InputMethodPrivilegedOperationsImpl(this, token)); } catch (RemoteException e) { } args.recycle(); @@ -4432,8 +4433,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - @Override - public IInputContentUriToken createInputContentUriToken(@Nullable IBinder token, + @BinderThread + private IInputContentUriToken createInputContentUriToken(@Nullable IBinder token, @Nullable Uri contentUri, @Nullable String packageName) { if (!calledFromValidUser()) { return null; @@ -4490,8 +4491,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - @Override - public void reportFullscreenMode(IBinder token, boolean fullscreen) { + @BinderThread + private void reportFullscreenMode(IBinder token, boolean fullscreen) { if (!calledFromValidUser()) { return; } @@ -4966,4 +4967,45 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return ShellCommandResult.SUCCESS; } } + + private static final class InputMethodPrivilegedOperationsImpl + extends IInputMethodPrivilegedOperations.Stub { + private final InputMethodManagerService mImms; + private final IBinder mToken; + InputMethodPrivilegedOperationsImpl(InputMethodManagerService imms, IBinder token) { + mImms = imms; + mToken = token; + } + + @BinderThread + @Override + public void setImeWindowStatus(int vis, int backDisposition) { + mImms.setImeWindowStatus(mToken, vis, backDisposition); + } + + @BinderThread + @Override + public void reportStartInput(IBinder startInputToken) { + mImms.reportStartInput(mToken, startInputToken); + } + + @BinderThread + @Override + public void clearLastInputMethodWindowForTransition() { + mImms.clearLastInputMethodWindowForTransition(mToken); + } + + @BinderThread + @Override + public IInputContentUriToken createInputContentUriToken(Uri contentUri, + String packageName) { + return mImms.createInputContentUriToken(mToken, contentUri, packageName); + } + + @BinderThread + @Override + public void reportFullscreenMode(boolean fullscreen) { + mImms.reportFullscreenMode(mToken, fullscreen); + } + } }