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
17package com.android.internal.view;
18
19import com.android.internal.annotations.GuardedBy;
20
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.os.Bundle;
24import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
27import android.os.RemoteException;
28import android.util.Log;
29import android.view.KeyEvent;
30import android.view.inputmethod.CompletionInfo;
31import android.view.inputmethod.CorrectionInfo;
32import android.view.inputmethod.ExtractedTextRequest;
33import android.view.inputmethod.InputConnection;
34import android.view.inputmethod.InputConnectionInspector;
35import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
36import android.view.inputmethod.InputContentInfo;
37
38public 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                        try {
584                            inputContentInfo.requestPermission();
585                        } catch (Exception e) {
586                            Log.e(TAG, "InputConnectionInfo.requestPermission() failed", e);
587                            args.callback.setCommitContentResult(false, args.seq);
588                            return;
589                        }
590                    }
591                    final boolean result =
592                            ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
593                    // If this request is not handled, then there is no reason to keep the URI
594                    // permission.
595                    if (grantUriPermission && !result) {
596                        inputContentInfo.releasePermission();
597                    }
598                    args.callback.setCommitContentResult(result, args.seq);
599                } catch (RemoteException e) {
600                    Log.w(TAG, "Got RemoteException calling commitContent", e);
601                }
602                return;
603            }
604        }
605        Log.w(TAG, "Unhandled message code: " + msg.what);
606    }
607
608    Message obtainMessage(int what) {
609        return mH.obtainMessage(what);
610    }
611
612    Message obtainMessageII(int what, int arg1, int arg2) {
613        return mH.obtainMessage(what, arg1, arg2);
614    }
615
616    Message obtainMessageO(int what, Object arg1) {
617        return mH.obtainMessage(what, 0, 0, arg1);
618    }
619
620    Message obtainMessageISC(int what, int arg1, int seq, IInputContextCallback callback) {
621        SomeArgs args = new SomeArgs();
622        args.callback = callback;
623        args.seq = seq;
624        return mH.obtainMessage(what, arg1, 0, args);
625    }
626
627    Message obtainMessageIISC(int what, int arg1, int arg2, int seq, IInputContextCallback callback) {
628        SomeArgs args = new SomeArgs();
629        args.callback = callback;
630        args.seq = seq;
631        return mH.obtainMessage(what, arg1, arg2, args);
632    }
633
634    Message obtainMessageIOOSC(int what, int arg1, Object objArg1, Object objArg2, int seq,
635            IInputContextCallback callback) {
636        SomeArgs args = new SomeArgs();
637        args.arg1 = objArg1;
638        args.arg2 = objArg2;
639        args.callback = callback;
640        args.seq = seq;
641        return mH.obtainMessage(what, arg1, 0, args);
642    }
643
644    Message obtainMessageIOSC(int what, int arg1, Object arg2, int seq,
645            IInputContextCallback callback) {
646        SomeArgs args = new SomeArgs();
647        args.arg1 = arg2;
648        args.callback = callback;
649        args.seq = seq;
650        return mH.obtainMessage(what, arg1, 0, args);
651    }
652
653    Message obtainMessageIO(int what, int arg1, Object arg2) {
654        return mH.obtainMessage(what, arg1, 0, arg2);
655    }
656
657    Message obtainMessageOO(int what, Object arg1, Object arg2) {
658        SomeArgs args = new SomeArgs();
659        args.arg1 = arg1;
660        args.arg2 = arg2;
661        return mH.obtainMessage(what, 0, 0, args);
662    }
663}
664