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