blob: 5275314b036aa4801a38a7b083d378c63fc2c830 [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080017package android.inputmethodservice;
18
19import com.android.internal.os.HandlerCaller;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070020import com.android.internal.os.SomeArgs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import com.android.internal.view.IInputContext;
22import com.android.internal.view.IInputMethod;
23import com.android.internal.view.IInputMethodCallback;
24import com.android.internal.view.IInputMethodSession;
25import com.android.internal.view.InputConnectionWrapper;
26
27import android.content.Context;
28import android.content.pm.PackageManager;
29import android.os.Binder;
30import android.os.IBinder;
31import android.os.Message;
32import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -080033import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.util.Log;
35import android.view.inputmethod.EditorInfo;
36import android.view.inputmethod.InputBinding;
37import android.view.inputmethod.InputConnection;
38import android.view.inputmethod.InputMethod;
39import android.view.inputmethod.InputMethodSession;
satokab751aa2010-09-14 19:17:36 +090040import android.view.inputmethod.InputMethodSubtype;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
Devin Taylor0c33ed22010-02-23 13:26:46 -060044import java.lang.ref.WeakReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import java.util.concurrent.CountDownLatch;
46import java.util.concurrent.TimeUnit;
47
48/**
49 * Implements the internal IInputMethod interface to convert incoming calls
50 * on to it back to calls on the public InputMethod interface, scheduling
51 * them on the main thread of the process.
52 */
53class IInputMethodWrapper extends IInputMethod.Stub
54 implements HandlerCaller.Callback {
55 private static final String TAG = "InputMethodWrapper";
56 private static final boolean DEBUG = false;
57
58 private static final int DO_DUMP = 1;
59 private static final int DO_ATTACH_TOKEN = 10;
60 private static final int DO_SET_INPUT_CONTEXT = 20;
61 private static final int DO_UNSET_INPUT_CONTEXT = 30;
62 private static final int DO_START_INPUT = 32;
63 private static final int DO_RESTART_INPUT = 34;
64 private static final int DO_CREATE_SESSION = 40;
65 private static final int DO_SET_SESSION_ENABLED = 45;
66 private static final int DO_REVOKE_SESSION = 50;
67 private static final int DO_SHOW_SOFT_INPUT = 60;
68 private static final int DO_HIDE_SOFT_INPUT = 70;
satokab751aa2010-09-14 19:17:36 +090069 private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
Devin Taylor0c33ed22010-02-23 13:26:46 -060071 final WeakReference<AbstractInputMethodService> mTarget;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 final HandlerCaller mCaller;
Devin Taylor0c33ed22010-02-23 13:26:46 -060073 final WeakReference<InputMethod> mInputMethod;
Dianne Hackborndea3ef72010-10-28 14:24:22 -070074 final int mTargetSdkVersion;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
76 static class Notifier {
77 boolean notified;
78 }
79
80 // NOTE: we should have a cache of these.
81 static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
82 final Context mContext;
83 final IInputMethodCallback mCb;
84 InputMethodSessionCallbackWrapper(Context context, IInputMethodCallback cb) {
85 mContext = context;
86 mCb = cb;
87 }
88 public void sessionCreated(InputMethodSession session) {
89 try {
90 if (session != null) {
91 IInputMethodSessionWrapper wrap =
92 new IInputMethodSessionWrapper(mContext, session);
93 mCb.sessionCreated(wrap);
94 } else {
95 mCb.sessionCreated(null);
96 }
97 } catch (RemoteException e) {
98 }
99 }
100 }
101
102 public IInputMethodWrapper(AbstractInputMethodService context,
103 InputMethod inputMethod) {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600104 mTarget = new WeakReference<AbstractInputMethodService>(context);
105 mCaller = new HandlerCaller(context.getApplicationContext(), this);
106 mInputMethod = new WeakReference<InputMethod>(inputMethod);
Dianne Hackborndea3ef72010-10-28 14:24:22 -0700107 mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 }
109
110 public InputMethod getInternalInputMethod() {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600111 return mInputMethod.get();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 }
113
114 public void executeMessage(Message msg) {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600115 InputMethod inputMethod = mInputMethod.get();
116 // Need a valid reference to the inputMethod for everything except a dump.
117 if (inputMethod == null && msg.what != DO_DUMP) {
118 Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
119 return;
120 }
121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 switch (msg.what) {
123 case DO_DUMP: {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600124 AbstractInputMethodService target = mTarget.get();
125 if (target == null) {
126 return;
127 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700128 SomeArgs args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 try {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600130 target.dump((FileDescriptor)args.arg1,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 (PrintWriter)args.arg2, (String[])args.arg3);
132 } catch (RuntimeException e) {
133 ((PrintWriter)args.arg2).println("Exception: " + e);
134 }
135 synchronized (args.arg4) {
136 ((CountDownLatch)args.arg4).countDown();
137 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700138 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 return;
140 }
141
142 case DO_ATTACH_TOKEN: {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600143 inputMethod.attachToken((IBinder)msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 return;
145 }
146 case DO_SET_INPUT_CONTEXT: {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600147 inputMethod.bindInput((InputBinding)msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 return;
149 }
150 case DO_UNSET_INPUT_CONTEXT:
Devin Taylor0c33ed22010-02-23 13:26:46 -0600151 inputMethod.unbindInput();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 return;
153 case DO_START_INPUT: {
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700154 SomeArgs args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 IInputContext inputContext = (IInputContext)args.arg1;
156 InputConnection ic = inputContext != null
157 ? new InputConnectionWrapper(inputContext) : null;
Dianne Hackborndea3ef72010-10-28 14:24:22 -0700158 EditorInfo info = (EditorInfo)args.arg2;
159 info.makeCompatible(mTargetSdkVersion);
160 inputMethod.startInput(ic, info);
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700161 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 return;
163 }
164 case DO_RESTART_INPUT: {
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700165 SomeArgs args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 IInputContext inputContext = (IInputContext)args.arg1;
167 InputConnection ic = inputContext != null
168 ? new InputConnectionWrapper(inputContext) : null;
Dianne Hackborndea3ef72010-10-28 14:24:22 -0700169 EditorInfo info = (EditorInfo)args.arg2;
170 info.makeCompatible(mTargetSdkVersion);
171 inputMethod.restartInput(ic, info);
Svetoslav Ganov758143e2012-08-06 16:40:27 -0700172 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 return;
174 }
175 case DO_CREATE_SESSION: {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600176 inputMethod.createSession(new InputMethodSessionCallbackWrapper(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 mCaller.mContext, (IInputMethodCallback)msg.obj));
178 return;
179 }
180 case DO_SET_SESSION_ENABLED:
Devin Taylor0c33ed22010-02-23 13:26:46 -0600181 inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 msg.arg1 != 0);
183 return;
184 case DO_REVOKE_SESSION:
Devin Taylor0c33ed22010-02-23 13:26:46 -0600185 inputMethod.revokeSession((InputMethodSession)msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 return;
187 case DO_SHOW_SOFT_INPUT:
Devin Taylor0c33ed22010-02-23 13:26:46 -0600188 inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 return;
190 case DO_HIDE_SOFT_INPUT:
Devin Taylor0c33ed22010-02-23 13:26:46 -0600191 inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 return;
satokab751aa2010-09-14 19:17:36 +0900193 case DO_CHANGE_INPUTMETHOD_SUBTYPE:
194 inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
195 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 }
197 Log.w(TAG, "Unhandled message code: " + msg.what);
198 }
199
200 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Devin Taylor0c33ed22010-02-23 13:26:46 -0600201 AbstractInputMethodService target = mTarget.get();
202 if (target == null) {
203 return;
204 }
205 if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 != PackageManager.PERMISSION_GRANTED) {
207
208 fout.println("Permission Denial: can't dump InputMethodManager from from pid="
209 + Binder.getCallingPid()
210 + ", uid=" + Binder.getCallingUid());
211 return;
212 }
213
214 CountDownLatch latch = new CountDownLatch(1);
215 mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP,
216 fd, fout, args, latch));
217 try {
218 if (!latch.await(5, TimeUnit.SECONDS)) {
219 fout.println("Timeout waiting for dump");
220 }
221 } catch (InterruptedException e) {
222 fout.println("Interrupted waiting for dump");
223 }
224 }
225
226 public void attachToken(IBinder token) {
227 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
228 }
229
230 public void bindInput(InputBinding binding) {
231 InputConnection ic = new InputConnectionWrapper(
232 IInputContext.Stub.asInterface(binding.getConnectionToken()));
233 InputBinding nu = new InputBinding(ic, binding);
234 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
235 }
236
237 public void unbindInput() {
238 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
239 }
240
241 public void startInput(IInputContext inputContext, EditorInfo attribute) {
242 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
243 inputContext, attribute));
244 }
245
246 public void restartInput(IInputContext inputContext, EditorInfo attribute) {
247 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
248 inputContext, attribute));
249 }
250
251 public void createSession(IInputMethodCallback callback) {
252 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback));
253 }
254
255 public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
256 try {
257 InputMethodSession ls = ((IInputMethodSessionWrapper)
258 session).getInternalInputMethodSession();
259 mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
260 DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls));
261 } catch (ClassCastException e) {
262 Log.w(TAG, "Incoming session not of correct type: " + session, e);
263 }
264 }
265
266 public void revokeSession(IInputMethodSession session) {
267 try {
268 InputMethodSession ls = ((IInputMethodSessionWrapper)
269 session).getInternalInputMethodSession();
270 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls));
271 } catch (ClassCastException e) {
272 Log.w(TAG, "Incoming session not of correct type: " + session, e);
273 }
274 }
275
The Android Open Source Project4df24232009-03-05 14:34:35 -0800276 public void showSoftInput(int flags, ResultReceiver resultReceiver) {
277 mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
278 flags, resultReceiver));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 }
280
The Android Open Source Project4df24232009-03-05 14:34:35 -0800281 public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
282 mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
283 flags, resultReceiver));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
satokab751aa2010-09-14 19:17:36 +0900285
286 public void changeInputMethodSubtype(InputMethodSubtype subtype) {
287 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
288 subtype));
289 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290}