blob: 63d7c94eee7105633380f211f0c10af208f5fe92 [file] [log] [blame]
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001/*
2 * Copyright (C) 2006-2008 The Android Open Source Project
3 *
4 * 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
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, 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
19import com.android.internal.os.HandlerCaller;
20import com.android.internal.view.IInputContext;
21import com.android.internal.view.IInputMethod;
22import com.android.internal.view.IInputMethodCallback;
23import com.android.internal.view.IInputMethodClient;
24import com.android.internal.view.IInputMethodManager;
25import com.android.internal.view.IInputMethodSession;
26import com.android.internal.view.InputBindResult;
27
28import com.android.server.status.IconData;
29import com.android.server.status.StatusBarService;
30
31import org.xmlpull.v1.XmlPullParserException;
32
33import android.app.AlertDialog;
34import android.content.ComponentName;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.DialogInterface;
38import android.content.IntentFilter;
39import android.content.DialogInterface.OnCancelListener;
40import android.content.Intent;
41import android.content.ServiceConnection;
42import android.content.pm.PackageManager;
43import android.content.pm.ResolveInfo;
44import android.content.pm.ServiceInfo;
45import android.content.res.Resources;
46import android.content.res.TypedArray;
47import android.database.ContentObserver;
48import android.net.Uri;
49import android.os.Binder;
50import android.os.Handler;
51import android.os.IBinder;
52import android.os.IInterface;
53import android.os.Message;
54import android.os.Parcel;
55import android.os.RemoteException;
56import android.os.ServiceManager;
57import android.provider.Settings;
58import android.text.TextUtils;
59import android.util.Log;
60import android.util.PrintWriterPrinter;
61import android.util.Printer;
62import android.view.IWindowManager;
63import android.view.WindowManager;
64import android.view.inputmethod.DefaultInputMethod;
65import android.view.inputmethod.InputBinding;
66import android.view.inputmethod.InputMethod;
67import android.view.inputmethod.InputMethodInfo;
68import android.view.inputmethod.InputMethodManager;
69import android.view.inputmethod.EditorInfo;
70
71import java.io.FileDescriptor;
72import java.io.IOException;
73import java.io.PrintWriter;
74import java.util.ArrayList;
75import java.util.HashMap;
76import java.util.List;
77
78/**
79 * This class provides a system service that manages input methods.
80 */
81public class InputMethodManagerService extends IInputMethodManager.Stub
82 implements ServiceConnection, Handler.Callback {
83 static final boolean DEBUG = false;
84 static final String TAG = "InputManagerService";
85
86 static final int MSG_SHOW_IM_PICKER = 1;
87
88 static final int MSG_UNBIND_INPUT = 1000;
89 static final int MSG_BIND_INPUT = 1010;
90 static final int MSG_SHOW_SOFT_INPUT = 1020;
91 static final int MSG_HIDE_SOFT_INPUT = 1030;
92 static final int MSG_ATTACH_TOKEN = 1040;
93 static final int MSG_CREATE_SESSION = 1050;
94
95 static final int MSG_START_INPUT = 2000;
96 static final int MSG_RESTART_INPUT = 2010;
97
98 static final int MSG_UNBIND_METHOD = 3000;
99 static final int MSG_BIND_METHOD = 3010;
100
101 final Context mContext;
102 final Handler mHandler;
103 final SettingsObserver mSettingsObserver;
104 final StatusBarService mStatusBar;
105 final IBinder mInputMethodIcon;
106 final IconData mInputMethodData;
107 final IWindowManager mIWindowManager;
108 final HandlerCaller mCaller;
109
110 final InputBindResult mNoBinding = new InputBindResult(null, null, -1);
111
112 // All known input methods. mMethodMap also serves as the global
113 // lock for this class.
114 final ArrayList<InputMethodInfo> mMethodList
115 = new ArrayList<InputMethodInfo>();
116 final HashMap<String, InputMethodInfo> mMethodMap
117 = new HashMap<String, InputMethodInfo>();
118
119 final TextUtils.SimpleStringSplitter mStringColonSplitter
120 = new TextUtils.SimpleStringSplitter(':');
121
122 class SessionState {
123 final ClientState client;
124 final IInputMethod method;
125 final IInputMethodSession session;
126
127 @Override
128 public String toString() {
129 return "SessionState{uid " + client.uid + " pid " + client.pid
130 + " method " + Integer.toHexString(
131 System.identityHashCode(method))
132 + " session " + Integer.toHexString(
133 System.identityHashCode(session))
134 + "}";
135 }
136
137 SessionState(ClientState _client, IInputMethod _method,
138 IInputMethodSession _session) {
139 client = _client;
140 method = _method;
141 session = _session;
142 }
143 }
144
145 class ClientState {
146 final IInputMethodClient client;
147 final IInputContext inputContext;
148 final int uid;
149 final int pid;
150 final InputBinding binding;
151
152 boolean sessionRequested;
153 SessionState curSession;
154
155 @Override
156 public String toString() {
157 return "ClientState{" + Integer.toHexString(
158 System.identityHashCode(this)) + " uid " + uid
159 + " pid " + pid + "}";
160 }
161
162 ClientState(IInputMethodClient _client, IInputContext _inputContext,
163 int _uid, int _pid) {
164 client = _client;
165 inputContext = _inputContext;
166 uid = _uid;
167 pid = _pid;
168 binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
169 }
170 }
171
172 final HashMap<IBinder, ClientState> mClients
173 = new HashMap<IBinder, ClientState>();
174
175 /**
176 * Id of the currently selected input method.
177 */
178 String mCurMethodId;
179
180 /**
181 * The current binding sequence number, incremented every time there is
182 * a new bind performed.
183 */
184 int mCurSeq;
185
186 /**
187 * The client that is currently bound to an input method.
188 */
189 ClientState mCurClient;
190
191 /**
192 * The attributes last provided by the current client.
193 */
194 EditorInfo mCurAttribute;
195
196 /**
197 * The input method ID of the input method service that we are currently
198 * connected to or in the process of connecting to.
199 */
200 String mCurId;
201
202 /**
203 * Set to true if our ServiceConnection is currently actively bound to
204 * a service (whether or not we have gotten its IBinder back yet).
205 */
206 boolean mHaveConnection;
207
208 /**
209 * Set if the client has asked for the input method to be shown.
210 */
211 boolean mShowRequested;
212
213 /**
214 * Set if we last told the input method to show itself.
215 */
216 boolean mInputShown;
217
218 /**
219 * The Intent used to connect to the current input method.
220 */
221 Intent mCurIntent;
222
223 /**
224 * The token we have made for the currently active input method, to
225 * identify it in the future.
226 */
227 IBinder mCurToken;
228
229 /**
230 * If non-null, this is the input method service we are currently connected
231 * to.
232 */
233 IInputMethod mCurMethod;
234
235 /**
236 * Have we called mCurMethod.bindInput()?
237 */
238 boolean mBoundToMethod;
239
240 /**
241 * Currently enabled session. Only touched by service thread, not
242 * protected by a lock.
243 */
244 SessionState mEnabledSession;
245
246 /**
247 * True if the screen is on. The value is true initially.
248 */
249 boolean mScreenOn = true;
250
251 AlertDialog.Builder mDialogBuilder;
252 AlertDialog mSwitchingDialog;
253 InputMethodInfo[] mIms;
254 CharSequence[] mItems;
255
256 class SettingsObserver extends ContentObserver {
257 SettingsObserver(Handler handler) {
258 super(handler);
259 ContentResolver resolver = mContext.getContentResolver();
260 resolver.registerContentObserver(Settings.Secure.getUriFor(
261 Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
262 }
263
264 @Override public void onChange(boolean selfChange) {
265 synchronized (mMethodMap) {
266 updateFromSettingsLocked();
267 }
268 }
269 }
270
271 class ScreenOnOffReceiver extends android.content.BroadcastReceiver {
272 @Override
273 public void onReceive(Context context, Intent intent) {
274 if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
275 mScreenOn = true;
276 } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
277 mScreenOn = false;
278 } else {
279 Log.e(TAG, "Unexpected intent " + intent);
280 }
281
282 // Inform the current client of the change in active status
283 try {
284 if (mCurClient != null && mCurClient.client != null) {
285 mCurClient.client.setActive(mScreenOn);
286 }
287 } catch (RemoteException e) {
288 Log.e(TAG, "Got RemoteException sending 'screen on/off' notification", e);
289 }
290 }
291 }
292
293 class PackageReceiver extends android.content.BroadcastReceiver {
294 @Override
295 public void onReceive(Context context, Intent intent) {
296 synchronized (mMethodMap) {
297 buildInputMethodListLocked(mMethodList, mMethodMap);
298
299 InputMethodInfo curIm = null;
300 String curInputMethodId = Settings.Secure.getString(context
301 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
302 final int N = mMethodList.size();
303 if (curInputMethodId != null) {
304 for (int i=0; i<N; i++) {
305 if (mMethodList.get(i).getId().equals(curInputMethodId)) {
306 curIm = mMethodList.get(i);
307 }
308 }
309 }
310
311 boolean changed = false;
312
313 Uri uri = intent.getData();
314 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
315 if (curIm != null && curIm.getPackageName().equals(pkg)) {
316 ServiceInfo si = null;
317 try {
318 si = mContext.getPackageManager().getServiceInfo(
319 curIm.getComponent(), 0);
320 } catch (PackageManager.NameNotFoundException ex) {
321 }
322 if (si == null) {
323 // Uh oh, current input method is no longer around!
324 // Pick another one...
325 Log.i(TAG, "Current input method removed: " + curInputMethodId);
326 List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
327 if (enabled != null && enabled.size() > 0) {
328 changed = true;
329 curIm = enabled.get(0);
330 curInputMethodId = curIm.getId();
331 Log.i(TAG, "Switching to: " + curInputMethodId);
332 Settings.Secure.putString(mContext.getContentResolver(),
333 Settings.Secure.DEFAULT_INPUT_METHOD,
334 curInputMethodId);
335 } else if (curIm != null) {
336 changed = true;
337 curIm = null;
338 curInputMethodId = "";
339 Log.i(TAG, "Unsetting current input method");
340 Settings.Secure.putString(mContext.getContentResolver(),
341 Settings.Secure.DEFAULT_INPUT_METHOD,
342 curInputMethodId);
343 }
344 }
345
346 } else if (curIm == null) {
347 // We currently don't have a default input method... is
348 // one now available?
349 List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
350 if (enabled != null && enabled.size() > 0) {
351 changed = true;
352 curIm = enabled.get(0);
353 curInputMethodId = curIm.getId();
354 Log.i(TAG, "New default input method: " + curInputMethodId);
355 Settings.Secure.putString(mContext.getContentResolver(),
356 Settings.Secure.DEFAULT_INPUT_METHOD,
357 curInputMethodId);
358 }
359 }
360
361 if (changed) {
362 updateFromSettingsLocked();
363 }
364 }
365 }
366 }
367
368 class MethodCallback extends IInputMethodCallback.Stub {
369 final IInputMethod mMethod;
370
371 MethodCallback(IInputMethod method) {
372 mMethod = method;
373 }
374
375 public void finishedEvent(int seq, boolean handled) throws RemoteException {
376 }
377
378 public void sessionCreated(IInputMethodSession session) throws RemoteException {
379 onSessionCreated(mMethod, session);
380 }
381 }
382
383 public InputMethodManagerService(Context context, StatusBarService statusBar) {
384 mContext = context;
385 mHandler = new Handler(this);
386 mIWindowManager = IWindowManager.Stub.asInterface(
387 ServiceManager.getService(Context.WINDOW_SERVICE));
388 mCaller = new HandlerCaller(context, new HandlerCaller.Callback() {
389 public void executeMessage(Message msg) {
390 handleMessage(msg);
391 }
392 });
393
394 IntentFilter packageFilt = new IntentFilter();
395 packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
396 packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
397 packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
398 packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
399 packageFilt.addDataScheme("package");
400 mContext.registerReceiver(new PackageReceiver(), packageFilt);
401
402 IntentFilter screenOnOffFilt = new IntentFilter();
403 screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON);
404 screenOnOffFilt.addAction(Intent.ACTION_SCREEN_OFF);
405 mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
406
407 buildInputMethodListLocked(mMethodList, mMethodMap);
408
409 final String enabledStr = Settings.Secure.getString(
410 mContext.getContentResolver(),
411 Settings.Secure.ENABLED_INPUT_METHODS);
412 Log.i(TAG, "Enabled input methods: " + enabledStr);
413 if (enabledStr == null) {
414 Log.i(TAG, "Enabled input methods has not been set, enabling all");
415 InputMethodInfo defIm = null;
416 StringBuilder sb = new StringBuilder(256);
417 final int N = mMethodList.size();
418 for (int i=0; i<N; i++) {
419 InputMethodInfo imi = mMethodList.get(i);
420 Log.i(TAG, "Adding: " + imi.getId());
421 if (i > 0) sb.append(':');
422 sb.append(imi.getId());
423 if (defIm == null && imi.getIsDefaultResourceId() != 0) {
424 try {
425 Resources res = mContext.createPackageContext(
426 imi.getPackageName(), 0).getResources();
427 if (res.getBoolean(imi.getIsDefaultResourceId())) {
428 defIm = imi;
429 Log.i(TAG, "Selected default: " + imi.getId());
430 }
431 } catch (PackageManager.NameNotFoundException ex) {
432 } catch (Resources.NotFoundException ex) {
433 }
434 }
435 }
436 if (defIm == null && N > 0) {
437 defIm = mMethodList.get(0);
438 Log.i(TAG, "No default found, using " + defIm.getId());
439 }
440 Settings.Secure.putString(mContext.getContentResolver(),
441 Settings.Secure.ENABLED_INPUT_METHODS, sb.toString());
442 if (defIm != null) {
443 Settings.Secure.putString(mContext.getContentResolver(),
444 Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
445 }
446 }
447
448 mStatusBar = statusBar;
449 mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
450 mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
451 statusBar.setIconVisibility(mInputMethodIcon, false);
452
453 mSettingsObserver = new SettingsObserver(mHandler);
454 updateFromSettingsLocked();
455 }
456
457 @Override
458 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
459 throws RemoteException {
460 try {
461 return super.onTransact(code, data, reply, flags);
462 } catch (RuntimeException e) {
463 // The input method manager only throws security exceptions, so let's
464 // log all others.
465 if (!(e instanceof SecurityException)) {
466 Log.e(TAG, "Input Method Manager Crash", e);
467 }
468 throw e;
469 }
470 }
471
472 public void systemReady() {
473
474 }
475
476 public List<InputMethodInfo> getInputMethodList() {
477 synchronized (mMethodMap) {
478 return new ArrayList<InputMethodInfo>(mMethodList);
479 }
480 }
481
482 public List<InputMethodInfo> getEnabledInputMethodList() {
483 synchronized (mMethodMap) {
484 return getEnabledInputMethodListLocked();
485 }
486 }
487
488 List<InputMethodInfo> getEnabledInputMethodListLocked() {
489 final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
490
491 final String enabledStr = Settings.Secure.getString(
492 mContext.getContentResolver(),
493 Settings.Secure.ENABLED_INPUT_METHODS);
494 if (enabledStr != null) {
495 final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
496 splitter.setString(enabledStr);
497
498 while (splitter.hasNext()) {
499 InputMethodInfo info = mMethodMap.get(splitter.next());
500 if (info != null) {
501 res.add(info);
502 }
503 }
504 }
505
506 return res;
507 }
508
509 public void addClient(IInputMethodClient client,
510 IInputContext inputContext, int uid, int pid) {
511 synchronized (mMethodMap) {
512 mClients.put(client.asBinder(), new ClientState(client,
513 inputContext, uid, pid));
514 }
515 }
516
517 public void removeClient(IInputMethodClient client) {
518 synchronized (mMethodMap) {
519 mClients.remove(client.asBinder());
520 }
521 }
522
523 void executeOrSendMessage(IInterface target, Message msg) {
524 if (target.asBinder() instanceof Binder) {
525 mCaller.sendMessage(msg);
526 } else {
527 handleMessage(msg);
528 msg.recycle();
529 }
530 }
531
532 void unbindCurrentInputLocked() {
533 if (mCurClient != null) {
534 if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = "
535 + mCurClient.client.asBinder());
536 if (mBoundToMethod) {
537 mBoundToMethod = false;
538 if (mCurMethod != null) {
539 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
540 MSG_UNBIND_INPUT, mCurMethod));
541 }
542 }
543 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
544 MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
545 mCurClient.sessionRequested = false;
546
547 // Call setActive(false) on the old client
548 try {
549 mCurClient.client.setActive(false);
550 } catch (RemoteException e) {
551 Log.e(TAG, "Got RemoteException sending setActive(false) notification", e);
552 }
553 mCurClient = null;
554 }
555 }
556
557 InputBindResult attachNewInputLocked(boolean initial, boolean needResult) {
558 if (!mBoundToMethod) {
559 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
560 MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
561 mBoundToMethod = true;
562 }
563 final SessionState session = mCurClient.curSession;
564 if (initial) {
565 executeOrSendMessage(session.method, mCaller.obtainMessageOO(
566 MSG_START_INPUT, session, mCurAttribute));
567 } else {
568 executeOrSendMessage(session.method, mCaller.obtainMessageOO(
569 MSG_RESTART_INPUT, session, mCurAttribute));
570 }
571 if (mShowRequested) {
572 showCurrentInputLocked();
573 }
574 return needResult
575 ? new InputBindResult(session.session, mCurId, mCurSeq)
576 : null;
577 }
578
579 InputBindResult startInputLocked(IInputMethodClient client,
580 EditorInfo attribute, boolean initial, boolean needResult) {
581 // If no method is currently selected, do nothing.
582 if (mCurMethodId == null) {
583 return mNoBinding;
584 }
585
586 ClientState cs = mClients.get(client.asBinder());
587 if (cs == null) {
588 throw new IllegalArgumentException("unknown client "
589 + client.asBinder());
590 }
591
592 try {
593 if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
594 // Check with the window manager to make sure this client actually
595 // has a window with focus. If not, reject. This is thread safe
596 // because if the focus changes some time before or after, the
597 // next client receiving focus that has any interest in input will
598 // be calling through here after that change happens.
599 Log.w(TAG, "Starting input on non-focused client " + cs.client
600 + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
601 return null;
602 }
603 } catch (RemoteException e) {
604 }
605
606 if (mCurClient != cs) {
607 // If the client is changing, we need to switch over to the new
608 // one.
609 unbindCurrentInputLocked();
610 if (DEBUG) Log.v(TAG, "switching to client: client = "
611 + cs.client.asBinder());
612
613 // If the screen is on, inform the new client it is active
614 if (mScreenOn) {
615 try {
616 cs.client.setActive(mScreenOn);
617 } catch (RemoteException e) {
618 Log.e(TAG, "Got RemoteException sending setActive notification", e);
619 }
620 }
621 }
622
623 // Bump up the sequence for this client and attach it.
624 mCurSeq++;
625 if (mCurSeq <= 0) mCurSeq = 1;
626 mCurClient = cs;
627 mCurAttribute = attribute;
628
629 // Check if the input method is changing.
630 if (mCurId != null && mCurId.equals(mCurMethodId)) {
631 if (cs.curSession != null) {
632 // Fast case: if we are already connected to the input method,
633 // then just return it.
634 return attachNewInputLocked(initial, needResult);
635 }
636 if (mHaveConnection) {
637 if (mCurMethod != null && !cs.sessionRequested) {
638 cs.sessionRequested = true;
639 if (DEBUG) Log.v(TAG, "Creating new session for client " + cs);
640 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
641 MSG_CREATE_SESSION, mCurMethod,
642 new MethodCallback(mCurMethod)));
643 }
644 return new InputBindResult(null, mCurId, mCurSeq);
645 }
646 }
647
648 InputMethodInfo info = mMethodMap.get(mCurMethodId);
649 if (info == null) {
650 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
651 }
652
653 if (mCurToken != null) {
654 try {
655 mIWindowManager.removeWindowToken(mCurToken);
656 } catch (RemoteException e) {
657 }
658 mCurToken = null;
659 }
660
661 if (mHaveConnection) {
662 mContext.unbindService(this);
663 mHaveConnection = false;
664 }
665
666 clearCurMethod();
667
668 mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
669 mCurIntent.setComponent(info.getComponent());
670 if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {
671 mHaveConnection = true;
672 mCurId = info.getId();
673 mCurToken = new Binder();
674 try {
675 mIWindowManager.addWindowToken(mCurToken,
676 WindowManager.LayoutParams.TYPE_INPUT_METHOD);
677 } catch (RemoteException e) {
678 }
679 return new InputBindResult(null, mCurId, mCurSeq);
680 } else {
681 mCurIntent = null;
682 Log.e(TAG, "Failure connecting to input method service: "
683 + mCurIntent);
684 }
685 return null;
686 }
687
688 public InputBindResult startInput(IInputMethodClient client,
689 EditorInfo attribute, boolean initial, boolean needResult) {
690 synchronized (mMethodMap) {
691 final long ident = Binder.clearCallingIdentity();
692 try {
693 return startInputLocked(client, attribute, initial, needResult);
694 } finally {
695 Binder.restoreCallingIdentity(ident);
696 }
697 }
698 }
699
700 public void finishInput(IInputMethodClient client) {
701 }
702
703 public void onServiceConnected(ComponentName name, IBinder service) {
704 synchronized (mMethodMap) {
705 if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
706 mCurMethod = IInputMethod.Stub.asInterface(service);
707 if (mCurClient != null) {
708 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
709 MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
710 if (mCurClient != null) {
711 if (DEBUG) Log.v(TAG, "Creating first session while with client "
712 + mCurClient);
713 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
714 MSG_CREATE_SESSION, mCurMethod,
715 new MethodCallback(mCurMethod)));
716 }
717 }
718 }
719 }
720 }
721
722 void onSessionCreated(IInputMethod method, IInputMethodSession session) {
723 synchronized (mMethodMap) {
724 if (mCurMethod == method) {
725 if (mCurClient != null) {
726 mCurClient.curSession = new SessionState(mCurClient,
727 method, session);
728 mCurClient.sessionRequested = false;
729 InputBindResult res = attachNewInputLocked(true, true);
730 if (res.method != null) {
731 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
732 MSG_BIND_METHOD, mCurClient.client, res));
733 }
734 }
735 }
736 }
737 }
738
739 void clearCurMethod() {
740 if (mCurMethod != null) {
741 for (ClientState cs : mClients.values()) {
742 cs.sessionRequested = false;
743 cs.curSession = null;
744 }
745 mCurMethod = null;
746 }
747 }
748
749 public void onServiceDisconnected(ComponentName name) {
750 synchronized (mMethodMap) {
751 if (DEBUG) Log.v(TAG, "Service disconnected: " + name
752 + " mCurIntent=" + mCurIntent);
753 if (mCurMethod != null && mCurIntent != null
754 && name.equals(mCurIntent.getComponent())) {
755 clearCurMethod();
756 mShowRequested = mInputShown;
757 mInputShown = false;
758 if (mCurClient != null) {
759 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
760 MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
761 }
762 }
763 }
764 }
765
766 public void updateStatusIcon(int iconId, String iconPackage) {
767 if (iconId == 0) {
768 Log.d(TAG, "hide the small icon for the input method");
769 mStatusBar.setIconVisibility(mInputMethodIcon, false);
770 } else {
771 Log.d(TAG, "show a small icon for the input method");
772
773 if (iconPackage != null
774 && iconPackage
775 .equals(InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) {
776 iconPackage = null;
777 }
778
779 mInputMethodData.iconId = iconId;
780 mInputMethodData.iconPackage = iconPackage;
781 mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
782 mStatusBar.setIconVisibility(mInputMethodIcon, true);
783 }
784 }
785
786 void updateFromSettingsLocked() {
787 String id = Settings.Secure.getString(mContext.getContentResolver(),
788 Settings.Secure.DEFAULT_INPUT_METHOD);
789 if (id != null) {
790 try {
791 setInputMethodLocked(id);
792 } catch (IllegalArgumentException e) {
793 Log.w(TAG, "Unknown input method from prefs: " + id, e);
794 }
795 }
796 }
797
798 void setInputMethodLocked(String id) {
799 InputMethodInfo info = mMethodMap.get(id);
800 if (info == null) {
801 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
802 }
803
804 if (id.equals(mCurMethodId)) {
805 return;
806 }
807
808 final long ident = Binder.clearCallingIdentity();
809 try {
810 mCurMethodId = id;
811 Settings.Secure.putString(mContext.getContentResolver(),
812 Settings.Secure.DEFAULT_INPUT_METHOD, id);
813
814 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
815 intent.putExtra("input_method_id", id);
816 mContext.sendBroadcast(intent);
817 unbindCurrentInputLocked();
818 } finally {
819 Binder.restoreCallingIdentity(ident);
820 }
821 }
822
823 public void showSoftInput(IInputMethodClient client) {
824 synchronized (mMethodMap) {
825 if (mCurClient == null || client == null
826 || mCurClient.client.asBinder() != client.asBinder()) {
827 try {
828 // We need to check if this is the current client with
829 // focus in the window manager, to allow this call to
830 // be made before input is started in it.
831 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
832 Log.w(TAG, "Ignoring showSoftInput of: " + client);
833 return;
834 }
835 } catch (RemoteException e) {
836 }
837 }
838
839 showCurrentInputLocked();
840 }
841 }
842
843 void showCurrentInputLocked() {
844 mShowRequested = true;
845 if (!mInputShown) {
846 if (mCurMethod != null) {
847 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
848 MSG_SHOW_SOFT_INPUT, mCurMethod));
849 mInputShown = true;
850 }
851 }
852 }
853
854 public void hideSoftInput(IInputMethodClient client) {
855 synchronized (mMethodMap) {
856 if (mCurClient == null || client == null
857 || mCurClient.client.asBinder() != client.asBinder()) {
858 try {
859 // We need to check if this is the current client with
860 // focus in the window manager, to allow this call to
861 // be made before input is started in it.
862 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
863 Log.w(TAG, "Ignoring hideSoftInput of: " + client);
864 return;
865 }
866 } catch (RemoteException e) {
867 }
868 }
869
870 hideCurrentInputLocked();
871 }
872 }
873
874 void hideCurrentInputLocked() {
875 if (mInputShown && mCurMethod != null) {
876 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
877 MSG_HIDE_SOFT_INPUT, mCurMethod));
878 }
879 mInputShown = false;
880 mShowRequested = false;
881 }
882
883 public void windowGainedFocus(IInputMethodClient client,
884 boolean viewHasFocus, int softInputMode, boolean first,
885 int windowFlags) {
886 synchronized (mMethodMap) {
887 if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder()
888 + " viewHasFocus=" + viewHasFocus
889 + " softInputMode=#" + Integer.toHexString(softInputMode)
890 + " first=" + first + " flags=#"
891 + Integer.toHexString(windowFlags));
892
893 if (mCurClient == null || client == null
894 || mCurClient.client.asBinder() != client.asBinder()) {
895 try {
896 // We need to check if this is the current client with
897 // focus in the window manager, to allow this call to
898 // be made before input is started in it.
899 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
900 Log.w(TAG, "Ignoring focus gain of: " + client);
901 return;
902 }
903 } catch (RemoteException e) {
904 }
905 }
906
907 switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
908 case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
909 if (!viewHasFocus || (softInputMode &
910 WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
911 != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
912 if ((windowFlags&WindowManager.LayoutParams
913 .FLAG_ALT_FOCUSABLE_IM) == 0) {
914 // There is no focus view, and this window will
915 // be behind any soft input window, then hide the
916 // soft input window if it is shown.
917 hideCurrentInputLocked();
918 }
919 }
920 break;
921 case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
922 // Do nothing.
923 break;
924 case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
925 hideCurrentInputLocked();
926 break;
927 case WindowManager.LayoutParams.SOFT_INPUT_STATE_FIRST_VISIBLE:
928 if (first && !viewHasFocus && (windowFlags
929 & WindowManager.LayoutParams.FLAG_RESTORED_STATE) == 0) {
930 showCurrentInputLocked();
931 }
932 break;
933 case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
934 if (viewHasFocus) {
935 showCurrentInputLocked();
936 }
937 break;
938 }
939 }
940 }
941
942 public void showInputMethodPickerFromClient(IInputMethodClient client) {
943 synchronized (mMethodMap) {
944 if (mCurClient == null || client == null
945 || mCurClient.client.asBinder() != client.asBinder()) {
946 Log.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
947 }
948
949 mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
950 }
951 }
952
953 public void setInputMethod(IBinder token, String id) {
954 synchronized (mMethodMap) {
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800955 if (mCurToken == null) {
956 if (mContext.checkCallingOrSelfPermission(
957 android.Manifest.permission.WRITE_SECURE_SETTINGS)
958 != PackageManager.PERMISSION_GRANTED) {
959 throw new SecurityException(
960 "Using null token requires permission "
961 + android.Manifest.permission.WRITE_SECURE_SETTINGS);
962 }
963 } else if (mCurToken != token) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800964 Log.w(TAG, "Ignoring setInputMethod of token: " + token);
965 }
966
967 setInputMethodLocked(id);
968 }
969 }
970
971 public void hideMySoftInput(IBinder token) {
972 synchronized (mMethodMap) {
973 if (mCurToken == null || mCurToken != token) {
974 Log.w(TAG, "Ignoring hideInputMethod of token: " + token);
975 }
976
977 if (mInputShown && mCurMethod != null) {
978 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
979 MSG_HIDE_SOFT_INPUT, mCurMethod));
980 }
981 mInputShown = false;
982 mShowRequested = false;
983 }
984 }
985
986 void setEnabledSessionInMainThread(SessionState session) {
987 if (mEnabledSession != session) {
988 if (mEnabledSession != null) {
989 try {
990 if (DEBUG) Log.v(TAG, "Disabling: " + mEnabledSession);
991 mEnabledSession.method.setSessionEnabled(
992 mEnabledSession.session, false);
993 } catch (RemoteException e) {
994 }
995 }
996 mEnabledSession = session;
997 try {
998 if (DEBUG) Log.v(TAG, "Enabling: " + mEnabledSession);
999 session.method.setSessionEnabled(
1000 session.session, true);
1001 } catch (RemoteException e) {
1002 }
1003 }
1004 }
1005
1006 public boolean handleMessage(Message msg) {
1007 HandlerCaller.SomeArgs args;
1008 switch (msg.what) {
1009 case MSG_SHOW_IM_PICKER:
1010 showInputMethodMenu();
1011 return true;
1012
1013 // ---------------------------------------------------------
1014
1015 case MSG_UNBIND_INPUT:
1016 try {
1017 ((IInputMethod)msg.obj).unbindInput();
1018 } catch (RemoteException e) {
1019 // There is nothing interesting about the method dying.
1020 }
1021 return true;
1022 case MSG_BIND_INPUT:
1023 args = (HandlerCaller.SomeArgs)msg.obj;
1024 try {
1025 ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
1026 } catch (RemoteException e) {
1027 }
1028 return true;
1029 case MSG_SHOW_SOFT_INPUT:
1030 try {
1031 ((IInputMethod)msg.obj).showSoftInput();
1032 } catch (RemoteException e) {
1033 }
1034 return true;
1035 case MSG_HIDE_SOFT_INPUT:
1036 try {
1037 ((IInputMethod)msg.obj).hideSoftInput();
1038 } catch (RemoteException e) {
1039 }
1040 return true;
1041 case MSG_ATTACH_TOKEN:
1042 args = (HandlerCaller.SomeArgs)msg.obj;
1043 try {
1044 ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
1045 } catch (RemoteException e) {
1046 }
1047 return true;
1048 case MSG_CREATE_SESSION:
1049 args = (HandlerCaller.SomeArgs)msg.obj;
1050 try {
1051 ((IInputMethod)args.arg1).createSession(
1052 (IInputMethodCallback)args.arg2);
1053 } catch (RemoteException e) {
1054 }
1055 return true;
1056
1057 // ---------------------------------------------------------
1058
1059 case MSG_START_INPUT:
1060 args = (HandlerCaller.SomeArgs)msg.obj;
1061 try {
1062 SessionState session = (SessionState)args.arg1;
1063 setEnabledSessionInMainThread(session);
1064 session.method.startInput((EditorInfo)args.arg2);
1065 } catch (RemoteException e) {
1066 }
1067 return true;
1068 case MSG_RESTART_INPUT:
1069 args = (HandlerCaller.SomeArgs)msg.obj;
1070 try {
1071 SessionState session = (SessionState)args.arg1;
1072 setEnabledSessionInMainThread(session);
1073 session.method.restartInput((EditorInfo)args.arg2);
1074 } catch (RemoteException e) {
1075 }
1076 return true;
1077
1078 // ---------------------------------------------------------
1079
1080 case MSG_UNBIND_METHOD:
1081 try {
1082 ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1);
1083 } catch (RemoteException e) {
1084 // There is nothing interesting about the last client dying.
1085 }
1086 return true;
1087 case MSG_BIND_METHOD:
1088 args = (HandlerCaller.SomeArgs)msg.obj;
1089 try {
1090 ((IInputMethodClient)args.arg1).onBindMethod(
1091 (InputBindResult)args.arg2);
1092 } catch (RemoteException e) {
1093 Log.w(TAG, "Client died receiving input method " + args.arg2);
1094 }
1095 return true;
1096 }
1097 return false;
1098 }
1099
1100 void buildInputMethodListLocked(ArrayList<InputMethodInfo> list,
1101 HashMap<String, InputMethodInfo> map) {
1102 list.clear();
1103 map.clear();
1104
1105 PackageManager pm = mContext.getPackageManager();
1106
1107 Object[][] buildin = {{
1108 DefaultInputMethod.class.getName(),
1109 DefaultInputMethod.getMetaInfo()}};
1110
1111 List<ResolveInfo> services = pm.queryIntentServices(
1112 new Intent(InputMethod.SERVICE_INTERFACE),
1113 PackageManager.GET_META_DATA);
1114
1115 for (int i = 0; i < services.size(); ++i) {
1116 ResolveInfo ri = services.get(i);
1117 ServiceInfo si = ri.serviceInfo;
1118 ComponentName compName = new ComponentName(si.packageName, si.name);
1119 if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(
1120 si.permission)) {
1121 Log.w(TAG, "Skipping input method " + compName
1122 + ": it does not require the permission "
1123 + android.Manifest.permission.BIND_INPUT_METHOD);
1124 continue;
1125 }
1126
1127 if (DEBUG) Log.d(TAG, "Checking " + compName);
1128
1129 /* Built-in input methods are not currently supported... this will
1130 * need to be reworked to bring them back (all input methods must
1131 * now be published in a manifest).
1132 */
1133 /*
1134 if (compName.getPackageName().equals(
1135 InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) {
1136 // System build-in input methods;
1137 String inputMethodName = null;
1138 int kbType = 0;
1139 String skbName = null;
1140
1141 for (int j = 0; j < buildin.length; ++j) {
1142 Object[] obj = buildin[j];
1143 if (compName.getClassName().equals(obj[0])) {
1144 InputMethodMetaInfo imp = (InputMethodMetaInfo) obj[1];
1145 inputMethodName = imp.inputMethodName;
1146 }
1147 }
1148
1149 InputMethodMetaInfo p = new InputMethodMetaInfo(compName,
1150 inputMethodName, "");
1151
1152 list.add(p);
1153
1154 if (DEBUG) {
1155 Log.d(TAG, "Found a build-in input method " + p);
1156 }
1157
1158 continue;
1159 }
1160 */
1161
1162 try {
1163 InputMethodInfo p = new InputMethodInfo(mContext, ri);
1164 list.add(p);
1165 map.put(p.getId(), p);
1166
1167 if (DEBUG) {
1168 Log.d(TAG, "Found a third-party input method " + p);
1169 }
1170
1171 } catch (XmlPullParserException e) {
1172 Log.w(TAG, "Unable to load input method " + compName, e);
1173 } catch (IOException e) {
1174 Log.w(TAG, "Unable to load input method " + compName, e);
1175 }
1176 }
1177 }
1178
1179 // ----------------------------------------------------------------------
1180
1181 public void showInputMethodMenu() {
1182 if (DEBUG) Log.v(TAG, "Show switching menu");
1183
1184 hideInputMethodMenu();
1185
1186 final Context context = mContext;
1187
1188 final PackageManager pm = context.getPackageManager();
1189
1190 String lastInputMethodId = Settings.Secure.getString(context
1191 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
1192 if (DEBUG) Log.v(TAG, "Current IME: " + lastInputMethodId);
1193
1194 final List<InputMethodInfo> immis = getEnabledInputMethodList();
1195
1196 int N = (immis == null ? 0 : immis.size());
1197
1198 mItems = new CharSequence[N];
1199 mIms = new InputMethodInfo[N];
1200
1201 for (int i = 0; i < N; ++i) {
1202 InputMethodInfo property = immis.get(i);
1203 mItems[i] = property.loadLabel(pm);
1204 mIms[i] = property;
1205 }
1206
1207 int checkedItem = 0;
1208 for (int i = 0; i < N; ++i) {
1209 if (mIms[i].getId().equals(lastInputMethodId)) {
1210 checkedItem = i;
1211 break;
1212 }
1213 }
1214
1215 AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
1216 public void onClick(DialogInterface dialog, int which) {
1217 hideInputMethodMenu();
1218 }
1219 };
1220
1221 TypedArray a = context.obtainStyledAttributes(null,
1222 com.android.internal.R.styleable.DialogPreference,
1223 com.android.internal.R.attr.alertDialogStyle, 0);
1224 mDialogBuilder = new AlertDialog.Builder(context)
1225 .setTitle(com.android.internal.R.string.select_input_method)
1226 .setOnCancelListener(new OnCancelListener() {
1227 public void onCancel(DialogInterface dialog) {
1228 hideInputMethodMenu();
1229 }
1230 })
1231 .setIcon(a.getDrawable(
1232 com.android.internal.R.styleable.DialogPreference_dialogTitle));
1233 a.recycle();
1234
1235 mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
1236 new AlertDialog.OnClickListener() {
1237 public void onClick(DialogInterface dialog, int which) {
1238 synchronized (mMethodMap) {
1239 InputMethodInfo im = mIms[which];
1240 hideInputMethodMenu();
1241 setInputMethodLocked(im.getId());
1242 }
1243 }
1244 });
1245
1246 synchronized (mMethodMap) {
1247 mSwitchingDialog = mDialogBuilder.create();
1248 mSwitchingDialog.getWindow().setType(
1249 WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
1250 mSwitchingDialog.show();
1251 }
1252 }
1253
1254 void hideInputMethodMenu() {
1255 if (DEBUG) Log.v(TAG, "Hide switching menu");
1256
1257 synchronized (mMethodMap) {
1258 if (mSwitchingDialog != null) {
1259 mSwitchingDialog.dismiss();
1260 mSwitchingDialog = null;
1261 }
1262
1263 mDialogBuilder = null;
1264 mItems = null;
1265 mIms = null;
1266 }
1267 }
1268
The Android Open Source Projectb7986892009-01-09 17:51:23 -08001269 // ----------------------------------------------------------------------
1270
1271 public boolean setInputMethodEnabled(String id, boolean enabled) {
1272 synchronized (mMethodMap) {
1273 if (mContext.checkCallingOrSelfPermission(
1274 android.Manifest.permission.WRITE_SECURE_SETTINGS)
1275 != PackageManager.PERMISSION_GRANTED) {
1276 throw new SecurityException(
1277 "Requires permission "
1278 + android.Manifest.permission.WRITE_SECURE_SETTINGS);
1279 }
1280
1281 // Make sure this is a valid input method.
1282 InputMethodInfo imm = mMethodMap.get(id);
1283 if (imm == null) {
1284 if (imm == null) {
1285 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
1286 }
1287 }
1288
1289 StringBuilder builder = new StringBuilder(256);
1290
1291 boolean removed = false;
1292 String firstId = null;
1293
1294 // Look through the currently enabled input methods.
1295 String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
1296 Settings.Secure.ENABLED_INPUT_METHODS);
1297 if (enabledStr != null) {
1298 final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
1299 splitter.setString(enabledStr);
1300 while (splitter.hasNext()) {
1301 String curId = splitter.next();
1302 if (curId.equals(id)) {
1303 if (enabled) {
1304 // We are enabling this input method, but it is
1305 // already enabled. Nothing to do. The previous
1306 // state was enabled.
1307 return true;
1308 }
1309 // We are disabling this input method, and it is
1310 // currently enabled. Skip it to remove from the
1311 // new list.
1312 removed = true;
1313 } else if (!enabled) {
1314 // We are building a new list of input methods that
1315 // doesn't contain the given one.
1316 if (firstId == null) firstId = curId;
1317 if (builder.length() > 0) builder.append(':');
1318 builder.append(curId);
1319 }
1320 }
1321 }
1322
1323 if (!enabled) {
1324 if (!removed) {
1325 // We are disabling the input method but it is already
1326 // disabled. Nothing to do. The previous state was
1327 // disabled.
1328 return false;
1329 }
1330 // Update the setting with the new list of input methods.
1331 Settings.Secure.putString(mContext.getContentResolver(),
1332 Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
1333 // We the disabled input method is currently selected, switch
1334 // to another one.
1335 String selId = Settings.Secure.getString(mContext.getContentResolver(),
1336 Settings.Secure.DEFAULT_INPUT_METHOD);
1337 if (id.equals(selId)) {
1338 Settings.Secure.putString(mContext.getContentResolver(),
1339 Settings.Secure.DEFAULT_INPUT_METHOD,
1340 firstId != null ? firstId : "");
1341 }
1342 // Previous state was enabled.
1343 return true;
1344 }
1345
1346 // Add in the newly enabled input method.
1347 if (enabledStr == null || enabledStr.length() == 0) {
1348 enabledStr = id;
1349 } else {
1350 enabledStr = enabledStr + ':' + id;
1351 }
1352
1353 Settings.Secure.putString(mContext.getContentResolver(),
1354 Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
1355
1356 // Previous state was disabled.
1357 return false;
1358 }
1359 }
1360
1361 // ----------------------------------------------------------------------
1362
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001363 @Override
1364 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1365 if (mContext.checkCallingPermission("android.permission.DUMP")
1366 != PackageManager.PERMISSION_GRANTED) {
1367
1368 pw.println("Permission Denial: can't dump InputMethodManager from from pid="
1369 + Binder.getCallingPid()
1370 + ", uid=" + Binder.getCallingUid());
1371 return;
1372 }
1373
1374 synchronized (mMethodMap) {
1375 final Printer p = new PrintWriterPrinter(pw);
1376 p.println("Current Input Method Manager state:");
1377 int N = mMethodList.size();
1378 p.println(" Input Methods:");
1379 for (int i=0; i<N; i++) {
1380 InputMethodInfo info = mMethodList.get(i);
1381 p.println(" InputMethod #" + i + ":");
1382 info.dump(p, " ");
1383 }
1384 p.println(" Clients:");
1385 for (ClientState ci : mClients.values()) {
1386 p.println(" Client " + ci + ":");
1387 p.println(" client=" + ci.client);
1388 p.println(" inputContext=" + ci.inputContext);
1389 p.println(" sessionRequested=" + ci.sessionRequested);
1390 p.println(" curSession=" + ci.curSession);
1391 }
1392 p.println(" mInputMethodIcon=" + mInputMethodIcon);
1393 p.println(" mInputMethodData=" + mInputMethodData);
1394 p.println(" mCurrentMethod=" + mCurMethodId);
1395 p.println(" mCurSeq=" + mCurSeq + " mCurClient=" + mCurClient);
1396 p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
1397 + " mBoundToMethod=" + mBoundToMethod);
1398 p.println(" mCurToken=" + mCurToken);
1399 p.println(" mCurIntent=" + mCurIntent);
1400 p.println(" mCurMethod=" + mCurMethod);
1401 p.println(" mEnabledSession=" + mEnabledSession);
1402 p.println(" mShowRequested=" + mShowRequested
1403 + " mInputShown=" + mInputShown);
1404 p.println(" mScreenOn=" + mScreenOn);
1405 }
1406 }
1407}