• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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