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.EditorInfo; 31 import android.view.inputmethod.ImeTracker; 32 import android.view.inputmethod.InputBinding; 33 import android.view.inputmethod.InputMethod; 34 import android.view.inputmethod.InputMethodSubtype; 35 import android.window.ImeOnBackInvokedDispatcher; 36 37 import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; 38 import com.android.internal.inputmethod.IInputMethod; 39 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; 40 import com.android.internal.inputmethod.IInputMethodSession; 41 import com.android.internal.inputmethod.IInputMethodSessionCallback; 42 import com.android.internal.inputmethod.IRemoteInputConnection; 43 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; 44 import com.android.internal.inputmethod.InputMethodNavButtonFlags; 45 46 import java.util.List; 47 48 /** 49 * A wrapper class to invoke IPCs defined in {@link IInputMethod}. 50 */ 51 final class IInputMethodInvoker { 52 private static final String TAG = InputMethodManagerService.TAG; 53 private static final boolean DEBUG = InputMethodManagerService.DEBUG; 54 55 @AnyThread 56 @Nullable create(@ullable IInputMethod inputMethod)57 static IInputMethodInvoker create(@Nullable IInputMethod inputMethod) { 58 if (inputMethod == null) { 59 return null; 60 } 61 if (!Binder.isProxy(inputMethod)) { 62 // IInputMethodInvoker must be used only within the system_server and InputMethodService 63 // must not be running in the system_server. Therefore, "inputMethod" must be a Proxy. 64 throw new UnsupportedOperationException(inputMethod + " must have been a BinderProxy."); 65 } 66 return new IInputMethodInvoker(inputMethod); 67 } 68 69 /** 70 * A simplified version of {@link android.os.Debug#getCaller()}. 71 * 72 * @return method name of the caller. 73 */ 74 @AnyThread getCallerMethodName()75 private static String getCallerMethodName() { 76 final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); 77 if (callStack.length <= 4) { 78 return "<bottom of call stack>"; 79 } 80 return callStack[4].getMethodName(); 81 } 82 83 @AnyThread logRemoteException(@onNull RemoteException e)84 private static void logRemoteException(@NonNull RemoteException e) { 85 if (DEBUG || !(e instanceof DeadObjectException)) { 86 Slog.w(TAG, "IPC failed at IInputMethodInvoker#" + getCallerMethodName(), e); 87 } 88 } 89 90 @AnyThread getBinderIdentityHashCode(@ullable IInputMethodInvoker obj)91 static int getBinderIdentityHashCode(@Nullable IInputMethodInvoker obj) { 92 if (obj == null) { 93 return 0; 94 } 95 96 return System.identityHashCode(obj.mTarget); 97 } 98 99 @NonNull 100 private final IInputMethod mTarget; 101 IInputMethodInvoker(@onNull IInputMethod target)102 private IInputMethodInvoker(@NonNull IInputMethod target) { 103 mTarget = target; 104 } 105 106 @AnyThread 107 @NonNull asBinder()108 IBinder asBinder() { 109 return mTarget.asBinder(); 110 } 111 112 @AnyThread initializeInternal(IBinder token, IInputMethodPrivilegedOperations privilegedOperations, @InputMethodNavButtonFlags int navigationBarFlags)113 void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privilegedOperations, 114 @InputMethodNavButtonFlags int navigationBarFlags) { 115 final IInputMethod.InitParams params = new IInputMethod.InitParams(); 116 params.token = token; 117 params.privilegedOperations = privilegedOperations; 118 params.navigationBarFlags = navigationBarFlags; 119 try { 120 mTarget.initializeInternal(params); 121 } catch (RemoteException e) { 122 logRemoteException(e); 123 } 124 } 125 126 @AnyThread onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb)127 void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, 128 IInlineSuggestionsRequestCallback cb) { 129 try { 130 mTarget.onCreateInlineSuggestionsRequest(requestInfo, cb); 131 } catch (RemoteException e) { 132 logRemoteException(e); 133 } 134 } 135 136 @AnyThread bindInput(InputBinding binding)137 void bindInput(InputBinding binding) { 138 try { 139 mTarget.bindInput(binding); 140 } catch (RemoteException e) { 141 logRemoteException(e); 142 } 143 } 144 145 @AnyThread unbindInput()146 void unbindInput() { 147 try { 148 mTarget.unbindInput(); 149 } catch (RemoteException e) { 150 logRemoteException(e); 151 } 152 } 153 154 @AnyThread startInput(IBinder startInputToken, IRemoteInputConnection remoteInputConnection, EditorInfo editorInfo, boolean restarting, @InputMethodNavButtonFlags int navButtonFlags, @NonNull ImeOnBackInvokedDispatcher imeDispatcher)155 void startInput(IBinder startInputToken, IRemoteInputConnection remoteInputConnection, 156 EditorInfo editorInfo, boolean restarting, 157 @InputMethodNavButtonFlags int navButtonFlags, 158 @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { 159 final IInputMethod.StartInputParams params = new IInputMethod.StartInputParams(); 160 params.startInputToken = startInputToken; 161 params.remoteInputConnection = remoteInputConnection; 162 params.editorInfo = editorInfo; 163 params.restarting = restarting; 164 params.navigationBarFlags = navButtonFlags; 165 params.imeDispatcher = imeDispatcher; 166 try { 167 mTarget.startInput(params); 168 } catch (RemoteException e) { 169 logRemoteException(e); 170 } 171 } 172 173 @AnyThread onNavButtonFlagsChanged(@nputMethodNavButtonFlags int navButtonFlags)174 void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) { 175 try { 176 mTarget.onNavButtonFlagsChanged(navButtonFlags); 177 } catch (RemoteException e) { 178 logRemoteException(e); 179 } 180 } 181 182 @AnyThread createSession(InputChannel channel, IInputMethodSessionCallback callback)183 void createSession(InputChannel channel, IInputMethodSessionCallback callback) { 184 try { 185 mTarget.createSession(channel, callback); 186 } catch (RemoteException e) { 187 logRemoteException(e); 188 } 189 } 190 191 @AnyThread setSessionEnabled(IInputMethodSession session, boolean enabled)192 void setSessionEnabled(IInputMethodSession session, boolean enabled) { 193 try { 194 mTarget.setSessionEnabled(session, enabled); 195 } catch (RemoteException e) { 196 logRemoteException(e); 197 } 198 } 199 200 // TODO(b/192412909): Convert this back to void method 201 @AnyThread showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver)202 boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, 203 @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { 204 try { 205 mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver); 206 } catch (RemoteException e) { 207 logRemoteException(e); 208 return false; 209 } 210 return true; 211 } 212 213 // TODO(b/192412909): Convert this back to void method 214 @AnyThread hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver)215 boolean hideSoftInput(IBinder hideInputToken, @Nullable ImeTracker.Token statsToken, int flags, 216 ResultReceiver resultReceiver) { 217 try { 218 mTarget.hideSoftInput(hideInputToken, statsToken, flags, resultReceiver); 219 } catch (RemoteException e) { 220 logRemoteException(e); 221 return false; 222 } 223 return true; 224 } 225 226 @AnyThread updateEditorToolType(@otionEvent.ToolType int toolType)227 void updateEditorToolType(@MotionEvent.ToolType int toolType) { 228 try { 229 mTarget.updateEditorToolType(toolType); 230 } catch (RemoteException e) { 231 logRemoteException(e); 232 } 233 } 234 235 @AnyThread changeInputMethodSubtype(InputMethodSubtype subtype)236 void changeInputMethodSubtype(InputMethodSubtype subtype) { 237 try { 238 mTarget.changeInputMethodSubtype(subtype); 239 } catch (RemoteException e) { 240 logRemoteException(e); 241 } 242 } 243 244 @AnyThread canStartStylusHandwriting(int requestId)245 void canStartStylusHandwriting(int requestId) { 246 try { 247 mTarget.canStartStylusHandwriting(requestId); 248 } catch (RemoteException e) { 249 logRemoteException(e); 250 } 251 } 252 253 @AnyThread startStylusHandwriting(int requestId, InputChannel channel, List<MotionEvent> events)254 boolean startStylusHandwriting(int requestId, InputChannel channel, List<MotionEvent> events) { 255 try { 256 mTarget.startStylusHandwriting(requestId, channel, events); 257 } catch (RemoteException e) { 258 logRemoteException(e); 259 return false; 260 } 261 return true; 262 } 263 264 @AnyThread initInkWindow()265 void initInkWindow() { 266 try { 267 mTarget.initInkWindow(); 268 } catch (RemoteException e) { 269 logRemoteException(e); 270 } 271 } 272 273 @AnyThread finishStylusHandwriting()274 void finishStylusHandwriting() { 275 try { 276 mTarget.finishStylusHandwriting(); 277 } catch (RemoteException e) { 278 logRemoteException(e); 279 } 280 } 281 282 @AnyThread removeStylusHandwritingWindow()283 void removeStylusHandwritingWindow() { 284 try { 285 mTarget.removeStylusHandwritingWindow(); 286 } catch (RemoteException e) { 287 logRemoteException(e); 288 } 289 } 290 291 @AnyThread setStylusWindowIdleTimeoutForTest(long timeout)292 void setStylusWindowIdleTimeoutForTest(long timeout) { 293 try { 294 mTarget.setStylusWindowIdleTimeoutForTest(timeout); 295 } catch (RemoteException e) { 296 logRemoteException(e); 297 } 298 } 299 } 300