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