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