blob: 59d4c9b5a6ffa28b7971c8e4af962724850b6c92 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006-2008 The Android Open Source Project
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
Doug Zongkerab5c49c2009-12-04 10:31:43 -08007 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008 * http://www.apache.org/licenses/LICENSE-2.0
Doug Zongkerab5c49c2009-12-04 10:31:43 -08009 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080010 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.server;
18
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080019import com.android.internal.content.PackageMonitor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import com.android.internal.os.HandlerCaller;
21import com.android.internal.view.IInputContext;
22import com.android.internal.view.IInputMethod;
23import com.android.internal.view.IInputMethodCallback;
24import com.android.internal.view.IInputMethodClient;
25import com.android.internal.view.IInputMethodManager;
26import com.android.internal.view.IInputMethodSession;
27import com.android.internal.view.InputBindResult;
28
29import com.android.server.status.IconData;
30import com.android.server.status.StatusBarService;
31
32import org.xmlpull.v1.XmlPullParserException;
33
34import android.app.ActivityManagerNative;
35import android.app.AlertDialog;
Dianne Hackborndd9b82c2009-09-03 00:18:47 -070036import android.app.PendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.ComponentName;
38import android.content.ContentResolver;
39import android.content.Context;
40import android.content.DialogInterface;
41import android.content.IntentFilter;
42import android.content.DialogInterface.OnCancelListener;
43import android.content.Intent;
44import android.content.ServiceConnection;
Brandon Ballinger6da35a02009-10-21 00:38:13 -070045import android.content.pm.ApplicationInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.pm.PackageManager;
47import android.content.pm.ResolveInfo;
48import android.content.pm.ServiceInfo;
49import android.content.res.Resources;
50import android.content.res.TypedArray;
51import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.Binder;
53import android.os.Handler;
54import android.os.IBinder;
55import android.os.IInterface;
56import android.os.Message;
57import android.os.Parcel;
58import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -080059import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.os.ServiceManager;
61import android.os.SystemClock;
62import android.provider.Settings;
63import android.text.TextUtils;
64import android.util.EventLog;
65import android.util.Log;
66import android.util.PrintWriterPrinter;
67import android.util.Printer;
68import android.view.IWindowManager;
69import android.view.WindowManager;
70import android.view.inputmethod.InputBinding;
71import android.view.inputmethod.InputMethod;
72import android.view.inputmethod.InputMethodInfo;
73import android.view.inputmethod.InputMethodManager;
74import android.view.inputmethod.EditorInfo;
75
76import java.io.FileDescriptor;
77import java.io.IOException;
78import java.io.PrintWriter;
79import java.util.ArrayList;
80import java.util.HashMap;
81import java.util.List;
82
83/**
84 * This class provides a system service that manages input methods.
85 */
86public class InputMethodManagerService extends IInputMethodManager.Stub
87 implements ServiceConnection, Handler.Callback {
88 static final boolean DEBUG = false;
89 static final String TAG = "InputManagerService";
90
91 static final int MSG_SHOW_IM_PICKER = 1;
Doug Zongkerab5c49c2009-12-04 10:31:43 -080092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 static final int MSG_UNBIND_INPUT = 1000;
94 static final int MSG_BIND_INPUT = 1010;
95 static final int MSG_SHOW_SOFT_INPUT = 1020;
96 static final int MSG_HIDE_SOFT_INPUT = 1030;
97 static final int MSG_ATTACH_TOKEN = 1040;
98 static final int MSG_CREATE_SESSION = 1050;
Doug Zongkerab5c49c2009-12-04 10:31:43 -080099
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 static final int MSG_START_INPUT = 2000;
101 static final int MSG_RESTART_INPUT = 2010;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 static final int MSG_UNBIND_METHOD = 3000;
104 static final int MSG_BIND_METHOD = 3010;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 static final long TIME_TO_RECONNECT = 10*1000;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 final Context mContext;
109 final Handler mHandler;
110 final SettingsObserver mSettingsObserver;
111 final StatusBarService mStatusBar;
112 final IBinder mInputMethodIcon;
113 final IconData mInputMethodData;
114 final IWindowManager mIWindowManager;
115 final HandlerCaller mCaller;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 final InputBindResult mNoBinding = new InputBindResult(null, null, -1);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800118
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 // All known input methods. mMethodMap also serves as the global
120 // lock for this class.
121 final ArrayList<InputMethodInfo> mMethodList
122 = new ArrayList<InputMethodInfo>();
123 final HashMap<String, InputMethodInfo> mMethodMap
124 = new HashMap<String, InputMethodInfo>();
125
126 final TextUtils.SimpleStringSplitter mStringColonSplitter
127 = new TextUtils.SimpleStringSplitter(':');
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 class SessionState {
130 final ClientState client;
131 final IInputMethod method;
132 final IInputMethodSession session;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 @Override
135 public String toString() {
136 return "SessionState{uid " + client.uid + " pid " + client.pid
137 + " method " + Integer.toHexString(
138 System.identityHashCode(method))
139 + " session " + Integer.toHexString(
140 System.identityHashCode(session))
141 + "}";
142 }
143
144 SessionState(ClientState _client, IInputMethod _method,
145 IInputMethodSession _session) {
146 client = _client;
147 method = _method;
148 session = _session;
149 }
150 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 class ClientState {
153 final IInputMethodClient client;
154 final IInputContext inputContext;
155 final int uid;
156 final int pid;
157 final InputBinding binding;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 boolean sessionRequested;
160 SessionState curSession;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 @Override
163 public String toString() {
164 return "ClientState{" + Integer.toHexString(
165 System.identityHashCode(this)) + " uid " + uid
166 + " pid " + pid + "}";
167 }
168
169 ClientState(IInputMethodClient _client, IInputContext _inputContext,
170 int _uid, int _pid) {
171 client = _client;
172 inputContext = _inputContext;
173 uid = _uid;
174 pid = _pid;
175 binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
176 }
177 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 final HashMap<IBinder, ClientState> mClients
180 = new HashMap<IBinder, ClientState>();
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 /**
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700183 * Set once the system is ready to run third party code.
184 */
185 boolean mSystemReady;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800186
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700187 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 * Id of the currently selected input method.
189 */
190 String mCurMethodId;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 /**
193 * The current binding sequence number, incremented every time there is
194 * a new bind performed.
195 */
196 int mCurSeq;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 /**
199 * The client that is currently bound to an input method.
200 */
201 ClientState mCurClient;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 /**
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700204 * The last window token that gained focus.
205 */
206 IBinder mCurFocusedWindow;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800207
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700208 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 * The input context last provided by the current client.
210 */
211 IInputContext mCurInputContext;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 /**
214 * The attributes last provided by the current client.
215 */
216 EditorInfo mCurAttribute;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 /**
219 * The input method ID of the input method service that we are currently
220 * connected to or in the process of connecting to.
221 */
222 String mCurId;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 /**
225 * Set to true if our ServiceConnection is currently actively bound to
226 * a service (whether or not we have gotten its IBinder back yet).
227 */
228 boolean mHaveConnection;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 /**
231 * Set if the client has asked for the input method to be shown.
232 */
233 boolean mShowRequested;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 /**
236 * Set if we were explicitly told to show the input method.
237 */
238 boolean mShowExplicitlyRequested;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800239
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 /**
241 * Set if we were forced to be shown.
242 */
243 boolean mShowForced;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 /**
246 * Set if we last told the input method to show itself.
247 */
248 boolean mInputShown;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800249
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 /**
251 * The Intent used to connect to the current input method.
252 */
253 Intent mCurIntent;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 /**
256 * The token we have made for the currently active input method, to
257 * identify it in the future.
258 */
259 IBinder mCurToken;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800260
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 /**
262 * If non-null, this is the input method service we are currently connected
263 * to.
264 */
265 IInputMethod mCurMethod;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800266
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 /**
268 * Time that we last initiated a bind to the input method, to determine
269 * if we should try to disconnect and reconnect to it.
270 */
271 long mLastBindTime;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 /**
274 * Have we called mCurMethod.bindInput()?
275 */
276 boolean mBoundToMethod;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 /**
279 * Currently enabled session. Only touched by service thread, not
280 * protected by a lock.
281 */
282 SessionState mEnabledSession;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800283
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 /**
285 * True if the screen is on. The value is true initially.
286 */
287 boolean mScreenOn = true;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 AlertDialog.Builder mDialogBuilder;
290 AlertDialog mSwitchingDialog;
291 InputMethodInfo[] mIms;
292 CharSequence[] mItems;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800293
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 class SettingsObserver extends ContentObserver {
295 SettingsObserver(Handler handler) {
296 super(handler);
297 ContentResolver resolver = mContext.getContentResolver();
298 resolver.registerContentObserver(Settings.Secure.getUriFor(
299 Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
300 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800301
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 @Override public void onChange(boolean selfChange) {
303 synchronized (mMethodMap) {
304 updateFromSettingsLocked();
305 }
306 }
307 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 class ScreenOnOffReceiver extends android.content.BroadcastReceiver {
310 @Override
311 public void onReceive(Context context, Intent intent) {
312 if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
313 mScreenOn = true;
314 } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
315 mScreenOn = false;
The Android Open Source Project10592532009-03-18 17:39:46 -0700316 } else if (intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
317 hideInputMethodMenu();
318 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 } else {
320 Log.w(TAG, "Unexpected intent " + intent);
321 }
322
323 // Inform the current client of the change in active status
324 try {
325 if (mCurClient != null && mCurClient.client != null) {
326 mCurClient.client.setActive(mScreenOn);
327 }
328 } catch (RemoteException e) {
329 Log.w(TAG, "Got RemoteException sending 'screen on/off' notification to pid "
330 + mCurClient.pid + " uid " + mCurClient.uid);
331 }
332 }
333 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800334
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800335 class MyPackageMonitor extends PackageMonitor {
336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 @Override
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800338 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 synchronized (mMethodMap) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800340 String curInputMethodId = Settings.Secure.getString(mContext
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
342 final int N = mMethodList.size();
343 if (curInputMethodId != null) {
344 for (int i=0; i<N; i++) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800345 InputMethodInfo imi = mMethodList.get(i);
346 if (imi.getId().equals(curInputMethodId)) {
347 for (String pkg : packages) {
348 if (imi.getPackageName().equals(pkg)) {
349 if (!doit) {
350 return true;
351 }
352
353 Settings.Secure.putString(mContext.getContentResolver(),
354 Settings.Secure.DEFAULT_INPUT_METHOD, "");
355 chooseNewDefaultIMELocked();
356 return true;
357 }
358 }
359 }
360 }
361 }
362 }
363 return false;
364 }
365
366 @Override
367 public void onSomePackagesChanged() {
368 synchronized (mMethodMap) {
369 InputMethodInfo curIm = null;
370 String curInputMethodId = Settings.Secure.getString(mContext
371 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
372 final int N = mMethodList.size();
373 if (curInputMethodId != null) {
374 for (int i=0; i<N; i++) {
375 InputMethodInfo imi = mMethodList.get(i);
376 if (imi.getId().equals(curInputMethodId)) {
377 curIm = imi;
378 }
379 int change = isPackageDisappearing(imi.getPackageName());
380 if (change == PACKAGE_TEMPORARY_CHANGE
381 || change == PACKAGE_PERMANENT_CHANGE) {
382 Log.i(TAG, "Input method uninstalled, disabling: "
383 + imi.getComponent());
384 setInputMethodEnabledLocked(imi.getId(), false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 }
386 }
387 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800388
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800389 buildInputMethodListLocked(mMethodList, mMethodMap);
390
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 boolean changed = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800392
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800393 if (curIm != null) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800394 int change = isPackageDisappearing(curIm.getPackageName());
395 if (change == PACKAGE_TEMPORARY_CHANGE
396 || change == PACKAGE_PERMANENT_CHANGE) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800397 ServiceInfo si = null;
398 try {
399 si = mContext.getPackageManager().getServiceInfo(
400 curIm.getComponent(), 0);
401 } catch (PackageManager.NameNotFoundException ex) {
402 }
403 if (si == null) {
404 // Uh oh, current input method is no longer around!
405 // Pick another one...
406 Log.i(TAG, "Current input method removed: " + curInputMethodId);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800407 if (!chooseNewDefaultIMELocked()) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800408 changed = true;
409 curIm = null;
410 curInputMethodId = "";
411 Log.i(TAG, "Unsetting current input method");
412 Settings.Secure.putString(mContext.getContentResolver(),
413 Settings.Secure.DEFAULT_INPUT_METHOD,
414 curInputMethodId);
415 }
416 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -0800417 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800418 }
419
420 if (curIm == null) {
421 // We currently don't have a default input method... is
422 // one now available?
423 changed = chooseNewDefaultIMELocked();
424 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800425
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800426 if (changed) {
427 updateFromSettingsLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 }
429 }
430 }
431 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 class MethodCallback extends IInputMethodCallback.Stub {
434 final IInputMethod mMethod;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 MethodCallback(IInputMethod method) {
437 mMethod = method;
438 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 public void finishedEvent(int seq, boolean handled) throws RemoteException {
441 }
442
443 public void sessionCreated(IInputMethodSession session) throws RemoteException {
444 onSessionCreated(mMethod, session);
445 }
446 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 public InputMethodManagerService(Context context, StatusBarService statusBar) {
449 mContext = context;
450 mHandler = new Handler(this);
451 mIWindowManager = IWindowManager.Stub.asInterface(
452 ServiceManager.getService(Context.WINDOW_SERVICE));
453 mCaller = new HandlerCaller(context, new HandlerCaller.Callback() {
454 public void executeMessage(Message msg) {
455 handleMessage(msg);
456 }
457 });
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800458
Dianne Hackborn21f1bd12010-02-19 17:02:21 -0800459 (new MyPackageMonitor()).register(mContext, true);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800460
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 IntentFilter screenOnOffFilt = new IntentFilter();
462 screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON);
463 screenOnOffFilt.addAction(Intent.ACTION_SCREEN_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700464 screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800466
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 buildInputMethodListLocked(mMethodList, mMethodMap);
468
469 final String enabledStr = Settings.Secure.getString(
470 mContext.getContentResolver(),
471 Settings.Secure.ENABLED_INPUT_METHODS);
472 Log.i(TAG, "Enabled input methods: " + enabledStr);
473 if (enabledStr == null) {
474 Log.i(TAG, "Enabled input methods has not been set, enabling all");
475 InputMethodInfo defIm = null;
476 StringBuilder sb = new StringBuilder(256);
477 final int N = mMethodList.size();
478 for (int i=0; i<N; i++) {
479 InputMethodInfo imi = mMethodList.get(i);
480 Log.i(TAG, "Adding: " + imi.getId());
481 if (i > 0) sb.append(':');
482 sb.append(imi.getId());
483 if (defIm == null && imi.getIsDefaultResourceId() != 0) {
484 try {
485 Resources res = mContext.createPackageContext(
486 imi.getPackageName(), 0).getResources();
487 if (res.getBoolean(imi.getIsDefaultResourceId())) {
488 defIm = imi;
489 Log.i(TAG, "Selected default: " + imi.getId());
490 }
491 } catch (PackageManager.NameNotFoundException ex) {
492 } catch (Resources.NotFoundException ex) {
493 }
494 }
495 }
496 if (defIm == null && N > 0) {
497 defIm = mMethodList.get(0);
498 Log.i(TAG, "No default found, using " + defIm.getId());
499 }
500 Settings.Secure.putString(mContext.getContentResolver(),
501 Settings.Secure.ENABLED_INPUT_METHODS, sb.toString());
502 if (defIm != null) {
503 Settings.Secure.putString(mContext.getContentResolver(),
504 Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
505 }
506 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 mStatusBar = statusBar;
509 mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
510 mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
511 statusBar.setIconVisibility(mInputMethodIcon, false);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 mSettingsObserver = new SettingsObserver(mHandler);
514 updateFromSettingsLocked();
515 }
516
517 @Override
518 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
519 throws RemoteException {
520 try {
521 return super.onTransact(code, data, reply, flags);
522 } catch (RuntimeException e) {
523 // The input method manager only throws security exceptions, so let's
524 // log all others.
525 if (!(e instanceof SecurityException)) {
526 Log.e(TAG, "Input Method Manager Crash", e);
527 }
528 throw e;
529 }
530 }
531
532 public void systemReady() {
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700533 synchronized (mMethodMap) {
534 if (!mSystemReady) {
535 mSystemReady = true;
Dianne Hackborncc278702009-09-02 23:07:23 -0700536 try {
537 startInputInnerLocked();
538 } catch (RuntimeException e) {
539 Log.w(TAG, "Unexpected exception", e);
540 }
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700541 }
542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 public List<InputMethodInfo> getInputMethodList() {
546 synchronized (mMethodMap) {
547 return new ArrayList<InputMethodInfo>(mMethodList);
548 }
549 }
550
551 public List<InputMethodInfo> getEnabledInputMethodList() {
552 synchronized (mMethodMap) {
553 return getEnabledInputMethodListLocked();
554 }
555 }
556
557 List<InputMethodInfo> getEnabledInputMethodListLocked() {
558 final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 final String enabledStr = Settings.Secure.getString(
561 mContext.getContentResolver(),
562 Settings.Secure.ENABLED_INPUT_METHODS);
563 if (enabledStr != null) {
564 final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
565 splitter.setString(enabledStr);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800566
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 while (splitter.hasNext()) {
568 InputMethodInfo info = mMethodMap.get(splitter.next());
569 if (info != null) {
570 res.add(info);
571 }
572 }
573 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 return res;
576 }
577
578 public void addClient(IInputMethodClient client,
579 IInputContext inputContext, int uid, int pid) {
580 synchronized (mMethodMap) {
581 mClients.put(client.asBinder(), new ClientState(client,
582 inputContext, uid, pid));
583 }
584 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800585
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 public void removeClient(IInputMethodClient client) {
587 synchronized (mMethodMap) {
588 mClients.remove(client.asBinder());
589 }
590 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800591
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 void executeOrSendMessage(IInterface target, Message msg) {
593 if (target.asBinder() instanceof Binder) {
594 mCaller.sendMessage(msg);
595 } else {
596 handleMessage(msg);
597 msg.recycle();
598 }
599 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800600
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700601 void unbindCurrentClientLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 if (mCurClient != null) {
603 if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = "
604 + mCurClient.client.asBinder());
605 if (mBoundToMethod) {
606 mBoundToMethod = false;
607 if (mCurMethod != null) {
608 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
609 MSG_UNBIND_INPUT, mCurMethod));
610 }
611 }
612 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
613 MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
614 mCurClient.sessionRequested = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 // Call setActive(false) on the old client
617 try {
618 mCurClient.client.setActive(false);
619 } catch (RemoteException e) {
620 Log.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
621 + mCurClient.pid + " uid " + mCurClient.uid);
622 }
623 mCurClient = null;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800624
The Android Open Source Project10592532009-03-18 17:39:46 -0700625 hideInputMethodMenuLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 }
627 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 private int getImeShowFlags() {
630 int flags = 0;
631 if (mShowForced) {
632 flags |= InputMethod.SHOW_FORCED
633 | InputMethod.SHOW_EXPLICIT;
634 } else if (mShowExplicitlyRequested) {
635 flags |= InputMethod.SHOW_EXPLICIT;
636 }
637 return flags;
638 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800639
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 private int getAppShowFlags() {
641 int flags = 0;
642 if (mShowForced) {
643 flags |= InputMethodManager.SHOW_FORCED;
644 } else if (!mShowExplicitlyRequested) {
645 flags |= InputMethodManager.SHOW_IMPLICIT;
646 }
647 return flags;
648 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800649
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 InputBindResult attachNewInputLocked(boolean initial, boolean needResult) {
651 if (!mBoundToMethod) {
652 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
653 MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
654 mBoundToMethod = true;
655 }
656 final SessionState session = mCurClient.curSession;
657 if (initial) {
658 executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
659 MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
660 } else {
661 executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
662 MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
663 }
664 if (mShowRequested) {
665 if (DEBUG) Log.v(TAG, "Attach new input asks to show input");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800666 showCurrentInputLocked(getAppShowFlags(), null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 }
668 return needResult
669 ? new InputBindResult(session.session, mCurId, mCurSeq)
670 : null;
671 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800672
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 InputBindResult startInputLocked(IInputMethodClient client,
674 IInputContext inputContext, EditorInfo attribute,
675 boolean initial, boolean needResult) {
676 // If no method is currently selected, do nothing.
677 if (mCurMethodId == null) {
678 return mNoBinding;
679 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800680
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 ClientState cs = mClients.get(client.asBinder());
682 if (cs == null) {
683 throw new IllegalArgumentException("unknown client "
684 + client.asBinder());
685 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800686
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 try {
688 if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
689 // Check with the window manager to make sure this client actually
690 // has a window with focus. If not, reject. This is thread safe
691 // because if the focus changes some time before or after, the
692 // next client receiving focus that has any interest in input will
693 // be calling through here after that change happens.
694 Log.w(TAG, "Starting input on non-focused client " + cs.client
695 + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
696 return null;
697 }
698 } catch (RemoteException e) {
699 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800700
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 if (mCurClient != cs) {
702 // If the client is changing, we need to switch over to the new
703 // one.
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700704 unbindCurrentClientLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 if (DEBUG) Log.v(TAG, "switching to client: client = "
706 + cs.client.asBinder());
707
708 // If the screen is on, inform the new client it is active
709 if (mScreenOn) {
710 try {
711 cs.client.setActive(mScreenOn);
712 } catch (RemoteException e) {
713 Log.w(TAG, "Got RemoteException sending setActive notification to pid "
714 + cs.pid + " uid " + cs.uid);
715 }
716 }
717 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 // Bump up the sequence for this client and attach it.
720 mCurSeq++;
721 if (mCurSeq <= 0) mCurSeq = 1;
722 mCurClient = cs;
723 mCurInputContext = inputContext;
724 mCurAttribute = attribute;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800725
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 // Check if the input method is changing.
727 if (mCurId != null && mCurId.equals(mCurMethodId)) {
728 if (cs.curSession != null) {
729 // Fast case: if we are already connected to the input method,
730 // then just return it.
731 return attachNewInputLocked(initial, needResult);
732 }
733 if (mHaveConnection) {
734 if (mCurMethod != null) {
735 if (!cs.sessionRequested) {
736 cs.sessionRequested = true;
737 if (DEBUG) Log.v(TAG, "Creating new session for client " + cs);
738 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
739 MSG_CREATE_SESSION, mCurMethod,
740 new MethodCallback(mCurMethod)));
741 }
742 // Return to client, and we will get back with it when
743 // we have had a session made for it.
744 return new InputBindResult(null, mCurId, mCurSeq);
745 } else if (SystemClock.uptimeMillis()
746 < (mLastBindTime+TIME_TO_RECONNECT)) {
747 // In this case we have connected to the service, but
748 // don't yet have its interface. If it hasn't been too
749 // long since we did the connection, we'll return to
750 // the client and wait to get the service interface so
751 // we can report back. If it has been too long, we want
752 // to fall through so we can try a disconnect/reconnect
753 // to see if we can get back in touch with the service.
754 return new InputBindResult(null, mCurId, mCurSeq);
755 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800756 EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
757 mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 }
759 }
760 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800761
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700762 return startInputInnerLocked();
763 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800764
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700765 InputBindResult startInputInnerLocked() {
766 if (mCurMethodId == null) {
767 return mNoBinding;
768 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800769
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700770 if (!mSystemReady) {
771 // If the system is not yet ready, we shouldn't be running third
772 // party code.
Dianne Hackborncc278702009-09-02 23:07:23 -0700773 return new InputBindResult(null, mCurMethodId, mCurSeq);
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700774 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800775
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 InputMethodInfo info = mMethodMap.get(mCurMethodId);
777 if (info == null) {
778 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
779 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800780
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700781 unbindCurrentMethodLocked(false);
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800782
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
784 mCurIntent.setComponent(info.getComponent());
Dianne Hackborndd9b82c2009-09-03 00:18:47 -0700785 mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
786 com.android.internal.R.string.input_method_binding_label);
787 mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
788 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {
790 mLastBindTime = SystemClock.uptimeMillis();
791 mHaveConnection = true;
792 mCurId = info.getId();
793 mCurToken = new Binder();
794 try {
795 if (DEBUG) Log.v(TAG, "Adding window token: " + mCurToken);
796 mIWindowManager.addWindowToken(mCurToken,
797 WindowManager.LayoutParams.TYPE_INPUT_METHOD);
798 } catch (RemoteException e) {
799 }
800 return new InputBindResult(null, mCurId, mCurSeq);
801 } else {
802 mCurIntent = null;
803 Log.w(TAG, "Failure connecting to input method service: "
804 + mCurIntent);
805 }
806 return null;
807 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800808
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 public InputBindResult startInput(IInputMethodClient client,
810 IInputContext inputContext, EditorInfo attribute,
811 boolean initial, boolean needResult) {
812 synchronized (mMethodMap) {
813 final long ident = Binder.clearCallingIdentity();
814 try {
815 return startInputLocked(client, inputContext, attribute,
816 initial, needResult);
817 } finally {
818 Binder.restoreCallingIdentity(ident);
819 }
820 }
821 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800822
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 public void finishInput(IInputMethodClient client) {
824 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800825
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 public void onServiceConnected(ComponentName name, IBinder service) {
827 synchronized (mMethodMap) {
828 if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
829 mCurMethod = IInputMethod.Stub.asInterface(service);
Dianne Hackborncc278702009-09-02 23:07:23 -0700830 if (mCurToken == null) {
831 Log.w(TAG, "Service connected without a token!");
832 unbindCurrentMethodLocked(false);
833 return;
834 }
835 if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken);
836 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
837 MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 if (mCurClient != null) {
Dianne Hackborncc278702009-09-02 23:07:23 -0700839 if (DEBUG) Log.v(TAG, "Creating first session while with client "
840 + mCurClient);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
Dianne Hackborncc278702009-09-02 23:07:23 -0700842 MSG_CREATE_SESSION, mCurMethod,
843 new MethodCallback(mCurMethod)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 }
845 }
846 }
847 }
848
849 void onSessionCreated(IInputMethod method, IInputMethodSession session) {
850 synchronized (mMethodMap) {
851 if (mCurMethod != null && method != null
852 && mCurMethod.asBinder() == method.asBinder()) {
853 if (mCurClient != null) {
854 mCurClient.curSession = new SessionState(mCurClient,
855 method, session);
856 mCurClient.sessionRequested = false;
857 InputBindResult res = attachNewInputLocked(true, true);
858 if (res.method != null) {
859 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
860 MSG_BIND_METHOD, mCurClient.client, res));
861 }
862 }
863 }
864 }
865 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800866
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700867 void unbindCurrentMethodLocked(boolean reportToClient) {
868 if (mHaveConnection) {
869 mContext.unbindService(this);
870 mHaveConnection = false;
871 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800872
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700873 if (mCurToken != null) {
874 try {
875 if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
876 mIWindowManager.removeWindowToken(mCurToken);
877 } catch (RemoteException e) {
878 }
879 mCurToken = null;
880 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800881
The Android Open Source Project10592532009-03-18 17:39:46 -0700882 mCurId = null;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700883 clearCurMethodLocked();
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800884
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700885 if (reportToClient && mCurClient != null) {
886 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
887 MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
888 }
889 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800890
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700891 void clearCurMethodLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 if (mCurMethod != null) {
893 for (ClientState cs : mClients.values()) {
894 cs.sessionRequested = false;
895 cs.curSession = null;
896 }
897 mCurMethod = null;
898 }
899 mStatusBar.setIconVisibility(mInputMethodIcon, false);
900 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800901
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 public void onServiceDisconnected(ComponentName name) {
903 synchronized (mMethodMap) {
904 if (DEBUG) Log.v(TAG, "Service disconnected: " + name
905 + " mCurIntent=" + mCurIntent);
906 if (mCurMethod != null && mCurIntent != null
907 && name.equals(mCurIntent.getComponent())) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700908 clearCurMethodLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 // We consider this to be a new bind attempt, since the system
910 // should now try to restart the service for us.
911 mLastBindTime = SystemClock.uptimeMillis();
912 mShowRequested = mInputShown;
913 mInputShown = false;
914 if (mCurClient != null) {
915 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
916 MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
917 }
918 }
919 }
920 }
921
922 public void updateStatusIcon(IBinder token, String packageName, int iconId) {
923 long ident = Binder.clearCallingIdentity();
924 try {
925 if (token == null || mCurToken != token) {
926 Log.w(TAG, "Ignoring setInputMethod of token: " + token);
927 return;
928 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800929
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 synchronized (mMethodMap) {
931 if (iconId == 0) {
932 if (DEBUG) Log.d(TAG, "hide the small icon for the input method");
933 mStatusBar.setIconVisibility(mInputMethodIcon, false);
934 } else if (packageName != null) {
935 if (DEBUG) Log.d(TAG, "show a small icon for the input method");
936 mInputMethodData.iconId = iconId;
937 mInputMethodData.iconPackage = packageName;
938 mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
939 mStatusBar.setIconVisibility(mInputMethodIcon, true);
940 }
941 }
942 } finally {
943 Binder.restoreCallingIdentity(ident);
944 }
945 }
946
947 void updateFromSettingsLocked() {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700948 // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
949 // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
950 // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
951 // enabled.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 String id = Settings.Secure.getString(mContext.getContentResolver(),
953 Settings.Secure.DEFAULT_INPUT_METHOD);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700954 if (id != null && id.length() > 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 try {
956 setInputMethodLocked(id);
957 } catch (IllegalArgumentException e) {
958 Log.w(TAG, "Unknown input method from prefs: " + id, e);
The Android Open Source Project10592532009-03-18 17:39:46 -0700959 mCurMethodId = null;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700960 unbindCurrentMethodLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700962 } else {
963 // There is no longer an input method set, so stop any current one.
The Android Open Source Project10592532009-03-18 17:39:46 -0700964 mCurMethodId = null;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700965 unbindCurrentMethodLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 }
967 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800968
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 void setInputMethodLocked(String id) {
970 InputMethodInfo info = mMethodMap.get(id);
971 if (info == null) {
972 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
973 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800974
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 if (id.equals(mCurMethodId)) {
976 return;
977 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 final long ident = Binder.clearCallingIdentity();
980 try {
981 mCurMethodId = id;
982 Settings.Secure.putString(mContext.getContentResolver(),
983 Settings.Secure.DEFAULT_INPUT_METHOD, id);
984
985 if (ActivityManagerNative.isSystemReady()) {
986 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -0800987 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 intent.putExtra("input_method_id", id);
989 mContext.sendBroadcast(intent);
990 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700991 unbindCurrentClientLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 } finally {
993 Binder.restoreCallingIdentity(ident);
994 }
995 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800996
The Android Open Source Project4df24232009-03-05 14:34:35 -0800997 public boolean showSoftInput(IInputMethodClient client, int flags,
998 ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 long ident = Binder.clearCallingIdentity();
1000 try {
1001 synchronized (mMethodMap) {
1002 if (mCurClient == null || client == null
1003 || mCurClient.client.asBinder() != client.asBinder()) {
1004 try {
1005 // We need to check if this is the current client with
1006 // focus in the window manager, to allow this call to
1007 // be made before input is started in it.
1008 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
1009 Log.w(TAG, "Ignoring showSoftInput of: " + client);
The Android Open Source Project4df24232009-03-05 14:34:35 -08001010 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 }
1012 } catch (RemoteException e) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001013 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 }
1015 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001016
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 if (DEBUG) Log.v(TAG, "Client requesting input be shown");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001018 return showCurrentInputLocked(flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020 } finally {
1021 Binder.restoreCallingIdentity(ident);
1022 }
1023 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001024
The Android Open Source Project4df24232009-03-05 14:34:35 -08001025 boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 mShowRequested = true;
1027 if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
1028 mShowExplicitlyRequested = true;
1029 }
1030 if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
1031 mShowExplicitlyRequested = true;
1032 mShowForced = true;
1033 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001034
Dianne Hackborncc278702009-09-02 23:07:23 -07001035 if (!mSystemReady) {
1036 return false;
1037 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001038
The Android Open Source Project4df24232009-03-05 14:34:35 -08001039 boolean res = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 if (mCurMethod != null) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001041 executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
1042 MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
1043 resultReceiver));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 mInputShown = true;
The Android Open Source Project4df24232009-03-05 14:34:35 -08001045 res = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 } else if (mHaveConnection && SystemClock.uptimeMillis()
1047 < (mLastBindTime+TIME_TO_RECONNECT)) {
1048 // The client has asked to have the input method shown, but
1049 // we have been sitting here too long with a connection to the
1050 // service and no interface received, so let's disconnect/connect
1051 // to try to prod things along.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001052 EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 SystemClock.uptimeMillis()-mLastBindTime,1);
1054 mContext.unbindService(this);
1055 mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE);
1056 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001057
The Android Open Source Project4df24232009-03-05 14:34:35 -08001058 return res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001060
The Android Open Source Project4df24232009-03-05 14:34:35 -08001061 public boolean hideSoftInput(IInputMethodClient client, int flags,
1062 ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 long ident = Binder.clearCallingIdentity();
1064 try {
1065 synchronized (mMethodMap) {
1066 if (mCurClient == null || client == null
1067 || mCurClient.client.asBinder() != client.asBinder()) {
1068 try {
1069 // We need to check if this is the current client with
1070 // focus in the window manager, to allow this call to
1071 // be made before input is started in it.
1072 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
1073 Log.w(TAG, "Ignoring hideSoftInput of: " + client);
The Android Open Source Project4df24232009-03-05 14:34:35 -08001074 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 }
1076 } catch (RemoteException e) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001077 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 }
1079 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 if (DEBUG) Log.v(TAG, "Client requesting input be hidden");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001082 return hideCurrentInputLocked(flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 }
1084 } finally {
1085 Binder.restoreCallingIdentity(ident);
1086 }
1087 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001088
The Android Open Source Project4df24232009-03-05 14:34:35 -08001089 boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
1091 && (mShowExplicitlyRequested || mShowForced)) {
1092 if (DEBUG) Log.v(TAG,
1093 "Not hiding: explicit show not cancelled by non-explicit hide");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001094 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 }
1096 if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
1097 if (DEBUG) Log.v(TAG,
1098 "Not hiding: forced show not cancelled by not-always hide");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001099 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001101 boolean res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 if (mInputShown && mCurMethod != null) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001103 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
1104 MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
1105 res = true;
1106 } else {
1107 res = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 }
1109 mInputShown = false;
1110 mShowRequested = false;
1111 mShowExplicitlyRequested = false;
1112 mShowForced = false;
The Android Open Source Project4df24232009-03-05 14:34:35 -08001113 return res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001115
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001116 public void windowGainedFocus(IInputMethodClient client, IBinder windowToken,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 boolean viewHasFocus, boolean isTextEditor, int softInputMode,
1118 boolean first, int windowFlags) {
1119 long ident = Binder.clearCallingIdentity();
1120 try {
1121 synchronized (mMethodMap) {
1122 if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder()
1123 + " viewHasFocus=" + viewHasFocus
1124 + " isTextEditor=" + isTextEditor
1125 + " softInputMode=#" + Integer.toHexString(softInputMode)
1126 + " first=" + first + " flags=#"
1127 + Integer.toHexString(windowFlags));
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 if (mCurClient == null || client == null
1130 || mCurClient.client.asBinder() != client.asBinder()) {
1131 try {
1132 // We need to check if this is the current client with
1133 // focus in the window manager, to allow this call to
1134 // be made before input is started in it.
1135 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001136 Log.w(TAG, "Client not active, ignoring focus gain of: " + client);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 return;
1138 }
1139 } catch (RemoteException e) {
1140 }
1141 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001142
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001143 if (mCurFocusedWindow == windowToken) {
1144 Log.w(TAG, "Window already focused, ignoring focus gain of: " + client);
1145 return;
1146 }
1147 mCurFocusedWindow = windowToken;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001148
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
1150 case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
1151 if (!isTextEditor || (softInputMode &
1152 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
1153 != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
1154 if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
1155 // There is no focus view, and this window will
1156 // be behind any soft input window, so hide the
1157 // soft input window if it is shown.
1158 if (DEBUG) Log.v(TAG, "Unspecified window will hide input");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001159 hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 }
1161 } else if (isTextEditor && (softInputMode &
1162 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
1163 == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
1164 && (softInputMode &
1165 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
1166 // There is a focus view, and we are navigating forward
1167 // into the window, so show the input window for the user.
1168 if (DEBUG) Log.v(TAG, "Unspecified window will show input");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001169 showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 }
1171 break;
1172 case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
1173 // Do nothing.
1174 break;
1175 case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
1176 if ((softInputMode &
1177 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
1178 if (DEBUG) Log.v(TAG, "Window asks to hide input going forward");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001179 hideCurrentInputLocked(0, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 }
1181 break;
1182 case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
1183 if (DEBUG) Log.v(TAG, "Window asks to hide input");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001184 hideCurrentInputLocked(0, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 break;
1186 case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
1187 if ((softInputMode &
1188 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
1189 if (DEBUG) Log.v(TAG, "Window asks to show input going forward");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001190 showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 }
1192 break;
1193 case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
1194 if (DEBUG) Log.v(TAG, "Window asks to always show input");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001195 showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 break;
1197 }
1198 }
1199 } finally {
1200 Binder.restoreCallingIdentity(ident);
1201 }
1202 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 public void showInputMethodPickerFromClient(IInputMethodClient client) {
1205 synchronized (mMethodMap) {
1206 if (mCurClient == null || client == null
1207 || mCurClient.client.asBinder() != client.asBinder()) {
1208 Log.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
1209 }
1210
1211 mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
1212 }
1213 }
1214
1215 public void setInputMethod(IBinder token, String id) {
1216 synchronized (mMethodMap) {
1217 if (token == null) {
1218 if (mContext.checkCallingOrSelfPermission(
1219 android.Manifest.permission.WRITE_SECURE_SETTINGS)
1220 != PackageManager.PERMISSION_GRANTED) {
1221 throw new SecurityException(
1222 "Using null token requires permission "
1223 + android.Manifest.permission.WRITE_SECURE_SETTINGS);
1224 }
1225 } else if (mCurToken != token) {
1226 Log.w(TAG, "Ignoring setInputMethod of token: " + token);
1227 return;
1228 }
1229
1230 long ident = Binder.clearCallingIdentity();
1231 try {
1232 setInputMethodLocked(id);
1233 } finally {
1234 Binder.restoreCallingIdentity(ident);
1235 }
1236 }
1237 }
1238
1239 public void hideMySoftInput(IBinder token, int flags) {
1240 synchronized (mMethodMap) {
1241 if (token == null || mCurToken != token) {
1242 Log.w(TAG, "Ignoring hideInputMethod of token: " + token);
1243 return;
1244 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 long ident = Binder.clearCallingIdentity();
1246 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001247 hideCurrentInputLocked(flags, null);
1248 } finally {
1249 Binder.restoreCallingIdentity(ident);
1250 }
1251 }
1252 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001253
The Android Open Source Project4df24232009-03-05 14:34:35 -08001254 public void showMySoftInput(IBinder token, int flags) {
1255 synchronized (mMethodMap) {
1256 if (token == null || mCurToken != token) {
1257 Log.w(TAG, "Ignoring hideInputMethod of token: " + token);
1258 return;
1259 }
1260 long ident = Binder.clearCallingIdentity();
1261 try {
1262 showCurrentInputLocked(flags, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 } finally {
1264 Binder.restoreCallingIdentity(ident);
1265 }
1266 }
1267 }
1268
1269 void setEnabledSessionInMainThread(SessionState session) {
1270 if (mEnabledSession != session) {
1271 if (mEnabledSession != null) {
1272 try {
1273 if (DEBUG) Log.v(TAG, "Disabling: " + mEnabledSession);
1274 mEnabledSession.method.setSessionEnabled(
1275 mEnabledSession.session, false);
1276 } catch (RemoteException e) {
1277 }
1278 }
1279 mEnabledSession = session;
1280 try {
1281 if (DEBUG) Log.v(TAG, "Enabling: " + mEnabledSession);
1282 session.method.setSessionEnabled(
1283 session.session, true);
1284 } catch (RemoteException e) {
1285 }
1286 }
1287 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001289 public boolean handleMessage(Message msg) {
1290 HandlerCaller.SomeArgs args;
1291 switch (msg.what) {
1292 case MSG_SHOW_IM_PICKER:
1293 showInputMethodMenu();
1294 return true;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001295
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 // ---------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 case MSG_UNBIND_INPUT:
1299 try {
1300 ((IInputMethod)msg.obj).unbindInput();
1301 } catch (RemoteException e) {
1302 // There is nothing interesting about the method dying.
1303 }
1304 return true;
1305 case MSG_BIND_INPUT:
1306 args = (HandlerCaller.SomeArgs)msg.obj;
1307 try {
1308 ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
1309 } catch (RemoteException e) {
1310 }
1311 return true;
1312 case MSG_SHOW_SOFT_INPUT:
The Android Open Source Project4df24232009-03-05 14:34:35 -08001313 args = (HandlerCaller.SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001315 ((IInputMethod)args.arg1).showSoftInput(msg.arg1,
1316 (ResultReceiver)args.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 } catch (RemoteException e) {
1318 }
1319 return true;
1320 case MSG_HIDE_SOFT_INPUT:
The Android Open Source Project4df24232009-03-05 14:34:35 -08001321 args = (HandlerCaller.SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001323 ((IInputMethod)args.arg1).hideSoftInput(0,
1324 (ResultReceiver)args.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 } catch (RemoteException e) {
1326 }
1327 return true;
1328 case MSG_ATTACH_TOKEN:
1329 args = (HandlerCaller.SomeArgs)msg.obj;
1330 try {
1331 if (DEBUG) Log.v(TAG, "Sending attach of token: " + args.arg2);
1332 ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
1333 } catch (RemoteException e) {
1334 }
1335 return true;
1336 case MSG_CREATE_SESSION:
1337 args = (HandlerCaller.SomeArgs)msg.obj;
1338 try {
1339 ((IInputMethod)args.arg1).createSession(
1340 (IInputMethodCallback)args.arg2);
1341 } catch (RemoteException e) {
1342 }
1343 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 // ---------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 case MSG_START_INPUT:
1347 args = (HandlerCaller.SomeArgs)msg.obj;
1348 try {
1349 SessionState session = (SessionState)args.arg1;
1350 setEnabledSessionInMainThread(session);
1351 session.method.startInput((IInputContext)args.arg2,
1352 (EditorInfo)args.arg3);
1353 } catch (RemoteException e) {
1354 }
1355 return true;
1356 case MSG_RESTART_INPUT:
1357 args = (HandlerCaller.SomeArgs)msg.obj;
1358 try {
1359 SessionState session = (SessionState)args.arg1;
1360 setEnabledSessionInMainThread(session);
1361 session.method.restartInput((IInputContext)args.arg2,
1362 (EditorInfo)args.arg3);
1363 } catch (RemoteException e) {
1364 }
1365 return true;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001366
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 // ---------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 case MSG_UNBIND_METHOD:
1370 try {
1371 ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1);
1372 } catch (RemoteException e) {
1373 // There is nothing interesting about the last client dying.
1374 }
1375 return true;
1376 case MSG_BIND_METHOD:
1377 args = (HandlerCaller.SomeArgs)msg.obj;
1378 try {
1379 ((IInputMethodClient)args.arg1).onBindMethod(
1380 (InputBindResult)args.arg2);
1381 } catch (RemoteException e) {
1382 Log.w(TAG, "Client died receiving input method " + args.arg2);
1383 }
1384 return true;
1385 }
1386 return false;
1387 }
1388
Brandon Ballinger6da35a02009-10-21 00:38:13 -07001389 private boolean isSystemIme(InputMethodInfo inputMethod) {
1390 return (inputMethod.getServiceInfo().applicationInfo.flags
1391 & ApplicationInfo.FLAG_SYSTEM) != 0;
1392 }
1393
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001394 private boolean chooseNewDefaultIMELocked() {
Brandon Ballinger6da35a02009-10-21 00:38:13 -07001395 List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
1396 if (enabled != null && enabled.size() > 0) {
1397 Settings.Secure.putString(mContext.getContentResolver(),
1398 Settings.Secure.DEFAULT_INPUT_METHOD,
1399 enabled.get(0).getId());
1400 return true;
1401 }
1402
1403 return false;
1404 }
1405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 void buildInputMethodListLocked(ArrayList<InputMethodInfo> list,
1407 HashMap<String, InputMethodInfo> map) {
1408 list.clear();
1409 map.clear();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 PackageManager pm = mContext.getPackageManager();
1412
1413 List<ResolveInfo> services = pm.queryIntentServices(
1414 new Intent(InputMethod.SERVICE_INTERFACE),
1415 PackageManager.GET_META_DATA);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001416
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 for (int i = 0; i < services.size(); ++i) {
1418 ResolveInfo ri = services.get(i);
1419 ServiceInfo si = ri.serviceInfo;
1420 ComponentName compName = new ComponentName(si.packageName, si.name);
1421 if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(
1422 si.permission)) {
1423 Log.w(TAG, "Skipping input method " + compName
1424 + ": it does not require the permission "
1425 + android.Manifest.permission.BIND_INPUT_METHOD);
1426 continue;
1427 }
1428
1429 if (DEBUG) Log.d(TAG, "Checking " + compName);
1430
1431 try {
1432 InputMethodInfo p = new InputMethodInfo(mContext, ri);
1433 list.add(p);
1434 map.put(p.getId(), p);
1435
Brandon Ballinger6da35a02009-10-21 00:38:13 -07001436 // System IMEs are enabled by default
1437 if (isSystemIme(p)) {
1438 setInputMethodEnabled(p.getId(), true);
1439 }
1440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 if (DEBUG) {
1442 Log.d(TAG, "Found a third-party input method " + p);
1443 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001445 } catch (XmlPullParserException e) {
1446 Log.w(TAG, "Unable to load input method " + compName, e);
1447 } catch (IOException e) {
1448 Log.w(TAG, "Unable to load input method " + compName, e);
1449 }
1450 }
Brandon Ballinger6da35a02009-10-21 00:38:13 -07001451
1452 String defaultIme = Settings.Secure.getString(mContext
1453 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
1454 if (!map.containsKey(defaultIme)) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001455 if (chooseNewDefaultIMELocked()) {
Brandon Ballinger6da35a02009-10-21 00:38:13 -07001456 updateFromSettingsLocked();
1457 }
1458 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001459 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001460
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 // ----------------------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001463 void showInputMethodMenu() {
1464 if (DEBUG) Log.v(TAG, "Show switching menu");
1465
1466 hideInputMethodMenu();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001467
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 final Context context = mContext;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 final PackageManager pm = context.getPackageManager();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001471
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 String lastInputMethodId = Settings.Secure.getString(context
1473 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
1474 if (DEBUG) Log.v(TAG, "Current IME: " + lastInputMethodId);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 final List<InputMethodInfo> immis = getEnabledInputMethodList();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 int N = (immis == null ? 0 : immis.size());
1479
1480 mItems = new CharSequence[N];
1481 mIms = new InputMethodInfo[N];
1482
1483 for (int i = 0; i < N; ++i) {
1484 InputMethodInfo property = immis.get(i);
1485 mItems[i] = property.loadLabel(pm);
1486 mIms[i] = property;
1487 }
1488
1489 int checkedItem = 0;
1490 for (int i = 0; i < N; ++i) {
1491 if (mIms[i].getId().equals(lastInputMethodId)) {
1492 checkedItem = i;
1493 break;
1494 }
1495 }
1496
1497 AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
1498 public void onClick(DialogInterface dialog, int which) {
1499 hideInputMethodMenu();
1500 }
1501 };
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001502
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 TypedArray a = context.obtainStyledAttributes(null,
1504 com.android.internal.R.styleable.DialogPreference,
1505 com.android.internal.R.attr.alertDialogStyle, 0);
1506 mDialogBuilder = new AlertDialog.Builder(context)
1507 .setTitle(com.android.internal.R.string.select_input_method)
1508 .setOnCancelListener(new OnCancelListener() {
1509 public void onCancel(DialogInterface dialog) {
1510 hideInputMethodMenu();
1511 }
1512 })
1513 .setIcon(a.getDrawable(
1514 com.android.internal.R.styleable.DialogPreference_dialogTitle));
1515 a.recycle();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
1518 new AlertDialog.OnClickListener() {
1519 public void onClick(DialogInterface dialog, int which) {
1520 synchronized (mMethodMap) {
1521 InputMethodInfo im = mIms[which];
1522 hideInputMethodMenu();
1523 setInputMethodLocked(im.getId());
1524 }
1525 }
1526 });
1527
1528 synchronized (mMethodMap) {
1529 mSwitchingDialog = mDialogBuilder.create();
1530 mSwitchingDialog.getWindow().setType(
1531 WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
1532 mSwitchingDialog.show();
1533 }
1534 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001535
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 void hideInputMethodMenu() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001537 synchronized (mMethodMap) {
1538 hideInputMethodMenuLocked();
1539 }
1540 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001541
The Android Open Source Project10592532009-03-18 17:39:46 -07001542 void hideInputMethodMenuLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 if (DEBUG) Log.v(TAG, "Hide switching menu");
1544
The Android Open Source Project10592532009-03-18 17:39:46 -07001545 if (mSwitchingDialog != null) {
1546 mSwitchingDialog.dismiss();
1547 mSwitchingDialog = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001549
The Android Open Source Project10592532009-03-18 17:39:46 -07001550 mDialogBuilder = null;
1551 mItems = null;
1552 mIms = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 // ----------------------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 public boolean setInputMethodEnabled(String id, boolean enabled) {
1558 synchronized (mMethodMap) {
1559 if (mContext.checkCallingOrSelfPermission(
1560 android.Manifest.permission.WRITE_SECURE_SETTINGS)
1561 != PackageManager.PERMISSION_GRANTED) {
1562 throw new SecurityException(
1563 "Requires permission "
1564 + android.Manifest.permission.WRITE_SECURE_SETTINGS);
1565 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001566
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 long ident = Binder.clearCallingIdentity();
1568 try {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001569 return setInputMethodEnabledLocked(id, enabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570 } finally {
1571 Binder.restoreCallingIdentity(ident);
1572 }
1573 }
1574 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001575
1576 boolean setInputMethodEnabledLocked(String id, boolean enabled) {
1577 // Make sure this is a valid input method.
1578 InputMethodInfo imm = mMethodMap.get(id);
1579 if (imm == null) {
1580 if (imm == null) {
1581 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
1582 }
1583 }
1584
1585 StringBuilder builder = new StringBuilder(256);
1586
1587 boolean removed = false;
1588 String firstId = null;
1589
1590 // Look through the currently enabled input methods.
1591 String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
1592 Settings.Secure.ENABLED_INPUT_METHODS);
1593 if (enabledStr != null) {
1594 final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
1595 splitter.setString(enabledStr);
1596 while (splitter.hasNext()) {
1597 String curId = splitter.next();
1598 if (curId.equals(id)) {
1599 if (enabled) {
1600 // We are enabling this input method, but it is
1601 // already enabled. Nothing to do. The previous
1602 // state was enabled.
1603 return true;
1604 }
1605 // We are disabling this input method, and it is
1606 // currently enabled. Skip it to remove from the
1607 // new list.
1608 removed = true;
1609 } else if (!enabled) {
1610 // We are building a new list of input methods that
1611 // doesn't contain the given one.
1612 if (firstId == null) firstId = curId;
1613 if (builder.length() > 0) builder.append(':');
1614 builder.append(curId);
1615 }
1616 }
1617 }
1618
1619 if (!enabled) {
1620 if (!removed) {
1621 // We are disabling the input method but it is already
1622 // disabled. Nothing to do. The previous state was
1623 // disabled.
1624 return false;
1625 }
1626 // Update the setting with the new list of input methods.
1627 Settings.Secure.putString(mContext.getContentResolver(),
1628 Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
1629 // We the disabled input method is currently selected, switch
1630 // to another one.
1631 String selId = Settings.Secure.getString(mContext.getContentResolver(),
1632 Settings.Secure.DEFAULT_INPUT_METHOD);
1633 if (id.equals(selId)) {
1634 Settings.Secure.putString(mContext.getContentResolver(),
1635 Settings.Secure.DEFAULT_INPUT_METHOD,
1636 firstId != null ? firstId : "");
1637 }
1638 // Previous state was enabled.
1639 return true;
1640 }
1641
1642 // Add in the newly enabled input method.
1643 if (enabledStr == null || enabledStr.length() == 0) {
1644 enabledStr = id;
1645 } else {
1646 enabledStr = enabledStr + ':' + id;
1647 }
1648
1649 Settings.Secure.putString(mContext.getContentResolver(),
1650 Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
1651
1652 // Previous state was disabled.
1653 return false;
1654 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08001655
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 // ----------------------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001657
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 @Override
1659 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1660 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1661 != PackageManager.PERMISSION_GRANTED) {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001662
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 pw.println("Permission Denial: can't dump InputMethodManager from from pid="
1664 + Binder.getCallingPid()
1665 + ", uid=" + Binder.getCallingUid());
1666 return;
1667 }
1668
1669 IInputMethod method;
1670 ClientState client;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001671
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001672 final Printer p = new PrintWriterPrinter(pw);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001674 synchronized (mMethodMap) {
1675 p.println("Current Input Method Manager state:");
1676 int N = mMethodList.size();
1677 p.println(" Input Methods:");
1678 for (int i=0; i<N; i++) {
1679 InputMethodInfo info = mMethodList.get(i);
1680 p.println(" InputMethod #" + i + ":");
1681 info.dump(p, " ");
1682 }
1683 p.println(" Clients:");
1684 for (ClientState ci : mClients.values()) {
1685 p.println(" Client " + ci + ":");
1686 p.println(" client=" + ci.client);
1687 p.println(" inputContext=" + ci.inputContext);
1688 p.println(" sessionRequested=" + ci.sessionRequested);
1689 p.println(" curSession=" + ci.curSession);
1690 }
1691 p.println(" mInputMethodIcon=" + mInputMethodIcon);
1692 p.println(" mInputMethodData=" + mInputMethodData);
The Android Open Source Project10592532009-03-18 17:39:46 -07001693 p.println(" mCurMethodId=" + mCurMethodId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 client = mCurClient;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07001695 p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
1696 p.println(" mCurFocusedWindow=" + mCurFocusedWindow);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001697 p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
1698 + " mBoundToMethod=" + mBoundToMethod);
1699 p.println(" mCurToken=" + mCurToken);
1700 p.println(" mCurIntent=" + mCurIntent);
1701 method = mCurMethod;
1702 p.println(" mCurMethod=" + mCurMethod);
1703 p.println(" mEnabledSession=" + mEnabledSession);
1704 p.println(" mShowRequested=" + mShowRequested
1705 + " mShowExplicitlyRequested=" + mShowExplicitlyRequested
1706 + " mShowForced=" + mShowForced
1707 + " mInputShown=" + mInputShown);
Dianne Hackborncc278702009-09-02 23:07:23 -07001708 p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001709 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001710
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001711 if (client != null) {
1712 p.println(" ");
1713 pw.flush();
1714 try {
1715 client.client.asBinder().dump(fd, args);
1716 } catch (RemoteException e) {
1717 p.println("Input method client dead: " + e);
1718 }
1719 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001720
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001721 if (method != null) {
1722 p.println(" ");
1723 pw.flush();
1724 try {
1725 method.asBinder().dump(fd, args);
1726 } catch (RemoteException e) {
1727 p.println("Input method service dead: " + e);
1728 }
1729 }
1730 }
1731}