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 and {@link KeyEvent#KEYCODE_BACK}. 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 if caller doesn't have 268 * {@code android.car.permission.CAR_MONITOR_INPUT} permission 269 * granted. Currently this method also accept 270 * {@code android.permission.MONITOR_INPUT} 271 * @throws IllegalArgumentException if targetDisplayType parameter correspond to a non supported 272 * display type 273 * @throws IllegalArgumentException if inputTypes parameter contains invalid or non supported 274 * values 275 * @param targetDisplayType the display type to register callback for 276 * @param inputTypes the input type to register callback for 277 * @param requestFlags the capture request flag 278 * @param callback the callback to receive the input events 279 * @return the input capture response indicating if registration succeed, failed or delayed 280 */ 281 @RequiresPermission(Car.PERMISSION_CAR_MONITOR_INPUT) 282 @InputCaptureResponseEnum requestInputEventCapture(@isplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] inputTypes, @CaptureRequestFlags int requestFlags, @NonNull CarInputCaptureCallback callback)283 public int requestInputEventCapture(@DisplayTypeEnum int targetDisplayType, 284 @NonNull @InputTypeEnum int[] inputTypes, 285 @CaptureRequestFlags int requestFlags, 286 @NonNull CarInputCaptureCallback callback) { 287 Handler handler = getEventHandler(); 288 return requestInputEventCapture(targetDisplayType, inputTypes, requestFlags, handler::post, 289 callback); 290 } 291 292 /** 293 * Works just like {@link CarInputManager#requestInputEventCapture(int, int[], int, 294 * CarInputCaptureCallback)} except that callbacks are invoked using 295 * the executor passed as parameter. 296 * 297 * @throws SecurityException if caller doesn't have 298 * {@code android.permission.MONITOR_INPUT} permission 299 * granted. Currently this method also accept 300 * {@code android.car.permission.CAR_MONITOR_INPUT} 301 * @param targetDisplayType the display type to register callback against 302 * @param inputTypes the input type to register callback against 303 * @param requestFlags the capture request flag 304 * @param executor {@link Executor} to handle the callbacks 305 * @param callback the callback to receive the input events 306 * @return the input capture response indicating if registration succeed, failed or delayed 307 * @see CarInputManager#requestInputEventCapture(int, int[], int, CarInputCaptureCallback) 308 */ 309 @RequiresPermission(android.Manifest.permission.MONITOR_INPUT) 310 @InputCaptureResponseEnum requestInputEventCapture(@isplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] inputTypes, @CaptureRequestFlags int requestFlags, @NonNull @CallbackExecutor Executor executor, @NonNull CarInputCaptureCallback callback)311 public int requestInputEventCapture(@DisplayTypeEnum int targetDisplayType, 312 @NonNull @InputTypeEnum int[] inputTypes, 313 @CaptureRequestFlags int requestFlags, 314 @NonNull @CallbackExecutor Executor executor, 315 @NonNull CarInputCaptureCallback callback) { 316 Objects.requireNonNull(executor); 317 Objects.requireNonNull(callback); 318 319 synchronized (mLock) { 320 mCarInputCaptureCallbacks.put(targetDisplayType, 321 new CallbackHolder(callback, executor)); 322 } 323 try { 324 return mService.requestInputEventCapture(mServiceCallback, targetDisplayType, 325 inputTypes, requestFlags); 326 } catch (RemoteException e) { 327 return handleRemoteExceptionFromCarService(e, INPUT_CAPTURE_RESPONSE_FAILED); 328 } 329 } 330 331 /** 332 * Stops capturing of given display. 333 */ releaseInputEventCapture(@isplayTypeEnum int targetDisplayType)334 public void releaseInputEventCapture(@DisplayTypeEnum int targetDisplayType) { 335 CallbackHolder callbackHolder; 336 synchronized (mLock) { 337 callbackHolder = mCarInputCaptureCallbacks.removeReturnOld(targetDisplayType); 338 } 339 if (callbackHolder == null) { 340 return; 341 } 342 try { 343 mService.releaseInputEventCapture(mServiceCallback, targetDisplayType); 344 } catch (RemoteException e) { 345 // ignore 346 } 347 } 348 349 /** 350 * Injects the {@link KeyEvent} passed as parameter against Car Input API. 351 * <p> 352 * The event parameter display id will be overridden accordingly to the display type also passed 353 * as parameter. 354 * 355 * @param event the key event to inject 356 * @param targetDisplayType the display type associated with the key event 357 * @throws RemoteException in case of failure when invoking car input service 358 */ 359 @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) injectKeyEvent(@onNull KeyEvent event, @DisplayTypeEnum int targetDisplayType)360 public void injectKeyEvent(@NonNull KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 361 try { 362 mService.injectKeyEvent(event, targetDisplayType); 363 } catch (RemoteException e) { 364 e.rethrowFromSystemServer(); 365 } 366 } 367 368 /** @hide */ 369 @Override onCarDisconnected()370 protected void onCarDisconnected() { 371 synchronized (mLock) { 372 mCarInputCaptureCallbacks.clear(); 373 } 374 } 375 getCallback(@isplayTypeEnum int targetDisplayType)376 private CallbackHolder getCallback(@DisplayTypeEnum int targetDisplayType) { 377 synchronized (mLock) { 378 return mCarInputCaptureCallbacks.get(targetDisplayType); 379 } 380 } 381 dispatchKeyEvents(@isplayTypeEnum int targetDisplayType, List<KeyEvent> keyEvents)382 private void dispatchKeyEvents(@DisplayTypeEnum int targetDisplayType, 383 List<KeyEvent> keyEvents) { 384 CallbackHolder callbackHolder = getCallback(targetDisplayType); 385 if (callbackHolder == null) { 386 return; 387 } 388 callbackHolder.mExecutor.execute(() -> { 389 callbackHolder.mCallback.onKeyEvents(targetDisplayType, keyEvents); 390 }); 391 } 392 dispatchRotaryEvents(@isplayTypeEnum int targetDisplayType, List<RotaryEvent> events)393 private void dispatchRotaryEvents(@DisplayTypeEnum int targetDisplayType, 394 List<RotaryEvent> events) { 395 CallbackHolder callbackHolder = getCallback(targetDisplayType); 396 if (callbackHolder == null) { 397 return; 398 } 399 callbackHolder.mExecutor.execute(() -> { 400 callbackHolder.mCallback.onRotaryEvents(targetDisplayType, events); 401 }); 402 } 403 dispatchOnCaptureStateChanged(@isplayTypeEnum int targetDisplayType, int[] activeInputTypes)404 private void dispatchOnCaptureStateChanged(@DisplayTypeEnum int targetDisplayType, 405 int[] activeInputTypes) { 406 CallbackHolder callbackHolder = getCallback(targetDisplayType); 407 if (callbackHolder == null) { 408 return; 409 } 410 callbackHolder.mExecutor.execute(() -> { 411 callbackHolder.mCallback.onCaptureStateChanged(targetDisplayType, activeInputTypes); 412 }); 413 } 414 dispatchCustomInputEvents(@isplayTypeEnum int targetDisplayType, List<CustomInputEvent> events)415 private void dispatchCustomInputEvents(@DisplayTypeEnum int targetDisplayType, 416 List<CustomInputEvent> events) { 417 CallbackHolder callbackHolder = getCallback(targetDisplayType); 418 if (callbackHolder == null) { 419 return; 420 } 421 callbackHolder.mExecutor.execute(() -> { 422 if (DEBUG) { 423 Slog.d(TAG, "Firing events " + events + " on callback " + callbackHolder.mCallback); 424 } 425 callbackHolder.mCallback.onCustomInputEvents(targetDisplayType, events); 426 }); 427 } 428 429 private static final class ICarInputCallbackImpl extends ICarInputCallback.Stub { 430 431 private final WeakReference<CarInputManager> mManager; 432 ICarInputCallbackImpl(CarInputManager manager)433 private ICarInputCallbackImpl(CarInputManager manager) { 434 mManager = new WeakReference<>(manager); 435 } 436 437 @Override onKeyEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<KeyEvent> keyEvents)438 public void onKeyEvents(@DisplayTypeEnum int targetDisplayType, 439 @NonNull List<KeyEvent> keyEvents) { 440 CarInputManager manager = mManager.get(); 441 if (manager == null) { 442 return; 443 } 444 manager.dispatchKeyEvents(targetDisplayType, keyEvents); 445 } 446 447 @Override onRotaryEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<RotaryEvent> events)448 public void onRotaryEvents(@DisplayTypeEnum int targetDisplayType, 449 @NonNull List<RotaryEvent> events) { 450 CarInputManager manager = mManager.get(); 451 if (manager == null) { 452 return; 453 } 454 manager.dispatchRotaryEvents(targetDisplayType, events); 455 } 456 457 @Override onCaptureStateChanged(@isplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] activeInputTypes)458 public void onCaptureStateChanged(@DisplayTypeEnum int targetDisplayType, 459 @NonNull @InputTypeEnum int[] activeInputTypes) { 460 CarInputManager manager = mManager.get(); 461 if (manager == null) { 462 return; 463 } 464 manager.dispatchOnCaptureStateChanged(targetDisplayType, activeInputTypes); 465 } 466 467 @Override onCustomInputEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<CustomInputEvent> events)468 public void onCustomInputEvents(@DisplayTypeEnum int targetDisplayType, 469 @NonNull List<CustomInputEvent> events) { 470 CarInputManager manager = mManager.get(); 471 if (manager == null) { 472 return; 473 } 474 manager.dispatchCustomInputEvents(targetDisplayType, events); 475 } 476 } 477 478 /** 479 * Class used to bind {@link CarInputCaptureCallback} and their associated {@link Executor}. 480 */ 481 private static final class CallbackHolder { 482 483 final CarInputCaptureCallback mCallback; 484 485 final Executor mExecutor; 486 CallbackHolder(CarInputCaptureCallback callback, Executor executor)487 CallbackHolder(CarInputCaptureCallback callback, Executor executor) { 488 mCallback = callback; 489 mExecutor = executor; 490 } 491 } 492 } 493