Loading core/java/android/inputmethodservice/InputMethodService.java +11 −49 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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(); } // This needs to be dispatched to interface methods rather than doStartInput(). // Otherwise IME developers who have overridden those interface methods will lose // notifications. Loading Loading @@ -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(); } } /** Loading Loading @@ -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(); } } /** Loading Loading @@ -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(); } } /** Loading Loading @@ -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); final IInputContentUriToken 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); + " packageName=" + editorInfo.packageName); return; } inputContentInfo.setUriToken(uriToken); return; } private static int mapToImeWindowStatus(boolean isInputViewShown) { Loading core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java 0 → 100644 +192 −0 Original line number Diff line number Diff line /* * 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(); } } } Loading
core/java/android/inputmethodservice/InputMethodService.java +11 −49 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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(); } // This needs to be dispatched to interface methods rather than doStartInput(). // Otherwise IME developers who have overridden those interface methods will lose // notifications. Loading Loading @@ -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(); } } /** Loading Loading @@ -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(); } } /** Loading Loading @@ -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(); } } /** Loading Loading @@ -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); final IInputContentUriToken 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); + " packageName=" + editorInfo.packageName); return; } inputContentInfo.setUriToken(uriToken); return; } private static int mapToImeWindowStatus(boolean isInputViewShown) { Loading
core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java 0 → 100644 +192 −0 Original line number Diff line number Diff line /* * 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(); } } }