1 /* 2 * Copyright (C) 2007-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 17 package android.view.inputmethod; 18 19 import com.android.internal.os.SomeArgs; 20 import com.android.internal.view.IInputConnectionWrapper; 21 import com.android.internal.view.IInputContext; 22 import com.android.internal.view.IInputMethodClient; 23 import com.android.internal.view.IInputMethodManager; 24 import com.android.internal.view.IInputMethodSession; 25 import com.android.internal.view.InputBindResult; 26 import com.android.internal.view.InputMethodClient; 27 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.RequiresPermission; 31 import android.content.Context; 32 import android.graphics.Rect; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.os.IBinder; 36 import android.os.Looper; 37 import android.os.Message; 38 import android.os.RemoteException; 39 import android.os.ResultReceiver; 40 import android.os.ServiceManager; 41 import android.os.Trace; 42 import android.text.TextUtils; 43 import android.text.style.SuggestionSpan; 44 import android.util.Log; 45 import android.util.Pools.Pool; 46 import android.util.Pools.SimplePool; 47 import android.util.PrintWriterPrinter; 48 import android.util.Printer; 49 import android.util.SparseArray; 50 import android.view.InputChannel; 51 import android.view.InputEvent; 52 import android.view.InputEventSender; 53 import android.view.KeyEvent; 54 import android.view.View; 55 import android.view.ViewRootImpl; 56 57 import java.io.FileDescriptor; 58 import java.io.PrintWriter; 59 import java.util.ArrayList; 60 import java.util.Arrays; 61 import java.util.HashMap; 62 import java.util.List; 63 import java.util.Map; 64 import java.util.Objects; 65 import java.util.concurrent.CountDownLatch; 66 import java.util.concurrent.TimeUnit; 67 68 import static android.Manifest.permission.WRITE_SECURE_SETTINGS; 69 70 /** 71 * Central system API to the overall input method framework (IMF) architecture, 72 * which arbitrates interaction between applications and the current input method. 73 * You can retrieve an instance of this interface with 74 * {@link Context#getSystemService(String) Context.getSystemService()}. 75 * 76 * <p>Topics covered here: 77 * <ol> 78 * <li><a href="#ArchitectureOverview">Architecture Overview</a> 79 * <li><a href="#Applications">Applications</a> 80 * <li><a href="#InputMethods">Input Methods</a> 81 * <li><a href="#Security">Security</a> 82 * </ol> 83 * 84 * <a name="ArchitectureOverview"></a> 85 * <h3>Architecture Overview</h3> 86 * 87 * <p>There are three primary parties involved in the input method 88 * framework (IMF) architecture:</p> 89 * 90 * <ul> 91 * <li> The <strong>input method manager</strong> as expressed by this class 92 * is the central point of the system that manages interaction between all 93 * other parts. It is expressed as the client-side API here which exists 94 * in each application context and communicates with a global system service 95 * that manages the interaction across all processes. 96 * <li> An <strong>input method (IME)</strong> implements a particular 97 * interaction model allowing the user to generate text. The system binds 98 * to the current input method that is use, causing it to be created and run, 99 * and tells it when to hide and show its UI. Only one IME is running at a time. 100 * <li> Multiple <strong>client applications</strong> arbitrate with the input 101 * method manager for input focus and control over the state of the IME. Only 102 * one such client is ever active (working with the IME) at a time. 103 * </ul> 104 * 105 * 106 * <a name="Applications"></a> 107 * <h3>Applications</h3> 108 * 109 * <p>In most cases, applications that are using the standard 110 * {@link android.widget.TextView} or its subclasses will have little they need 111 * to do to work well with soft input methods. The main things you need to 112 * be aware of are:</p> 113 * 114 * <ul> 115 * <li> Properly set the {@link android.R.attr#inputType} in your editable 116 * text views, so that the input method will have enough context to help the 117 * user in entering text into them. 118 * <li> Deal well with losing screen space when the input method is 119 * displayed. Ideally an application should handle its window being resized 120 * smaller, but it can rely on the system performing panning of the window 121 * if needed. You should set the {@link android.R.attr#windowSoftInputMode} 122 * attribute on your activity or the corresponding values on windows you 123 * create to help the system determine whether to pan or resize (it will 124 * try to determine this automatically but may get it wrong). 125 * <li> You can also control the preferred soft input state (open, closed, etc) 126 * for your window using the same {@link android.R.attr#windowSoftInputMode} 127 * attribute. 128 * </ul> 129 * 130 * <p>More finer-grained control is available through the APIs here to directly 131 * interact with the IMF and its IME -- either showing or hiding the input 132 * area, letting the user pick an input method, etc.</p> 133 * 134 * <p>For the rare people amongst us writing their own text editors, you 135 * will need to implement {@link android.view.View#onCreateInputConnection} 136 * to return a new instance of your own {@link InputConnection} interface 137 * allowing the IME to interact with your editor.</p> 138 * 139 * 140 * <a name="InputMethods"></a> 141 * <h3>Input Methods</h3> 142 * 143 * <p>An input method (IME) is implemented 144 * as a {@link android.app.Service}, typically deriving from 145 * {@link android.inputmethodservice.InputMethodService}. It must provide 146 * the core {@link InputMethod} interface, though this is normally handled by 147 * {@link android.inputmethodservice.InputMethodService} and implementors will 148 * only need to deal with the higher-level API there.</p> 149 * 150 * See the {@link android.inputmethodservice.InputMethodService} class for 151 * more information on implementing IMEs. 152 * 153 * 154 * <a name="Security"></a> 155 * <h3>Security</h3> 156 * 157 * <p>There are a lot of security issues associated with input methods, 158 * since they essentially have freedom to completely drive the UI and monitor 159 * everything the user enters. The Android input method framework also allows 160 * arbitrary third party IMEs, so care must be taken to restrict their 161 * selection and interactions.</p> 162 * 163 * <p>Here are some key points about the security architecture behind the 164 * IMF:</p> 165 * 166 * <ul> 167 * <li> <p>Only the system is allowed to directly access an IME's 168 * {@link InputMethod} interface, via the 169 * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission. This is 170 * enforced in the system by not binding to an input method service that does 171 * not require this permission, so the system can guarantee no other untrusted 172 * clients are accessing the current input method outside of its control.</p> 173 * 174 * <li> <p>There may be many client processes of the IMF, but only one may 175 * be active at a time. The inactive clients can not interact with key 176 * parts of the IMF through the mechanisms described below.</p> 177 * 178 * <li> <p>Clients of an input method are only given access to its 179 * {@link InputMethodSession} interface. One instance of this interface is 180 * created for each client, and only calls from the session associated with 181 * the active client will be processed by the current IME. This is enforced 182 * by {@link android.inputmethodservice.AbstractInputMethodService} for normal 183 * IMEs, but must be explicitly handled by an IME that is customizing the 184 * raw {@link InputMethodSession} implementation.</p> 185 * 186 * <li> <p>Only the active client's {@link InputConnection} will accept 187 * operations. The IMF tells each client process whether it is active, and 188 * the framework enforces that in inactive processes calls on to the current 189 * InputConnection will be ignored. This ensures that the current IME can 190 * only deliver events and text edits to the UI that the user sees as 191 * being in focus.</p> 192 * 193 * <li> <p>An IME can never interact with an {@link InputConnection} while 194 * the screen is off. This is enforced by making all clients inactive while 195 * the screen is off, and prevents bad IMEs from driving the UI when the user 196 * can not be aware of its behavior.</p> 197 * 198 * <li> <p>A client application can ask that the system let the user pick a 199 * new IME, but can not programmatically switch to one itself. This avoids 200 * malicious applications from switching the user to their own IME, which 201 * remains running when the user navigates away to another application. An 202 * IME, on the other hand, <em>is</em> allowed to programmatically switch 203 * the system to another IME, since it already has full control of user 204 * input.</p> 205 * 206 * <li> <p>The user must explicitly enable a new IME in settings before 207 * they can switch to it, to confirm with the system that they know about it 208 * and want to make it available for use.</p> 209 * </ul> 210 */ 211 public final class InputMethodManager { 212 static final boolean DEBUG = false; 213 static final String TAG = "InputMethodManager"; 214 215 static final String PENDING_EVENT_COUNTER = "aq:imm"; 216 217 static InputMethodManager sInstance; 218 219 /** 220 * @hide Flag for IInputMethodManager.windowGainedFocus: a view in 221 * the window has input focus. 222 */ 223 public static final int CONTROL_WINDOW_VIEW_HAS_FOCUS = 1<<0; 224 225 /** 226 * @hide Flag for IInputMethodManager.windowGainedFocus: the focus 227 * is a text editor. 228 */ 229 public static final int CONTROL_WINDOW_IS_TEXT_EDITOR = 1<<1; 230 231 /** 232 * @hide Flag for IInputMethodManager.windowGainedFocus: this is the first 233 * time the window has gotten focus. 234 */ 235 public static final int CONTROL_WINDOW_FIRST = 1<<2; 236 237 /** 238 * @hide Flag for IInputMethodManager.startInput: this is the first 239 * time the window has gotten focus. 240 */ 241 public static final int CONTROL_START_INITIAL = 1<<8; 242 243 /** 244 * Timeout in milliseconds for delivering a key to an IME. 245 */ 246 static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500; 247 248 /** @hide */ 249 public static final int DISPATCH_IN_PROGRESS = -1; 250 251 /** @hide */ 252 public static final int DISPATCH_NOT_HANDLED = 0; 253 254 /** @hide */ 255 public static final int DISPATCH_HANDLED = 1; 256 257 /** @hide */ 258 public static final int SHOW_IM_PICKER_MODE_AUTO = 0; 259 /** @hide */ 260 public static final int SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES = 1; 261 /** @hide */ 262 public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2; 263 264 final IInputMethodManager mService; 265 final Looper mMainLooper; 266 267 // For scheduling work on the main thread. This also serves as our 268 // global lock. 269 final H mH; 270 271 // Our generic input connection if the current target does not have its own. 272 final IInputContext mIInputContext; 273 274 /** 275 * True if this input method client is active, initially false. 276 */ 277 boolean mActive = false; 278 279 /** 280 * Set whenever this client becomes inactive, to know we need to reset 281 * state with the IME the next time we receive focus. 282 */ 283 boolean mHasBeenInactive = true; 284 285 /** 286 * As reported by IME through InputConnection. 287 */ 288 boolean mFullscreenMode; 289 290 // ----------------------------------------------------------- 291 292 /** 293 * This is the root view of the overall window that currently has input 294 * method focus. 295 */ 296 View mCurRootView; 297 /** 298 * This is the view that should currently be served by an input method, 299 * regardless of the state of setting that up. 300 */ 301 View mServedView; 302 /** 303 * This is then next view that will be served by the input method, when 304 * we get around to updating things. 305 */ 306 View mNextServedView; 307 /** 308 * This is set when we are in the process of connecting, to determine 309 * when we have actually finished. 310 */ 311 boolean mServedConnecting; 312 /** 313 * This is non-null when we have connected the served view; it holds 314 * the attributes that were last retrieved from the served view and given 315 * to the input connection. 316 */ 317 EditorInfo mCurrentTextBoxAttribute; 318 /** 319 * The InputConnection that was last retrieved from the served view. 320 */ 321 ControlledInputConnectionWrapper mServedInputConnectionWrapper; 322 /** 323 * The completions that were last provided by the served view. 324 */ 325 CompletionInfo[] mCompletions; 326 327 // Cursor position on the screen. 328 Rect mTmpCursorRect = new Rect(); 329 Rect mCursorRect = new Rect(); 330 int mCursorSelStart; 331 int mCursorSelEnd; 332 int mCursorCandStart; 333 int mCursorCandEnd; 334 335 /** 336 * Represents an invalid action notification sequence number. {@link InputMethodManagerService} 337 * always issues a positive integer for action notification sequence numbers. Thus -1 is 338 * guaranteed to be different from any valid sequence number. 339 */ 340 private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1; 341 /** 342 * The next sequence number that is to be sent to {@link InputMethodManagerService} via 343 * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed. 344 */ 345 private int mNextUserActionNotificationSequenceNumber = 346 NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER; 347 348 /** 349 * The last sequence number that is already sent to {@link InputMethodManagerService}. 350 */ 351 private int mLastSentUserActionNotificationSequenceNumber = 352 NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER; 353 354 /** 355 * The instance that has previously been sent to the input method. 356 */ 357 private CursorAnchorInfo mCursorAnchorInfo = null; 358 359 // ----------------------------------------------------------- 360 361 /** 362 * Sequence number of this binding, as returned by the server. 363 */ 364 int mBindSequence = -1; 365 /** 366 * ID of the method we are bound to. 367 */ 368 String mCurId; 369 /** 370 * The actual instance of the method to make calls on it. 371 */ 372 IInputMethodSession mCurMethod; 373 InputChannel mCurChannel; 374 ImeInputEventSender mCurSender; 375 376 private static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE = 0x0; 377 378 /** 379 * The monitor mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}. 380 */ 381 private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE; 382 383 final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20); 384 final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20); 385 386 // ----------------------------------------------------------- 387 388 static final int MSG_DUMP = 1; 389 static final int MSG_BIND = 2; 390 static final int MSG_UNBIND = 3; 391 static final int MSG_SET_ACTIVE = 4; 392 static final int MSG_SEND_INPUT_EVENT = 5; 393 static final int MSG_TIMEOUT_INPUT_EVENT = 6; 394 static final int MSG_FLUSH_INPUT_EVENT = 7; 395 static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9; 396 397 class H extends Handler { H(Looper looper)398 H(Looper looper) { 399 super(looper, null, true); 400 } 401 402 @Override handleMessage(Message msg)403 public void handleMessage(Message msg) { 404 switch (msg.what) { 405 case MSG_DUMP: { 406 SomeArgs args = (SomeArgs)msg.obj; 407 try { 408 doDump((FileDescriptor)args.arg1, 409 (PrintWriter)args.arg2, (String[])args.arg3); 410 } catch (RuntimeException e) { 411 ((PrintWriter)args.arg2).println("Exception: " + e); 412 } 413 synchronized (args.arg4) { 414 ((CountDownLatch)args.arg4).countDown(); 415 } 416 args.recycle(); 417 return; 418 } 419 case MSG_BIND: { 420 final InputBindResult res = (InputBindResult)msg.obj; 421 if (DEBUG) { 422 Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id); 423 } 424 synchronized (mH) { 425 if (mBindSequence < 0 || mBindSequence != res.sequence) { 426 Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence 427 + ", given seq=" + res.sequence); 428 if (res.channel != null && res.channel != mCurChannel) { 429 res.channel.dispose(); 430 } 431 return; 432 } 433 434 mRequestUpdateCursorAnchorInfoMonitorMode = 435 REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE; 436 437 setInputChannelLocked(res.channel); 438 mCurMethod = res.method; 439 mCurId = res.id; 440 mBindSequence = res.sequence; 441 } 442 startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS, 443 null, 0, 0, 0); 444 return; 445 } 446 case MSG_UNBIND: { 447 final int sequence = msg.arg1; 448 @InputMethodClient.UnbindReason 449 final int reason = msg.arg2; 450 if (DEBUG) { 451 Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence + 452 " reason=" + InputMethodClient.getUnbindReason(reason)); 453 } 454 final boolean startInput; 455 synchronized (mH) { 456 if (mBindSequence != sequence) { 457 return; 458 } 459 clearBindingLocked(); 460 // If we were actively using the last input method, then 461 // we would like to re-connect to the next input method. 462 if (mServedView != null && mServedView.isFocused()) { 463 mServedConnecting = true; 464 } 465 startInput = mActive; 466 } 467 if (startInput) { 468 startInputInner( 469 InputMethodClient.START_INPUT_REASON_UNBOUND_FROM_IMMS, null, 0, 0, 470 0); 471 } 472 return; 473 } 474 case MSG_SET_ACTIVE: { 475 final boolean active = msg.arg1 != 0; 476 if (DEBUG) { 477 Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive); 478 } 479 synchronized (mH) { 480 mActive = active; 481 mFullscreenMode = false; 482 if (!active) { 483 // Some other client has starting using the IME, so note 484 // that this happened and make sure our own editor's 485 // state is reset. 486 mHasBeenInactive = true; 487 try { 488 // Note that finishComposingText() is allowed to run 489 // even when we are not active. 490 mIInputContext.finishComposingText(); 491 } catch (RemoteException e) { 492 } 493 } 494 // Check focus again in case that "onWindowFocus" is called before 495 // handling this message. 496 if (mServedView != null && mServedView.hasWindowFocus()) { 497 if (checkFocusNoStartInput(mHasBeenInactive)) { 498 final int reason = active ? 499 InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS : 500 InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS; 501 startInputInner(reason, null, 0, 0, 0); 502 } 503 } 504 } 505 return; 506 } 507 case MSG_SEND_INPUT_EVENT: { 508 sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj); 509 return; 510 } 511 case MSG_TIMEOUT_INPUT_EVENT: { 512 finishedInputEvent(msg.arg1, false, true); 513 return; 514 } 515 case MSG_FLUSH_INPUT_EVENT: { 516 finishedInputEvent(msg.arg1, false, false); 517 return; 518 } 519 case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: { 520 synchronized (mH) { 521 mNextUserActionNotificationSequenceNumber = msg.arg1; 522 } 523 } 524 } 525 } 526 } 527 528 private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper { 529 private final InputMethodManager mParentInputMethodManager; 530 ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn, final InputMethodManager inputMethodManager)531 public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn, 532 final InputMethodManager inputMethodManager) { 533 super(mainLooper, conn); 534 mParentInputMethodManager = inputMethodManager; 535 } 536 537 @Override isActive()538 public boolean isActive() { 539 return mParentInputMethodManager.mActive && !isFinished(); 540 } 541 deactivate()542 void deactivate() { 543 if (isFinished()) { 544 // This is a small performance optimization. Still only the 1st call of 545 // reportFinish() will take effect. 546 return; 547 } 548 closeConnection(); 549 } 550 551 @Override onUserAction()552 protected void onUserAction() { 553 mParentInputMethodManager.notifyUserAction(); 554 } 555 556 @Override onReportFullscreenMode(boolean enabled, boolean calledInBackground)557 protected void onReportFullscreenMode(boolean enabled, boolean calledInBackground) { 558 mParentInputMethodManager.onReportFullscreenMode(enabled, calledInBackground, 559 getInputMethodId()); 560 } 561 562 @Override toString()563 public String toString() { 564 return "ControlledInputConnectionWrapper{" 565 + "connection=" + getInputConnection() 566 + " finished=" + isFinished() 567 + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive 568 + " mInputMethodId=" + getInputMethodId() 569 + "}"; 570 } 571 } 572 573 final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { 574 @Override 575 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 576 // No need to check for dump permission, since we only give this 577 // interface to the system. 578 CountDownLatch latch = new CountDownLatch(1); 579 SomeArgs sargs = SomeArgs.obtain(); 580 sargs.arg1 = fd; 581 sargs.arg2 = fout; 582 sargs.arg3 = args; 583 sargs.arg4 = latch; 584 mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs)); 585 try { 586 if (!latch.await(5, TimeUnit.SECONDS)) { 587 fout.println("Timeout waiting for dump"); 588 } 589 } catch (InterruptedException e) { 590 fout.println("Interrupted waiting for dump"); 591 } 592 } 593 594 @Override 595 public void setUsingInputMethod(boolean state) { 596 } 597 598 @Override 599 public void onBindMethod(InputBindResult res) { 600 mH.sendMessage(mH.obtainMessage(MSG_BIND, res)); 601 } 602 603 @Override 604 public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) { 605 mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, unbindReason)); 606 } 607 608 @Override 609 public void setActive(boolean active) { 610 mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0)); 611 } 612 613 @Override 614 public void setUserActionNotificationSequenceNumber(int sequenceNumber) { 615 mH.sendMessage(mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, 616 sequenceNumber, 0)); 617 } 618 }; 619 620 final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); 621 InputMethodManager(IInputMethodManager service, Looper looper)622 InputMethodManager(IInputMethodManager service, Looper looper) { 623 mService = service; 624 mMainLooper = looper; 625 mH = new H(looper); 626 mIInputContext = new ControlledInputConnectionWrapper(looper, 627 mDummyInputConnection, this); 628 } 629 630 /** 631 * Retrieve the global InputMethodManager instance, creating it if it 632 * doesn't already exist. 633 * @hide 634 */ getInstance()635 public static InputMethodManager getInstance() { 636 synchronized (InputMethodManager.class) { 637 if (sInstance == null) { 638 IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE); 639 IInputMethodManager service = IInputMethodManager.Stub.asInterface(b); 640 sInstance = new InputMethodManager(service, Looper.getMainLooper()); 641 } 642 return sInstance; 643 } 644 } 645 646 /** 647 * Private optimization: retrieve the global InputMethodManager instance, 648 * if it exists. 649 * @hide 650 */ peekInstance()651 public static InputMethodManager peekInstance() { 652 return sInstance; 653 } 654 655 /** @hide */ getClient()656 public IInputMethodClient getClient() { 657 return mClient; 658 } 659 660 /** @hide */ getInputContext()661 public IInputContext getInputContext() { 662 return mIInputContext; 663 } 664 getInputMethodList()665 public List<InputMethodInfo> getInputMethodList() { 666 try { 667 return mService.getInputMethodList(); 668 } catch (RemoteException e) { 669 throw e.rethrowFromSystemServer(); 670 } 671 } 672 getEnabledInputMethodList()673 public List<InputMethodInfo> getEnabledInputMethodList() { 674 try { 675 return mService.getEnabledInputMethodList(); 676 } catch (RemoteException e) { 677 throw e.rethrowFromSystemServer(); 678 } 679 } 680 681 /** 682 * Returns a list of enabled input method subtypes for the specified input method info. 683 * @param imi An input method info whose subtypes list will be returned. 684 * @param allowsImplicitlySelectedSubtypes A boolean flag to allow to return the implicitly 685 * selected subtypes. If an input method info doesn't have enabled subtypes, the framework 686 * will implicitly enable subtypes according to the current system language. 687 */ getEnabledInputMethodSubtypeList(InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes)688 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi, 689 boolean allowsImplicitlySelectedSubtypes) { 690 try { 691 return mService.getEnabledInputMethodSubtypeList( 692 imi == null ? null : imi.getId(), allowsImplicitlySelectedSubtypes); 693 } catch (RemoteException e) { 694 throw e.rethrowFromSystemServer(); 695 } 696 } 697 showStatusIcon(IBinder imeToken, String packageName, int iconId)698 public void showStatusIcon(IBinder imeToken, String packageName, int iconId) { 699 try { 700 mService.updateStatusIcon(imeToken, packageName, iconId); 701 } catch (RemoteException e) { 702 throw e.rethrowFromSystemServer(); 703 } 704 } 705 hideStatusIcon(IBinder imeToken)706 public void hideStatusIcon(IBinder imeToken) { 707 try { 708 mService.updateStatusIcon(imeToken, null, 0); 709 } catch (RemoteException e) { 710 throw e.rethrowFromSystemServer(); 711 } 712 } 713 714 /** @hide */ setImeWindowStatus(IBinder imeToken, int vis, int backDisposition)715 public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) { 716 try { 717 mService.setImeWindowStatus(imeToken, vis, backDisposition); 718 } catch (RemoteException e) { 719 throw e.rethrowFromSystemServer(); 720 } 721 } 722 723 /** @hide */ onReportFullscreenMode(boolean fullScreen, boolean calledInBackground, String inputMethodId)724 public void onReportFullscreenMode(boolean fullScreen, boolean calledInBackground, 725 String inputMethodId) { 726 synchronized (mH) { 727 if (!calledInBackground || TextUtils.equals(mCurId, inputMethodId)) { 728 mFullscreenMode = fullScreen; 729 } 730 } 731 } 732 733 /** @hide */ registerSuggestionSpansForNotification(SuggestionSpan[] spans)734 public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) { 735 try { 736 mService.registerSuggestionSpansForNotification(spans); 737 } catch (RemoteException e) { 738 throw e.rethrowFromSystemServer(); 739 } 740 } 741 742 /** @hide */ notifySuggestionPicked(SuggestionSpan span, String originalString, int index)743 public void notifySuggestionPicked(SuggestionSpan span, String originalString, int index) { 744 try { 745 mService.notifySuggestionPicked(span, originalString, index); 746 } catch (RemoteException e) { 747 throw e.rethrowFromSystemServer(); 748 } 749 } 750 751 /** 752 * Allows you to discover whether the attached input method is running 753 * in fullscreen mode. Return true if it is fullscreen, entirely covering 754 * your UI, else returns false. 755 */ isFullscreenMode()756 public boolean isFullscreenMode() { 757 synchronized (mH) { 758 return mFullscreenMode; 759 } 760 } 761 762 /** 763 * Return true if the given view is the currently active view for the 764 * input method. 765 */ isActive(View view)766 public boolean isActive(View view) { 767 checkFocus(); 768 synchronized (mH) { 769 return (mServedView == view 770 || (mServedView != null 771 && mServedView.checkInputConnectionProxy(view))) 772 && mCurrentTextBoxAttribute != null; 773 } 774 } 775 776 /** 777 * Return true if any view is currently active in the input method. 778 */ isActive()779 public boolean isActive() { 780 checkFocus(); 781 synchronized (mH) { 782 return mServedView != null && mCurrentTextBoxAttribute != null; 783 } 784 } 785 786 /** 787 * Return true if the currently served view is accepting full text edits. 788 * If false, it has no input connection, so can only handle raw key events. 789 */ isAcceptingText()790 public boolean isAcceptingText() { 791 checkFocus(); 792 return mServedInputConnectionWrapper != null && 793 mServedInputConnectionWrapper.getInputConnection() != null; 794 } 795 796 /** 797 * Reset all of the state associated with being bound to an input method. 798 */ clearBindingLocked()799 void clearBindingLocked() { 800 if (DEBUG) Log.v(TAG, "Clearing binding!"); 801 clearConnectionLocked(); 802 setInputChannelLocked(null); 803 mBindSequence = -1; 804 mCurId = null; 805 mCurMethod = null; 806 } 807 setInputChannelLocked(InputChannel channel)808 void setInputChannelLocked(InputChannel channel) { 809 if (mCurChannel != channel) { 810 if (mCurSender != null) { 811 flushPendingEventsLocked(); 812 mCurSender.dispose(); 813 mCurSender = null; 814 } 815 if (mCurChannel != null) { 816 mCurChannel.dispose(); 817 } 818 mCurChannel = channel; 819 } 820 } 821 822 /** 823 * Reset all of the state associated with a served view being connected 824 * to an input method 825 */ clearConnectionLocked()826 void clearConnectionLocked() { 827 mCurrentTextBoxAttribute = null; 828 if (mServedInputConnectionWrapper != null) { 829 mServedInputConnectionWrapper.deactivate(); 830 mServedInputConnectionWrapper = null; 831 } 832 } 833 834 /** 835 * Disconnect any existing input connection, clearing the served view. 836 */ finishInputLocked()837 void finishInputLocked() { 838 mNextServedView = null; 839 if (mServedView != null) { 840 if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView)); 841 if (mCurrentTextBoxAttribute != null) { 842 try { 843 mService.finishInput(mClient); 844 } catch (RemoteException e) { 845 throw e.rethrowFromSystemServer(); 846 } 847 } 848 mServedView = null; 849 mCompletions = null; 850 mServedConnecting = false; 851 clearConnectionLocked(); 852 } 853 } 854 displayCompletions(View view, CompletionInfo[] completions)855 public void displayCompletions(View view, CompletionInfo[] completions) { 856 checkFocus(); 857 synchronized (mH) { 858 if (mServedView != view && (mServedView == null 859 || !mServedView.checkInputConnectionProxy(view))) { 860 return; 861 } 862 863 mCompletions = completions; 864 if (mCurMethod != null) { 865 try { 866 mCurMethod.displayCompletions(mCompletions); 867 } catch (RemoteException e) { 868 } 869 } 870 } 871 } 872 updateExtractedText(View view, int token, ExtractedText text)873 public void updateExtractedText(View view, int token, ExtractedText text) { 874 checkFocus(); 875 synchronized (mH) { 876 if (mServedView != view && (mServedView == null 877 || !mServedView.checkInputConnectionProxy(view))) { 878 return; 879 } 880 881 if (mCurMethod != null) { 882 try { 883 mCurMethod.updateExtractedText(token, text); 884 } catch (RemoteException e) { 885 } 886 } 887 } 888 } 889 890 /** 891 * Flag for {@link #showSoftInput} to indicate that this is an implicit 892 * request to show the input window, not as the result of a direct request 893 * by the user. The window may not be shown in this case. 894 */ 895 public static final int SHOW_IMPLICIT = 0x0001; 896 897 /** 898 * Flag for {@link #showSoftInput} to indicate that the user has forced 899 * the input method open (such as by long-pressing menu) so it should 900 * not be closed until they explicitly do so. 901 */ 902 public static final int SHOW_FORCED = 0x0002; 903 904 /** 905 * Synonym for {@link #showSoftInput(View, int, ResultReceiver)} without 906 * a result receiver: explicitly request that the current input method's 907 * soft input area be shown to the user, if needed. 908 * 909 * @param view The currently focused view, which would like to receive 910 * soft keyboard input. 911 * @param flags Provides additional operating flags. Currently may be 912 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 913 */ showSoftInput(View view, int flags)914 public boolean showSoftInput(View view, int flags) { 915 return showSoftInput(view, flags, null); 916 } 917 918 /** 919 * Flag for the {@link ResultReceiver} result code from 920 * {@link #showSoftInput(View, int, ResultReceiver)} and 921 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 922 * state of the soft input window was unchanged and remains shown. 923 */ 924 public static final int RESULT_UNCHANGED_SHOWN = 0; 925 926 /** 927 * Flag for the {@link ResultReceiver} result code from 928 * {@link #showSoftInput(View, int, ResultReceiver)} and 929 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 930 * state of the soft input window was unchanged and remains hidden. 931 */ 932 public static final int RESULT_UNCHANGED_HIDDEN = 1; 933 934 /** 935 * Flag for the {@link ResultReceiver} result code from 936 * {@link #showSoftInput(View, int, ResultReceiver)} and 937 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 938 * state of the soft input window changed from hidden to shown. 939 */ 940 public static final int RESULT_SHOWN = 2; 941 942 /** 943 * Flag for the {@link ResultReceiver} result code from 944 * {@link #showSoftInput(View, int, ResultReceiver)} and 945 * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)}: the 946 * state of the soft input window changed from shown to hidden. 947 */ 948 public static final int RESULT_HIDDEN = 3; 949 950 /** 951 * Explicitly request that the current input method's soft input area be 952 * shown to the user, if needed. Call this if the user interacts with 953 * your view in such a way that they have expressed they would like to 954 * start performing input into it. 955 * 956 * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to 957 * this method can be a long-lived object, because it may not be 958 * garbage-collected until all the corresponding {@link ResultReceiver} 959 * objects transferred to different processes get garbage-collected. 960 * Follow the general patterns to avoid memory leaks in Android. 961 * Consider to use {@link java.lang.ref.WeakReference} so that application 962 * logic objects such as {@link android.app.Activity} and {@link Context} 963 * can be garbage collected regardless of the lifetime of 964 * {@link ResultReceiver}. 965 * 966 * @param view The currently focused view, which would like to receive 967 * soft keyboard input. 968 * @param flags Provides additional operating flags. Currently may be 969 * 0 or have the {@link #SHOW_IMPLICIT} bit set. 970 * @param resultReceiver If non-null, this will be called by the IME when 971 * it has processed your request to tell you what it has done. The result 972 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 973 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 974 * {@link #RESULT_HIDDEN}. 975 */ showSoftInput(View view, int flags, ResultReceiver resultReceiver)976 public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { 977 checkFocus(); 978 synchronized (mH) { 979 if (mServedView != view && (mServedView == null 980 || !mServedView.checkInputConnectionProxy(view))) { 981 return false; 982 } 983 984 try { 985 return mService.showSoftInput(mClient, flags, resultReceiver); 986 } catch (RemoteException e) { 987 throw e.rethrowFromSystemServer(); 988 } 989 } 990 } 991 992 /** @hide */ showSoftInputUnchecked(int flags, ResultReceiver resultReceiver)993 public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) { 994 try { 995 mService.showSoftInput(mClient, flags, resultReceiver); 996 } catch (RemoteException e) { 997 throw e.rethrowFromSystemServer(); 998 } 999 } 1000 1001 /** 1002 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 1003 * input window should only be hidden if it was not explicitly shown 1004 * by the user. 1005 */ 1006 public static final int HIDE_IMPLICIT_ONLY = 0x0001; 1007 1008 /** 1009 * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft 1010 * input window should normally be hidden, unless it was originally 1011 * shown with {@link #SHOW_FORCED}. 1012 */ 1013 public static final int HIDE_NOT_ALWAYS = 0x0002; 1014 1015 /** 1016 * Synonym for {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} 1017 * without a result: request to hide the soft input window from the 1018 * context of the window that is currently accepting input. 1019 * 1020 * @param windowToken The token of the window that is making the request, 1021 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 1022 * @param flags Provides additional operating flags. Currently may be 1023 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 1024 */ hideSoftInputFromWindow(IBinder windowToken, int flags)1025 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) { 1026 return hideSoftInputFromWindow(windowToken, flags, null); 1027 } 1028 1029 /** 1030 * Request to hide the soft input window from the context of the window 1031 * that is currently accepting input. This should be called as a result 1032 * of the user doing some actually than fairly explicitly requests to 1033 * have the input window hidden. 1034 * 1035 * <p><strong>Caveat:</strong> {@link ResultReceiver} instance passed to 1036 * this method can be a long-lived object, because it may not be 1037 * garbage-collected until all the corresponding {@link ResultReceiver} 1038 * objects transferred to different processes get garbage-collected. 1039 * Follow the general patterns to avoid memory leaks in Android. 1040 * Consider to use {@link java.lang.ref.WeakReference} so that application 1041 * logic objects such as {@link android.app.Activity} and {@link Context} 1042 * can be garbage collected regardless of the lifetime of 1043 * {@link ResultReceiver}. 1044 * 1045 * @param windowToken The token of the window that is making the request, 1046 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 1047 * @param flags Provides additional operating flags. Currently may be 1048 * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set. 1049 * @param resultReceiver If non-null, this will be called by the IME when 1050 * it has processed your request to tell you what it has done. The result 1051 * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN}, 1052 * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or 1053 * {@link #RESULT_HIDDEN}. 1054 */ hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver)1055 public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, 1056 ResultReceiver resultReceiver) { 1057 checkFocus(); 1058 synchronized (mH) { 1059 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 1060 return false; 1061 } 1062 1063 try { 1064 return mService.hideSoftInput(mClient, flags, resultReceiver); 1065 } catch (RemoteException e) { 1066 throw e.rethrowFromSystemServer(); 1067 } 1068 } 1069 } 1070 1071 1072 /** 1073 * This method toggles the input method window display. 1074 * If the input window is already displayed, it gets hidden. 1075 * If not the input window will be displayed. 1076 * @param windowToken The token of the window that is making the request, 1077 * as returned by {@link View#getWindowToken() View.getWindowToken()}. 1078 * @param showFlags Provides additional operating flags. May be 1079 * 0 or have the {@link #SHOW_IMPLICIT}, 1080 * {@link #SHOW_FORCED} bit set. 1081 * @param hideFlags Provides additional operating flags. May be 1082 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 1083 * {@link #HIDE_NOT_ALWAYS} bit set. 1084 **/ toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags)1085 public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) { 1086 synchronized (mH) { 1087 if (mServedView == null || mServedView.getWindowToken() != windowToken) { 1088 return; 1089 } 1090 if (mCurMethod != null) { 1091 try { 1092 mCurMethod.toggleSoftInput(showFlags, hideFlags); 1093 } catch (RemoteException e) { 1094 } 1095 } 1096 } 1097 } 1098 1099 /* 1100 * This method toggles the input method window display. 1101 * If the input window is already displayed, it gets hidden. 1102 * If not the input window will be displayed. 1103 * @param showFlags Provides additional operating flags. May be 1104 * 0 or have the {@link #SHOW_IMPLICIT}, 1105 * {@link #SHOW_FORCED} bit set. 1106 * @param hideFlags Provides additional operating flags. May be 1107 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 1108 * {@link #HIDE_NOT_ALWAYS} bit set. 1109 * @hide 1110 */ toggleSoftInput(int showFlags, int hideFlags)1111 public void toggleSoftInput(int showFlags, int hideFlags) { 1112 if (mCurMethod != null) { 1113 try { 1114 mCurMethod.toggleSoftInput(showFlags, hideFlags); 1115 } catch (RemoteException e) { 1116 } 1117 } 1118 } 1119 1120 /** 1121 * If the input method is currently connected to the given view, 1122 * restart it with its new contents. You should call this when the text 1123 * within your view changes outside of the normal input method or key 1124 * input flow, such as when an application calls TextView.setText(). 1125 * 1126 * @param view The view whose text has changed. 1127 */ restartInput(View view)1128 public void restartInput(View view) { 1129 checkFocus(); 1130 synchronized (mH) { 1131 if (mServedView != view && (mServedView == null 1132 || !mServedView.checkInputConnectionProxy(view))) { 1133 return; 1134 } 1135 1136 mServedConnecting = true; 1137 } 1138 1139 startInputInner(InputMethodClient.START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, null, 0, 1140 0, 0); 1141 } 1142 startInputInner(@nputMethodClient.StartInputReason final int startInputReason, IBinder windowGainingFocus, int controlFlags, int softInputMode, int windowFlags)1143 boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason, 1144 IBinder windowGainingFocus, int controlFlags, int softInputMode, 1145 int windowFlags) { 1146 final View view; 1147 synchronized (mH) { 1148 view = mServedView; 1149 1150 // Make sure we have a window token for the served view. 1151 if (DEBUG) { 1152 Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) + 1153 " reason=" + InputMethodClient.getStartInputReason(startInputReason)); 1154 } 1155 if (view == null) { 1156 if (DEBUG) Log.v(TAG, "ABORT input: no served view!"); 1157 return false; 1158 } 1159 } 1160 1161 // Now we need to get an input connection from the served view. 1162 // This is complicated in a couple ways: we can't be holding our lock 1163 // when calling out to the view, and we need to make sure we call into 1164 // the view on the same thread that is driving its view hierarchy. 1165 Handler vh = view.getHandler(); 1166 if (vh == null) { 1167 // If the view doesn't have a handler, something has changed out 1168 // from under us, so just close the current input. 1169 // If we don't close the current input, the current input method can remain on the 1170 // screen without a connection. 1171 if (DEBUG) Log.v(TAG, "ABORT input: no handler for view! Close current input."); 1172 closeCurrentInput(); 1173 return false; 1174 } 1175 if (vh.getLooper() != Looper.myLooper()) { 1176 // The view is running on a different thread than our own, so 1177 // we need to reschedule our work for over there. 1178 if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); 1179 vh.post(new Runnable() { 1180 @Override 1181 public void run() { 1182 startInputInner(startInputReason, null, 0, 0, 0); 1183 } 1184 }); 1185 return false; 1186 } 1187 1188 // Okay we are now ready to call into the served view and have it 1189 // do its stuff. 1190 // Life is good: let's hook everything up! 1191 EditorInfo tba = new EditorInfo(); 1192 // Note: Use Context#getOpPackageName() rather than Context#getPackageName() so that the 1193 // system can verify the consistency between the uid of this process and package name passed 1194 // from here. See comment of Context#getOpPackageName() for details. 1195 tba.packageName = view.getContext().getOpPackageName(); 1196 tba.fieldId = view.getId(); 1197 InputConnection ic = view.onCreateInputConnection(tba); 1198 if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic); 1199 1200 synchronized (mH) { 1201 // Now that we are locked again, validate that our state hasn't 1202 // changed. 1203 if (mServedView != view || !mServedConnecting) { 1204 // Something else happened, so abort. 1205 if (DEBUG) Log.v(TAG, 1206 "Starting input: finished by someone else. view=" + dumpViewInfo(view) 1207 + " mServedView=" + dumpViewInfo(mServedView) 1208 + " mServedConnecting=" + mServedConnecting); 1209 return false; 1210 } 1211 1212 // If we already have a text box, then this view is already 1213 // connected so we want to restart it. 1214 if (mCurrentTextBoxAttribute == null) { 1215 controlFlags |= CONTROL_START_INITIAL; 1216 } 1217 1218 // Hook 'em up and let 'er rip. 1219 mCurrentTextBoxAttribute = tba; 1220 mServedConnecting = false; 1221 if (mServedInputConnectionWrapper != null) { 1222 mServedInputConnectionWrapper.deactivate(); 1223 mServedInputConnectionWrapper = null; 1224 } 1225 ControlledInputConnectionWrapper servedContext; 1226 final int missingMethodFlags; 1227 if (ic != null) { 1228 mCursorSelStart = tba.initialSelStart; 1229 mCursorSelEnd = tba.initialSelEnd; 1230 mCursorCandStart = -1; 1231 mCursorCandEnd = -1; 1232 mCursorRect.setEmpty(); 1233 mCursorAnchorInfo = null; 1234 final Handler icHandler; 1235 missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic); 1236 if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER) 1237 != 0) { 1238 // InputConnection#getHandler() is not implemented. 1239 icHandler = null; 1240 } else { 1241 icHandler = ic.getHandler(); 1242 } 1243 servedContext = new ControlledInputConnectionWrapper( 1244 icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this); 1245 } else { 1246 servedContext = null; 1247 missingMethodFlags = 0; 1248 } 1249 mServedInputConnectionWrapper = servedContext; 1250 1251 try { 1252 if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic=" 1253 + ic + " tba=" + tba + " controlFlags=#" 1254 + Integer.toHexString(controlFlags)); 1255 final InputBindResult res = mService.startInputOrWindowGainedFocus( 1256 startInputReason, mClient, windowGainingFocus, controlFlags, softInputMode, 1257 windowFlags, tba, servedContext, missingMethodFlags); 1258 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); 1259 if (res != null) { 1260 if (res.id != null) { 1261 setInputChannelLocked(res.channel); 1262 mBindSequence = res.sequence; 1263 mCurMethod = res.method; 1264 mCurId = res.id; 1265 mNextUserActionNotificationSequenceNumber = 1266 res.userActionNotificationSequenceNumber; 1267 if (mServedInputConnectionWrapper != null) { 1268 mServedInputConnectionWrapper.setInputMethodId(mCurId); 1269 } 1270 } else { 1271 if (res.channel != null && res.channel != mCurChannel) { 1272 res.channel.dispose(); 1273 } 1274 if (mCurMethod == null) { 1275 // This means there is no input method available. 1276 if (DEBUG) Log.v(TAG, "ABORT input: no input method!"); 1277 return true; 1278 } 1279 } 1280 } else { 1281 if (startInputReason 1282 == InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN) { 1283 // We are here probably because of an obsolete window-focus-in message sent 1284 // to windowGainingFocus. Since IMMS determines whether a Window can have 1285 // IME focus or not by using the latest window focus state maintained in the 1286 // WMS, this kind of race condition cannot be avoided. One obvious example 1287 // would be that we have already received a window-focus-out message but the 1288 // UI thread is still handling previous window-focus-in message here. 1289 // TODO: InputBindResult should have the error code. 1290 if (DEBUG) Log.w(TAG, "startInputOrWindowGainedFocus failed. " 1291 + "Window focus may have already been lost. " 1292 + "win=" + windowGainingFocus + " view=" + dumpViewInfo(view)); 1293 if (!mActive) { 1294 // mHasBeenInactive is a latch switch to forcefully refresh IME focus 1295 // state when an inactive (mActive == false) client is gaining window 1296 // focus. In case we have unnecessary disable the latch due to this 1297 // spurious wakeup, we re-enable the latch here. 1298 // TODO: Come up with more robust solution. 1299 mHasBeenInactive = true; 1300 } 1301 } 1302 } 1303 if (mCurMethod != null && mCompletions != null) { 1304 try { 1305 mCurMethod.displayCompletions(mCompletions); 1306 } catch (RemoteException e) { 1307 } 1308 } 1309 } catch (RemoteException e) { 1310 Log.w(TAG, "IME died: " + mCurId, e); 1311 } 1312 } 1313 1314 return true; 1315 } 1316 1317 /** 1318 * When the focused window is dismissed, this method is called to finish the 1319 * input method started before. 1320 * @hide 1321 */ windowDismissed(IBinder appWindowToken)1322 public void windowDismissed(IBinder appWindowToken) { 1323 checkFocus(); 1324 synchronized (mH) { 1325 if (mServedView != null && 1326 mServedView.getWindowToken() == appWindowToken) { 1327 finishInputLocked(); 1328 } 1329 } 1330 } 1331 1332 /** 1333 * Call this when a view receives focus. 1334 * @hide 1335 */ focusIn(View view)1336 public void focusIn(View view) { 1337 synchronized (mH) { 1338 focusInLocked(view); 1339 } 1340 } 1341 focusInLocked(View view)1342 void focusInLocked(View view) { 1343 if (DEBUG) Log.v(TAG, "focusIn: " + dumpViewInfo(view)); 1344 1345 if (view != null && view.isTemporarilyDetached()) { 1346 // This is a request from a view that is temporarily detached from a window. 1347 if (DEBUG) Log.v(TAG, "Temporarily detached view, ignoring"); 1348 return; 1349 } 1350 1351 if (mCurRootView != view.getRootView()) { 1352 // This is a request from a window that isn't in the window with 1353 // IME focus, so ignore it. 1354 if (DEBUG) Log.v(TAG, "Not IME target window, ignoring"); 1355 return; 1356 } 1357 1358 mNextServedView = view; 1359 scheduleCheckFocusLocked(view); 1360 } 1361 1362 /** 1363 * Call this when a view loses focus. 1364 * @hide 1365 */ focusOut(View view)1366 public void focusOut(View view) { 1367 synchronized (mH) { 1368 if (DEBUG) Log.v(TAG, "focusOut: view=" + dumpViewInfo(view) 1369 + " mServedView=" + dumpViewInfo(mServedView)); 1370 if (mServedView != view) { 1371 // The following code would auto-hide the IME if we end up 1372 // with no more views with focus. This can happen, however, 1373 // whenever we go into touch mode, so it ends up hiding 1374 // at times when we don't really want it to. For now it 1375 // seems better to just turn it all off. 1376 // TODO: Check view.isTemporarilyDetached() when re-enable the following code. 1377 if (false && view.hasWindowFocus()) { 1378 mNextServedView = null; 1379 scheduleCheckFocusLocked(view); 1380 } 1381 } 1382 } 1383 } 1384 1385 /** 1386 * Call this when a view is being detached from a {@link android.view.Window}. 1387 * @hide 1388 */ onViewDetachedFromWindow(View view)1389 public void onViewDetachedFromWindow(View view) { 1390 synchronized (mH) { 1391 if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: view=" + dumpViewInfo(view) 1392 + " mServedView=" + dumpViewInfo(mServedView)); 1393 if (mServedView == view) { 1394 mNextServedView = null; 1395 scheduleCheckFocusLocked(view); 1396 } 1397 } 1398 } 1399 scheduleCheckFocusLocked(View view)1400 static void scheduleCheckFocusLocked(View view) { 1401 ViewRootImpl viewRootImpl = view.getViewRootImpl(); 1402 if (viewRootImpl != null) { 1403 viewRootImpl.dispatchCheckFocus(); 1404 } 1405 } 1406 1407 /** 1408 * @hide 1409 */ checkFocus()1410 public void checkFocus() { 1411 if (checkFocusNoStartInput(false)) { 1412 startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0); 1413 } 1414 } 1415 checkFocusNoStartInput(boolean forceNewFocus)1416 private boolean checkFocusNoStartInput(boolean forceNewFocus) { 1417 // This is called a lot, so short-circuit before locking. 1418 if (mServedView == mNextServedView && !forceNewFocus) { 1419 return false; 1420 } 1421 1422 final ControlledInputConnectionWrapper ic; 1423 synchronized (mH) { 1424 if (mServedView == mNextServedView && !forceNewFocus) { 1425 return false; 1426 } 1427 if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView 1428 + " next=" + mNextServedView 1429 + " forceNewFocus=" + forceNewFocus 1430 + " package=" 1431 + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>")); 1432 1433 if (mNextServedView == null) { 1434 finishInputLocked(); 1435 // In this case, we used to have a focused view on the window, 1436 // but no longer do. We should make sure the input method is 1437 // no longer shown, since it serves no purpose. 1438 closeCurrentInput(); 1439 return false; 1440 } 1441 1442 ic = mServedInputConnectionWrapper; 1443 1444 mServedView = mNextServedView; 1445 mCurrentTextBoxAttribute = null; 1446 mCompletions = null; 1447 mServedConnecting = true; 1448 } 1449 1450 if (ic != null) { 1451 ic.finishComposingText(); 1452 } 1453 1454 return true; 1455 } 1456 closeCurrentInput()1457 void closeCurrentInput() { 1458 try { 1459 mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null); 1460 } catch (RemoteException e) { 1461 throw e.rethrowFromSystemServer(); 1462 } 1463 } 1464 1465 /** 1466 * Called by ViewAncestor when its window gets input focus. 1467 * @hide 1468 */ onPostWindowFocus(View rootView, View focusedView, int softInputMode, boolean first, int windowFlags)1469 public void onPostWindowFocus(View rootView, View focusedView, int softInputMode, 1470 boolean first, int windowFlags) { 1471 boolean forceNewFocus = false; 1472 synchronized (mH) { 1473 if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView 1474 + " softInputMode=" + softInputMode 1475 + " first=" + first + " flags=#" 1476 + Integer.toHexString(windowFlags)); 1477 if (mHasBeenInactive) { 1478 if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh"); 1479 mHasBeenInactive = false; 1480 forceNewFocus = true; 1481 } 1482 focusInLocked(focusedView != null ? focusedView : rootView); 1483 } 1484 1485 int controlFlags = 0; 1486 if (focusedView != null) { 1487 controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS; 1488 if (focusedView.onCheckIsTextEditor()) { 1489 controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR; 1490 } 1491 } 1492 if (first) { 1493 controlFlags |= CONTROL_WINDOW_FIRST; 1494 } 1495 1496 if (checkFocusNoStartInput(forceNewFocus)) { 1497 // We need to restart input on the current focus view. This 1498 // should be done in conjunction with telling the system service 1499 // about the window gaining focus, to help make the transition 1500 // smooth. 1501 if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN, 1502 rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) { 1503 return; 1504 } 1505 } 1506 1507 // For some reason we didn't do a startInput + windowFocusGain, so 1508 // we'll just do a window focus gain and call it a day. 1509 synchronized (mH) { 1510 try { 1511 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput"); 1512 mService.startInputOrWindowGainedFocus( 1513 InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, 1514 rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null, 1515 null, 0 /* missingMethodFlags */); 1516 } catch (RemoteException e) { 1517 throw e.rethrowFromSystemServer(); 1518 } 1519 } 1520 } 1521 1522 /** @hide */ onPreWindowFocus(View rootView, boolean hasWindowFocus)1523 public void onPreWindowFocus(View rootView, boolean hasWindowFocus) { 1524 synchronized (mH) { 1525 if (rootView == null) { 1526 mCurRootView = null; 1527 } if (hasWindowFocus) { 1528 mCurRootView = rootView; 1529 } else if (rootView == mCurRootView) { 1530 // If the mCurRootView is losing window focus, release the strong reference to it 1531 // so as not to prevent it from being garbage-collected. 1532 mCurRootView = null; 1533 } else { 1534 if (DEBUG) { 1535 Log.v(TAG, "Ignoring onPreWindowFocus()." 1536 + " mCurRootView=" + mCurRootView + " rootView=" + rootView); 1537 } 1538 } 1539 } 1540 } 1541 1542 /** 1543 * Report the current selection range. 1544 * 1545 * <p><strong>Editor authors</strong>, you need to call this method whenever 1546 * the cursor moves in your editor. Remember that in addition to doing this, your 1547 * editor needs to always supply current cursor values in 1548 * {@link EditorInfo#initialSelStart} and {@link EditorInfo#initialSelEnd} every 1549 * time {@link android.view.View#onCreateInputConnection(EditorInfo)} is 1550 * called, which happens whenever the keyboard shows up or the focus changes 1551 * to a text field, among other cases.</p> 1552 */ updateSelection(View view, int selStart, int selEnd, int candidatesStart, int candidatesEnd)1553 public void updateSelection(View view, int selStart, int selEnd, 1554 int candidatesStart, int candidatesEnd) { 1555 checkFocus(); 1556 synchronized (mH) { 1557 if ((mServedView != view && (mServedView == null 1558 || !mServedView.checkInputConnectionProxy(view))) 1559 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1560 return; 1561 } 1562 1563 if (mCursorSelStart != selStart || mCursorSelEnd != selEnd 1564 || mCursorCandStart != candidatesStart 1565 || mCursorCandEnd != candidatesEnd) { 1566 if (DEBUG) Log.d(TAG, "updateSelection"); 1567 1568 try { 1569 if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod); 1570 final int oldSelStart = mCursorSelStart; 1571 final int oldSelEnd = mCursorSelEnd; 1572 // Update internal values before sending updateSelection to the IME, because 1573 // if it changes the text within its onUpdateSelection handler in a way that 1574 // does not move the cursor we don't want to call it again with the same values. 1575 mCursorSelStart = selStart; 1576 mCursorSelEnd = selEnd; 1577 mCursorCandStart = candidatesStart; 1578 mCursorCandEnd = candidatesEnd; 1579 mCurMethod.updateSelection(oldSelStart, oldSelEnd, 1580 selStart, selEnd, candidatesStart, candidatesEnd); 1581 } catch (RemoteException e) { 1582 Log.w(TAG, "IME died: " + mCurId, e); 1583 } 1584 } 1585 } 1586 } 1587 1588 /** 1589 * Notify the event when the user tapped or clicked the text view. 1590 */ viewClicked(View view)1591 public void viewClicked(View view) { 1592 final boolean focusChanged = mServedView != mNextServedView; 1593 checkFocus(); 1594 synchronized (mH) { 1595 if ((mServedView != view && (mServedView == null 1596 || !mServedView.checkInputConnectionProxy(view))) 1597 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1598 return; 1599 } 1600 try { 1601 if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged); 1602 mCurMethod.viewClicked(focusChanged); 1603 } catch (RemoteException e) { 1604 Log.w(TAG, "IME died: " + mCurId, e); 1605 } 1606 } 1607 } 1608 1609 /** 1610 * Return true if the current input method wants to watch the location 1611 * of the input editor's cursor in its window. 1612 * 1613 * @deprecated Use {@link InputConnection#requestCursorUpdates(int)} instead. 1614 */ 1615 @Deprecated isWatchingCursor(View view)1616 public boolean isWatchingCursor(View view) { 1617 return false; 1618 } 1619 1620 /** 1621 * Return true if the current input method wants to be notified when cursor/anchor location 1622 * is changed. 1623 * 1624 * @hide 1625 */ isCursorAnchorInfoEnabled()1626 public boolean isCursorAnchorInfoEnabled() { 1627 synchronized (mH) { 1628 final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode & 1629 InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0; 1630 final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode & 1631 InputConnection.CURSOR_UPDATE_MONITOR) != 0; 1632 return isImmediate || isMonitoring; 1633 } 1634 } 1635 1636 /** 1637 * Set the requested mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}. 1638 * 1639 * @hide 1640 */ setUpdateCursorAnchorInfoMode(int flags)1641 public void setUpdateCursorAnchorInfoMode(int flags) { 1642 synchronized (mH) { 1643 mRequestUpdateCursorAnchorInfoMonitorMode = flags; 1644 } 1645 } 1646 1647 /** 1648 * Report the current cursor location in its window. 1649 * 1650 * @deprecated Use {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} instead. 1651 */ 1652 @Deprecated updateCursor(View view, int left, int top, int right, int bottom)1653 public void updateCursor(View view, int left, int top, int right, int bottom) { 1654 checkFocus(); 1655 synchronized (mH) { 1656 if ((mServedView != view && (mServedView == null 1657 || !mServedView.checkInputConnectionProxy(view))) 1658 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1659 return; 1660 } 1661 1662 mTmpCursorRect.set(left, top, right, bottom); 1663 if (!mCursorRect.equals(mTmpCursorRect)) { 1664 if (DEBUG) Log.d(TAG, "updateCursor"); 1665 1666 try { 1667 if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); 1668 mCurMethod.updateCursor(mTmpCursorRect); 1669 mCursorRect.set(mTmpCursorRect); 1670 } catch (RemoteException e) { 1671 Log.w(TAG, "IME died: " + mCurId, e); 1672 } 1673 } 1674 } 1675 } 1676 1677 /** 1678 * Report positional change of the text insertion point and/or characters in the composition 1679 * string. 1680 */ updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo)1681 public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) { 1682 if (view == null || cursorAnchorInfo == null) { 1683 return; 1684 } 1685 checkFocus(); 1686 synchronized (mH) { 1687 if ((mServedView != view && 1688 (mServedView == null || !mServedView.checkInputConnectionProxy(view))) 1689 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1690 return; 1691 } 1692 // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has 1693 // not been changed from the previous call. 1694 final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode & 1695 InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0; 1696 if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) { 1697 // TODO: Consider always emitting this message once we have addressed redundant 1698 // calls of this method from android.widget.Editor. 1699 if (DEBUG) { 1700 Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" 1701 + cursorAnchorInfo); 1702 } 1703 return; 1704 } 1705 if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo); 1706 try { 1707 mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo); 1708 mCursorAnchorInfo = cursorAnchorInfo; 1709 // Clear immediate bit (if any). 1710 mRequestUpdateCursorAnchorInfoMonitorMode &= 1711 ~InputConnection.CURSOR_UPDATE_IMMEDIATE; 1712 } catch (RemoteException e) { 1713 Log.w(TAG, "IME died: " + mCurId, e); 1714 } 1715 } 1716 } 1717 1718 /** 1719 * Call {@link InputMethodSession#appPrivateCommand(String, Bundle) 1720 * InputMethodSession.appPrivateCommand()} on the current Input Method. 1721 * @param view Optional View that is sending the command, or null if 1722 * you want to send the command regardless of the view that is attached 1723 * to the input method. 1724 * @param action Name of the command to be performed. This <em>must</em> 1725 * be a scoped name, i.e. prefixed with a package name you own, so that 1726 * different developers will not create conflicting commands. 1727 * @param data Any data to include with the command. 1728 */ sendAppPrivateCommand(View view, String action, Bundle data)1729 public void sendAppPrivateCommand(View view, String action, Bundle data) { 1730 checkFocus(); 1731 synchronized (mH) { 1732 if ((mServedView != view && (mServedView == null 1733 || !mServedView.checkInputConnectionProxy(view))) 1734 || mCurrentTextBoxAttribute == null || mCurMethod == null) { 1735 return; 1736 } 1737 try { 1738 if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data); 1739 mCurMethod.appPrivateCommand(action, data); 1740 } catch (RemoteException e) { 1741 Log.w(TAG, "IME died: " + mCurId, e); 1742 } 1743 } 1744 } 1745 1746 /** 1747 * Force switch to a new input method component. This can only be called 1748 * from an application or a service which has a token of the currently active input method. 1749 * @param token Supplies the identifying token given to an input method 1750 * when it was started, which allows it to perform this operation on 1751 * itself. 1752 * @param id The unique identifier for the new input method to be switched to. 1753 */ setInputMethod(IBinder token, String id)1754 public void setInputMethod(IBinder token, String id) { 1755 try { 1756 mService.setInputMethod(token, id); 1757 } catch (RemoteException e) { 1758 throw e.rethrowFromSystemServer(); 1759 } 1760 } 1761 1762 /** 1763 * Force switch to a new input method and subtype. This can only be called 1764 * from an application or a service which has a token of the currently active input method. 1765 * @param token Supplies the identifying token given to an input method 1766 * when it was started, which allows it to perform this operation on 1767 * itself. 1768 * @param id The unique identifier for the new input method to be switched to. 1769 * @param subtype The new subtype of the new input method to be switched to. 1770 */ setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype)1771 public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) { 1772 try { 1773 mService.setInputMethodAndSubtype(token, id, subtype); 1774 } catch (RemoteException e) { 1775 throw e.rethrowFromSystemServer(); 1776 } 1777 } 1778 1779 /** 1780 * Close/hide the input method's soft input area, so the user no longer 1781 * sees it or can interact with it. This can only be called 1782 * from the currently active input method, as validated by the given token. 1783 * 1784 * @param token Supplies the identifying token given to an input method 1785 * when it was started, which allows it to perform this operation on 1786 * itself. 1787 * @param flags Provides additional operating flags. Currently may be 1788 * 0 or have the {@link #HIDE_IMPLICIT_ONLY}, 1789 * {@link #HIDE_NOT_ALWAYS} bit set. 1790 */ hideSoftInputFromInputMethod(IBinder token, int flags)1791 public void hideSoftInputFromInputMethod(IBinder token, int flags) { 1792 try { 1793 mService.hideMySoftInput(token, flags); 1794 } catch (RemoteException e) { 1795 throw e.rethrowFromSystemServer(); 1796 } 1797 } 1798 1799 /** 1800 * Show the input method's soft input area, so the user 1801 * sees the input method window and can interact with it. 1802 * This can only be called from the currently active input method, 1803 * as validated by the given token. 1804 * 1805 * @param token Supplies the identifying token given to an input method 1806 * when it was started, which allows it to perform this operation on 1807 * itself. 1808 * @param flags Provides additional operating flags. Currently may be 1809 * 0 or have the {@link #SHOW_IMPLICIT} or 1810 * {@link #SHOW_FORCED} bit set. 1811 */ showSoftInputFromInputMethod(IBinder token, int flags)1812 public void showSoftInputFromInputMethod(IBinder token, int flags) { 1813 try { 1814 mService.showMySoftInput(token, flags); 1815 } catch (RemoteException e) { 1816 throw e.rethrowFromSystemServer(); 1817 } 1818 } 1819 1820 /** 1821 * Dispatches an input event to the IME. 1822 * 1823 * Returns {@link #DISPATCH_HANDLED} if the event was handled. 1824 * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled. 1825 * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the 1826 * callback will be invoked later. 1827 * 1828 * @hide 1829 */ dispatchInputEvent(InputEvent event, Object token, FinishedInputEventCallback callback, Handler handler)1830 public int dispatchInputEvent(InputEvent event, Object token, 1831 FinishedInputEventCallback callback, Handler handler) { 1832 synchronized (mH) { 1833 if (mCurMethod != null) { 1834 if (event instanceof KeyEvent) { 1835 KeyEvent keyEvent = (KeyEvent)event; 1836 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN 1837 && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM 1838 && keyEvent.getRepeatCount() == 0) { 1839 showInputMethodPickerLocked(); 1840 return DISPATCH_HANDLED; 1841 } 1842 } 1843 1844 if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod); 1845 1846 PendingEvent p = obtainPendingEventLocked( 1847 event, token, mCurId, callback, handler); 1848 if (mMainLooper.isCurrentThread()) { 1849 // Already running on the IMM thread so we can send the event immediately. 1850 return sendInputEventOnMainLooperLocked(p); 1851 } 1852 1853 // Post the event to the IMM thread. 1854 Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p); 1855 msg.setAsynchronous(true); 1856 mH.sendMessage(msg); 1857 return DISPATCH_IN_PROGRESS; 1858 } 1859 } 1860 return DISPATCH_NOT_HANDLED; 1861 } 1862 1863 /** 1864 * Provides the default implementation of {@link InputConnection#sendKeyEvent(KeyEvent)}, which 1865 * is expected to dispatch an keyboard event sent from the IME to an appropriate event target 1866 * depending on the given {@link View} and the current focus state. 1867 * 1868 * <p>CAUTION: This method is provided only for the situation where 1869 * {@link InputConnection#sendKeyEvent(KeyEvent)} needs to be implemented without relying on 1870 * {@link BaseInputConnection}. Do not use this API for anything else.</p> 1871 * 1872 * @param targetView the default target view. If {@code null} is specified, then this method 1873 * tries to find a good event target based on the current focus state. 1874 * @param event the key event to be dispatched. 1875 */ dispatchKeyEventFromInputMethod(@ullable View targetView, @NonNull KeyEvent event)1876 public void dispatchKeyEventFromInputMethod(@Nullable View targetView, 1877 @NonNull KeyEvent event) { 1878 synchronized (mH) { 1879 ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; 1880 if (viewRootImpl == null) { 1881 if (mServedView != null) { 1882 viewRootImpl = mServedView.getViewRootImpl(); 1883 } 1884 } 1885 if (viewRootImpl != null) { 1886 viewRootImpl.dispatchKeyFromIme(event); 1887 } 1888 } 1889 } 1890 1891 // Must be called on the main looper sendInputEventAndReportResultOnMainLooper(PendingEvent p)1892 void sendInputEventAndReportResultOnMainLooper(PendingEvent p) { 1893 final boolean handled; 1894 synchronized (mH) { 1895 int result = sendInputEventOnMainLooperLocked(p); 1896 if (result == DISPATCH_IN_PROGRESS) { 1897 return; 1898 } 1899 1900 handled = (result == DISPATCH_HANDLED); 1901 } 1902 1903 invokeFinishedInputEventCallback(p, handled); 1904 } 1905 1906 // Must be called on the main looper sendInputEventOnMainLooperLocked(PendingEvent p)1907 int sendInputEventOnMainLooperLocked(PendingEvent p) { 1908 if (mCurChannel != null) { 1909 if (mCurSender == null) { 1910 mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper()); 1911 } 1912 1913 final InputEvent event = p.mEvent; 1914 final int seq = event.getSequenceNumber(); 1915 if (mCurSender.sendInputEvent(seq, event)) { 1916 mPendingEvents.put(seq, p); 1917 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, 1918 mPendingEvents.size()); 1919 1920 Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p); 1921 msg.setAsynchronous(true); 1922 mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT); 1923 return DISPATCH_IN_PROGRESS; 1924 } 1925 1926 Log.w(TAG, "Unable to send input event to IME: " 1927 + mCurId + " dropping: " + event); 1928 } 1929 return DISPATCH_NOT_HANDLED; 1930 } 1931 finishedInputEvent(int seq, boolean handled, boolean timeout)1932 void finishedInputEvent(int seq, boolean handled, boolean timeout) { 1933 final PendingEvent p; 1934 synchronized (mH) { 1935 int index = mPendingEvents.indexOfKey(seq); 1936 if (index < 0) { 1937 return; // spurious, event already finished or timed out 1938 } 1939 1940 p = mPendingEvents.valueAt(index); 1941 mPendingEvents.removeAt(index); 1942 Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size()); 1943 1944 if (timeout) { 1945 Log.w(TAG, "Timeout waiting for IME to handle input event after " 1946 + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId); 1947 } else { 1948 mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p); 1949 } 1950 } 1951 1952 invokeFinishedInputEventCallback(p, handled); 1953 } 1954 1955 // Assumes the event has already been removed from the queue. invokeFinishedInputEventCallback(PendingEvent p, boolean handled)1956 void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) { 1957 p.mHandled = handled; 1958 if (p.mHandler.getLooper().isCurrentThread()) { 1959 // Already running on the callback handler thread so we can send the 1960 // callback immediately. 1961 p.run(); 1962 } else { 1963 // Post the event to the callback handler thread. 1964 // In this case, the callback will be responsible for recycling the event. 1965 Message msg = Message.obtain(p.mHandler, p); 1966 msg.setAsynchronous(true); 1967 msg.sendToTarget(); 1968 } 1969 } 1970 flushPendingEventsLocked()1971 private void flushPendingEventsLocked() { 1972 mH.removeMessages(MSG_FLUSH_INPUT_EVENT); 1973 1974 final int count = mPendingEvents.size(); 1975 for (int i = 0; i < count; i++) { 1976 int seq = mPendingEvents.keyAt(i); 1977 Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0); 1978 msg.setAsynchronous(true); 1979 msg.sendToTarget(); 1980 } 1981 } 1982 obtainPendingEventLocked(InputEvent event, Object token, String inputMethodId, FinishedInputEventCallback callback, Handler handler)1983 private PendingEvent obtainPendingEventLocked(InputEvent event, Object token, 1984 String inputMethodId, FinishedInputEventCallback callback, Handler handler) { 1985 PendingEvent p = mPendingEventPool.acquire(); 1986 if (p == null) { 1987 p = new PendingEvent(); 1988 } 1989 p.mEvent = event; 1990 p.mToken = token; 1991 p.mInputMethodId = inputMethodId; 1992 p.mCallback = callback; 1993 p.mHandler = handler; 1994 return p; 1995 } 1996 recyclePendingEventLocked(PendingEvent p)1997 private void recyclePendingEventLocked(PendingEvent p) { 1998 p.recycle(); 1999 mPendingEventPool.release(p); 2000 } 2001 showInputMethodPicker()2002 public void showInputMethodPicker() { 2003 synchronized (mH) { 2004 showInputMethodPickerLocked(); 2005 } 2006 } 2007 2008 /** 2009 * Shows the input method chooser dialog. 2010 * 2011 * @param showAuxiliarySubtypes Set true to show auxiliary input methods. 2012 * @hide 2013 */ showInputMethodPicker(boolean showAuxiliarySubtypes)2014 public void showInputMethodPicker(boolean showAuxiliarySubtypes) { 2015 synchronized (mH) { 2016 try { 2017 final int mode = showAuxiliarySubtypes ? 2018 SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES: 2019 SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES; 2020 mService.showInputMethodPickerFromClient(mClient, mode); 2021 } catch (RemoteException e) { 2022 throw e.rethrowFromSystemServer(); 2023 } 2024 } 2025 } 2026 showInputMethodPickerLocked()2027 private void showInputMethodPickerLocked() { 2028 try { 2029 mService.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO); 2030 } catch (RemoteException e) { 2031 throw e.rethrowFromSystemServer(); 2032 } 2033 } 2034 2035 /** 2036 * Show the settings for enabling subtypes of the specified input method. 2037 * @param imiId An input method, whose subtypes settings will be shown. If imiId is null, 2038 * subtypes of all input methods will be shown. 2039 */ showInputMethodAndSubtypeEnabler(String imiId)2040 public void showInputMethodAndSubtypeEnabler(String imiId) { 2041 synchronized (mH) { 2042 try { 2043 mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId); 2044 } catch (RemoteException e) { 2045 throw e.rethrowFromSystemServer(); 2046 } 2047 } 2048 } 2049 2050 /** 2051 * Returns the current input method subtype. This subtype is one of the subtypes in 2052 * the current input method. This method returns null when the current input method doesn't 2053 * have any input method subtype. 2054 */ getCurrentInputMethodSubtype()2055 public InputMethodSubtype getCurrentInputMethodSubtype() { 2056 synchronized (mH) { 2057 try { 2058 return mService.getCurrentInputMethodSubtype(); 2059 } catch (RemoteException e) { 2060 throw e.rethrowFromSystemServer(); 2061 } 2062 } 2063 } 2064 2065 /** 2066 * Switch to a new input method subtype of the current input method. 2067 * @param subtype A new input method subtype to switch. 2068 * @return true if the current subtype was successfully switched. When the specified subtype is 2069 * null, this method returns false. 2070 */ 2071 @RequiresPermission(WRITE_SECURE_SETTINGS) setCurrentInputMethodSubtype(InputMethodSubtype subtype)2072 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { 2073 synchronized (mH) { 2074 try { 2075 return mService.setCurrentInputMethodSubtype(subtype); 2076 } catch (RemoteException e) { 2077 throw e.rethrowFromSystemServer(); 2078 } 2079 } 2080 } 2081 2082 /** 2083 * Notify that a user took some action with this input method. 2084 * @hide 2085 */ notifyUserAction()2086 public void notifyUserAction() { 2087 synchronized (mH) { 2088 if (mLastSentUserActionNotificationSequenceNumber == 2089 mNextUserActionNotificationSequenceNumber) { 2090 if (DEBUG) { 2091 Log.w(TAG, "Ignoring notifyUserAction as it has already been sent." 2092 + " mLastSentUserActionNotificationSequenceNumber: " 2093 + mLastSentUserActionNotificationSequenceNumber 2094 + " mNextUserActionNotificationSequenceNumber: " 2095 + mNextUserActionNotificationSequenceNumber); 2096 } 2097 return; 2098 } 2099 try { 2100 if (DEBUG) { 2101 Log.w(TAG, "notifyUserAction: " 2102 + " mLastSentUserActionNotificationSequenceNumber: " 2103 + mLastSentUserActionNotificationSequenceNumber 2104 + " mNextUserActionNotificationSequenceNumber: " 2105 + mNextUserActionNotificationSequenceNumber); 2106 } 2107 mService.notifyUserAction(mNextUserActionNotificationSequenceNumber); 2108 mLastSentUserActionNotificationSequenceNumber = 2109 mNextUserActionNotificationSequenceNumber; 2110 } catch (RemoteException e) { 2111 throw e.rethrowFromSystemServer(); 2112 } 2113 } 2114 } 2115 2116 /** 2117 * Returns a map of all shortcut input method info and their subtypes. 2118 */ getShortcutInputMethodsAndSubtypes()2119 public Map<InputMethodInfo, List<InputMethodSubtype>> getShortcutInputMethodsAndSubtypes() { 2120 synchronized (mH) { 2121 HashMap<InputMethodInfo, List<InputMethodSubtype>> ret = new HashMap<>(); 2122 try { 2123 // TODO: We should change the return type from List<Object> to List<Parcelable> 2124 List<Object> info = mService.getShortcutInputMethodsAndSubtypes(); 2125 // "info" has imi1, subtype1, subtype2, imi2, subtype2, imi3, subtype3..in the list 2126 ArrayList<InputMethodSubtype> subtypes = null; 2127 if (info != null && !info.isEmpty()) { 2128 final int N = info.size(); 2129 for (int i = 0; i < N; ++i) { 2130 Object o = info.get(i); 2131 if (o instanceof InputMethodInfo) { 2132 if (ret.containsKey(o)) { 2133 Log.e(TAG, "IMI list already contains the same InputMethod."); 2134 break; 2135 } 2136 subtypes = new ArrayList<>(); 2137 ret.put((InputMethodInfo)o, subtypes); 2138 } else if (subtypes != null && o instanceof InputMethodSubtype) { 2139 subtypes.add((InputMethodSubtype)o); 2140 } 2141 } 2142 } 2143 } catch (RemoteException e) { 2144 throw e.rethrowFromSystemServer(); 2145 } 2146 return ret; 2147 } 2148 } 2149 2150 /** 2151 * @return The current height of the input method window. 2152 * @hide 2153 */ getInputMethodWindowVisibleHeight()2154 public int getInputMethodWindowVisibleHeight() { 2155 synchronized (mH) { 2156 try { 2157 return mService.getInputMethodWindowVisibleHeight(); 2158 } catch (RemoteException e) { 2159 throw e.rethrowFromSystemServer(); 2160 } 2161 } 2162 } 2163 2164 /** 2165 * Tells the system that the IME decided to not show a window and the system no longer needs to 2166 * use the previous IME's inset. 2167 * 2168 * <p>Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()} 2169 * is the only expected caller of this method. Do not depend on this anywhere else.</p> 2170 * 2171 * <p>TODO: We probably need to reconsider how IME should be handled.</p> 2172 * @hide 2173 * @param token Supplies the identifying token given to an input method when it was started, 2174 * which allows it to perform this operation on itself. 2175 */ clearLastInputMethodWindowForTransition(final IBinder token)2176 public void clearLastInputMethodWindowForTransition(final IBinder token) { 2177 synchronized (mH) { 2178 try { 2179 mService.clearLastInputMethodWindowForTransition(token); 2180 } catch (RemoteException e) { 2181 throw e.rethrowFromSystemServer(); 2182 } 2183 } 2184 } 2185 2186 /** 2187 * Force switch to the last used input method and subtype. If the last input method didn't have 2188 * any subtypes, the framework will simply switch to the last input method with no subtype 2189 * specified. 2190 * @param imeToken Supplies the identifying token given to an input method when it was started, 2191 * which allows it to perform this operation on itself. 2192 * @return true if the current input method and subtype was successfully switched to the last 2193 * used input method and subtype. 2194 */ switchToLastInputMethod(IBinder imeToken)2195 public boolean switchToLastInputMethod(IBinder imeToken) { 2196 synchronized (mH) { 2197 try { 2198 return mService.switchToLastInputMethod(imeToken); 2199 } catch (RemoteException e) { 2200 throw e.rethrowFromSystemServer(); 2201 } 2202 } 2203 } 2204 2205 /** 2206 * Force switch to the next input method and subtype. If there is no IME enabled except 2207 * current IME and subtype, do nothing. 2208 * @param imeToken Supplies the identifying token given to an input method when it was started, 2209 * which allows it to perform this operation on itself. 2210 * @param onlyCurrentIme if true, the framework will find the next subtype which 2211 * belongs to the current IME 2212 * @return true if the current input method and subtype was successfully switched to the next 2213 * input method and subtype. 2214 */ switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme)2215 public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) { 2216 synchronized (mH) { 2217 try { 2218 return mService.switchToNextInputMethod(imeToken, onlyCurrentIme); 2219 } catch (RemoteException e) { 2220 throw e.rethrowFromSystemServer(); 2221 } 2222 } 2223 } 2224 2225 /** 2226 * Returns true if the current IME needs to offer the users ways to switch to a next input 2227 * method (e.g. a globe key.). 2228 * When an IME sets supportsSwitchingToNextInputMethod and this method returns true, 2229 * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly. 2230 * <p> Note that the system determines the most appropriate next input method 2231 * and subtype in order to provide the consistent user experience in switching 2232 * between IMEs and subtypes. 2233 * @param imeToken Supplies the identifying token given to an input method when it was started, 2234 * which allows it to perform this operation on itself. 2235 */ shouldOfferSwitchingToNextInputMethod(IBinder imeToken)2236 public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) { 2237 synchronized (mH) { 2238 try { 2239 return mService.shouldOfferSwitchingToNextInputMethod(imeToken); 2240 } catch (RemoteException e) { 2241 throw e.rethrowFromSystemServer(); 2242 } 2243 } 2244 } 2245 2246 /** 2247 * Set additional input method subtypes. Only a process which shares the same uid with the IME 2248 * can add additional input method subtypes to the IME. 2249 * Please note that a subtype's status is stored in the system. 2250 * For example, enabled subtypes are remembered by the framework even after they are removed 2251 * by using this method. If you re-add the same subtypes again, 2252 * they will just get enabled. If you want to avoid such conflicts, for instance, you may 2253 * want to create a "different" new subtype even with the same locale and mode, 2254 * by changing its extra value. The different subtype won't get affected by the stored past 2255 * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer 2256 * to the current implementation.) 2257 * 2258 * <p>NOTE: If the same subtype exists in both the manifest XML file and additional subtypes 2259 * specified by {@code subtypes}, those multiple instances are automatically merged into one 2260 * instance.</p> 2261 * 2262 * <p>CAVEAT: In API Level 23 and prior, the system may do nothing if an empty 2263 * {@link InputMethodSubtype} is specified in {@code subtypes}, which prevents you from removing 2264 * the last one entry of additional subtypes. If your IME statically defines one or more 2265 * subtypes in the manifest XML file, you may be able to work around this limitation by 2266 * specifying one of those statically defined subtypes in {@code subtypes}.</p> 2267 * 2268 * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to. 2269 * @param subtypes subtypes will be added as additional subtypes of the current input method. 2270 */ setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes)2271 public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) { 2272 synchronized (mH) { 2273 try { 2274 mService.setAdditionalInputMethodSubtypes(imiId, subtypes); 2275 } catch (RemoteException e) { 2276 throw e.rethrowFromSystemServer(); 2277 } 2278 } 2279 } 2280 getLastInputMethodSubtype()2281 public InputMethodSubtype getLastInputMethodSubtype() { 2282 synchronized (mH) { 2283 try { 2284 return mService.getLastInputMethodSubtype(); 2285 } catch (RemoteException e) { 2286 throw e.rethrowFromSystemServer(); 2287 } 2288 } 2289 } 2290 doDump(FileDescriptor fd, PrintWriter fout, String[] args)2291 void doDump(FileDescriptor fd, PrintWriter fout, String[] args) { 2292 final Printer p = new PrintWriterPrinter(fout); 2293 p.println("Input method client state for " + this + ":"); 2294 2295 p.println(" mService=" + mService); 2296 p.println(" mMainLooper=" + mMainLooper); 2297 p.println(" mIInputContext=" + mIInputContext); 2298 p.println(" mActive=" + mActive 2299 + " mHasBeenInactive=" + mHasBeenInactive 2300 + " mBindSequence=" + mBindSequence 2301 + " mCurId=" + mCurId); 2302 p.println(" mCurMethod=" + mCurMethod); 2303 p.println(" mCurRootView=" + mCurRootView); 2304 p.println(" mServedView=" + mServedView); 2305 p.println(" mNextServedView=" + mNextServedView); 2306 p.println(" mServedConnecting=" + mServedConnecting); 2307 if (mCurrentTextBoxAttribute != null) { 2308 p.println(" mCurrentTextBoxAttribute:"); 2309 mCurrentTextBoxAttribute.dump(p, " "); 2310 } else { 2311 p.println(" mCurrentTextBoxAttribute: null"); 2312 } 2313 p.println(" mServedInputConnectionWrapper=" + mServedInputConnectionWrapper); 2314 p.println(" mCompletions=" + Arrays.toString(mCompletions)); 2315 p.println(" mCursorRect=" + mCursorRect); 2316 p.println(" mCursorSelStart=" + mCursorSelStart 2317 + " mCursorSelEnd=" + mCursorSelEnd 2318 + " mCursorCandStart=" + mCursorCandStart 2319 + " mCursorCandEnd=" + mCursorCandEnd); 2320 p.println(" mNextUserActionNotificationSequenceNumber=" 2321 + mNextUserActionNotificationSequenceNumber 2322 + " mLastSentUserActionNotificationSequenceNumber=" 2323 + mLastSentUserActionNotificationSequenceNumber); 2324 } 2325 2326 /** 2327 * Callback that is invoked when an input event that was dispatched to 2328 * the IME has been finished. 2329 * @hide 2330 */ 2331 public interface FinishedInputEventCallback { onFinishedInputEvent(Object token, boolean handled)2332 public void onFinishedInputEvent(Object token, boolean handled); 2333 } 2334 2335 private final class ImeInputEventSender extends InputEventSender { ImeInputEventSender(InputChannel inputChannel, Looper looper)2336 public ImeInputEventSender(InputChannel inputChannel, Looper looper) { 2337 super(inputChannel, looper); 2338 } 2339 2340 @Override onInputEventFinished(int seq, boolean handled)2341 public void onInputEventFinished(int seq, boolean handled) { 2342 finishedInputEvent(seq, handled, false); 2343 } 2344 } 2345 2346 private final class PendingEvent implements Runnable { 2347 public InputEvent mEvent; 2348 public Object mToken; 2349 public String mInputMethodId; 2350 public FinishedInputEventCallback mCallback; 2351 public Handler mHandler; 2352 public boolean mHandled; 2353 recycle()2354 public void recycle() { 2355 mEvent = null; 2356 mToken = null; 2357 mInputMethodId = null; 2358 mCallback = null; 2359 mHandler = null; 2360 mHandled = false; 2361 } 2362 2363 @Override run()2364 public void run() { 2365 mCallback.onFinishedInputEvent(mToken, mHandled); 2366 2367 synchronized (mH) { 2368 recyclePendingEventLocked(this); 2369 } 2370 } 2371 } 2372 dumpViewInfo(@ullable final View view)2373 private static String dumpViewInfo(@Nullable final View view) { 2374 if (view == null) { 2375 return "null"; 2376 } 2377 final StringBuilder sb = new StringBuilder(); 2378 sb.append(view); 2379 sb.append(",focus=" + view.hasFocus()); 2380 sb.append(",windowFocus=" + view.hasWindowFocus()); 2381 sb.append(",window=" + view.getWindowToken()); 2382 sb.append(",temporaryDetach=" + view.isTemporarilyDetached()); 2383 return sb.toString(); 2384 } 2385 } 2386