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