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 com.android.internal.annotations.GuardedBy; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 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.util.Log; 29 import android.view.KeyEvent; 30 import android.view.inputmethod.CompletionInfo; 31 import android.view.inputmethod.CorrectionInfo; 32 import android.view.inputmethod.ExtractedTextRequest; 33 import android.view.inputmethod.InputConnection; 34 import android.view.inputmethod.InputConnectionInspector; 35 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; 36 import android.view.inputmethod.InputContentInfo; 37 38 public abstract class IInputConnectionWrapper extends IInputContext.Stub { 39 static final String TAG = "IInputConnectionWrapper"; 40 41 private static final int DO_GET_TEXT_AFTER_CURSOR = 10; 42 private static final int DO_GET_TEXT_BEFORE_CURSOR = 20; 43 private static final int DO_GET_SELECTED_TEXT = 25; 44 private static final int DO_GET_CURSOR_CAPS_MODE = 30; 45 private static final int DO_GET_EXTRACTED_TEXT = 40; 46 private static final int DO_COMMIT_TEXT = 50; 47 private static final int DO_COMMIT_COMPLETION = 55; 48 private static final int DO_COMMIT_CORRECTION = 56; 49 private static final int DO_SET_SELECTION = 57; 50 private static final int DO_PERFORM_EDITOR_ACTION = 58; 51 private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59; 52 private static final int DO_SET_COMPOSING_TEXT = 60; 53 private static final int DO_SET_COMPOSING_REGION = 63; 54 private static final int DO_FINISH_COMPOSING_TEXT = 65; 55 private static final int DO_SEND_KEY_EVENT = 70; 56 private static final int DO_DELETE_SURROUNDING_TEXT = 80; 57 private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81; 58 private static final int DO_BEGIN_BATCH_EDIT = 90; 59 private static final int DO_END_BATCH_EDIT = 95; 60 private static final int DO_REPORT_FULLSCREEN_MODE = 100; 61 private static final int DO_PERFORM_PRIVATE_COMMAND = 120; 62 private static final int DO_CLEAR_META_KEY_STATES = 130; 63 private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; 64 private static final int DO_CLOSE_CONNECTION = 150; 65 private static final int DO_COMMIT_CONTENT = 160; 66 67 @GuardedBy("mLock") 68 @Nullable 69 private InputConnection mInputConnection; 70 71 private Looper mMainLooper; 72 private Handler mH; 73 private Object mLock = new Object(); 74 @GuardedBy("mLock") 75 private boolean mFinished = false; 76 @GuardedBy("mLock") 77 private String mInputMethodId; 78 79 static class SomeArgs { 80 Object arg1; 81 Object arg2; 82 IInputContextCallback callback; 83 int seq; 84 } 85 86 class MyHandler extends Handler { 87 MyHandler(Looper looper) { 88 super(looper); 89 } 90 91 @Override 92 public void handleMessage(Message msg) { 93 executeMessage(msg); 94 } 95 } 96 97 public IInputConnectionWrapper(Looper mainLooper, @NonNull InputConnection inputConnection) { 98 mInputConnection = inputConnection; 99 mMainLooper = mainLooper; 100 mH = new MyHandler(mMainLooper); 101 } 102 103 @Nullable 104 public InputConnection getInputConnection() { 105 synchronized (mLock) { 106 return mInputConnection; 107 } 108 } 109 110 protected boolean isFinished() { 111 synchronized (mLock) { 112 return mFinished; 113 } 114 } 115 116 public String getInputMethodId() { 117 synchronized (mLock) { 118 return mInputMethodId; 119 } 120 } 121 122 public void setInputMethodId(final String inputMethodId) { 123 synchronized (mLock) { 124 mInputMethodId = inputMethodId; 125 } 126 } 127 128 abstract protected boolean isActive(); 129 130 /** 131 * Called when the user took some actions that should be taken into consideration to update the 132 * LRU list for input method rotation. 133 */ 134 abstract protected void onUserAction(); 135 136 /** 137 * Called when the input method started or stopped full-screen mode. 138 * @param enabled {@code true} if the input method starts full-screen mode. 139 * @param calledInBackground {@code true} if this input connection is in a state when incoming 140 * events are usually ignored. 141 */ 142 abstract protected void onReportFullscreenMode(boolean enabled, boolean calledInBackground); 143 144 public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) { 145 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback)); 146 } 147 148 public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) { 149 dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback)); 150 } 151 152 public void getSelectedText(int flags, int seq, IInputContextCallback callback) { 153 dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback)); 154 } 155 156 public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) { 157 dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback)); 158 } 159 160 public void getExtractedText(ExtractedTextRequest request, 161 int flags, int seq, IInputContextCallback callback) { 162 dispatchMessage(obtainMessageIOSC(DO_GET_EXTRACTED_TEXT, flags, 163 request, seq, callback)); 164 } 165 166 public void commitText(CharSequence text, int newCursorPosition) { 167 dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text)); 168 } 169 170 public void commitCompletion(CompletionInfo text) { 171 dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text)); 172 } 173 174 public void commitCorrection(CorrectionInfo info) { 175 dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info)); 176 } 177 178 public void setSelection(int start, int end) { 179 dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end)); 180 } 181 182 public void performEditorAction(int id) { 183 dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0)); 184 } 185 186 public void performContextMenuAction(int id) { 187 dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0)); 188 } 189 190 public void setComposingRegion(int start, int end) { 191 dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end)); 192 } 193 194 public void setComposingText(CharSequence text, int newCursorPosition) { 195 dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text)); 196 } 197 198 public void finishComposingText() { 199 dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT)); 200 } 201 202 public void sendKeyEvent(KeyEvent event) { 203 dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event)); 204 } 205 206 public void clearMetaKeyStates(int states) { 207 dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0)); 208 } 209 210 public void deleteSurroundingText(int beforeLength, int afterLength) { 211 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT, 212 beforeLength, afterLength)); 213 } 214 215 public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { 216 dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS, 217 beforeLength, afterLength)); 218 } 219 220 public void beginBatchEdit() { 221 dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT)); 222 } 223 224 public void endBatchEdit() { 225 dispatchMessage(obtainMessage(DO_END_BATCH_EDIT)); 226 } 227 228 public void reportFullscreenMode(boolean enabled) { 229 dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0)); 230 } 231 232 public void performPrivateCommand(String action, Bundle data) { 233 dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data)); 234 } 235 236 public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq, 237 IInputContextCallback callback) { 238 dispatchMessage(obtainMessageISC(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode, 239 seq, callback)); 240 } 241 242 public void closeConnection() { 243 dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION)); 244 } 245 246 public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts, 247 int seq, IInputContextCallback callback) { 248 dispatchMessage(obtainMessageIOOSC(DO_COMMIT_CONTENT, flags, inputContentInfo, opts, seq, 249 callback)); 250 } 251 252 void dispatchMessage(Message msg) { 253 // If we are calling this from the main thread, then we can call 254 // right through. Otherwise, we need to send the message to the 255 // main thread. 256 if (Looper.myLooper() == mMainLooper) { 257 executeMessage(msg); 258 msg.recycle(); 259 return; 260 } 261 262 mH.sendMessage(msg); 263 } 264 265 void executeMessage(Message msg) { 266 switch (msg.what) { 267 case DO_GET_TEXT_AFTER_CURSOR: { 268 SomeArgs args = (SomeArgs)msg.obj; 269 try { 270 InputConnection ic = getInputConnection(); 271 if (ic == null || !isActive()) { 272 Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); 273 args.callback.setTextAfterCursor(null, args.seq); 274 return; 275 } 276 args.callback.setTextAfterCursor(ic.getTextAfterCursor( 277 msg.arg1, msg.arg2), args.seq); 278 } catch (RemoteException e) { 279 Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e); 280 } 281 return; 282 } 283 case DO_GET_TEXT_BEFORE_CURSOR: { 284 SomeArgs args = (SomeArgs)msg.obj; 285 try { 286 InputConnection ic = getInputConnection(); 287 if (ic == null || !isActive()) { 288 Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); 289 args.callback.setTextBeforeCursor(null, args.seq); 290 return; 291 } 292 args.callback.setTextBeforeCursor(ic.getTextBeforeCursor( 293 msg.arg1, msg.arg2), args.seq); 294 } catch (RemoteException e) { 295 Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e); 296 } 297 return; 298 } 299 case DO_GET_SELECTED_TEXT: { 300 SomeArgs args = (SomeArgs)msg.obj; 301 try { 302 InputConnection ic = getInputConnection(); 303 if (ic == null || !isActive()) { 304 Log.w(TAG, "getSelectedText on inactive InputConnection"); 305 args.callback.setSelectedText(null, args.seq); 306 return; 307 } 308 args.callback.setSelectedText(ic.getSelectedText( 309 msg.arg1), args.seq); 310 } catch (RemoteException e) { 311 Log.w(TAG, "Got RemoteException calling setSelectedText", e); 312 } 313 return; 314 } 315 case DO_GET_CURSOR_CAPS_MODE: { 316 SomeArgs args = (SomeArgs)msg.obj; 317 try { 318 InputConnection ic = getInputConnection(); 319 if (ic == null || !isActive()) { 320 Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); 321 args.callback.setCursorCapsMode(0, args.seq); 322 return; 323 } 324 args.callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1), 325 args.seq); 326 } catch (RemoteException e) { 327 Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e); 328 } 329 return; 330 } 331 case DO_GET_EXTRACTED_TEXT: { 332 SomeArgs args = (SomeArgs)msg.obj; 333 try { 334 InputConnection ic = getInputConnection(); 335 if (ic == null || !isActive()) { 336 Log.w(TAG, "getExtractedText on inactive InputConnection"); 337 args.callback.setExtractedText(null, args.seq); 338 return; 339 } 340 args.callback.setExtractedText(ic.getExtractedText( 341 (ExtractedTextRequest)args.arg1, msg.arg1), args.seq); 342 } catch (RemoteException e) { 343 Log.w(TAG, "Got RemoteException calling setExtractedText", e); 344 } 345 return; 346 } 347 case DO_COMMIT_TEXT: { 348 InputConnection ic = getInputConnection(); 349 if (ic == null || !isActive()) { 350 Log.w(TAG, "commitText on inactive InputConnection"); 351 return; 352 } 353 ic.commitText((CharSequence)msg.obj, msg.arg1); 354 onUserAction(); 355 return; 356 } 357 case DO_SET_SELECTION: { 358 InputConnection ic = getInputConnection(); 359 if (ic == null || !isActive()) { 360 Log.w(TAG, "setSelection on inactive InputConnection"); 361 return; 362 } 363 ic.setSelection(msg.arg1, msg.arg2); 364 return; 365 } 366 case DO_PERFORM_EDITOR_ACTION: { 367 InputConnection ic = getInputConnection(); 368 if (ic == null || !isActive()) { 369 Log.w(TAG, "performEditorAction on inactive InputConnection"); 370 return; 371 } 372 ic.performEditorAction(msg.arg1); 373 return; 374 } 375 case DO_PERFORM_CONTEXT_MENU_ACTION: { 376 InputConnection ic = getInputConnection(); 377 if (ic == null || !isActive()) { 378 Log.w(TAG, "performContextMenuAction on inactive InputConnection"); 379 return; 380 } 381 ic.performContextMenuAction(msg.arg1); 382 return; 383 } 384 case DO_COMMIT_COMPLETION: { 385 InputConnection ic = getInputConnection(); 386 if (ic == null || !isActive()) { 387 Log.w(TAG, "commitCompletion on inactive InputConnection"); 388 return; 389 } 390 ic.commitCompletion((CompletionInfo)msg.obj); 391 return; 392 } 393 case DO_COMMIT_CORRECTION: { 394 InputConnection ic = getInputConnection(); 395 if (ic == null || !isActive()) { 396 Log.w(TAG, "commitCorrection on inactive InputConnection"); 397 return; 398 } 399 ic.commitCorrection((CorrectionInfo)msg.obj); 400 return; 401 } 402 case DO_SET_COMPOSING_TEXT: { 403 InputConnection ic = getInputConnection(); 404 if (ic == null || !isActive()) { 405 Log.w(TAG, "setComposingText on inactive InputConnection"); 406 return; 407 } 408 ic.setComposingText((CharSequence)msg.obj, msg.arg1); 409 onUserAction(); 410 return; 411 } 412 case DO_SET_COMPOSING_REGION: { 413 InputConnection ic = getInputConnection(); 414 if (ic == null || !isActive()) { 415 Log.w(TAG, "setComposingRegion on inactive InputConnection"); 416 return; 417 } 418 ic.setComposingRegion(msg.arg1, msg.arg2); 419 return; 420 } 421 case DO_FINISH_COMPOSING_TEXT: { 422 InputConnection ic = getInputConnection(); 423 // Note we do NOT check isActive() here, because this is safe 424 // for an IME to call at any time, and we need to allow it 425 // through to clean up our state after the IME has switched to 426 // another client. 427 if (ic == null) { 428 Log.w(TAG, "finishComposingText on inactive InputConnection"); 429 return; 430 } 431 ic.finishComposingText(); 432 return; 433 } 434 case DO_SEND_KEY_EVENT: { 435 InputConnection ic = getInputConnection(); 436 if (ic == null || !isActive()) { 437 Log.w(TAG, "sendKeyEvent on inactive InputConnection"); 438 return; 439 } 440 ic.sendKeyEvent((KeyEvent)msg.obj); 441 onUserAction(); 442 return; 443 } 444 case DO_CLEAR_META_KEY_STATES: { 445 InputConnection ic = getInputConnection(); 446 if (ic == null || !isActive()) { 447 Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); 448 return; 449 } 450 ic.clearMetaKeyStates(msg.arg1); 451 return; 452 } 453 case DO_DELETE_SURROUNDING_TEXT: { 454 InputConnection ic = getInputConnection(); 455 if (ic == null || !isActive()) { 456 Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); 457 return; 458 } 459 ic.deleteSurroundingText(msg.arg1, msg.arg2); 460 return; 461 } 462 case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: { 463 InputConnection ic = getInputConnection(); 464 if (ic == null || !isActive()) { 465 Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); 466 return; 467 } 468 ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); 469 return; 470 } 471 case DO_BEGIN_BATCH_EDIT: { 472 InputConnection ic = getInputConnection(); 473 if (ic == null || !isActive()) { 474 Log.w(TAG, "beginBatchEdit on inactive InputConnection"); 475 return; 476 } 477 ic.beginBatchEdit(); 478 return; 479 } 480 case DO_END_BATCH_EDIT: { 481 InputConnection ic = getInputConnection(); 482 if (ic == null || !isActive()) { 483 Log.w(TAG, "endBatchEdit on inactive InputConnection"); 484 return; 485 } 486 ic.endBatchEdit(); 487 return; 488 } 489 case DO_REPORT_FULLSCREEN_MODE: { 490 InputConnection ic = getInputConnection(); 491 boolean isBackground = false; 492 if (ic == null || !isActive()) { 493 Log.w(TAG, "reportFullscreenMode on inexistent InputConnection"); 494 isBackground = true; 495 } 496 final boolean enabled = msg.arg1 == 1; 497 if (!isBackground) { 498 ic.reportFullscreenMode(enabled); 499 } 500 // Due to the nature of asynchronous event handling, currently InputMethodService 501 // has relied on the fact that #reportFullscreenMode() can be handled even when the 502 // InputConnection is inactive. We have to notify this event to InputMethodManager. 503 onReportFullscreenMode(enabled, isBackground); 504 return; 505 } 506 case DO_PERFORM_PRIVATE_COMMAND: { 507 InputConnection ic = getInputConnection(); 508 if (ic == null || !isActive()) { 509 Log.w(TAG, "performPrivateCommand on inactive InputConnection"); 510 return; 511 } 512 SomeArgs args = (SomeArgs)msg.obj; 513 ic.performPrivateCommand((String)args.arg1, 514 (Bundle)args.arg2); 515 return; 516 } 517 case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { 518 SomeArgs args = (SomeArgs)msg.obj; 519 try { 520 InputConnection ic = getInputConnection(); 521 if (ic == null || !isActive()) { 522 Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); 523 args.callback.setRequestUpdateCursorAnchorInfoResult(false, args.seq); 524 return; 525 } 526 args.callback.setRequestUpdateCursorAnchorInfoResult( 527 ic.requestCursorUpdates(msg.arg1), args.seq); 528 } catch (RemoteException e) { 529 Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e); 530 } 531 return; 532 } 533 case DO_CLOSE_CONNECTION: { 534 // Note that we do not need to worry about race condition here, because 1) mFinished 535 // is updated only inside this block, and 2) the code here is running on a Handler 536 // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the 537 // same time. 538 if (isFinished()) { 539 return; 540 } 541 try { 542 InputConnection ic = getInputConnection(); 543 // Note we do NOT check isActive() here, because this is safe 544 // for an IME to call at any time, and we need to allow it 545 // through to clean up our state after the IME has switched to 546 // another client. 547 if (ic == null) { 548 return; 549 } 550 @MissingMethodFlags 551 final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic); 552 if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) { 553 ic.closeConnection(); 554 } 555 } finally { 556 synchronized (mLock) { 557 mInputConnection = null; 558 mFinished = true; 559 } 560 } 561 return; 562 } 563 case DO_COMMIT_CONTENT: { 564 final int flags = msg.arg1; 565 final boolean grantUriPermission = 566 (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0; 567 SomeArgs args = (SomeArgs) msg.obj; 568 try { 569 InputConnection ic = getInputConnection(); 570 if (ic == null || !isActive()) { 571 Log.w(TAG, "commitContent on inactive InputConnection"); 572 args.callback.setCommitContentResult(false, args.seq); 573 return; 574 } 575 final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1; 576 if (inputContentInfo == null || !inputContentInfo.validate()) { 577 Log.w(TAG, "commitContent with invalid inputContentInfo=" 578 + inputContentInfo); 579 args.callback.setCommitContentResult(false, args.seq); 580 return; 581 } 582 if (grantUriPermission) { 583 inputContentInfo.requestPermission(); 584 } 585 final boolean result = 586 ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2); 587 // If this request is not handled, then there is no reason to keep the URI 588 // permission. 589 if (grantUriPermission && !result) { 590 inputContentInfo.releasePermission(); 591 } 592 args.callback.setCommitContentResult(result, args.seq); 593 } catch (RemoteException e) { 594 Log.w(TAG, "Got RemoteException calling commitContent", e); 595 } 596 return; 597 } 598 } 599 Log.w(TAG, "Unhandled message code: " + msg.what); 600 } 601 602 Message obtainMessage(int what) { 603 return mH.obtainMessage(what); 604 } 605 606 Message obtainMessageII(int what, int arg1, int arg2) { 607 return mH.obtainMessage(what, arg1, arg2); 608 } 609 610 Message obtainMessageO(int what, Object arg1) { 611 return mH.obtainMessage(what, 0, 0, arg1); 612 } 613 614 Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) { 615 SomeArgs args = new SomeArgs(); 616 args.callback = callback; 617 args.seq = seq; 618 return mH.obtainMessage(what, arg1, 0, args); 619 } 620 621 Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) { 622 SomeArgs args = new SomeArgs(); 623 args.callback = callback; 624 args.seq = seq; 625 return mH.obtainMessage(what, arg1, arg2, args); 626 } 627 628 Message obtainMessageIOOSC(int what, int arg1, Object objArg1, Object objArg2, int seq, 629 IInputContextCallback callback) { 630 SomeArgs args = new SomeArgs(); 631 args.arg1 = objArg1; 632 args.arg2 = objArg2; 633 args.callback = callback; 634 args.seq = seq; 635 return mH.obtainMessage(what, arg1, 0, args); 636 } 637 638 Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq, 639 IInputContextCallback callback) { 640 SomeArgs args = new SomeArgs(); 641 args.arg1 = arg2; 642 args.callback = callback; 643 args.seq = seq; 644 return mH.obtainMessage(what, arg1, 0, args); 645 } 646 647 Message obtainMessageIO(int what, int arg1, Object arg2) { 648 return mH.obtainMessage(what, arg1, 0, arg2); 649 } 650 651 Message obtainMessageOO(int what, Object arg1, Object arg2) { 652 SomeArgs args = new SomeArgs(); 653 args.arg1 = arg1; 654 args.arg2 = arg2; 655 return mH.obtainMessage(what, 0, 0, args); 656 } 657 } 658