OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / core / java / com / android / internal / view / IInputConnectionWrapper.java
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 }