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