diff options
| author | 2018-09-09 20:48:34 -0700 | |
|---|---|---|
| committer | 2018-09-09 20:48:34 -0700 | |
| commit | 2bc3d6f0a6da8bbd503d8615538e72b69ba16c9c (patch) | |
| tree | 45212f01c7764480472128a35187d90e72198126 | |
| parent | 83b7d72a055bb6a1f85f1c25c02a2d529f5da1ca (diff) | |
Add a wrapper for IInputMethodPrivilegedOperations
This is a mechanical refactoring to split out boilerplate code around
IPCs from InputMethodManager to another file.
Bug: 114418674
Bug: 113177698
Test: atest CtsInputMethodTestCases CtsInputMethodServiceHostTestCases
Change-Id: I9ca251482867daea84c2777f74fd9b8a2b0f29cd
| -rw-r--r-- | core/java/android/inputmethodservice/InputMethodService.java | 60 | ||||
| -rw-r--r-- | core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java | 192 |
2 files changed, 203 insertions, 49 deletions
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ea7d42ee79a9..d3abe7f38808 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -42,7 +42,6 @@ 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; @@ -82,6 +81,7 @@ import android.widget.TextView; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; +import com.android.internal.inputmethod.InputMethodPrivilegedOperations; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -346,7 +346,7 @@ public class InputMethodService extends AbstractInputMethodService { private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING; InputMethodManager mImm; - private IInputMethodPrivilegedOperations mPrivOps; + private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations(); @UnsupportedAppUsage int mTheme = 0; @@ -457,11 +457,7 @@ public class InputMethodService extends AbstractInputMethodService { @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; + mPrivOps.set(privilegedOperations); attachToken(token); } @@ -540,12 +536,7 @@ public class InputMethodService extends AbstractInputMethodService { public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, @NonNull IBinder startInputToken) { - try { - mPrivOps.reportStartInput(startInputToken); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - + mPrivOps.reportStartInput(startInputToken); // This needs to be dispatched to interface methods rather than doStartInput(). // Otherwise IME developers who have overridden those interface methods will lose // notifications. @@ -607,14 +598,7 @@ 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(); - } + mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); } /** @@ -1223,14 +1207,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void reportFullscreenMode() { - if (mPrivOps == null) { - return; - } - try { - mPrivOps.reportFullscreenMode(mIsFullscreen); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mPrivOps.reportFullscreenMode(mIsFullscreen); } /** @@ -1945,14 +1922,7 @@ public class InputMethodService extends AbstractInputMethodService { * <p>TODO: We probably need to reconsider how IME should be handled.</p> */ private void clearLastInputMethodWindowForTransition() { - if (mPrivOps == null) { - return; - } - try { - mPrivOps.clearLastInputMethodWindowForTransition(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mPrivOps.clearLastInputMethodWindowForTransition(); } /** @@ -2885,23 +2855,15 @@ public class InputMethodService extends AbstractInputMethodService { */ 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) { + final IInputContentUriToken uriToken = + mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName); + if (uriToken == null) { Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString() - + " packageName=" + editorInfo.packageName, e); + + " packageName=" + editorInfo.packageName); return; } inputContentInfo.setUriToken(uriToken); - return; } private static int mapToImeWindowStatus(boolean isInputViewShown) { diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java new file mode 100644 index 000000000000..45719f057ed6 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -0,0 +1,192 @@ +/* + * 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.annotation.AnyThread; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +/** + * A utility class to take care of boilerplate code around IPCs. + */ +public final class InputMethodPrivilegedOperations { + private static final String TAG = "InputMethodPrivilegedOperations"; + + private static final class OpsHolder { + @Nullable + @GuardedBy("this") + private IInputMethodPrivilegedOperations mPrivOps; + + /** + * Sets {@link IInputMethodPrivilegedOperations}. + * + * <p>This method can be called only once.</p> + * + * @param privOps Binder interface to be set + */ + @AnyThread + public synchronized void set(IInputMethodPrivilegedOperations privOps) { + if (mPrivOps != null) { + throw new IllegalStateException( + "IInputMethodPrivilegedOperations must be set at most once." + + " privOps=" + privOps); + } + mPrivOps = privOps; + } + + /** + * A simplified version of {@link android.os.Debug#getCaller()}. + * + * @return method name of the caller. + */ + @AnyThread + private static String getCallerMethodName() { + final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); + if (callStack.length <= 4) { + return "<bottom of call stack>"; + } + return callStack[4].getMethodName(); + } + + @AnyThread + @Nullable + public synchronized IInputMethodPrivilegedOperations getAndWarnIfNull() { + if (mPrivOps == null) { + Log.e(TAG, getCallerMethodName() + " is ignored." + + " Call it within attachToken() and InputMethodService.onDestroy()"); + } + return mPrivOps; + } + } + private final OpsHolder mOps = new OpsHolder(); + + /** + * Sets {@link IInputMethodPrivilegedOperations}. + * + * <p>This method can be called only once.</p> + * + * @param privOps Binder interface to be set + */ + @AnyThread + public void set(IInputMethodPrivilegedOperations privOps) { + mOps.set(privOps); + } + + /** + * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatus(int, int)}. + * + * @param vis visibility flags + * @param backDisposition disposition flags + * @see android.inputmethodservice.InputMethodService#IME_ACTIVE + * @see android.inputmethodservice.InputMethodService#IME_VISIBLE + * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT + * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING + */ + @AnyThread + public void setImeWindowStatus(int vis, int backDisposition) { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.setImeWindowStatus(vis, backDisposition); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder)}. + * + * @param startInputToken {@link IBinder} token to distinguish startInput session + */ + @AnyThread + public void reportStartInput(IBinder startInputToken) { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.reportStartInput(startInputToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Calls {@link IInputMethodPrivilegedOperations#clearLastInputMethodWindowForTransition()}. + */ + @AnyThread + public void clearLastInputMethodWindowForTransition() { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.clearLastInputMethodWindowForTransition(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String)}. + * + * @param contentUri Content URI to which a temporary read permission should be granted + * @param packageName Indicates what package needs to have a temporary read permission + * @return special Binder token that should be set to + * {@link android.view.inputmethod.InputContentInfo#setUriToken(IInputContentUriToken)} + */ + @AnyThread + public IInputContentUriToken createInputContentUriToken(Uri contentUri, String packageName) { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return null; + } + try { + return ops.createInputContentUriToken(contentUri, packageName); + } catch (RemoteException e) { + // For historical reasons, this error was silently ignored. + // Note that the caller already logs error so we do not need additional Log.e() here. + // TODO(team): Check if it is safe to rethrow error here. + return null; + } + } + + /** + * Calls {@link IInputMethodPrivilegedOperations#reportFullscreenMode(boolean)}. + * + * @param fullscreen {@code true} if the IME enters full screen mode + */ + @AnyThread + public void reportFullscreenMode(boolean fullscreen) { + final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); + if (ops == null) { + return; + } + try { + ops.reportFullscreenMode(fullscreen); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} |