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