OSDN Git Service

Merge "docs: Added note - test subscriptions cannot use reserved IDs" into nyc-dev
[android-x86/frameworks-base.git] / core / java / android / inputmethodservice / IInputMethodWrapper.java
1 /*
2  * Copyright (C) 2008 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 android.inputmethodservice;
18
19 import com.android.internal.os.HandlerCaller;
20 import com.android.internal.os.SomeArgs;
21 import com.android.internal.view.IInputContext;
22 import com.android.internal.view.IInputMethod;
23 import com.android.internal.view.IInputMethodSession;
24 import com.android.internal.view.IInputSessionCallback;
25 import com.android.internal.view.InputConnectionWrapper;
26
27 import android.content.Context;
28 import android.content.pm.PackageManager;
29 import android.os.Binder;
30 import android.os.IBinder;
31 import android.os.Message;
32 import android.os.RemoteException;
33 import android.os.ResultReceiver;
34 import android.util.Log;
35 import android.view.InputChannel;
36 import android.view.inputmethod.EditorInfo;
37 import android.view.inputmethod.InputBinding;
38 import android.view.inputmethod.InputConnection;
39 import android.view.inputmethod.InputConnectionInspector;
40 import android.view.inputmethod.InputMethod;
41 import android.view.inputmethod.InputMethodSession;
42 import android.view.inputmethod.InputMethodSubtype;
43
44 import java.io.FileDescriptor;
45 import java.io.PrintWriter;
46 import java.lang.ref.WeakReference;
47 import java.util.concurrent.CountDownLatch;
48 import java.util.concurrent.TimeUnit;
49
50 /**
51  * Implements the internal IInputMethod interface to convert incoming calls
52  * on to it back to calls on the public InputMethod interface, scheduling
53  * them on the main thread of the process.
54  */
55 class IInputMethodWrapper extends IInputMethod.Stub
56         implements HandlerCaller.Callback {
57     private static final String TAG = "InputMethodWrapper";
58
59     private static final int DO_DUMP = 1;
60     private static final int DO_ATTACH_TOKEN = 10;
61     private static final int DO_SET_INPUT_CONTEXT = 20;
62     private static final int DO_UNSET_INPUT_CONTEXT = 30;
63     private static final int DO_START_INPUT = 32;
64     private static final int DO_RESTART_INPUT = 34;
65     private static final int DO_CREATE_SESSION = 40;
66     private static final int DO_SET_SESSION_ENABLED = 45;
67     private static final int DO_REVOKE_SESSION = 50;
68     private static final int DO_SHOW_SOFT_INPUT = 60;
69     private static final int DO_HIDE_SOFT_INPUT = 70;
70     private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
71    
72     final WeakReference<AbstractInputMethodService> mTarget;
73     final Context mContext;
74     final HandlerCaller mCaller;
75     final WeakReference<InputMethod> mInputMethod;
76     final int mTargetSdkVersion;
77     
78     static class Notifier {
79         boolean notified;
80     }
81     
82     // NOTE: we should have a cache of these.
83     static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
84         final Context mContext;
85         final InputChannel mChannel;
86         final IInputSessionCallback mCb;
87
88         InputMethodSessionCallbackWrapper(Context context, InputChannel channel,
89                 IInputSessionCallback cb) {
90             mContext = context;
91             mChannel = channel;
92             mCb = cb;
93         }
94
95         @Override
96         public void sessionCreated(InputMethodSession session) {
97             try {
98                 if (session != null) {
99                     IInputMethodSessionWrapper wrap =
100                             new IInputMethodSessionWrapper(mContext, session, mChannel);
101                     mCb.sessionCreated(wrap);
102                 } else {
103                     if (mChannel != null) {
104                         mChannel.dispose();
105                     }
106                     mCb.sessionCreated(null);
107                 }
108             } catch (RemoteException e) {
109             }
110         }
111     }
112     
113     public IInputMethodWrapper(AbstractInputMethodService context,
114             InputMethod inputMethod) {
115         mTarget = new WeakReference<AbstractInputMethodService>(context);
116         mContext = context.getApplicationContext();
117         mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
118         mInputMethod = new WeakReference<InputMethod>(inputMethod);
119         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
120     }
121
122     public InputMethod getInternalInputMethod() {
123         return mInputMethod.get();
124     }
125
126     @Override
127     public void executeMessage(Message msg) {
128         InputMethod inputMethod = mInputMethod.get();
129         // Need a valid reference to the inputMethod for everything except a dump.
130         if (inputMethod == null && msg.what != DO_DUMP) {
131             Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
132             return;
133         }
134
135         switch (msg.what) {
136             case DO_DUMP: {
137                 AbstractInputMethodService target = mTarget.get();
138                 if (target == null) {
139                     return;
140                 }
141                 SomeArgs args = (SomeArgs)msg.obj;
142                 try {
143                     target.dump((FileDescriptor)args.arg1,
144                             (PrintWriter)args.arg2, (String[])args.arg3);
145                 } catch (RuntimeException e) {
146                     ((PrintWriter)args.arg2).println("Exception: " + e);
147                 }
148                 synchronized (args.arg4) {
149                     ((CountDownLatch)args.arg4).countDown();
150                 }
151                 args.recycle();
152                 return;
153             }
154             
155             case DO_ATTACH_TOKEN: {
156                 inputMethod.attachToken((IBinder)msg.obj);
157                 return;
158             }
159             case DO_SET_INPUT_CONTEXT: {
160                 inputMethod.bindInput((InputBinding)msg.obj);
161                 return;
162             }
163             case DO_UNSET_INPUT_CONTEXT:
164                 inputMethod.unbindInput();
165                 return;
166             case DO_START_INPUT: {
167                 SomeArgs args = (SomeArgs)msg.obj;
168                 int missingMethods = msg.arg1;
169                 IInputContext inputContext = (IInputContext)args.arg1;
170                 InputConnection ic = inputContext != null
171                         ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null;
172                 EditorInfo info = (EditorInfo)args.arg2;
173                 info.makeCompatible(mTargetSdkVersion);
174                 inputMethod.startInput(ic, info);
175                 args.recycle();
176                 return;
177             }
178             case DO_RESTART_INPUT: {
179                 SomeArgs args = (SomeArgs)msg.obj;
180                 int missingMethods = msg.arg1;
181                 IInputContext inputContext = (IInputContext)args.arg1;
182                 InputConnection ic = inputContext != null
183                         ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null;
184                 EditorInfo info = (EditorInfo)args.arg2;
185                 info.makeCompatible(mTargetSdkVersion);
186                 inputMethod.restartInput(ic, info);
187                 args.recycle();
188                 return;
189             }
190             case DO_CREATE_SESSION: {
191                 SomeArgs args = (SomeArgs)msg.obj;
192                 inputMethod.createSession(new InputMethodSessionCallbackWrapper(
193                         mContext, (InputChannel)args.arg1,
194                         (IInputSessionCallback)args.arg2));
195                 args.recycle();
196                 return;
197             }
198             case DO_SET_SESSION_ENABLED:
199                 inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
200                         msg.arg1 != 0);
201                 return;
202             case DO_REVOKE_SESSION:
203                 inputMethod.revokeSession((InputMethodSession)msg.obj);
204                 return;
205             case DO_SHOW_SOFT_INPUT:
206                 inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
207                 return;
208             case DO_HIDE_SOFT_INPUT:
209                 inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
210                 return;
211             case DO_CHANGE_INPUTMETHOD_SUBTYPE:
212                 inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
213                 return;
214         }
215         Log.w(TAG, "Unhandled message code: " + msg.what);
216     }
217
218     @Override
219     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
220         AbstractInputMethodService target = mTarget.get();
221         if (target == null) {
222             return;
223         }
224         if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
225                 != PackageManager.PERMISSION_GRANTED) {
226             
227             fout.println("Permission Denial: can't dump InputMethodManager from from pid="
228                     + Binder.getCallingPid()
229                     + ", uid=" + Binder.getCallingUid());
230             return;
231         }
232
233         CountDownLatch latch = new CountDownLatch(1);
234         mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP,
235                 fd, fout, args, latch));
236         try {
237             if (!latch.await(5, TimeUnit.SECONDS)) {
238                 fout.println("Timeout waiting for dump");
239             }
240         } catch (InterruptedException e) {
241             fout.println("Interrupted waiting for dump");
242         }
243     }
244
245     @Override
246     public void attachToken(IBinder token) {
247         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
248     }
249
250     @Override
251     public void bindInput(InputBinding binding) {
252         // This IInputContext is guaranteed to implement all the methods.
253         final int missingMethodFlags = 0;
254         InputConnection ic = new InputConnectionWrapper(mTarget,
255                 IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags);
256         InputBinding nu = new InputBinding(ic, binding);
257         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
258     }
259
260     @Override
261     public void unbindInput() {
262         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
263     }
264
265     @Override
266     public void startInput(IInputContext inputContext,
267             @InputConnectionInspector.MissingMethodFlags final int missingMethods,
268             EditorInfo attribute) {
269         mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_START_INPUT,
270                 missingMethods, inputContext, attribute));
271     }
272
273     @Override
274     public void restartInput(IInputContext inputContext,
275             @InputConnectionInspector.MissingMethodFlags final int missingMethods,
276             EditorInfo attribute) {
277         mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_RESTART_INPUT,
278                 missingMethods, inputContext, attribute));
279     }
280
281     @Override
282     public void createSession(InputChannel channel, IInputSessionCallback callback) {
283         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
284                 channel, callback));
285     }
286
287     @Override
288     public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
289         try {
290             InputMethodSession ls = ((IInputMethodSessionWrapper)
291                     session).getInternalInputMethodSession();
292             if (ls == null) {
293                 Log.w(TAG, "Session is already finished: " + session);
294                 return;
295             }
296             mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
297                     DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls));
298         } catch (ClassCastException e) {
299             Log.w(TAG, "Incoming session not of correct type: " + session, e);
300         }
301     }
302
303     @Override
304     public void revokeSession(IInputMethodSession session) {
305         try {
306             InputMethodSession ls = ((IInputMethodSessionWrapper)
307                     session).getInternalInputMethodSession();
308             if (ls == null) {
309                 Log.w(TAG, "Session is already finished: " + session);
310                 return;
311             }
312             mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
313         } catch (ClassCastException e) {
314             Log.w(TAG, "Incoming session not of correct type: " + session, e);
315         }
316     }
317
318     @Override
319     public void showSoftInput(int flags, ResultReceiver resultReceiver) {
320         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
321                 flags, resultReceiver));
322     }
323
324     @Override
325     public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
326         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
327                 flags, resultReceiver));
328     }
329
330     @Override
331     public void changeInputMethodSubtype(InputMethodSubtype subtype) {
332         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
333                 subtype));
334     }
335 }