1 /*
2  * Copyright (C) 2007-2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.internal.view;
18 
19 import static java.lang.annotation.RetentionPolicy.SOURCE;
20 
21 import android.annotation.IntDef;
22 import android.annotation.Nullable;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.content.ComponentName;
25 import android.content.Intent;
26 import android.content.ServiceConnection;
27 import android.graphics.Matrix;
28 import android.os.IBinder;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.os.UserHandle;
32 import android.view.InputChannel;
33 
34 import java.lang.annotation.Retention;
35 
36 /**
37  * Bundle of information returned by input method manager about a successful
38  * binding to an input method.
39  */
40 public final class InputBindResult implements Parcelable {
41 
42     @Retention(SOURCE)
43     @IntDef({
44             ResultCode.SUCCESS_WITH_IME_SESSION,
45             ResultCode.SUCCESS_WAITING_IME_SESSION,
46             ResultCode.SUCCESS_WAITING_IME_BINDING,
47             ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
48             ResultCode.ERROR_NULL,
49             ResultCode.ERROR_NO_IME,
50             ResultCode.ERROR_INVALID_PACKAGE_NAME,
51             ResultCode.ERROR_SYSTEM_NOT_READY,
52             ResultCode.ERROR_IME_NOT_CONNECTED,
53             ResultCode.ERROR_INVALID_USER,
54             ResultCode.ERROR_NULL_EDITOR_INFO,
55             ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
56             ResultCode.ERROR_NO_EDITOR,
57             ResultCode.ERROR_DISPLAY_ID_MISMATCH,
58             ResultCode.ERROR_INVALID_DISPLAY_ID,
59             ResultCode.ERROR_INVALID_CLIENT,
60     })
61     public @interface ResultCode {
62         /**
63          * Indicates that everything in this result object including {@link #method} is valid.
64          */
65         int SUCCESS_WITH_IME_SESSION = 0;
66         /**
67          * Indicates that this is a temporary binding until the
68          * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
69          * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS).
70          *
71          * <p>Note that in this state the IMS is already bound to IMMS but the logical session
72          * is not yet established on top of the IPC channel.</p>
73          *
74          * <p>Some of fields such as {@link #channel} is not yet available.</p>
75          *
76          * @see android.inputmethodservice.InputMethodService##onCreateInputMethodSessionInterface()
77          **/
78         int SUCCESS_WAITING_IME_SESSION = 1;
79         /**
80          * Indicates that this is a temporary binding until the
81          * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session
82          * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS).
83          *
84          * <p>Note that in this state the IMMS has already initiated a connection to the IMS but
85          * the binding process is not completed yet.</p>
86          *
87          * <p>Some of fields such as {@link #channel} is not yet available.</p>
88          * @see android.content.ServiceConnection#onServiceConnected(ComponentName, IBinder)
89          */
90         int SUCCESS_WAITING_IME_BINDING = 2;
91         /**
92          * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} has a
93          * pending operation to switch to a different user.
94          *
95          * <p>Note that in this state even what would be the next current IME is not determined.</p>
96          */
97         int SUCCESS_WAITING_USER_SWITCHING = 3;
98         /**
99          * Indicates that this is not intended for starting input but just for reporting window
100          * focus change from the application process.
101          *
102          * <p>All other fields do not have meaningful value.</p>
103          */
104         int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 4;
105         /**
106          * Indicates somehow
107          * {@link
108          * com.android.server.inputmethod.InputMethodManagerService#startInputOrWindowGainedFocus}
109          * is trying to return null {@link InputBindResult}, which must never happen.
110          */
111         int ERROR_NULL = 5;
112         /**
113          * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService}
114          * recognizes no IME.
115          */
116         int ERROR_NO_IME = 6;
117         /**
118          * Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match
119          * the caller UID.
120          *
121          * @see android.view.inputmethod.EditorInfo#packageName
122          */
123         int ERROR_INVALID_PACKAGE_NAME = 7;
124         /**
125          * Indicates that the system is still in an early stage of the boot process and any 3rd
126          * party application is not allowed to run.
127          *
128          * @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START
129          */
130         int ERROR_SYSTEM_NOT_READY = 8;
131         /**
132          * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to
133          * connect to an {@link android.inputmethodservice.InputMethodService} but failed.
134          *
135          * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, 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 the client is not recognized by the system.
175          */
176         int ERROR_INVALID_CLIENT = 16;
177     }
178 
179     @ResultCode
180     public final int result;
181 
182     /**
183      * The input method service.
184      */
185     @UnsupportedAppUsage
186     public final IInputMethodSession method;
187 
188     /**
189      * The input channel used to send input events to this IME.
190      */
191     public final InputChannel channel;
192 
193     /**
194      * The ID for this input method, as found in InputMethodInfo; null if
195      * no input method will be bound.
196      */
197     public final String id;
198 
199     /**
200      * Sequence number of this binding.
201      */
202     public final int sequence;
203 
204     @Nullable
205     private final float[] mActivityViewToScreenMatrixValues;
206 
207     /**
208      * @return {@link Matrix} that corresponds to {@link #mActivityViewToScreenMatrixValues}.
209      *         {@code null} if {@link #mActivityViewToScreenMatrixValues} is {@code null}.
210      */
211     @Nullable
getActivityViewToScreenMatrix()212     public Matrix getActivityViewToScreenMatrix() {
213         if (mActivityViewToScreenMatrixValues == null) {
214             return null;
215         }
216         final Matrix matrix = new Matrix();
217         matrix.setValues(mActivityViewToScreenMatrixValues);
218         return matrix;
219     }
220 
InputBindResult(@esultCode int _result, IInputMethodSession _method, InputChannel _channel, String _id, int _sequence, @Nullable Matrix activityViewToScreenMatrix)221     public InputBindResult(@ResultCode int _result,
222             IInputMethodSession _method, InputChannel _channel, String _id, int _sequence,
223             @Nullable Matrix activityViewToScreenMatrix) {
224         result = _result;
225         method = _method;
226         channel = _channel;
227         id = _id;
228         sequence = _sequence;
229         if (activityViewToScreenMatrix == null) {
230             mActivityViewToScreenMatrixValues = null;
231         } else {
232             mActivityViewToScreenMatrixValues = new float[9];
233             activityViewToScreenMatrix.getValues(mActivityViewToScreenMatrixValues);
234         }
235     }
236 
InputBindResult(Parcel source)237     InputBindResult(Parcel source) {
238         result = source.readInt();
239         method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
240         if (source.readInt() != 0) {
241             channel = InputChannel.CREATOR.createFromParcel(source);
242         } else {
243             channel = null;
244         }
245         id = source.readString();
246         sequence = source.readInt();
247         mActivityViewToScreenMatrixValues = source.createFloatArray();
248     }
249 
250     @Override
toString()251     public String toString() {
252         return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
253                 + " sequence=" + sequence
254                 + " activityViewToScreenMatrix=" + getActivityViewToScreenMatrix()
255                 + "}";
256     }
257 
258     /**
259      * Used to package this object into a {@link Parcel}.
260      *
261      * @param dest The {@link Parcel} to be written.
262      * @param flags The flags used for parceling.
263      */
264     @Override
writeToParcel(Parcel dest, int flags)265     public void writeToParcel(Parcel dest, int flags) {
266         dest.writeInt(result);
267         dest.writeStrongInterface(method);
268         if (channel != null) {
269             dest.writeInt(1);
270             channel.writeToParcel(dest, flags);
271         } else {
272             dest.writeInt(0);
273         }
274         dest.writeString(id);
275         dest.writeInt(sequence);
276         dest.writeFloatArray(mActivityViewToScreenMatrixValues);
277     }
278 
279     /**
280      * Used to make this class parcelable.
281      */
282     @UnsupportedAppUsage
283     public static final Parcelable.Creator<InputBindResult> CREATOR =
284             new Parcelable.Creator<InputBindResult>() {
285         @Override
286         public InputBindResult createFromParcel(Parcel source) {
287             return new InputBindResult(source);
288         }
289 
290         @Override
291         public InputBindResult[] newArray(int size) {
292             return new InputBindResult[size];
293         }
294     };
295 
296     @Override
describeContents()297     public int describeContents() {
298         return channel != null ? channel.describeContents() : 0;
299     }
300 
getResultString()301     public String getResultString() {
302         switch (result) {
303             case ResultCode.SUCCESS_WITH_IME_SESSION:
304                 return "SUCCESS_WITH_IME_SESSION";
305             case ResultCode.SUCCESS_WAITING_IME_SESSION:
306                 return "SUCCESS_WAITING_IME_SESSION";
307             case ResultCode.SUCCESS_WAITING_IME_BINDING:
308                 return "SUCCESS_WAITING_IME_BINDING";
309             case ResultCode.SUCCESS_WAITING_USER_SWITCHING:
310                 return "SUCCESS_WAITING_USER_SWITCHING";
311             case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY:
312                 return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY";
313             case ResultCode.ERROR_NULL:
314                 return "ERROR_NULL";
315             case ResultCode.ERROR_NO_IME:
316                 return "ERROR_NO_IME";
317             case ResultCode.ERROR_NO_EDITOR:
318                 return "ERROR_NO_EDITOR";
319             case ResultCode.ERROR_INVALID_PACKAGE_NAME:
320                 return "ERROR_INVALID_PACKAGE_NAME";
321             case ResultCode.ERROR_SYSTEM_NOT_READY:
322                 return "ERROR_SYSTEM_NOT_READY";
323             case ResultCode.ERROR_IME_NOT_CONNECTED:
324                 return "ERROR_IME_NOT_CONNECTED";
325             case ResultCode.ERROR_INVALID_USER:
326                 return "ERROR_INVALID_USER";
327             case ResultCode.ERROR_NULL_EDITOR_INFO:
328                 return "ERROR_NULL_EDITOR_INFO";
329             case ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
330                 return "ERROR_NOT_IME_TARGET_WINDOW";
331             case ResultCode.ERROR_DISPLAY_ID_MISMATCH:
332                 return "ERROR_DISPLAY_ID_MISMATCH";
333             case ResultCode.ERROR_INVALID_DISPLAY_ID:
334                 return "ERROR_INVALID_DISPLAY_ID";
335             case ResultCode.ERROR_INVALID_CLIENT:
336                 return "ERROR_INVALID_CLIENT";
337             default:
338                 return "Unknown(" + result + ")";
339         }
340     }
341 
error(@esultCode int result)342     private static InputBindResult error(@ResultCode int result) {
343         return new InputBindResult(result, null, null, null, -1, null);
344     }
345 
346     /**
347      * Predefined error object for {@link ResultCode#ERROR_NULL}.
348      */
349     public static final InputBindResult NULL = error(ResultCode.ERROR_NULL);
350     /**
351      * Predefined error object for {@link ResultCode#NO_IME}.
352      */
353     public static final InputBindResult NO_IME = error(ResultCode.ERROR_NO_IME);
354     /**
355      * Predefined error object for {@link ResultCode#NO_EDITOR}.
356      */
357     public static final InputBindResult NO_EDITOR = error(ResultCode.ERROR_NO_EDITOR);
358     /**
359      * Predefined error object for {@link ResultCode#ERROR_INVALID_PACKAGE_NAME}.
360      */
361     public static final InputBindResult INVALID_PACKAGE_NAME =
362             error(ResultCode.ERROR_INVALID_PACKAGE_NAME);
363     /**
364      * Predefined error object for {@link ResultCode#ERROR_NULL_EDITOR_INFO}.
365      */
366     public static final InputBindResult NULL_EDITOR_INFO = error(ResultCode.ERROR_NULL_EDITOR_INFO);
367     /**
368      * Predefined error object for {@link ResultCode#ERROR_NOT_IME_TARGET_WINDOW}.
369      */
370     public static final InputBindResult NOT_IME_TARGET_WINDOW =
371             error(ResultCode.ERROR_NOT_IME_TARGET_WINDOW);
372     /**
373      * Predefined error object for {@link ResultCode#ERROR_IME_NOT_CONNECTED}.
374      */
375     public static final InputBindResult IME_NOT_CONNECTED =
376             error(ResultCode.ERROR_IME_NOT_CONNECTED);
377     /**
378      * Predefined error object for {@link ResultCode#ERROR_INVALID_USER}.
379      */
380     public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER);
381 
382     /**
383      * Predefined error object for {@link ResultCode#ERROR_DISPLAY_ID_MISMATCH}.
384      */
385     public static final InputBindResult DISPLAY_ID_MISMATCH =
386             error(ResultCode.ERROR_DISPLAY_ID_MISMATCH);
387 
388     /**
389      * Predefined error object for {@link ResultCode#ERROR_INVALID_DISPLAY_ID}.
390      */
391     public static final InputBindResult INVALID_DISPLAY_ID =
392             error(ResultCode.ERROR_INVALID_DISPLAY_ID);
393 
394     /**
395      * Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}.
396      */
397     public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT);
398 
399     /**
400      * Predefined <strong>success</strong> object for
401      * {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}.
402      */
403     public static final InputBindResult USER_SWITCHING =
404             error(ResultCode.SUCCESS_WAITING_USER_SWITCHING);
405 }
406