1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.inputmethod; 18 19 import android.annotation.AnyThread; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.os.Binder; 23 import android.os.DeadObjectException; 24 import android.os.IBinder; 25 import android.os.RemoteException; 26 import android.os.ResultReceiver; 27 import android.util.Slog; 28 import android.view.InputChannel; 29 import android.view.MotionEvent; 30 import android.view.inputmethod.CursorAnchorInfo; 31 import android.view.inputmethod.EditorInfo; 32 import android.view.inputmethod.ImeTracker; 33 import android.view.inputmethod.InputBinding; 34 import android.view.inputmethod.InputMethod; 35 import android.view.inputmethod.InputMethodSubtype; 36 import android.window.ImeOnBackInvokedDispatcher; 37 38 import com.android.internal.inputmethod.IConnectionlessHandwritingCallback; 39 import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; 40 import com.android.internal.inputmethod.IInputMethod; 41 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; 42 import com.android.internal.inputmethod.IInputMethodSession; 43 import com.android.internal.inputmethod.IInputMethodSessionCallback; 44 import com.android.internal.inputmethod.IRemoteInputConnection; 45 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; 46 import com.android.internal.inputmethod.InputMethodNavButtonFlags; 47 48 import java.util.List; 49 50 /** 51 * A wrapper class to invoke IPCs defined in {@link IInputMethod}. 52 */ 53 final class IInputMethodInvoker { 54 private static final String TAG = InputMethodManagerService.TAG; 55 private static final boolean DEBUG = InputMethodManagerService.DEBUG; 56 57 @AnyThread 58 @Nullable create(@ullable IInputMethod inputMethod)59 static IInputMethodInvoker create(@Nullable IInputMethod inputMethod) { 60 if (inputMethod == null) { 61 return null; 62 } 63 if (!Binder.isProxy(inputMethod)) { 64 // IInputMethodInvoker must be used only within the system_server and InputMethodService 65 // must not be running in the system_server. Therefore, "inputMethod" must be a Proxy. 66 throw new UnsupportedOperationException(inputMethod + " must have been a BinderProxy."); 67 } 68 return new IInputMethodInvoker(inputMethod); 69 } 70 71 /** 72 * A simplified version of {@link android.os.Debug#getCaller()}. 73 * 74 * @return method name of the caller. 75 */ 76 @AnyThread getCallerMethodName()77 private static String getCallerMethodName() { 78 final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); 79 if (callStack.length <= 4) { 80 return "<bottom of call stack>"; 81 } 82 return callStack[4].getMethodName(); 83 } 84 85 @AnyThread logRemoteException(@onNull RemoteException e)86 private static void logRemoteException(@NonNull RemoteException e) { 87 if (DEBUG || !(e instanceof DeadObjectException)) { 88 Slog.w(TAG, "IPC failed at IInputMethodInvoker#" + getCallerMethodName(), e); 89 } 90 } 91 92 @AnyThread getBinderIdentityHashCode(@ullable IInputMethodInvoker obj)93 static int getBinderIdentityHashCode(@Nullable IInputMethodInvoker obj) { 94 if (obj == null) { 95 return 0; 96 } 97 98 return System.identityHashCode(obj.mTarget); 99 } 100 101 @NonNull 102 private final IInputMethod mTarget; 103 IInputMethodInvoker(@onNull IInputMethod target)104 private IInputMethodInvoker(@NonNull IInputMethod target) { 105 mTarget = target; 106 } 107 108 @AnyThread 109 @NonNull asBinder()110 IBinder asBinder() { 111 return mTarget.asBinder(); 112 } 113 114 @AnyThread initializeInternal(IBinder token, IInputMethodPrivilegedOperations privilegedOperations, @InputMethodNavButtonFlags int navigationBarFlags)115 void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privilegedOperations, 116 @InputMethodNavButtonFlags int navigationBarFlags) { 117 final IInputMethod.InitParams params = new IInputMethod.InitParams(); 118 params.token = token; 119 params.privilegedOperations = privilegedOperations; 120 params.navigationBarFlags = navigationBarFlags; 121 try { 122 mTarget.initializeInternal(params); 123 } catch (RemoteException e) { 124 logRemoteException(e); 125 } 126 } 127 128 @AnyThread onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb)129 void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, 130 IInlineSuggestionsRequestCallback cb) { 131 try { 132 mTarget.onCreateInlineSuggestionsRequest(requestInfo, cb); 133 } catch (RemoteException e) { 134 logRemoteException(e); 135 } 136 } 137 138 @AnyThread bindInput(InputBinding binding)139 void bindInput(InputBinding binding) { 140 try { 141 mTarget.bindInput(binding); 142 } catch (RemoteException e) { 143 logRemoteException(e); 144 } 145 } 146 147 @AnyThread unbindInput()148 void unbindInput() { 149 try { 150 mTarget.unbindInput(); 151 } catch (RemoteException e) { 152 logRemoteException(e); 153 } 154 } 155 156 @AnyThread startInput(IBinder startInputToken, IRemoteInputConnection remoteInputConnection, EditorInfo editorInfo, boolean restarting, @InputMethodNavButtonFlags int navButtonFlags, @NonNull ImeOnBackInvokedDispatcher imeDispatcher)157 void startInput(IBinder startInputToken, IRemoteInputConnection remoteInputConnection, 158 EditorInfo editorInfo, boolean restarting, 159 @InputMethodNavButtonFlags int navButtonFlags, 160 @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { 161 final IInputMethod.StartInputParams params = new IInputMethod.StartInputParams(); 162 params.startInputToken = startInputToken; 163 params.remoteInputConnection = remoteInputConnection; 164 params.editorInfo = editorInfo; 165 params.restarting = restarting; 166 params.navigationBarFlags = navButtonFlags; 167 params.imeDispatcher = imeDispatcher; 168 try { 169 mTarget.startInput(params); 170 } catch (RemoteException e) { 171 logRemoteException(e); 172 } 173 } 174 175 @AnyThread onNavButtonFlagsChanged(@nputMethodNavButtonFlags int navButtonFlags)176 void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) { 177 try { 178 mTarget.onNavButtonFlagsChanged(navButtonFlags); 179 } catch (RemoteException e) { 180 logRemoteException(e); 181 } 182 } 183 184 @AnyThread createSession(InputChannel channel, IInputMethodSessionCallback callback)185 void createSession(InputChannel channel, IInputMethodSessionCallback callback) { 186 try { 187 mTarget.createSession(channel, callback); 188 } catch (RemoteException e) { 189 logRemoteException(e); 190 } 191 } 192 193 @AnyThread setSessionEnabled(IInputMethodSession session, boolean enabled)194 void setSessionEnabled(IInputMethodSession session, boolean enabled) { 195 try { 196 mTarget.setSessionEnabled(session, enabled); 197 } catch (RemoteException e) { 198 logRemoteException(e); 199 } 200 } 201 202 // TODO(b/192412909): Convert this back to void method 203 @AnyThread showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver)204 boolean showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, 205 @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { 206 try { 207 mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver); 208 } catch (RemoteException e) { 209 logRemoteException(e); 210 return false; 211 } 212 return true; 213 } 214 215 // TODO(b/192412909): Convert this back to void method 216 @AnyThread hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver)217 boolean hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, 218 int flags, ResultReceiver resultReceiver) { 219 try { 220 mTarget.hideSoftInput(hideInputToken, statsToken, flags, resultReceiver); 221 } catch (RemoteException e) { 222 logRemoteException(e); 223 return false; 224 } 225 return true; 226 } 227 228 @AnyThread updateEditorToolType(@otionEvent.ToolType int toolType)229 void updateEditorToolType(@MotionEvent.ToolType int toolType) { 230 try { 231 mTarget.updateEditorToolType(toolType); 232 } catch (RemoteException e) { 233 logRemoteException(e); 234 } 235 } 236 237 @AnyThread changeInputMethodSubtype(InputMethodSubtype subtype)238 void changeInputMethodSubtype(InputMethodSubtype subtype) { 239 try { 240 mTarget.changeInputMethodSubtype(subtype); 241 } catch (RemoteException e) { 242 logRemoteException(e); 243 } 244 } 245 246 @AnyThread canStartStylusHandwriting(int requestId, IConnectionlessHandwritingCallback connectionlessCallback, CursorAnchorInfo cursorAnchorInfo, boolean isConnectionlessForDelegation)247 void canStartStylusHandwriting(int requestId, 248 IConnectionlessHandwritingCallback connectionlessCallback, 249 CursorAnchorInfo cursorAnchorInfo, boolean isConnectionlessForDelegation) { 250 try { 251 mTarget.canStartStylusHandwriting(requestId, connectionlessCallback, cursorAnchorInfo, 252 isConnectionlessForDelegation); 253 } catch (RemoteException e) { 254 logRemoteException(e); 255 } 256 } 257 258 @AnyThread startStylusHandwriting(int requestId, InputChannel channel, List<MotionEvent> events)259 boolean startStylusHandwriting(int requestId, InputChannel channel, List<MotionEvent> events) { 260 try { 261 mTarget.startStylusHandwriting(requestId, channel, events); 262 } catch (RemoteException e) { 263 logRemoteException(e); 264 return false; 265 } 266 return true; 267 } 268 269 @AnyThread commitHandwritingDelegationTextIfAvailable()270 void commitHandwritingDelegationTextIfAvailable() { 271 try { 272 mTarget.commitHandwritingDelegationTextIfAvailable(); 273 } catch (RemoteException e) { 274 logRemoteException(e); 275 } 276 } 277 278 @AnyThread discardHandwritingDelegationText()279 void discardHandwritingDelegationText() { 280 try { 281 mTarget.discardHandwritingDelegationText(); 282 } catch (RemoteException e) { 283 logRemoteException(e); 284 } 285 } 286 287 @AnyThread initInkWindow()288 void initInkWindow() { 289 try { 290 mTarget.initInkWindow(); 291 } catch (RemoteException e) { 292 logRemoteException(e); 293 } 294 } 295 296 @AnyThread finishStylusHandwriting()297 void finishStylusHandwriting() { 298 try { 299 mTarget.finishStylusHandwriting(); 300 } catch (RemoteException e) { 301 logRemoteException(e); 302 } 303 } 304 305 @AnyThread removeStylusHandwritingWindow()306 void removeStylusHandwritingWindow() { 307 try { 308 mTarget.removeStylusHandwritingWindow(); 309 } catch (RemoteException e) { 310 logRemoteException(e); 311 } 312 } 313 314 @AnyThread setStylusWindowIdleTimeoutForTest(long timeout)315 void setStylusWindowIdleTimeoutForTest(long timeout) { 316 try { 317 mTarget.setStylusWindowIdleTimeoutForTest(timeout); 318 } catch (RemoteException e) { 319 logRemoteException(e); 320 } 321 } 322 } 323