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