• 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.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