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