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 android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.RequiresPermission;
22 import android.car.Car;
23 import android.car.CarManagerBase;
24 import android.os.IBinder;
25 import android.os.RemoteException;
26 import android.util.SparseArray;
27 import android.view.KeyEvent;
28 
29 import com.android.internal.annotations.GuardedBy;
30 
31 import java.lang.annotation.ElementType;
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 import java.lang.annotation.Target;
35 import java.lang.ref.WeakReference;
36 import java.util.List;
37 
38 /**
39  * This API allows capturing selected input events.
40  *
41  * @hide
42  */
43 public final class CarInputManager extends CarManagerBase {
44 
45     /**
46      * Callback for capturing input events.
47      */
48     public interface CarInputCaptureCallback {
49         /**
50          * Key events were captured.
51          */
onKeyEvents(int targetDisplayId, @NonNull List<KeyEvent> keyEvents)52         void onKeyEvents(int targetDisplayId, @NonNull List<KeyEvent> keyEvents);
53 
54         /**
55          * Rotary events were captured.
56          */
onRotaryEvents(int targetDisplayId, @NonNull List<RotaryEvent> events)57         void onRotaryEvents(int targetDisplayId, @NonNull List<RotaryEvent> events);
58 
59         /**
60          * Capture state for the display has changed due to other client making requests or
61          * releasing capture. Client should check {@code activeInputTypes} for which input types
62          * are currently captured.
63          */
onCaptureStateChanged(int targetDisplayId, @NonNull @InputTypeEnum int[] activeInputTypes)64         void onCaptureStateChanged(int targetDisplayId,
65                 @NonNull @InputTypeEnum int[] activeInputTypes);
66     }
67 
68     /**
69      * Represents main display for the system.
70      */
71     public static final int TARGET_DISPLAY_TYPE_MAIN = 0;
72 
73     /**
74      * Represents cluster display.
75      */
76     public static final int TARGET_DISPLAY_TYPE_CLUSTER = 1;
77 
78     /** @hide */
79     @Retention(RetentionPolicy.SOURCE)
80     @IntDef(prefix = "TARGET_DISPLAY_TYPE_", value = {
81             TARGET_DISPLAY_TYPE_MAIN,
82             TARGET_DISPLAY_TYPE_CLUSTER,
83     })
84     @Target({ElementType.TYPE_USE})
85     public @interface TargetDisplayTypeEnum {}
86 
87     /**
88      * Client will wait for grant if the request is failing due to higher priority client.
89      */
90     public static final int CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT = 0x1;
91 
92     /**
93      * Client wants to capture the keys for the whole display. This is only allowed to system
94      * process.
95      *
96      * @hide
97      */
98     public static final int CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY = 0x2;
99 
100     /** @hide */
101     @IntDef(flag = true, prefix = { "CAPTURE_REQ_FLAGS_" }, value = {
102             CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT,
103             CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY,
104     })
105     @Retention(RetentionPolicy.SOURCE)
106     @interface CaptureRequestFlags {}
107 
108     /**
109      * This is special type to cover all INPUT_TYPE_*. This is used for clients using
110      * {@link #CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY} flag.
111      *
112      * @hide
113      */
114     public static final int INPUT_TYPE_ALL_INPUTS = 1;
115 
116     /**
117      * This covers rotary input device for navigation.
118      */
119     public static final int INPUT_TYPE_ROTARY_NAVIGATION = 10;
120 
121     /**
122      * Volume knob.
123      * TODO (b/151666020): This will be only allowed to system apps later.
124      *
125      * @hide
126      */
127     public static final int INPUT_TYPE_ROTARY_VOLUME = 11;
128 
129     /**
130      * This is the group of keys for DPAD.
131      * Included key events are: {@link KeyEvent#KEYCODE_DPAD_UP},
132      * {@link KeyEvent#KEYCODE_DPAD_DOWN}, {@link KeyEvent#KEYCODE_DPAD_LEFT},
133      * {@link KeyEvent#KEYCODE_DPAD_RIGHT}, {@link KeyEvent#KEYCODE_DPAD_CENTER},
134      * {@link KeyEvent#KEYCODE_DPAD_DOWN_LEFT}, {@link KeyEvent#KEYCODE_DPAD_DOWN_RIGHT},
135      * {@link KeyEvent#KEYCODE_DPAD_UP_LEFT}, {@link KeyEvent#KEYCODE_DPAD_UP_RIGHT}
136      */
137     public static final int INPUT_TYPE_DPAD_KEYS = 100;
138 
139     /**
140      * This is for all KEYCODE_NAVIGATE_* keys.
141      */
142     public static final int INPUT_TYPE_NAVIGATE_KEYS = 101;
143 
144     /**
145      * This is for all KEYCODE_SYSTEM_NAVIGATE_* keys.
146      */
147     public static final int INPUT_TYPE_SYSTEM_NAVIGATE_KEYS = 102;
148 
149     /** @hide */
150     @Retention(RetentionPolicy.SOURCE)
151     @IntDef(prefix = "INPUT_TYPE_", value = {
152             INPUT_TYPE_ALL_INPUTS,
153             INPUT_TYPE_ROTARY_NAVIGATION,
154             INPUT_TYPE_ROTARY_VOLUME,
155             INPUT_TYPE_DPAD_KEYS,
156             INPUT_TYPE_NAVIGATE_KEYS,
157             INPUT_TYPE_SYSTEM_NAVIGATE_KEYS
158     })
159     @Target({ElementType.TYPE_USE})
160     public @interface InputTypeEnum {}
161 
162     /**
163      * The client's request has succeeded and capture will start.
164      */
165     public static final int INPUT_CAPTURE_RESPONSE_SUCCEEDED = 0;
166 
167     /**
168      * The client's request has failed due to higher priority client already capturing. If priority
169      * for the clients are the same, last client making request will be allowed to capture.
170      */
171     public static final int INPUT_CAPTURE_RESPONSE_FAILED = 1;
172 
173     /**
174      * This is used when client has set {@link #CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT} in
175      * {@code requestFlags} and capturing is blocked due to existing higher priority client.
176      * When the higher priority client stops capturing, this client can capture events after
177      * getting @link CarInputCaptureCallback#onCaptureStateChanged(int, int[])} call.
178      */
179     public static final int INPUT_CAPTURE_RESPONSE_DELAYED = 2;
180 
181     /** @hide */
182     @Retention(RetentionPolicy.SOURCE)
183     @IntDef(prefix = "INPUT_CAPTURE_RESPONSE_", value = {
184             INPUT_CAPTURE_RESPONSE_SUCCEEDED,
185             INPUT_CAPTURE_RESPONSE_FAILED,
186             INPUT_CAPTURE_RESPONSE_DELAYED
187     })
188     @Target({ElementType.TYPE_USE})
189     public @interface InputCaptureResponseEnum {}
190 
191     private final ICarInput mService;
192     private final ICarInputCallback mServiceCallback = new ICarInputCallbackImpl(this);
193 
194     private final Object mLock = new Object();
195 
196     @GuardedBy("mLock")
197     private final SparseArray<CarInputCaptureCallback> mCarInputCaptureCallbacks =
198             new SparseArray<>(1);
199 
200     /**
201      * @hide
202      */
CarInputManager(Car car, IBinder service)203     public CarInputManager(Car car, IBinder service) {
204         super(car);
205         mService = ICarInput.Stub.asInterface(service);
206     }
207 
208     /**
209      * Requests capturing of input event for the specified display for all requested input types.
210      *
211      * <p>The request can fail if a high priority client is holding it. The client can set
212      * {@link #CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT} in {@code requestFlags} to wait for the
213      * current high priority client to release it.
214      *
215      * <p>If only some of the input types specified are available, the request will either:
216      * <ul>
217      * <li>fail, returning {@link #INPUT_CAPTURE_RESPONSE_FAILED}, or
218      * <li>be deferred, returning {@link #INPUT_CAPTURE_RESPONSE_DELAYED}, if the
219      * {@link #CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT} flag is used.
220      * </ul>
221      *
222      * <p> After {@link #INPUT_CAPTURE_RESPONSE_DELAYED} is returned, no input types are captured
223      * until the client receives a {@link CarInputCaptureCallback#onCaptureStateChanged(int, int[])}
224      * call with valid input types.
225      */
226     @RequiresPermission(android.Manifest.permission.MONITOR_INPUT)
227     @InputCaptureResponseEnum
requestInputEventCapture(@onNull CarInputCaptureCallback callback, @TargetDisplayTypeEnum int targetDisplayType, @NonNull @InputTypeEnum int[] inputTypes, @CaptureRequestFlags int requestFlags)228     public int requestInputEventCapture(@NonNull CarInputCaptureCallback callback,
229             @TargetDisplayTypeEnum int targetDisplayType,
230             @NonNull @InputTypeEnum int[] inputTypes,
231             @CaptureRequestFlags int requestFlags) {
232         synchronized (mLock) {
233             mCarInputCaptureCallbacks.put(targetDisplayType, callback);
234         }
235         try {
236             return mService.requestInputEventCapture(mServiceCallback, targetDisplayType,
237                     inputTypes, requestFlags);
238         } catch (RemoteException e) {
239             return handleRemoteExceptionFromCarService(e, INPUT_CAPTURE_RESPONSE_FAILED);
240         }
241     }
242 
243     /**
244      * Stops capturing of given display.
245      */
releaseInputEventCapture(@argetDisplayTypeEnum int targetDisplayType)246     public void releaseInputEventCapture(@TargetDisplayTypeEnum int targetDisplayType) {
247         CarInputCaptureCallback callback;
248         synchronized (mLock) {
249             callback = mCarInputCaptureCallbacks.removeReturnOld(targetDisplayType);
250         }
251         if (callback == null) {
252             return;
253         }
254         try {
255             mService.releaseInputEventCapture(mServiceCallback, targetDisplayType);
256         } catch (RemoteException e) {
257             // ignore
258         }
259     }
260 
261     @Override
onCarDisconnected()262     protected void onCarDisconnected() {
263         synchronized (mLock) {
264             mCarInputCaptureCallbacks.clear();
265         }
266     }
267 
getCallback(int targetDisplayType)268     private CarInputCaptureCallback getCallback(int targetDisplayType) {
269         synchronized (mLock) {
270             return mCarInputCaptureCallbacks.get(targetDisplayType);
271         }
272     }
273 
dispatchKeyEvents(int targetDisplayType, List<KeyEvent> keyEvents)274     private void dispatchKeyEvents(int targetDisplayType, List<KeyEvent> keyEvents) {
275         getEventHandler().post(() -> {
276             CarInputCaptureCallback callback = getCallback(targetDisplayType);
277             if (callback != null) {
278                 callback.onKeyEvents(targetDisplayType, keyEvents);
279             }
280         });
281 
282     }
283 
dispatchRotaryEvents(int targetDisplayType, List<RotaryEvent> events)284     private void dispatchRotaryEvents(int targetDisplayType, List<RotaryEvent> events) {
285         getEventHandler().post(() -> {
286             CarInputCaptureCallback callback = getCallback(targetDisplayType);
287             if (callback != null) {
288                 callback.onRotaryEvents(targetDisplayType, events);
289             }
290         });
291     }
292 
dispatchOnCaptureStateChanged(int targetDisplayType, int[] activeInputTypes)293     private void dispatchOnCaptureStateChanged(int targetDisplayType, int[] activeInputTypes) {
294         getEventHandler().post(() -> {
295             CarInputCaptureCallback callback = getCallback(targetDisplayType);
296             if (callback != null) {
297                 callback.onCaptureStateChanged(targetDisplayType, activeInputTypes);
298             }
299         });
300     }
301 
302     private static final class ICarInputCallbackImpl extends ICarInputCallback.Stub {
303 
304         private final WeakReference<CarInputManager> mManager;
305 
ICarInputCallbackImpl(CarInputManager manager)306         private ICarInputCallbackImpl(CarInputManager manager) {
307             mManager = new WeakReference<>(manager);
308         }
309 
310         @Override
onKeyEvents(int targetDisplayType, List<KeyEvent> keyEvents)311         public void onKeyEvents(int targetDisplayType, List<KeyEvent> keyEvents) {
312             CarInputManager manager = mManager.get();
313             if (manager == null) {
314                 return;
315             }
316             manager.dispatchKeyEvents(targetDisplayType, keyEvents);
317         }
318 
319         @Override
onRotaryEvents(int targetDisplayType, List<RotaryEvent> events)320         public void onRotaryEvents(int targetDisplayType, List<RotaryEvent> events) {
321             CarInputManager manager = mManager.get();
322             if (manager == null) {
323                 return;
324             }
325             manager.dispatchRotaryEvents(targetDisplayType, events);
326         }
327 
328         @Override
onCaptureStateChanged(int targetDisplayType, int[] activeInputTypes)329         public void onCaptureStateChanged(int targetDisplayType, int[] activeInputTypes) {
330             CarInputManager manager = mManager.get();
331             if (manager == null) {
332                 return;
333             }
334             manager.dispatchOnCaptureStateChanged(targetDisplayType, activeInputTypes);
335         }
336     }
337 }
338