summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java60
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java192
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();
+ }
+ }
+}