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