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