1 /* 2 * Copyright (C) 2020 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 android.car.input; 18 19 import static android.car.CarOccupantZoneManager.DisplayTypeEnum; 20 21 import android.annotation.CallbackExecutor; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.RequiresPermission; 25 import android.annotation.SystemApi; 26 import android.car.Car; 27 import android.car.CarManagerBase; 28 import android.car.CarOccupantZoneManager; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.RemoteException; 32 import android.util.Slog; 33 import android.util.SparseArray; 34 import android.view.KeyEvent; 35 36 import com.android.internal.annotations.GuardedBy; 37 38 import java.lang.annotation.ElementType; 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 import java.lang.annotation.Target; 42 import java.lang.ref.WeakReference; 43 import java.util.List; 44 import java.util.Objects; 45 import java.util.concurrent.Executor; 46 47 /** 48 * This API allows capturing selected input events. 49 * 50 * @hide 51 */ 52 @SystemApi 53 public final class CarInputManager extends CarManagerBase { 54 55 private static final String TAG = CarInputManager.class.getSimpleName(); 56 57 private static final boolean DEBUG = false; 58 59 /** 60 * Callback for capturing input events. 61 * <p> 62 * Events (key, rotary and custom input events) are associated with display types. 63 * Display types are defined in {@link android.car.CarOccupantZoneManager}. This manager only 64 * accepts the driver display types ({@link CarOccupantZoneManager#DISPLAY_TYPE_MAIN} and 65 * {@link CarOccupantZoneManager#DISPLAY_TYPE_INSTRUMENT_CLUSTER}). 66 */ 67 public interface CarInputCaptureCallback { 68 /** 69 * Key events were captured. 70 * 71 * @param targetDisplayType the display type associated with the events passed as parameter 72 * @param keyEvents the key events to process 73 */ onKeyEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<KeyEvent> keyEvents)74 default void onKeyEvents(@DisplayTypeEnum int targetDisplayType, 75 @NonNull List<KeyEvent> keyEvents) {} 76 77 /** 78 * Rotary events were captured. 79 * 80 * @param targetDisplayType the display type associated with the events passed as parameter 81 * @param events the rotary events to process 82 */ onRotaryEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<RotaryEvent> events)83 default void onRotaryEvents(@DisplayTypeEnum int targetDisplayType, 84 @NonNull List<RotaryEvent> events) {} 85 86 /** 87 * Capture state for the display has changed due to other client making requests or 88 * releasing capture. Client should check {@code activeInputTypes} for which input types 89 * are currently captured. 90 * 91 * @param targetDisplayType the display type associated with the events passed as parameter 92 * @param activeInputTypes the input types to watch 93 */ onCaptureStateChanged(@isplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] activeInputTypes)94 default void onCaptureStateChanged(@DisplayTypeEnum int targetDisplayType, 95 @NonNull @InputTypeEnum int[] activeInputTypes) {} 96 97 /** 98 * Custom input events were captured. 99 * 100 * @param targetDisplayType the display type associated with the events passed as parameter 101 * @param events the custom input events to process 102 */ onCustomInputEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<CustomInputEvent> events)103 default void onCustomInputEvents(@DisplayTypeEnum int targetDisplayType, 104 @NonNull List<CustomInputEvent> events) {} 105 } 106 107 /** 108 * Client will wait for grant if the request is failing due to higher priority client. 109 */ 110 public static final int CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT = 0x1; 111 112 /** 113 * Client wants to capture the keys for the whole display. This is only allowed to system 114 * process. 115 * 116 * @hide 117 */ 118 public static final int CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY = 0x2; 119 120 /** @hide */ 121 @IntDef(flag = true, prefix = {"CAPTURE_REQ_FLAGS_"}, value = { 122 CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT, 123 CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, 124 }) 125 @Retention(RetentionPolicy.SOURCE) 126 public @interface CaptureRequestFlags {} 127 128 /** 129 * This is special type to cover all INPUT_TYPE_*. This is used for clients using 130 * {@link #CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY} flag. 131 * 132 * @hide 133 */ 134 public static final int INPUT_TYPE_ALL_INPUTS = 1; 135 136 /** 137 * This covers rotary input device for navigation. 138 */ 139 public static final int INPUT_TYPE_ROTARY_NAVIGATION = 10; 140 141 /** 142 * Volume knob. 143 * TODO (b/151666020): This will be only allowed to system apps later. 144 * 145 * @hide 146 */ 147 public static final int INPUT_TYPE_ROTARY_VOLUME = 11; 148 149 /** 150 * This is the group of keys for DPAD. 151 * Included key events are: {@link KeyEvent#KEYCODE_DPAD_UP}, 152 * {@link KeyEvent#KEYCODE_DPAD_DOWN}, {@link KeyEvent#KEYCODE_DPAD_LEFT}, 153 * {@link KeyEvent#KEYCODE_DPAD_RIGHT}, {@link KeyEvent#KEYCODE_DPAD_CENTER}, 154 * {@link KeyEvent#KEYCODE_DPAD_DOWN_LEFT}, {@link KeyEvent#KEYCODE_DPAD_DOWN_RIGHT}, 155 * {@link KeyEvent#KEYCODE_DPAD_UP_LEFT}, {@link KeyEvent#KEYCODE_DPAD_UP_RIGHT} 156 * 157 * @hide 158 */ 159 public static final int INPUT_TYPE_DPAD_KEYS = 100; 160 161 /** 162 * This is for all {@code KeyEvent#KEYCODE_NAVIGATE_*} keys. 163 * 164 * @hide 165 */ 166 public static final int INPUT_TYPE_NAVIGATE_KEYS = 101; 167 168 /** 169 * This is for all {@code KeyEvent#KEYCODE_SYSTEM_NAVIGATE_*} keys. 170 * 171 * @hide 172 */ 173 public static final int INPUT_TYPE_SYSTEM_NAVIGATE_KEYS = 102; 174 175 /** 176 * This is for {@code HW_CUSTOM_INPUT} events. 177 */ 178 public static final int INPUT_TYPE_CUSTOM_INPUT_EVENT = 200; 179 180 /** @hide */ 181 @Retention(RetentionPolicy.SOURCE) 182 @IntDef(prefix = "INPUT_TYPE_", value = { 183 INPUT_TYPE_ALL_INPUTS, 184 INPUT_TYPE_ROTARY_NAVIGATION, 185 INPUT_TYPE_ROTARY_VOLUME, 186 INPUT_TYPE_DPAD_KEYS, 187 INPUT_TYPE_NAVIGATE_KEYS, 188 INPUT_TYPE_SYSTEM_NAVIGATE_KEYS, 189 INPUT_TYPE_CUSTOM_INPUT_EVENT, 190 }) 191 @Target({ElementType.TYPE_USE}) 192 public @interface InputTypeEnum {} 193 194 /** 195 * The client's request has succeeded and capture will start. 196 */ 197 public static final int INPUT_CAPTURE_RESPONSE_SUCCEEDED = 0; 198 199 /** 200 * The client's request has failed due to higher priority client already capturing. If priority 201 * for the clients are the same, last client making request will be allowed to capture. 202 */ 203 public static final int INPUT_CAPTURE_RESPONSE_FAILED = 1; 204 205 /** 206 * This is used when client has set {@link #CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT} in 207 * {@code requestFlags} and capturing is blocked due to existing higher priority client. 208 * When the higher priority client stops capturing, this client can capture events after 209 * getting @link CarInputCaptureCallback#onCaptureStateChanged(int, int[])} call. 210 */ 211 public static final int INPUT_CAPTURE_RESPONSE_DELAYED = 2; 212 213 /** @hide */ 214 @Retention(RetentionPolicy.SOURCE) 215 @IntDef(prefix = "INPUT_CAPTURE_RESPONSE_", value = { 216 INPUT_CAPTURE_RESPONSE_SUCCEEDED, 217 INPUT_CAPTURE_RESPONSE_FAILED, 218 INPUT_CAPTURE_RESPONSE_DELAYED 219 }) 220 @Target({ElementType.TYPE_USE}) 221 public @interface InputCaptureResponseEnum {} 222 223 private final ICarInput mService; 224 private final ICarInputCallback mServiceCallback = new ICarInputCallbackImpl(this); 225 226 private final Object mLock = new Object(); 227 228 @GuardedBy("mLock") 229 private final SparseArray<CallbackHolder> mCarInputCaptureCallbacks = new SparseArray<>(1); 230 231 /** 232 * @hide 233 */ CarInputManager(Car car, IBinder service)234 public CarInputManager(Car car, IBinder service) { 235 super(car); 236 mService = ICarInput.Stub.asInterface(service); 237 } 238 239 /** 240 * Requests capturing of input event for the specified display for all requested input types. 241 * 242 * <p>The request can fail if a high priority client is holding it. The client can set 243 * {@link #CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT} in {@code requestFlags} to wait for the 244 * current high priority client to release it. 245 * 246 * <p>If only some of the input types specified are available, the request will either: 247 * <ul> 248 * <li>fail, returning {@link #INPUT_CAPTURE_RESPONSE_FAILED}, or 249 * <li>be deferred, returning {@link #INPUT_CAPTURE_RESPONSE_DELAYED}, if the 250 * {@link #CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT} flag is used. 251 * </ul> 252 * 253 * <p> After {@link #INPUT_CAPTURE_RESPONSE_DELAYED} is returned, no input types are captured 254 * until the client receives a {@link CarInputCaptureCallback#onCaptureStateChanged(int, int[])} 255 * call with valid input types. 256 * 257 * <p> The targetDisplayType parameter must only contain driver display types (which are 258 * {@link CarOccupantZoneManager#DISPLAY_TYPE_MAIN} and 259 * {@link CarOccupantZoneManager#DISPLAY_TYPE_INSTRUMENT_CLUSTER}. 260 * 261 * <p>Callbacks are grouped and stacked per display types. Only the most recently 262 * registered callback will receive the incoming events for the associated display and input 263 * types. For instance, if two callbacks are registered against the same display type, on the 264 * same {@link CarInputManager} instance, then only the last registered callback will receive 265 * events, even if they were registered for different input event types. 266 * 267 * @throws SecurityException is caller doesn't have android.car.permission.CAR_MONITOR_INPUT 268 * permission granted 269 * @throws IllegalArgumentException if targetDisplayType parameter correspond to a non supported 270 * display type 271 * @throws IllegalArgumentException if inputTypes parameter contains invalid or non supported 272 * values 273 * @param targetDisplayType the display type to register callback for 274 * @param inputTypes the input type to register callback for 275 * @param requestFlags the capture request flag 276 * @param callback the callback to receive the input events 277 * @return the input capture response indicating if registration succeed, failed or delayed 278 */ 279 @RequiresPermission(Car.PERMISSION_CAR_MONITOR_INPUT) 280 @InputCaptureResponseEnum requestInputEventCapture(@isplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] inputTypes, @CaptureRequestFlags int requestFlags, @NonNull CarInputCaptureCallback callback)281 public int requestInputEventCapture(@DisplayTypeEnum int targetDisplayType, 282 @NonNull @InputTypeEnum int[] inputTypes, 283 @CaptureRequestFlags int requestFlags, 284 @NonNull CarInputCaptureCallback callback) { 285 Handler handler = getEventHandler(); 286 return requestInputEventCapture(targetDisplayType, inputTypes, requestFlags, handler::post, 287 callback); 288 } 289 290 /** 291 * Works just like {@link CarInputManager#requestInputEventCapture(int, int[], int, 292 * CarInputCaptureCallback)} except that callbacks are invoked using 293 * the executor passed as parameter. 294 * 295 * @param targetDisplayType the display type to register callback against 296 * @param inputTypes the input type to register callback against 297 * @param requestFlags the capture request flag 298 * @param executor {@link Executor} to handle the callbacks 299 * @param callback the callback to receive the input events 300 * @return the input capture response indicating if registration succeed, failed or delayed 301 * @see CarInputManager#requestInputEventCapture(int, int[], int, CarInputCaptureCallback) 302 */ 303 @RequiresPermission(android.Manifest.permission.MONITOR_INPUT) 304 @InputCaptureResponseEnum requestInputEventCapture(@isplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] inputTypes, @CaptureRequestFlags int requestFlags, @NonNull @CallbackExecutor Executor executor, @NonNull CarInputCaptureCallback callback)305 public int requestInputEventCapture(@DisplayTypeEnum int targetDisplayType, 306 @NonNull @InputTypeEnum int[] inputTypes, 307 @CaptureRequestFlags int requestFlags, 308 @NonNull @CallbackExecutor Executor executor, 309 @NonNull CarInputCaptureCallback callback) { 310 Objects.requireNonNull(executor); 311 Objects.requireNonNull(callback); 312 313 synchronized (mLock) { 314 mCarInputCaptureCallbacks.put(targetDisplayType, 315 new CallbackHolder(callback, executor)); 316 } 317 try { 318 return mService.requestInputEventCapture(mServiceCallback, targetDisplayType, 319 inputTypes, requestFlags); 320 } catch (RemoteException e) { 321 return handleRemoteExceptionFromCarService(e, INPUT_CAPTURE_RESPONSE_FAILED); 322 } 323 } 324 325 /** 326 * Stops capturing of given display. 327 */ releaseInputEventCapture(@isplayTypeEnum int targetDisplayType)328 public void releaseInputEventCapture(@DisplayTypeEnum int targetDisplayType) { 329 CallbackHolder callbackHolder; 330 synchronized (mLock) { 331 callbackHolder = mCarInputCaptureCallbacks.removeReturnOld(targetDisplayType); 332 } 333 if (callbackHolder == null) { 334 return; 335 } 336 try { 337 mService.releaseInputEventCapture(mServiceCallback, targetDisplayType); 338 } catch (RemoteException e) { 339 // ignore 340 } 341 } 342 343 /** 344 * Injects the {@link KeyEvent} passed as parameter against Car Input API. 345 * <p> 346 * The event parameter display id will be overridden accordingly to the display type also passed 347 * as parameter. 348 * 349 * @param event the key event to inject 350 * @param targetDisplayType the display type associated with the key event 351 * @throws RemoteException in case of failure when invoking car input service 352 */ 353 @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) injectKeyEvent(@onNull KeyEvent event, @DisplayTypeEnum int targetDisplayType)354 public void injectKeyEvent(@NonNull KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 355 try { 356 mService.injectKeyEvent(event, targetDisplayType); 357 } catch (RemoteException e) { 358 e.rethrowFromSystemServer(); 359 } 360 } 361 362 /** @hide */ 363 @Override onCarDisconnected()364 protected void onCarDisconnected() { 365 synchronized (mLock) { 366 mCarInputCaptureCallbacks.clear(); 367 } 368 } 369 getCallback(@isplayTypeEnum int targetDisplayType)370 private CallbackHolder getCallback(@DisplayTypeEnum int targetDisplayType) { 371 synchronized (mLock) { 372 return mCarInputCaptureCallbacks.get(targetDisplayType); 373 } 374 } 375 dispatchKeyEvents(@isplayTypeEnum int targetDisplayType, List<KeyEvent> keyEvents)376 private void dispatchKeyEvents(@DisplayTypeEnum int targetDisplayType, 377 List<KeyEvent> keyEvents) { 378 CallbackHolder callbackHolder = getCallback(targetDisplayType); 379 if (callbackHolder == null) { 380 return; 381 } 382 callbackHolder.mExecutor.execute(() -> { 383 callbackHolder.mCallback.onKeyEvents(targetDisplayType, keyEvents); 384 }); 385 } 386 dispatchRotaryEvents(@isplayTypeEnum int targetDisplayType, List<RotaryEvent> events)387 private void dispatchRotaryEvents(@DisplayTypeEnum int targetDisplayType, 388 List<RotaryEvent> events) { 389 CallbackHolder callbackHolder = getCallback(targetDisplayType); 390 if (callbackHolder == null) { 391 return; 392 } 393 callbackHolder.mExecutor.execute(() -> { 394 callbackHolder.mCallback.onRotaryEvents(targetDisplayType, events); 395 }); 396 } 397 dispatchOnCaptureStateChanged(@isplayTypeEnum int targetDisplayType, int[] activeInputTypes)398 private void dispatchOnCaptureStateChanged(@DisplayTypeEnum int targetDisplayType, 399 int[] activeInputTypes) { 400 CallbackHolder callbackHolder = getCallback(targetDisplayType); 401 if (callbackHolder == null) { 402 return; 403 } 404 callbackHolder.mExecutor.execute(() -> { 405 callbackHolder.mCallback.onCaptureStateChanged(targetDisplayType, activeInputTypes); 406 }); 407 } 408 dispatchCustomInputEvents(@isplayTypeEnum int targetDisplayType, List<CustomInputEvent> events)409 private void dispatchCustomInputEvents(@DisplayTypeEnum int targetDisplayType, 410 List<CustomInputEvent> events) { 411 CallbackHolder callbackHolder = getCallback(targetDisplayType); 412 if (callbackHolder == null) { 413 return; 414 } 415 callbackHolder.mExecutor.execute(() -> { 416 if (DEBUG) { 417 Slog.d(TAG, "Firing events " + events + " on callback " + callbackHolder.mCallback); 418 } 419 callbackHolder.mCallback.onCustomInputEvents(targetDisplayType, events); 420 }); 421 } 422 423 private static final class ICarInputCallbackImpl extends ICarInputCallback.Stub { 424 425 private final WeakReference<CarInputManager> mManager; 426 ICarInputCallbackImpl(CarInputManager manager)427 private ICarInputCallbackImpl(CarInputManager manager) { 428 mManager = new WeakReference<>(manager); 429 } 430 431 @Override onKeyEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<KeyEvent> keyEvents)432 public void onKeyEvents(@DisplayTypeEnum int targetDisplayType, 433 @NonNull List<KeyEvent> keyEvents) { 434 CarInputManager manager = mManager.get(); 435 if (manager == null) { 436 return; 437 } 438 manager.dispatchKeyEvents(targetDisplayType, keyEvents); 439 } 440 441 @Override onRotaryEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<RotaryEvent> events)442 public void onRotaryEvents(@DisplayTypeEnum int targetDisplayType, 443 @NonNull List<RotaryEvent> events) { 444 CarInputManager manager = mManager.get(); 445 if (manager == null) { 446 return; 447 } 448 manager.dispatchRotaryEvents(targetDisplayType, events); 449 } 450 451 @Override onCaptureStateChanged(@isplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] activeInputTypes)452 public void onCaptureStateChanged(@DisplayTypeEnum int targetDisplayType, 453 @NonNull @InputTypeEnum int[] activeInputTypes) { 454 CarInputManager manager = mManager.get(); 455 if (manager == null) { 456 return; 457 } 458 manager.dispatchOnCaptureStateChanged(targetDisplayType, activeInputTypes); 459 } 460 461 @Override onCustomInputEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<CustomInputEvent> events)462 public void onCustomInputEvents(@DisplayTypeEnum int targetDisplayType, 463 @NonNull List<CustomInputEvent> events) { 464 CarInputManager manager = mManager.get(); 465 if (manager == null) { 466 return; 467 } 468 manager.dispatchCustomInputEvents(targetDisplayType, events); 469 } 470 } 471 472 /** 473 * Class used to bind {@link CarInputCaptureCallback} and their associated {@link Executor}. 474 */ 475 private static final class CallbackHolder { 476 477 final CarInputCaptureCallback mCallback; 478 479 final Executor mExecutor; 480 CallbackHolder(CarInputCaptureCallback callback, Executor executor)481 CallbackHolder(CarInputCaptureCallback callback, Executor executor) { 482 mCallback = callback; 483 mExecutor = executor; 484 } 485 } 486 } 487