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