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