1 /*
2  * Copyright (C) 2007 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.inputmethod;
18 
19 import static java.lang.annotation.RetentionPolicy.SOURCE;
20 
21 import android.annotation.IntDef;
22 import android.content.ComponentName;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.os.IBinder;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.SparseArray;
29 import android.view.InputChannel;
30 
31 import java.lang.annotation.Retention;
32 
33 /**
34  * Bundle of information returned by input method manager about a successful
35  * binding to an input method.
36  */
37 public final class InputBindResult implements Parcelable {
38 
39     @Retention(SOURCE)
40     @IntDef({
41             ResultCode.SUCCESS_WITH_IME_SESSION,
42             ResultCode.SUCCESS_WAITING_IME_SESSION,
43             ResultCode.SUCCESS_WAITING_IME_BINDING,
44             ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
45             ResultCode.ERROR_NULL,
46             ResultCode.ERROR_NO_IME,
47             ResultCode.ERROR_INVALID_PACKAGE_NAME,
48             ResultCode.ERROR_SYSTEM_NOT_READY,
49             ResultCode.ERROR_IME_NOT_CONNECTED,
50             ResultCode.ERROR_INVALID_USER,
51             ResultCode.ERROR_NULL_EDITOR_INFO,
52             ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
53             ResultCode.ERROR_NO_EDITOR,
54             ResultCode.ERROR_DISPLAY_ID_MISMATCH,
55             ResultCode.ERROR_INVALID_DISPLAY_ID,
56             ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION
57     })
58     public @interface ResultCode {
59         /**
60          * Indicates that everything in this result object including {@link #method} is valid.
61          */
62         int SUCCESS_WITH_IME_SESSION = 0;
63         /**
64          * Indicates that this is a temporary binding until the
65          * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
66          * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS).
67          *
68          * <p>Note that in this state the IMS is already bound to IMMS but the logical session
69          * is not yet established on top of the IPC channel.</p>
70          *
71          * <p>Some of fields such as {@link #channel} is not yet available.</p>
72          *
73          * @see android.inputmethodservice.InputMethodService#onCreateInputMethodSessionInterface()
74          **/
75         int SUCCESS_WAITING_IME_SESSION = 1;
76         /**
77          * Indicates that this is a temporary binding until the
78          * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
79          * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS).
80          *
81          * <p>Note that in this state the IMMS has already initiated a connection to the IMS but
82          * the binding process is not completed yet.</p>
83          *
84          * <p>Some of fields such as {@link #channel} is not yet available.</p>
85          * @see android.content.ServiceConnection#onServiceConnected(ComponentName, IBinder)
86          */
87         int SUCCESS_WAITING_IME_BINDING = 2;
88         /**
89          * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} has a
90          * pending operation to switch to a different user.
91          *
92          * <p>Note that in this state even what would be the next current IME is not determined.</p>
93          */
94         int SUCCESS_WAITING_USER_SWITCHING = 3;
95         /**
96          * Indicates that this is not intended for starting input but just for reporting window
97          * focus change from the application process.
98          *
99          * <p>All other fields do not have meaningful value.</p>
100          */
101         int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 4;
102         /**
103          * Indicates somehow
104          * {@link
105          * com.android.server.inputmethod.InputMethodManagerService#startInputOrWindowGainedFocus}
106          * is trying to return null {@link InputBindResult}, which must never happen.
107          */
108         int ERROR_NULL = 5;
109         /**
110          * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService}
111          * recognizes no IME.
112          */
113         int ERROR_NO_IME = 6;
114         /**
115          * Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match
116          * the caller UID.
117          *
118          * @see android.view.inputmethod.EditorInfo#packageName
119          */
120         int ERROR_INVALID_PACKAGE_NAME = 7;
121         /**
122          * Indicates that the system is still in an early stage of the boot process and any 3rd
123          * party application is not allowed to run.
124          *
125          * @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START
126          */
127         int ERROR_SYSTEM_NOT_READY = 8;
128         /**
129          * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to
130          * connect to an {@link android.inputmethodservice.InputMethodService} but failed.
131          *
132          * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int,
133          *      android.os.UserHandle)
134          */
135         int ERROR_IME_NOT_CONNECTED = 9;
136         /**
137          * Indicates that the caller is not the foreground user, does not have
138          * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission, or the user
139          * specified in {@link android.view.inputmethod.EditorInfo#targetInputMethodUser} is not
140          * running.
141          */
142         int ERROR_INVALID_USER = 10;
143         /**
144          * Indicates that the caller should have specified non-null
145          * {@link android.view.inputmethod.EditorInfo}.
146          */
147         int ERROR_NULL_EDITOR_INFO = 11;
148         /**
149          * Indicates that the target window the client specified cannot be the IME target right now.
150          *
151          * <p>Due to the asynchronous nature of Android OS, we cannot completely avoid this error.
152          * The client should try to restart input when its {@link android.view.Window} is focused
153          * again.</p>
154          *
155          * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int)
156          */
157         int ERROR_NOT_IME_TARGET_WINDOW = 12;
158         /**
159          * Indicates that focused view in the current window is not an editor.
160          */
161         int ERROR_NO_EDITOR = 13;
162         /**
163          * Indicates that there is a mismatch in display ID between IME client and focused Window.
164          */
165         int ERROR_DISPLAY_ID_MISMATCH = 14;
166         /**
167          * Indicates that current IME client is no longer allowed to access to the associated
168          * display.
169          */
170         int ERROR_INVALID_DISPLAY_ID = 15;
171         /**
172          * Indicates that a valid session is created and result is ready for accessibility.
173          */
174         int SUCCESS_WITH_ACCESSIBILITY_SESSION = 16;
175     }
176 
177     @ResultCode
178     public final int result;
179 
180     /**
181      * The input method service.
182      */
183     public final IInputMethodSession method;
184 
185     /**
186      * The accessibility services.
187      */
188     public final SparseArray<IAccessibilityInputMethodSession> accessibilitySessions;
189 
190     /**
191      * The input channel used to send input events to this IME.
192      */
193     public final InputChannel channel;
194 
195     /**
196      * The ID for this input method, as found in InputMethodInfo; null if
197      * no input method will be bound.
198      */
199     public final String id;
200 
201     /**
202      * Sequence number of this binding.
203      */
204     public final int sequence;
205 
206     /**
207      * {@code true} if the IME explicitly specifies {@code suppressesSpellChecker="true"}.
208      */
209     public final boolean isInputMethodSuppressingSpellChecker;
210 
211     /**
212      * Creates a new instance of {@link InputBindResult}.
213      *
214      * @param result A result code defined in {@link ResultCode}.
215      * @param method {@link IInputMethodSession} to interact with the IME.
216      * @param accessibilitySessions {@link IAccessibilityInputMethodSession} to interact with
217      *                              accessibility services.
218      * @param channel {@link InputChannel} to forward input events to the IME.
219      * @param id The {@link String} representations of the IME, which is the same as
220      *           {@link android.view.inputmethod.InputMethodInfo#getId()} and
221      *           {@link android.content.ComponentName#flattenToShortString()}.
222      * @param sequence A sequence number of this binding.
223      * @param isInputMethodSuppressingSpellChecker {@code true} if the IME explicitly specifies
224      *                                             {@code suppressesSpellChecker="true"}.
225      */
InputBindResult(@esultCode int result, IInputMethodSession method, SparseArray<IAccessibilityInputMethodSession> accessibilitySessions, InputChannel channel, String id, int sequence, boolean isInputMethodSuppressingSpellChecker)226     public InputBindResult(@ResultCode int result,
227             IInputMethodSession method,
228             SparseArray<IAccessibilityInputMethodSession> accessibilitySessions,
229             InputChannel channel, String id, int sequence,
230             boolean isInputMethodSuppressingSpellChecker) {
231         this.result = result;
232         this.method = method;
233         this.accessibilitySessions = accessibilitySessions;
234         this.channel = channel;
235         this.id = id;
236         this.sequence = sequence;
237         this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker;
238     }
239 
InputBindResult(Parcel source)240     private InputBindResult(Parcel source) {
241         result = source.readInt();
242         method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
243         int n = source.readInt();
244         if (n < 0) {
245             accessibilitySessions = null;
246         } else {
247             accessibilitySessions = new SparseArray<>(n);
248             while (n > 0) {
249                 int key = source.readInt();
250                 IAccessibilityInputMethodSession value =
251                         IAccessibilityInputMethodSession.Stub.asInterface(
252                                 source.readStrongBinder());
253                 accessibilitySessions.append(key, value);
254                 n--;
255             }
256         }
257         if (source.readInt() != 0) {
258             channel = InputChannel.CREATOR.createFromParcel(source);
259         } else {
260             channel = null;
261         }
262         id = source.readString();
263         sequence = source.readInt();
264         isInputMethodSuppressingSpellChecker = source.readBoolean();
265     }
266 
267     /**
268      * {@inheritDoc}
269      */
270     @Override
toString()271     public String toString() {
272         return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id
273                 + " sequence=" + sequence
274                 + " result=" + result
275                 + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
276                 + "}";
277     }
278 
279     /**
280      * {@inheritDoc}
281      */
282     @Override
writeToParcel(Parcel dest, int flags)283     public void writeToParcel(Parcel dest, int flags) {
284         dest.writeInt(result);
285         dest.writeStrongInterface(method);
286         if (accessibilitySessions == null) {
287             dest.writeInt(-1);
288         } else {
289             int n = accessibilitySessions.size();
290             dest.writeInt(n);
291             int i = 0;
292             while (i < n) {
293                 dest.writeInt(accessibilitySessions.keyAt(i));
294                 dest.writeStrongInterface(accessibilitySessions.valueAt(i));
295                 i++;
296             }
297         }
298         if (channel != null) {
299             dest.writeInt(1);
300             channel.writeToParcel(dest, flags);
301         } else {
302             dest.writeInt(0);
303         }
304         dest.writeString(id);
305         dest.writeInt(sequence);
306         dest.writeBoolean(isInputMethodSuppressingSpellChecker);
307     }
308 
309     /**
310      * Used to make this class parcelable.
311      */
312     public static final Parcelable.Creator<InputBindResult> CREATOR =
313             new Parcelable.Creator<InputBindResult>() {
314         @Override
315         public InputBindResult createFromParcel(Parcel source) {
316             return new InputBindResult(source);
317         }
318 
319         @Override
320         public InputBindResult[] newArray(int size) {
321             return new InputBindResult[size];
322         }
323     };
324 
325     /**
326      * {@inheritDoc}
327      */
328     @Override
describeContents()329     public int describeContents() {
330         return channel != null ? channel.describeContents() : 0;
331     }
332 
getResultString()333     private String getResultString() {
334         switch (result) {
335             case ResultCode.SUCCESS_WITH_IME_SESSION:
336                 return "SUCCESS_WITH_IME_SESSION";
337             case ResultCode.SUCCESS_WAITING_IME_SESSION:
338                 return "SUCCESS_WAITING_IME_SESSION";
339             case ResultCode.SUCCESS_WAITING_IME_BINDING:
340                 return "SUCCESS_WAITING_IME_BINDING";
341             case ResultCode.SUCCESS_WAITING_USER_SWITCHING:
342                 return "SUCCESS_WAITING_USER_SWITCHING";
343             case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY:
344                 return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY";
345             case ResultCode.ERROR_NULL:
346                 return "ERROR_NULL";
347             case ResultCode.ERROR_NO_IME:
348                 return "ERROR_NO_IME";
349             case ResultCode.ERROR_NO_EDITOR:
350                 return "ERROR_NO_EDITOR";
351             case ResultCode.ERROR_INVALID_PACKAGE_NAME:
352                 return "ERROR_INVALID_PACKAGE_NAME";
353             case ResultCode.ERROR_SYSTEM_NOT_READY:
354                 return "ERROR_SYSTEM_NOT_READY";
355             case ResultCode.ERROR_IME_NOT_CONNECTED:
356                 return "ERROR_IME_NOT_CONNECTED";
357             case ResultCode.ERROR_INVALID_USER:
358                 return "ERROR_INVALID_USER";
359             case ResultCode.ERROR_NULL_EDITOR_INFO:
360                 return "ERROR_NULL_EDITOR_INFO";
361             case ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
362                 return "ERROR_NOT_IME_TARGET_WINDOW";
363             case ResultCode.ERROR_DISPLAY_ID_MISMATCH:
364                 return "ERROR_DISPLAY_ID_MISMATCH";
365             case ResultCode.ERROR_INVALID_DISPLAY_ID:
366                 return "ERROR_INVALID_DISPLAY_ID";
367             case ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION:
368                 return "SUCCESS_WITH_ACCESSIBILITY_SESSION";
369             default:
370                 return "Unknown(" + result + ")";
371         }
372     }
373 
error(@esultCode int result)374     private static InputBindResult error(@ResultCode int result) {
375         return new InputBindResult(result, null, null, null, null, -1, false);
376     }
377 
378     /**
379      * Predefined error object for {@link ResultCode#ERROR_NULL}.
380      */
381     public static final InputBindResult NULL = error(ResultCode.ERROR_NULL);
382     /**
383      * Predefined error object for {@link ResultCode#NO_IME}.
384      */
385     public static final InputBindResult NO_IME = error(ResultCode.ERROR_NO_IME);
386     /**
387      * Predefined error object for {@link ResultCode#NO_EDITOR}.
388      */
389     public static final InputBindResult NO_EDITOR = error(ResultCode.ERROR_NO_EDITOR);
390     /**
391      * Predefined error object for {@link ResultCode#ERROR_INVALID_PACKAGE_NAME}.
392      */
393     public static final InputBindResult INVALID_PACKAGE_NAME =
394             error(ResultCode.ERROR_INVALID_PACKAGE_NAME);
395     /**
396      * Predefined error object for {@link ResultCode#ERROR_NULL_EDITOR_INFO}.
397      */
398     public static final InputBindResult NULL_EDITOR_INFO = error(ResultCode.ERROR_NULL_EDITOR_INFO);
399     /**
400      * Predefined error object for {@link ResultCode#ERROR_NOT_IME_TARGET_WINDOW}.
401      */
402     public static final InputBindResult NOT_IME_TARGET_WINDOW =
403             error(ResultCode.ERROR_NOT_IME_TARGET_WINDOW);
404     /**
405      * Predefined error object for {@link ResultCode#ERROR_IME_NOT_CONNECTED}.
406      */
407     public static final InputBindResult IME_NOT_CONNECTED =
408             error(ResultCode.ERROR_IME_NOT_CONNECTED);
409     /**
410      * Predefined error object for {@link ResultCode#ERROR_INVALID_USER}.
411      */
412     public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER);
413 
414     /**
415      * Predefined error object for {@link ResultCode#ERROR_DISPLAY_ID_MISMATCH}.
416      */
417     public static final InputBindResult DISPLAY_ID_MISMATCH =
418             error(ResultCode.ERROR_DISPLAY_ID_MISMATCH);
419 
420     /**
421      * Predefined error object for {@link ResultCode#ERROR_INVALID_DISPLAY_ID}.
422      */
423     public static final InputBindResult INVALID_DISPLAY_ID =
424             error(ResultCode.ERROR_INVALID_DISPLAY_ID);
425 
426     /**
427      * Predefined <strong>success</strong> object for
428      * {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}.
429      */
430     public static final InputBindResult USER_SWITCHING =
431             error(ResultCode.SUCCESS_WAITING_USER_SWITCHING);
432 }
433