• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package android.inputmethodservice;
2 
3 import com.android.internal.os.HandlerCaller;
4 import com.android.internal.view.IInputContext;
5 import com.android.internal.view.IInputMethod;
6 import com.android.internal.view.IInputMethodCallback;
7 import com.android.internal.view.IInputMethodSession;
8 import com.android.internal.view.InputConnectionWrapper;
9 
10 import android.content.Context;
11 import android.content.pm.PackageManager;
12 import android.os.Binder;
13 import android.os.IBinder;
14 import android.os.Message;
15 import android.os.RemoteException;
16 import android.os.ResultReceiver;
17 import android.util.Log;
18 import android.view.inputmethod.EditorInfo;
19 import android.view.inputmethod.InputBinding;
20 import android.view.inputmethod.InputConnection;
21 import android.view.inputmethod.InputMethod;
22 import android.view.inputmethod.InputMethodSession;
23 
24 import java.io.FileDescriptor;
25 import java.io.PrintWriter;
26 import java.util.concurrent.CountDownLatch;
27 import java.util.concurrent.TimeUnit;
28 
29 /**
30  * Implements the internal IInputMethod interface to convert incoming calls
31  * on to it back to calls on the public InputMethod interface, scheduling
32  * them on the main thread of the process.
33  */
34 class IInputMethodWrapper extends IInputMethod.Stub
35         implements HandlerCaller.Callback {
36     private static final String TAG = "InputMethodWrapper";
37     private static final boolean DEBUG = false;
38 
39     private static final int DO_DUMP = 1;
40     private static final int DO_ATTACH_TOKEN = 10;
41     private static final int DO_SET_INPUT_CONTEXT = 20;
42     private static final int DO_UNSET_INPUT_CONTEXT = 30;
43     private static final int DO_START_INPUT = 32;
44     private static final int DO_RESTART_INPUT = 34;
45     private static final int DO_CREATE_SESSION = 40;
46     private static final int DO_SET_SESSION_ENABLED = 45;
47     private static final int DO_REVOKE_SESSION = 50;
48     private static final int DO_SHOW_SOFT_INPUT = 60;
49     private static final int DO_HIDE_SOFT_INPUT = 70;
50 
51     final AbstractInputMethodService mTarget;
52     final HandlerCaller mCaller;
53     final InputMethod mInputMethod;
54 
55     static class Notifier {
56         boolean notified;
57     }
58 
59     // NOTE: we should have a cache of these.
60     static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
61         final Context mContext;
62         final IInputMethodCallback mCb;
InputMethodSessionCallbackWrapper(Context context, IInputMethodCallback cb)63         InputMethodSessionCallbackWrapper(Context context, IInputMethodCallback cb) {
64             mContext = context;
65             mCb = cb;
66         }
sessionCreated(InputMethodSession session)67         public void sessionCreated(InputMethodSession session) {
68             try {
69                 if (session != null) {
70                     IInputMethodSessionWrapper wrap =
71                             new IInputMethodSessionWrapper(mContext, session);
72                     mCb.sessionCreated(wrap);
73                 } else {
74                     mCb.sessionCreated(null);
75                 }
76             } catch (RemoteException e) {
77             }
78         }
79     }
80 
IInputMethodWrapper(AbstractInputMethodService context, InputMethod inputMethod)81     public IInputMethodWrapper(AbstractInputMethodService context,
82             InputMethod inputMethod) {
83         mTarget = context;
84         mCaller = new HandlerCaller(context, this);
85         mInputMethod = inputMethod;
86     }
87 
getInternalInputMethod()88     public InputMethod getInternalInputMethod() {
89         return mInputMethod;
90     }
91 
executeMessage(Message msg)92     public void executeMessage(Message msg) {
93         switch (msg.what) {
94             case DO_DUMP: {
95                 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
96                 try {
97                     mTarget.dump((FileDescriptor)args.arg1,
98                             (PrintWriter)args.arg2, (String[])args.arg3);
99                 } catch (RuntimeException e) {
100                     ((PrintWriter)args.arg2).println("Exception: " + e);
101                 }
102                 synchronized (args.arg4) {
103                     ((CountDownLatch)args.arg4).countDown();
104                 }
105                 return;
106             }
107 
108             case DO_ATTACH_TOKEN: {
109                 mInputMethod.attachToken((IBinder)msg.obj);
110                 return;
111             }
112             case DO_SET_INPUT_CONTEXT: {
113                 mInputMethod.bindInput((InputBinding)msg.obj);
114                 return;
115             }
116             case DO_UNSET_INPUT_CONTEXT:
117                 mInputMethod.unbindInput();
118                 return;
119             case DO_START_INPUT: {
120                 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
121                 IInputContext inputContext = (IInputContext)args.arg1;
122                 InputConnection ic = inputContext != null
123                         ? new InputConnectionWrapper(inputContext) : null;
124                 mInputMethod.startInput(ic, (EditorInfo)args.arg2);
125                 return;
126             }
127             case DO_RESTART_INPUT: {
128                 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
129                 IInputContext inputContext = (IInputContext)args.arg1;
130                 InputConnection ic = inputContext != null
131                         ? new InputConnectionWrapper(inputContext) : null;
132                 mInputMethod.restartInput(ic, (EditorInfo)args.arg2);
133                 return;
134             }
135             case DO_CREATE_SESSION: {
136                 mInputMethod.createSession(new InputMethodSessionCallbackWrapper(
137                         mCaller.mContext, (IInputMethodCallback)msg.obj));
138                 return;
139             }
140             case DO_SET_SESSION_ENABLED:
141                 mInputMethod.setSessionEnabled((InputMethodSession)msg.obj,
142                         msg.arg1 != 0);
143                 return;
144             case DO_REVOKE_SESSION:
145                 mInputMethod.revokeSession((InputMethodSession)msg.obj);
146                 return;
147             case DO_SHOW_SOFT_INPUT:
148                 mInputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
149                 return;
150             case DO_HIDE_SOFT_INPUT:
151                 mInputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
152                 return;
153         }
154         Log.w(TAG, "Unhandled message code: " + msg.what);
155     }
156 
dump(FileDescriptor fd, PrintWriter fout, String[] args)157     @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
158         if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
159                 != PackageManager.PERMISSION_GRANTED) {
160 
161             fout.println("Permission Denial: can't dump InputMethodManager from from pid="
162                     + Binder.getCallingPid()
163                     + ", uid=" + Binder.getCallingUid());
164             return;
165         }
166 
167         CountDownLatch latch = new CountDownLatch(1);
168         mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP,
169                 fd, fout, args, latch));
170         try {
171             if (!latch.await(5, TimeUnit.SECONDS)) {
172                 fout.println("Timeout waiting for dump");
173             }
174         } catch (InterruptedException e) {
175             fout.println("Interrupted waiting for dump");
176         }
177     }
178 
attachToken(IBinder token)179     public void attachToken(IBinder token) {
180         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
181     }
182 
bindInput(InputBinding binding)183     public void bindInput(InputBinding binding) {
184         InputConnection ic = new InputConnectionWrapper(
185                 IInputContext.Stub.asInterface(binding.getConnectionToken()));
186         InputBinding nu = new InputBinding(ic, binding);
187         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
188     }
189 
unbindInput()190     public void unbindInput() {
191         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
192     }
193 
startInput(IInputContext inputContext, EditorInfo attribute)194     public void startInput(IInputContext inputContext, EditorInfo attribute) {
195         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
196                 inputContext, attribute));
197     }
198 
restartInput(IInputContext inputContext, EditorInfo attribute)199     public void restartInput(IInputContext inputContext, EditorInfo attribute) {
200         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
201                 inputContext, attribute));
202     }
203 
createSession(IInputMethodCallback callback)204     public void createSession(IInputMethodCallback callback) {
205         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback));
206     }
207 
setSessionEnabled(IInputMethodSession session, boolean enabled)208     public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
209         try {
210             InputMethodSession ls = ((IInputMethodSessionWrapper)
211                     session).getInternalInputMethodSession();
212             mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
213                     DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls));
214         } catch (ClassCastException e) {
215             Log.w(TAG, "Incoming session not of correct type: " + session, e);
216         }
217     }
218 
revokeSession(IInputMethodSession session)219     public void revokeSession(IInputMethodSession session) {
220         try {
221             InputMethodSession ls = ((IInputMethodSessionWrapper)
222                     session).getInternalInputMethodSession();
223             mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
224         } catch (ClassCastException e) {
225             Log.w(TAG, "Incoming session not of correct type: " + session, e);
226         }
227     }
228 
showSoftInput(int flags, ResultReceiver resultReceiver)229     public void showSoftInput(int flags, ResultReceiver resultReceiver) {
230         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
231                 flags, resultReceiver));
232     }
233 
hideSoftInput(int flags, ResultReceiver resultReceiver)234     public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
235         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
236                 flags, resultReceiver));
237     }
238 }
239