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