1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.view; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.os.Build; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.Message; 27 import android.os.RemoteException; 28 import android.os.Trace; 29 import android.util.Log; 30 import android.util.imetracing.ImeTracing; 31 import android.util.imetracing.InputConnectionHelper; 32 import android.util.proto.ProtoOutputStream; 33 import android.view.KeyEvent; 34 import android.view.View; 35 import android.view.inputmethod.CompletionInfo; 36 import android.view.inputmethod.CorrectionInfo; 37 import android.view.inputmethod.DumpableInputConnection; 38 import android.view.inputmethod.ExtractedText; 39 import android.view.inputmethod.ExtractedTextRequest; 40 import android.view.inputmethod.InputConnection; 41 import android.view.inputmethod.InputConnectionInspector; 42 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; 43 import android.view.inputmethod.InputContentInfo; 44 import android.view.inputmethod.InputMethodManager; 45 import android.view.inputmethod.SurroundingText; 46 47 import com.android.internal.annotations.GuardedBy; 48 import com.android.internal.inputmethod.ICharSequenceResultCallback; 49 import com.android.internal.inputmethod.IExtractedTextResultCallback; 50 import com.android.internal.inputmethod.IIntResultCallback; 51 import com.android.internal.inputmethod.ISurroundingTextResultCallback; 52 import com.android.internal.os.SomeArgs; 53 54 import java.lang.ref.WeakReference; 55 56 public final class IInputConnectionWrapper extends IInputContext.Stub { 57 private static final String TAG = "IInputConnectionWrapper"; 58 private static final boolean DEBUG = false; 59 60 private static final int DO_GET_TEXT_AFTER_CURSOR = 10; 61 private static final int DO_GET_TEXT_BEFORE_CURSOR = 20; 62 private static final int DO_GET_SELECTED_TEXT = 25; 63 private static final int DO_GET_CURSOR_CAPS_MODE = 30; 64 private static final int DO_GET_EXTRACTED_TEXT = 40; 65 private static final int DO_COMMIT_TEXT = 50; 66 private static final int DO_COMMIT_COMPLETION = 55; 67 private static final int DO_COMMIT_CORRECTION = 56; 68 private static final int DO_SET_SELECTION = 57; 69 private static final int DO_PERFORM_EDITOR_ACTION = 58; 70 private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59; 71 private static final int DO_SET_COMPOSING_TEXT = 60; 72 private static final int DO_SET_COMPOSING_REGION = 63; 73 private static final int DO_FINISH_COMPOSING_TEXT = 65; 74 private static final int DO_SEND_KEY_EVENT = 70; 75 private static final int DO_DELETE_SURROUNDING_TEXT = 80; 76 private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81; 77 private static final int DO_BEGIN_BATCH_EDIT = 90; 78 private static final int DO_END_BATCH_EDIT = 95; 79 private static final int DO_PERFORM_SPELL_CHECK = 110; 80 private static final int DO_PERFORM_PRIVATE_COMMAND = 120; 81 private static final int DO_CLEAR_META_KEY_STATES = 130; 82 private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; 83 private static final int DO_CLOSE_CONNECTION = 150; 84 private static final int DO_COMMIT_CONTENT = 160; 85 private static final int DO_GET_SURROUNDING_TEXT = 41; 86 private static final int DO_SET_IME_CONSUMES_INPUT = 170; 87 88 89 @GuardedBy("mLock") 90 @Nullable 91 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 92 private InputConnection mInputConnection; 93 94 private Looper mMainLooper; 95 private Handler mH; 96 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 97 private final Object mLock = new Object(); 98 @GuardedBy("mLock") 99 private boolean mFinished = false; 100 101 private final InputMethodManager mParentInputMethodManager; 102 private final WeakReference<View> mServedView; 103 104 class MyHandler extends Handler { MyHandler(Looper looper)105 MyHandler(Looper looper) { 106 super(looper); 107 } 108 109 @Override handleMessage(Message msg)110 public void handleMessage(Message msg) { 111 executeMessage(msg); 112 } 113 } 114 IInputConnectionWrapper(@onNull Looper mainLooper, @NonNull InputConnection inputConnection, @NonNull InputMethodManager inputMethodManager, @Nullable View servedView)115 public IInputConnectionWrapper(@NonNull Looper mainLooper, 116 @NonNull InputConnection inputConnection, 117 @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) { 118 mInputConnection = inputConnection; 119 mMainLooper = mainLooper; 120 mH = new MyHandler(mMainLooper); 121 mParentInputMethodManager = inputMethodManager; 122 mServedView = new WeakReference<>(servedView); 123 } 124 125 @Nullable getInputConnection()126 public InputConnection getInputConnection() { 127 synchronized (mLock) { 128 return mInputConnection; 129 } 130 } 131 isFinished()132 private boolean isFinished() { 133 synchronized (mLock) { 134 return mFinished; 135 } 136 } 137 isActive()138 public boolean isActive() { 139 return mParentInputMethodManager.isActive() && !isFinished(); 140 } 141 getServedView()142 public View getServedView() { 143 return mServedView.get(); 144 } 145 deactivate()146 public void deactivate() { 147 if (isFinished()) { 148 // This is a small performance optimization. Still only the 1st call of 149 // reportFinish() will take effect. 150 return; 151 } 152 closeConnection(); 153 154 // Notify the app that the InputConnection was closed. 155 final View servedView = mServedView.get(); 156 if (servedView != null) { 157 final Handler handler = servedView.getHandler(); 158 // The handler is null if the view is already detached. When that's the case, for 159 // now, we simply don't dispatch this callback. 160 if (handler != null) { 161 if (DEBUG) { 162 Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView); 163 } 164 if (handler.getLooper().isCurrentThread()) { 165 servedView.onInputConnectionClosedInternal(); 166 } else { 167 handler.post(servedView::onInputConnectionClosedInternal); 168 } 169 } 170 } 171 } 172 173 @Override toString()174 public String toString() { 175 return "IInputConnectionWrapper{" 176 + "connection=" + getInputConnection() 177 + " finished=" + isFinished() 178 + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive() 179 + " mServedView=" + mServedView.get() 180 + "}"; 181 } 182 dumpDebug(ProtoOutputStream proto, long fieldId)183 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 184 synchronized (mLock) { 185 // Check that the call is initiated in the main thread of the current InputConnection 186 // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are 187 // executed on this thread. Otherwise the messages are dispatched to the correct thread 188 // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance 189 // reasons. 190 if ((mInputConnection instanceof DumpableInputConnection) 191 && Looper.myLooper() == mMainLooper) { 192 ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId); 193 } 194 } 195 } 196 getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback)197 public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) { 198 dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback)); 199 } 200 getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback)201 public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) { 202 dispatchMessage(mH.obtainMessage(DO_GET_TEXT_BEFORE_CURSOR, length, flags, callback)); 203 } 204 getSelectedText(int flags, ICharSequenceResultCallback callback)205 public void getSelectedText(int flags, ICharSequenceResultCallback callback) { 206 dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback)); 207 } 208 209 /** 210 * Dispatches the request for retrieving surrounding text. 211 * 212 * <p>See {@link InputConnection#getSurroundingText(int, int, int)}. 213 */ getSurroundingText(int beforeLength, int afterLength, int flags, ISurroundingTextResultCallback callback)214 public void getSurroundingText(int beforeLength, int afterLength, int flags, 215 ISurroundingTextResultCallback callback) { 216 final SomeArgs args = SomeArgs.obtain(); 217 args.arg1 = beforeLength; 218 args.arg2 = afterLength; 219 args.arg3 = flags; 220 args.arg4 = callback; 221 dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args)); 222 } 223 getCursorCapsMode(int reqModes, IIntResultCallback callback)224 public void getCursorCapsMode(int reqModes, IIntResultCallback callback) { 225 dispatchMessage( 226 mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback)); 227 } 228 getExtractedText(ExtractedTextRequest request, int flags, IExtractedTextResultCallback callback)229 public void getExtractedText(ExtractedTextRequest request, int flags, 230 IExtractedTextResultCallback callback) { 231 final SomeArgs args = SomeArgs.obtain(); 232 args.arg1 = request; 233 args.arg2 = callback; 234 dispatchMessage(mH.obtainMessage(DO_GET_EXTRACTED_TEXT, flags, 0 /* unused */, args)); 235 } 236 commitText(CharSequence text, int newCursorPosition)237 public void commitText(CharSequence text, int newCursorPosition) { 238 dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text)); 239 } 240 commitCompletion(CompletionInfo text)241 public void commitCompletion(CompletionInfo text) { 242 dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text)); 243 } 244 commitCorrection(CorrectionInfo info)245 public void commitCorrection(CorrectionInfo info) { 246 dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info)); 247 } 248 setSelection(int start, int end)249 public void setSelection(int start, int end) { 250 dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); 251 } 252 performEditorAction(int id)253 public void performEditorAction(int id) { 254 dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0)); 255 } 256 performContextMenuAction(int id)257 public void performContextMenuAction(int id) { 258 dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); 259 } 260 setComposingRegion(int start, int end)261 public void setComposingRegion(int start, int end) { 262 dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end)); 263 } 264 setComposingText(CharSequence text, int newCursorPosition)265 public void setComposingText(CharSequence text, int newCursorPosition) { 266 dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text)); 267 } 268 finishComposingText()269 public void finishComposingText() { 270 dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT)); 271 } 272 sendKeyEvent(KeyEvent event)273 public void sendKeyEvent(KeyEvent event) { 274 dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event)); 275 } 276 clearMetaKeyStates(int states)277 public void clearMetaKeyStates(int states) { 278 dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0)); 279 } 280 deleteSurroundingText(int beforeLength, int afterLength)281 public void deleteSurroundingText(int beforeLength, int afterLength) { 282 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT, 283 beforeLength, afterLength)); 284 } 285 deleteSurroundingTextInCodePoints(int beforeLength, int afterLength)286 public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { 287 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS, 288 beforeLength, afterLength)); 289 } 290 beginBatchEdit()291 public void beginBatchEdit() { 292 dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT)); 293 } 294 endBatchEdit()295 public void endBatchEdit() { 296 dispatchMessage(obtainMessage(DO_END_BATCH_EDIT)); 297 } 298 299 /** 300 * Dispatches the request for performing spell check. 301 * 302 * @see InputConnection#performSpellCheck() 303 */ performSpellCheck()304 public void performSpellCheck() { 305 dispatchMessage(obtainMessage(DO_PERFORM_SPELL_CHECK)); 306 } 307 performPrivateCommand(String action, Bundle data)308 public void performPrivateCommand(String action, Bundle data) { 309 dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data)); 310 } 311 requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback)312 public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback) { 313 dispatchMessage(mH.obtainMessage(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode, 314 0 /* unused */, callback)); 315 } 316 closeConnection()317 public void closeConnection() { 318 dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION)); 319 } 320 commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts, IIntResultCallback callback)321 public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts, 322 IIntResultCallback callback) { 323 final SomeArgs args = SomeArgs.obtain(); 324 args.arg1 = inputContentInfo; 325 args.arg2 = opts; 326 args.arg3 = callback; 327 dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args)); 328 } 329 330 /** 331 * Dispatches the request for setting ime consumes input. 332 * 333 * <p>See {@link InputConnection#setImeConsumesInput(boolean)}. 334 */ setImeConsumesInput(boolean imeConsumesInput)335 public void setImeConsumesInput(boolean imeConsumesInput) { 336 dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput)); 337 } 338 dispatchMessage(Message msg)339 void dispatchMessage(Message msg) { 340 // If we are calling this from the main thread, then we can call 341 // right through. Otherwise, we need to send the message to the 342 // main thread. 343 if (Looper.myLooper() == mMainLooper) { 344 executeMessage(msg); 345 msg.recycle(); 346 return; 347 } 348 349 mH.sendMessage(msg); 350 } 351 executeMessage(Message msg)352 void executeMessage(Message msg) { 353 ProtoOutputStream icProto; 354 switch (msg.what) { 355 case DO_GET_TEXT_AFTER_CURSOR: { 356 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); 357 try { 358 final ICharSequenceResultCallback callback = 359 (ICharSequenceResultCallback) msg.obj; 360 final InputConnection ic = getInputConnection(); 361 final CharSequence result; 362 if (ic == null || !isActive()) { 363 Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); 364 result = null; 365 } else { 366 result = ic.getTextAfterCursor(msg.arg1, msg.arg2); 367 } 368 if (ImeTracing.getInstance().isEnabled()) { 369 icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1, 370 msg.arg2, result); 371 ImeTracing.getInstance().triggerClientDump( 372 TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto); 373 } 374 try { 375 callback.onResult(result); 376 } catch (RemoteException e) { 377 Log.w(TAG, "Failed to return the result to getTextAfterCursor()." 378 + " result=" + result, e); 379 } 380 } finally { 381 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 382 } 383 return; 384 } 385 case DO_GET_TEXT_BEFORE_CURSOR: { 386 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor"); 387 try { 388 final ICharSequenceResultCallback callback = 389 (ICharSequenceResultCallback) msg.obj; 390 final InputConnection ic = getInputConnection(); 391 final CharSequence result; 392 if (ic == null || !isActive()) { 393 Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); 394 result = null; 395 } else { 396 result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); 397 } 398 if (ImeTracing.getInstance().isEnabled()) { 399 icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1, 400 msg.arg2, result); 401 ImeTracing.getInstance().triggerClientDump( 402 TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto); 403 } 404 try { 405 callback.onResult(result); 406 } catch (RemoteException e) { 407 Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." 408 + " result=" + result, e); 409 } 410 } finally { 411 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 412 } 413 return; 414 } 415 case DO_GET_SELECTED_TEXT: { 416 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText"); 417 try { 418 final ICharSequenceResultCallback callback = 419 (ICharSequenceResultCallback) msg.obj; 420 final InputConnection ic = getInputConnection(); 421 final CharSequence result; 422 if (ic == null || !isActive()) { 423 Log.w(TAG, "getSelectedText on inactive InputConnection"); 424 result = null; 425 } else { 426 result = ic.getSelectedText(msg.arg1); 427 } 428 if (ImeTracing.getInstance().isEnabled()) { 429 icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result); 430 ImeTracing.getInstance().triggerClientDump( 431 TAG + "#getSelectedText", mParentInputMethodManager, icProto); 432 } 433 try { 434 callback.onResult(result); 435 } catch (RemoteException e) { 436 Log.w(TAG, "Failed to return the result to getSelectedText()." 437 + " result=" + result, e); 438 } 439 } finally { 440 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 441 } 442 return; 443 } 444 case DO_GET_SURROUNDING_TEXT: { 445 final SomeArgs args = (SomeArgs) msg.obj; 446 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText"); 447 try { 448 int beforeLength = (int) args.arg1; 449 int afterLength = (int) args.arg2; 450 int flags = (int) args.arg3; 451 final ISurroundingTextResultCallback callback = 452 (ISurroundingTextResultCallback) args.arg4; 453 final InputConnection ic = getInputConnection(); 454 final SurroundingText result; 455 if (ic == null || !isActive()) { 456 Log.w(TAG, "getSurroundingText on inactive InputConnection"); 457 result = null; 458 } else { 459 result = ic.getSurroundingText(beforeLength, afterLength, flags); 460 } 461 if (ImeTracing.getInstance().isEnabled()) { 462 icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength, 463 afterLength, flags, result); 464 ImeTracing.getInstance().triggerClientDump( 465 TAG + "#getSurroundingText", mParentInputMethodManager, icProto); 466 } 467 try { 468 callback.onResult(result); 469 } catch (RemoteException e) { 470 Log.w(TAG, "Failed to return the result to getSurroundingText()." 471 + " result=" + result, e); 472 } 473 } finally { 474 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 475 args.recycle(); 476 } 477 return; 478 } 479 case DO_GET_CURSOR_CAPS_MODE: { 480 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode"); 481 try { 482 final IIntResultCallback callback = (IIntResultCallback) msg.obj; 483 final InputConnection ic = getInputConnection(); 484 final int result; 485 if (ic == null || !isActive()) { 486 Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); 487 result = 0; 488 } else { 489 result = ic.getCursorCapsMode(msg.arg1); 490 } 491 if (ImeTracing.getInstance().isEnabled()) { 492 icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1, 493 result); 494 ImeTracing.getInstance().triggerClientDump( 495 TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto); 496 } 497 try { 498 callback.onResult(result); 499 } catch (RemoteException e) { 500 Log.w(TAG, "Failed to return the result to getCursorCapsMode()." 501 + " result=" + result, e); 502 } 503 } finally { 504 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 505 } 506 return; 507 } 508 case DO_GET_EXTRACTED_TEXT: { 509 final SomeArgs args = (SomeArgs) msg.obj; 510 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText"); 511 try { 512 final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1; 513 final IExtractedTextResultCallback callback = 514 (IExtractedTextResultCallback) args.arg2; 515 final InputConnection ic = getInputConnection(); 516 final ExtractedText result; 517 if (ic == null || !isActive()) { 518 Log.w(TAG, "getExtractedText on inactive InputConnection"); 519 result = null; 520 } else { 521 result = ic.getExtractedText(request, msg.arg1); 522 } 523 if (ImeTracing.getInstance().isEnabled()) { 524 icProto = InputConnectionHelper.buildGetExtractedTextProto(request, 525 msg.arg1, result); 526 ImeTracing.getInstance().triggerClientDump( 527 TAG + "#getExtractedText", mParentInputMethodManager, icProto); 528 } 529 try { 530 callback.onResult(result); 531 } catch (RemoteException e) { 532 Log.w(TAG, "Failed to return the result to getExtractedText()." 533 + " result=" + result, e); 534 } 535 } finally { 536 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 537 args.recycle(); 538 } 539 return; 540 } 541 case DO_COMMIT_TEXT: { 542 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText"); 543 try { 544 InputConnection ic = getInputConnection(); 545 if (ic == null || !isActive()) { 546 Log.w(TAG, "commitText on inactive InputConnection"); 547 return; 548 } 549 ic.commitText((CharSequence) msg.obj, msg.arg1); 550 } finally { 551 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 552 } 553 return; 554 } 555 case DO_SET_SELECTION: { 556 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection"); 557 try { 558 InputConnection ic = getInputConnection(); 559 if (ic == null || !isActive()) { 560 Log.w(TAG, "setSelection on inactive InputConnection"); 561 return; 562 } 563 ic.setSelection(msg.arg1, msg.arg2); 564 } finally { 565 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 566 } 567 return; 568 } 569 case DO_PERFORM_EDITOR_ACTION: { 570 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction"); 571 try { 572 InputConnection ic = getInputConnection(); 573 if (ic == null || !isActive()) { 574 Log.w(TAG, "performEditorAction on inactive InputConnection"); 575 return; 576 } 577 ic.performEditorAction(msg.arg1); 578 } finally { 579 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 580 } 581 return; 582 } 583 case DO_PERFORM_CONTEXT_MENU_ACTION: { 584 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction"); 585 try { 586 InputConnection ic = getInputConnection(); 587 if (ic == null || !isActive()) { 588 Log.w(TAG, "performContextMenuAction on inactive InputConnection"); 589 return; 590 } 591 ic.performContextMenuAction(msg.arg1); 592 } finally { 593 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 594 } 595 return; 596 } 597 case DO_COMMIT_COMPLETION: { 598 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion"); 599 try { 600 InputConnection ic = getInputConnection(); 601 if (ic == null || !isActive()) { 602 Log.w(TAG, "commitCompletion on inactive InputConnection"); 603 return; 604 } 605 ic.commitCompletion((CompletionInfo) msg.obj); 606 } finally { 607 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 608 } 609 return; 610 } 611 case DO_COMMIT_CORRECTION: { 612 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection"); 613 try { 614 InputConnection ic = getInputConnection(); 615 if (ic == null || !isActive()) { 616 Log.w(TAG, "commitCorrection on inactive InputConnection"); 617 return; 618 } 619 ic.commitCorrection((CorrectionInfo) msg.obj); 620 } finally { 621 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 622 } 623 return; 624 } 625 case DO_SET_COMPOSING_TEXT: { 626 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText"); 627 try { 628 InputConnection ic = getInputConnection(); 629 if (ic == null || !isActive()) { 630 Log.w(TAG, "setComposingText on inactive InputConnection"); 631 return; 632 } 633 ic.setComposingText((CharSequence) msg.obj, msg.arg1); 634 } finally { 635 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 636 } 637 return; 638 } 639 case DO_SET_COMPOSING_REGION: { 640 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion"); 641 try { 642 InputConnection ic = getInputConnection(); 643 if (ic == null || !isActive()) { 644 Log.w(TAG, "setComposingRegion on inactive InputConnection"); 645 return; 646 } 647 ic.setComposingRegion(msg.arg1, msg.arg2); 648 } finally { 649 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 650 } 651 return; 652 } 653 case DO_FINISH_COMPOSING_TEXT: { 654 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText"); 655 try { 656 if (isFinished()) { 657 // In this case, #finishComposingText() is guaranteed to be called already. 658 // There should be no negative impact if we ignore this call silently. 659 if (DEBUG) { 660 Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); 661 } 662 return; 663 } 664 InputConnection ic = getInputConnection(); 665 // Note we do NOT check isActive() here, because this is safe 666 // for an IME to call at any time, and we need to allow it 667 // through to clean up our state after the IME has switched to 668 // another client. 669 if (ic == null) { 670 Log.w(TAG, "finishComposingText on inactive InputConnection"); 671 return; 672 } 673 ic.finishComposingText(); 674 } finally { 675 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 676 } 677 return; 678 } 679 case DO_SEND_KEY_EVENT: { 680 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent"); 681 try { 682 InputConnection ic = getInputConnection(); 683 if (ic == null || !isActive()) { 684 Log.w(TAG, "sendKeyEvent on inactive InputConnection"); 685 return; 686 } 687 ic.sendKeyEvent((KeyEvent) msg.obj); 688 } finally { 689 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 690 } 691 return; 692 } 693 case DO_CLEAR_META_KEY_STATES: { 694 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates"); 695 try { 696 InputConnection ic = getInputConnection(); 697 if (ic == null || !isActive()) { 698 Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); 699 return; 700 } 701 ic.clearMetaKeyStates(msg.arg1); 702 } finally { 703 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 704 } 705 return; 706 } 707 case DO_DELETE_SURROUNDING_TEXT: { 708 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText"); 709 try { 710 InputConnection ic = getInputConnection(); 711 if (ic == null || !isActive()) { 712 Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); 713 return; 714 } 715 ic.deleteSurroundingText(msg.arg1, msg.arg2); 716 } finally { 717 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 718 } 719 return; 720 } 721 case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: { 722 Trace.traceBegin(Trace.TRACE_TAG_INPUT, 723 "InputConnection#deleteSurroundingTextInCodePoints"); 724 try { 725 InputConnection ic = getInputConnection(); 726 if (ic == null || !isActive()) { 727 Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); 728 return; 729 } 730 ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); 731 } finally { 732 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 733 } 734 return; 735 } 736 case DO_BEGIN_BATCH_EDIT: { 737 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit"); 738 try { 739 InputConnection ic = getInputConnection(); 740 if (ic == null || !isActive()) { 741 Log.w(TAG, "beginBatchEdit on inactive InputConnection"); 742 return; 743 } 744 ic.beginBatchEdit(); 745 } finally { 746 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 747 } 748 return; 749 } 750 case DO_END_BATCH_EDIT: { 751 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit"); 752 try { 753 InputConnection ic = getInputConnection(); 754 if (ic == null || !isActive()) { 755 Log.w(TAG, "endBatchEdit on inactive InputConnection"); 756 return; 757 } 758 ic.endBatchEdit(); 759 } finally { 760 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 761 } 762 return; 763 } 764 case DO_PERFORM_SPELL_CHECK: { 765 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck"); 766 try { 767 InputConnection ic = getInputConnection(); 768 if (ic == null || !isActive()) { 769 Log.w(TAG, "performSpellCheck on inactive InputConnection"); 770 return; 771 } 772 ic.performSpellCheck(); 773 } finally { 774 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 775 } 776 return; 777 } 778 case DO_PERFORM_PRIVATE_COMMAND: { 779 final SomeArgs args = (SomeArgs) msg.obj; 780 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand"); 781 try { 782 final String action = (String) args.arg1; 783 final Bundle data = (Bundle) args.arg2; 784 InputConnection ic = getInputConnection(); 785 if (ic == null || !isActive()) { 786 Log.w(TAG, "performPrivateCommand on inactive InputConnection"); 787 return; 788 } 789 ic.performPrivateCommand(action, data); 790 } finally { 791 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 792 args.recycle(); 793 } 794 return; 795 } 796 case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { 797 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); 798 try { 799 final IIntResultCallback callback = (IIntResultCallback) msg.obj; 800 final InputConnection ic = getInputConnection(); 801 final boolean result; 802 if (ic == null || !isActive()) { 803 Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); 804 result = false; 805 } else { 806 result = ic.requestCursorUpdates(msg.arg1); 807 } 808 try { 809 callback.onResult(result ? 1 : 0); 810 } catch (RemoteException e) { 811 Log.w(TAG, "Failed to return the result to requestCursorUpdates()." 812 + " result=" + result, e); 813 } 814 } finally { 815 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 816 } 817 return; 818 } 819 case DO_CLOSE_CONNECTION: { 820 // Note that we do not need to worry about race condition here, because 1) mFinished 821 // is updated only inside this block, and 2) the code here is running on a Handler 822 // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the 823 // same time. 824 if (isFinished()) { 825 return; 826 } 827 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection"); 828 try { 829 InputConnection ic = getInputConnection(); 830 // Note we do NOT check isActive() here, because this is safe 831 // for an IME to call at any time, and we need to allow it 832 // through to clean up our state after the IME has switched to 833 // another client. 834 if (ic == null) { 835 return; 836 } 837 @MissingMethodFlags 838 final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic); 839 if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) { 840 ic.closeConnection(); 841 } 842 } finally { 843 synchronized (mLock) { 844 mInputConnection = null; 845 mFinished = true; 846 } 847 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 848 } 849 return; 850 } 851 case DO_COMMIT_CONTENT: { 852 final int flags = msg.arg1; 853 SomeArgs args = (SomeArgs) msg.obj; 854 Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent"); 855 try { 856 final IIntResultCallback callback = (IIntResultCallback) args.arg3; 857 final InputConnection ic = getInputConnection(); 858 final boolean result; 859 if (ic == null || !isActive()) { 860 Log.w(TAG, "commitContent on inactive InputConnection"); 861 result = false; 862 } else { 863 final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1; 864 if (inputContentInfo == null || !inputContentInfo.validate()) { 865 Log.w(TAG, "commitContent with invalid inputContentInfo=" 866 + inputContentInfo); 867 result = false; 868 } else { 869 result = ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2); 870 } 871 } 872 try { 873 callback.onResult(result ? 1 : 0); 874 } catch (RemoteException e) { 875 Log.w(TAG, "Failed to return the result to commitContent()." 876 + " result=" + result, e); 877 } 878 } finally { 879 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 880 args.recycle(); 881 } 882 return; 883 } 884 case DO_SET_IME_CONSUMES_INPUT: { 885 Trace.traceBegin(Trace.TRACE_TAG_INPUT, 886 "InputConnection#setImeConsumesInput"); 887 try { 888 InputConnection ic = getInputConnection(); 889 if (ic == null || !isActive()) { 890 Log.w(TAG, 891 "setImeConsumesInput on inactive InputConnection"); 892 return; 893 } 894 ic.setImeConsumesInput(msg.arg1 == 1); 895 } finally { 896 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 897 } 898 return; 899 } 900 } 901 Log.w(TAG, "Unhandled message code: " + msg.what); 902 } 903 obtainMessage(int what)904 Message obtainMessage(int what) { 905 return mH.obtainMessage(what); 906 } 907 obtainMessageII(int what, int arg1, int arg2)908 Message obtainMessageII(int what, int arg1, int arg2) { 909 return mH.obtainMessage(what, arg1, arg2); 910 } 911 obtainMessageO(int what, Object arg1)912 Message obtainMessageO(int what, Object arg1) { 913 return mH.obtainMessage(what, 0, 0, arg1); 914 } 915 obtainMessageIO(int what, int arg1, Object arg2)916 Message obtainMessageIO(int what, int arg1, Object arg2) { 917 return mH.obtainMessage(what, arg1, 0, arg2); 918 } 919 obtainMessageOO(int what, Object arg1, Object arg2)920 Message obtainMessageOO(int what, Object arg1, Object arg2) { 921 final SomeArgs args = SomeArgs.obtain(); 922 args.arg1 = arg1; 923 args.arg2 = arg2; 924 return mH.obtainMessage(what, 0, 0, args); 925 } 926 obtainMessageB(int what, boolean arg1)927 Message obtainMessageB(int what, boolean arg1) { 928 return mH.obtainMessage(what, arg1 ? 1 : 0, 0); 929 } 930 } 931