1 /* 2 * Copyright (C) 2016 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 package com.android.car; 17 18 import static android.car.CarOccupantZoneManager.DisplayTypeEnum; 19 import static android.car.PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0; 20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 21 22 import static com.android.car.BuiltinPackageDependency.CAR_ACCESSIBILITY_SERVICE_CLASS; 23 import static com.android.car.CarServiceUtils.getCommonHandlerThread; 24 import static com.android.car.CarServiceUtils.getContentResolverForUser; 25 import static com.android.car.CarServiceUtils.isEventOfType; 26 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 27 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU; 28 29 import android.annotation.Nullable; 30 import android.annotation.UserIdInt; 31 import android.car.Car; 32 import android.car.CarOccupantZoneManager; 33 import android.car.CarOccupantZoneManager.OccupantZoneInfo; 34 import android.car.CarProjectionManager; 35 import android.car.VehicleAreaSeat; 36 import android.car.builtin.input.InputManagerHelper; 37 import android.car.builtin.util.AssistUtilsHelper; 38 import android.car.builtin.util.AssistUtilsHelper.VoiceInteractionSessionShowCallbackHelper; 39 import android.car.builtin.util.Slogf; 40 import android.car.builtin.view.InputEventHelper; 41 import android.car.builtin.view.KeyEventHelper; 42 import android.car.input.CarInputManager; 43 import android.car.input.CustomInputEvent; 44 import android.car.input.ICarInput; 45 import android.car.input.ICarInputCallback; 46 import android.car.input.RotaryEvent; 47 import android.car.user.CarUserManager.UserLifecycleListener; 48 import android.car.user.UserLifecycleEventFilter; 49 import android.content.ContentResolver; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.pm.PackageManager; 53 import android.content.res.Resources; 54 import android.hardware.input.InputManager; 55 import android.net.Uri; 56 import android.os.Binder; 57 import android.os.Handler; 58 import android.os.UserHandle; 59 import android.os.UserManager; 60 import android.provider.CallLog.Calls; 61 import android.provider.Settings; 62 import android.telecom.TelecomManager; 63 import android.text.TextUtils; 64 import android.util.Log; 65 import android.util.SparseArray; 66 import android.util.SparseBooleanArray; 67 import android.view.Display; 68 import android.view.InputDevice; 69 import android.view.InputEvent; 70 import android.view.KeyEvent; 71 import android.view.MotionEvent; 72 import android.view.ViewConfiguration; 73 74 import com.android.car.bluetooth.CarBluetoothService; 75 import com.android.car.hal.InputHalService; 76 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 77 import com.android.car.internal.common.UserHelperLite; 78 import com.android.car.internal.util.IndentingPrintWriter; 79 import com.android.car.power.CarPowerManagementService; 80 import com.android.car.systeminterface.SystemInterface; 81 import com.android.car.user.CarUserService; 82 import com.android.internal.annotations.GuardedBy; 83 import com.android.internal.annotations.VisibleForTesting; 84 85 import java.util.ArrayList; 86 import java.util.Arrays; 87 import java.util.BitSet; 88 import java.util.Collections; 89 import java.util.List; 90 import java.util.function.BooleanSupplier; 91 import java.util.function.IntSupplier; 92 import java.util.function.Supplier; 93 94 /** 95 * CarInputService monitors and handles input event through vehicle HAL. 96 */ 97 public class CarInputService extends ICarInput.Stub 98 implements CarServiceBase, InputHalService.InputListener { 99 public static final String ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ":"; 100 101 private static final int MAX_RETRIES_FOR_ENABLING_ACCESSIBILITY_SERVICES = 5; 102 103 @VisibleForTesting 104 static final String TAG = CarLog.TAG_INPUT; 105 106 private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 107 108 @VisibleForTesting 109 static final String LONG_PRESS_TIMEOUT = "long_press_timeout"; 110 111 /** An interface to receive {@link KeyEvent}s as they occur. */ 112 public interface KeyEventListener { 113 /** Called when a key event occurs. */ 114 // TODO(b/247170915): This method is no needed anymore, please remove and use 115 // onKeyEvent(KeyEvent event, intDisplayType, int seat) onKeyEvent(KeyEvent event)116 default void onKeyEvent(KeyEvent event) { 117 } 118 119 /** 120 * Called when a key event occurs with seat. 121 * 122 * @param event the key event that occurred 123 * @param displayType target display the event is associated with 124 * should be one of {@link CarOccupantZoneManager#DISPLAY_TYPE_MAIN}, 125 * {@link CarOccupantZoneManager#DISPLAY_TYPE_INSTRUMENT_CLUSTER}, 126 * {@link CarOccupantZoneManager#DISPLAY_TYPE_HUD}, 127 * {@link CarOccupantZoneManager#DISPLAY_TYPE_INPUT}, 128 * {@link CarOccupantZoneManager#DISPLAY_TYPE_AUXILIARY}, 129 * @param seat the area id this event is occurring from 130 */ onKeyEvent(KeyEvent event, @DisplayTypeEnum int displayType, @VehicleAreaSeat.Enum int seat)131 default void onKeyEvent(KeyEvent event, @DisplayTypeEnum int displayType, 132 @VehicleAreaSeat.Enum int seat) { 133 } 134 } 135 136 /** An interface to receive {@link MotionEvent}s as they occur. */ 137 public interface MotionEventListener { 138 /** Called when a motion event occurs. */ onMotionEvent(MotionEvent event)139 void onMotionEvent(MotionEvent event); 140 } 141 142 private final class KeyPressTimer { 143 private final Runnable mLongPressRunnable; 144 private final Runnable mCallback = this::onTimerExpired; 145 private final IntSupplier mLongPressDelaySupplier; 146 147 @GuardedBy("CarInputService.this.mLock") 148 private final Handler mHandler; 149 @GuardedBy("CarInputService.this.mLock") 150 private boolean mDown; 151 @GuardedBy("CarInputService.this.mLock") 152 private boolean mLongPress = false; 153 KeyPressTimer( Handler handler, IntSupplier longPressDelaySupplier, Runnable longPressRunnable)154 KeyPressTimer( 155 Handler handler, IntSupplier longPressDelaySupplier, Runnable longPressRunnable) { 156 mHandler = handler; 157 mLongPressRunnable = longPressRunnable; 158 mLongPressDelaySupplier = longPressDelaySupplier; 159 } 160 161 /** Marks that a key was pressed, and starts the long-press timer. */ keyDown()162 void keyDown() { 163 synchronized (mLock) { 164 mDown = true; 165 mLongPress = false; 166 mHandler.removeCallbacks(mCallback); 167 mHandler.postDelayed(mCallback, mLongPressDelaySupplier.getAsInt()); 168 } 169 } 170 171 /** 172 * Marks that a key was released, and stops the long-press timer. 173 * <p> 174 * Returns true if the press was a long-press. 175 */ keyUp()176 boolean keyUp() { 177 synchronized (mLock) { 178 mHandler.removeCallbacks(mCallback); 179 mDown = false; 180 return mLongPress; 181 } 182 } 183 onTimerExpired()184 private void onTimerExpired() { 185 synchronized (mLock) { 186 // If the timer expires after key-up, don't retroactively make the press long. 187 if (!mDown) { 188 return; 189 } 190 mLongPress = true; 191 } 192 mLongPressRunnable.run(); 193 } 194 } 195 196 private final VoiceInteractionSessionShowCallbackHelper mShowCallback = 197 new VoiceInteractionSessionShowCallbackHelper() { 198 @Override 199 public void onFailed() { 200 Slogf.w(TAG, "Failed to show VoiceInteractionSession"); 201 } 202 203 @Override 204 public void onShown() { 205 Slogf.d(TAG, "VoiceInteractionSessionShowCallbackHelper onShown()"); 206 } 207 }; 208 209 private final Context mContext; 210 private final InputHalService mInputHalService; 211 private final CarUserService mUserService; 212 private final CarOccupantZoneService mCarOccupantZoneService; 213 private final CarBluetoothService mCarBluetoothService; 214 private final CarPowerManagementService mCarPowerService; 215 private final TelecomManager mTelecomManager; 216 private final SystemInterface mSystemInterface; 217 private final UserManager mUserManager; 218 219 // The default handler for main-display key events. By default, injects the events into 220 // the input queue via InputManager, but can be overridden for testing. 221 private final KeyEventListener mDefaultKeyHandler; 222 // The default handler for main-display motion events. By default, injects the events into 223 // the input queue via InputManager, but can be overridden for testing. 224 private final MotionEventListener mDefaultMotionHandler; 225 // The supplier for the last-called number. By default, gets the number from the call log. 226 // May be overridden for testing. 227 private final Supplier<String> mLastCalledNumberSupplier; 228 // The supplier for the system long-press delay, in milliseconds. By default, gets the value 229 // from Settings.Secure for the current user, falling back to the system-wide default 230 // long-press delay defined in ViewConfiguration. May be overridden for testing. 231 private final IntSupplier mLongPressDelaySupplier; 232 // ComponentName of the RotaryService. 233 private final String mRotaryServiceComponentName; 234 235 private final BooleanSupplier mShouldCallButtonEndOngoingCallSupplier; 236 237 private final Object mLock = new Object(); 238 239 @GuardedBy("mLock") 240 private CarProjectionManager.ProjectionKeyEventHandler mProjectionKeyEventHandler; 241 242 @GuardedBy("mLock") 243 private final BitSet mProjectionKeyEventsSubscribed = new BitSet(); 244 245 private final KeyPressTimer mVoiceKeyTimer; 246 private final KeyPressTimer mCallKeyTimer; 247 248 @GuardedBy("mLock") 249 private KeyEventListener mInstrumentClusterKeyListener; 250 251 @GuardedBy("mLock") 252 private final SparseArray<KeyEventListener> mListeners = new SparseArray<>(); 253 254 private final InputCaptureClientController mCaptureController; 255 256 private int mDriverSeat = VehicleAreaSeat.SEAT_UNKNOWN; 257 258 private boolean mHasDriver; 259 260 // key: seat, value: power key handled by ACTION_DOWN. 261 // {@code true} if the screen was turned on with the power key ACTION_DOWN. In this case, 262 // we need to block the power key's ACTION_UP to prevent the device from going back to sleep. 263 // When ACTION_UP, it is released with {@code false}. 264 private SparseBooleanArray mPowerKeyHandled = new SparseBooleanArray(); 265 266 // The default handler for special keys. The behavior of the keys is implemented in this 267 // service. It can be overridden by {@link #registerKeyEventListener}. 268 private final KeyEventListener mDefaultSpecialKeyHandler = new KeyEventListener() { 269 @Override 270 public void onKeyEvent(KeyEvent event, @DisplayTypeEnum int displayType, 271 @VehicleAreaSeat.Enum int seat) { 272 switch (event.getKeyCode()) { 273 case KeyEvent.KEYCODE_HOME: 274 handleHomeKey(event, displayType, seat); 275 break; 276 case KeyEvent.KEYCODE_POWER: 277 handlePowerKey(event, displayType, seat); 278 break; 279 default: 280 Slogf.e(TAG, "Key event %s is not supported by special key handler", 281 KeyEvent.keyCodeToString(event.getKeyCode())); 282 break; 283 } 284 } 285 }; 286 287 private final UserLifecycleListener mUserLifecycleListener = event -> { 288 if (!isEventOfType(TAG, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) { 289 return; 290 } 291 Slogf.d(TAG, "CarInputService.onEvent(%s)", event); 292 293 updateCarAccessibilityServicesSettings(event.getUserId()); 294 }; 295 getViewLongPressDelay(Context context)296 private static int getViewLongPressDelay(Context context) { 297 return Settings.Secure.getInt(getContentResolverForUser(context, 298 UserHandle.CURRENT.getIdentifier()), LONG_PRESS_TIMEOUT, 299 ViewConfiguration.getLongPressTimeout()); 300 } 301 CarInputService(Context context, InputHalService inputHalService, CarUserService userService, CarOccupantZoneService occupantZoneService, CarBluetoothService bluetoothService, CarPowerManagementService carPowerService, SystemInterface systemInterface, UserManager userManager)302 public CarInputService(Context context, InputHalService inputHalService, 303 CarUserService userService, CarOccupantZoneService occupantZoneService, 304 CarBluetoothService bluetoothService, CarPowerManagementService carPowerService, 305 SystemInterface systemInterface, UserManager userManager) { 306 this(context, inputHalService, userService, occupantZoneService, bluetoothService, 307 carPowerService, systemInterface, 308 new Handler(getCommonHandlerThread().getLooper()), 309 context.getSystemService(TelecomManager.class), 310 new KeyEventListener() { 311 @Override 312 public void onKeyEvent(KeyEvent event, @DisplayTypeEnum int displayType, 313 @VehicleAreaSeat.Enum int seat) { 314 InputManagerHelper.injectInputEvent( 315 context.getSystemService(InputManager.class), event); 316 } 317 }, 318 /* defaultMotionHandler= */ event -> InputManagerHelper.injectInputEvent( 319 context.getSystemService(InputManager.class), event), 320 /* lastCalledNumberSupplier= */ () -> Calls.getLastOutgoingCall(context), 321 /* longPressDelaySupplier= */ () -> getViewLongPressDelay(context), 322 /* shouldCallButtonEndOngoingCallSupplier= */ () -> context.getResources() 323 .getBoolean(R.bool.config_callButtonEndsOngoingCall), 324 new InputCaptureClientController(context), userManager); 325 } 326 327 @VisibleForTesting CarInputService(Context context, InputHalService inputHalService, CarUserService userService, CarOccupantZoneService occupantZoneService, CarBluetoothService bluetoothService, CarPowerManagementService carPowerService, SystemInterface systemInterface, Handler handler, TelecomManager telecomManager, KeyEventListener defaultKeyHandler, MotionEventListener defaultMotionHandler, Supplier<String> lastCalledNumberSupplier, IntSupplier longPressDelaySupplier, BooleanSupplier shouldCallButtonEndOngoingCallSupplier, InputCaptureClientController captureController, UserManager userManager)328 CarInputService(Context context, InputHalService inputHalService, CarUserService userService, 329 CarOccupantZoneService occupantZoneService, CarBluetoothService bluetoothService, 330 CarPowerManagementService carPowerService, SystemInterface systemInterface, 331 Handler handler, TelecomManager telecomManager, 332 KeyEventListener defaultKeyHandler, MotionEventListener defaultMotionHandler, 333 Supplier<String> lastCalledNumberSupplier, IntSupplier longPressDelaySupplier, 334 BooleanSupplier shouldCallButtonEndOngoingCallSupplier, 335 InputCaptureClientController captureController, 336 UserManager userManager) { 337 mContext = context; 338 mCaptureController = captureController; 339 mInputHalService = inputHalService; 340 mUserService = userService; 341 mCarOccupantZoneService = occupantZoneService; 342 mCarBluetoothService = bluetoothService; 343 mCarPowerService = carPowerService; 344 mSystemInterface = systemInterface; 345 mTelecomManager = telecomManager; 346 mDefaultKeyHandler = defaultKeyHandler; 347 mDefaultMotionHandler = defaultMotionHandler; 348 mLastCalledNumberSupplier = lastCalledNumberSupplier; 349 mLongPressDelaySupplier = longPressDelaySupplier; 350 mUserManager = userManager; 351 352 mVoiceKeyTimer = 353 new KeyPressTimer( 354 handler, longPressDelaySupplier, this::handleVoiceAssistLongPress); 355 mCallKeyTimer = 356 new KeyPressTimer(handler, longPressDelaySupplier, this::handleCallLongPress); 357 358 mRotaryServiceComponentName = mContext.getString(R.string.rotaryService); 359 mShouldCallButtonEndOngoingCallSupplier = shouldCallButtonEndOngoingCallSupplier; 360 361 registerKeyEventListener(mDefaultSpecialKeyHandler, 362 Arrays.asList(KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_POWER)); 363 } 364 365 /** 366 * Set projection key event listener. If null, unregister listener. 367 */ setProjectionKeyEventHandler( @ullable CarProjectionManager.ProjectionKeyEventHandler listener, @Nullable BitSet events)368 public void setProjectionKeyEventHandler( 369 @Nullable CarProjectionManager.ProjectionKeyEventHandler listener, 370 @Nullable BitSet events) { 371 synchronized (mLock) { 372 mProjectionKeyEventHandler = listener; 373 mProjectionKeyEventsSubscribed.clear(); 374 if (events != null) { 375 mProjectionKeyEventsSubscribed.or(events); 376 } 377 } 378 } 379 380 381 /** 382 * This method registers a keyEventListener to listen on key events that it is interested in. 383 * 384 * @param listener The listener to be registered. 385 * @param keyCodesOfInterest The events of interest that the listener is interested in. 386 * @throws IllegalArgumentException When an event is already registered to another listener 387 */ registerKeyEventListener(KeyEventListener listener, List<Integer> keyCodesOfInterest)388 public void registerKeyEventListener(KeyEventListener listener, 389 List<Integer> keyCodesOfInterest) { 390 synchronized (mLock) { 391 // Check for invalid key codes 392 for (int i = 0; i < keyCodesOfInterest.size(); i++) { 393 if (mListeners.contains(keyCodesOfInterest.get(i)) 394 && mListeners.get(keyCodesOfInterest.get(i)) != mDefaultSpecialKeyHandler) { 395 throw new IllegalArgumentException("Event " 396 + KeyEvent.keyCodeToString(keyCodesOfInterest.get(i)) 397 + " already registered to another listener"); 398 } 399 } 400 for (int i = 0; i < keyCodesOfInterest.size(); i++) { 401 mListeners.put(keyCodesOfInterest.get(i), listener); 402 } 403 } 404 } 405 406 /** 407 * Sets the instrument cluster key event listener. 408 */ setInstrumentClusterKeyListener(KeyEventListener listener)409 public void setInstrumentClusterKeyListener(KeyEventListener listener) { 410 synchronized (mLock) { 411 mInstrumentClusterKeyListener = listener; 412 } 413 } 414 415 @Override init()416 public void init() { 417 if (!mInputHalService.isKeyInputSupported()) { 418 Slogf.w(TAG, "Hal does not support key input."); 419 return; 420 } 421 Slogf.d(TAG, "Hal supports key input."); 422 mInputHalService.setInputListener(this); 423 UserLifecycleEventFilter userSwitchingEventFilter = new UserLifecycleEventFilter.Builder() 424 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build(); 425 mUserService.addUserLifecycleListener(userSwitchingEventFilter, mUserLifecycleListener); 426 mDriverSeat = mCarOccupantZoneService.getDriverSeat(); 427 mHasDriver = (mDriverSeat != VehicleAreaSeat.SEAT_UNKNOWN); 428 } 429 430 @Override release()431 public void release() { 432 synchronized (mLock) { 433 mProjectionKeyEventHandler = null; 434 mProjectionKeyEventsSubscribed.clear(); 435 mInstrumentClusterKeyListener = null; 436 mListeners.clear(); 437 } 438 mUserService.removeUserLifecycleListener(mUserLifecycleListener); 439 } 440 441 @Override onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType)442 public void onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 443 onKeyEvent(event, targetDisplayType, mDriverSeat); 444 } 445 446 /** 447 * Called for key event 448 * @throws IllegalArgumentException if the passed seat is an unknown seat and the driver seat 449 * is not an unknown seat 450 */ 451 @Override onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)452 public void onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType, 453 @VehicleAreaSeat.Enum int seat) { 454 if (mHasDriver && seat == VehicleAreaSeat.SEAT_UNKNOWN) { 455 // To support {@link #onKeyEvent(KeyEvent, int)}, we need to check whether the driver 456 // exists or not. 457 // For example, for a passenger-only system, the driver seat might be SEAT_UNKNOWN. 458 // In this case, no exception should be occurred. 459 throw new IllegalArgumentException("Unknown seat"); 460 } 461 462 // Update user activity information to car power management service. 463 notifyUserActivity(event, targetDisplayType, seat); 464 465 // Driver key events are handled the same as HW_KEY_INPUT. 466 if (seat == mDriverSeat) { 467 dispatchKeyEventForDriver(event, targetDisplayType); 468 return; 469 } 470 471 // Notifies the listeners of the key event. 472 notifyKeyEventListener(event, targetDisplayType, seat); 473 } 474 dispatchKeyEventForDriver(KeyEvent event, @DisplayTypeEnum int targetDisplayType)475 private void dispatchKeyEventForDriver(KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 476 // Special case key code that have special "long press" handling for automotive 477 switch (event.getKeyCode()) { 478 case KeyEvent.KEYCODE_VOICE_ASSIST: 479 handleVoiceAssistKey(event); 480 return; 481 case KeyEvent.KEYCODE_CALL: 482 handleCallKey(event); 483 return; 484 default: 485 break; 486 } 487 488 assignDisplayId(event, targetDisplayType); 489 490 // Allow specifically targeted keys to be routed to the cluster 491 if (targetDisplayType == CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER 492 && handleInstrumentClusterKey(event)) { 493 return; 494 } 495 if (mCaptureController.onKeyEvent(targetDisplayType, event)) { 496 return; 497 } 498 mDefaultKeyHandler.onKeyEvent(event, targetDisplayType, mDriverSeat); 499 } 500 501 /** 502 * Called for motion event 503 */ 504 @Override onMotionEvent(MotionEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)505 public void onMotionEvent(MotionEvent event, @DisplayTypeEnum int targetDisplayType, 506 @VehicleAreaSeat.Enum int seat) { 507 if (!Car.getPlatformVersion().isAtLeast(UPSIDE_DOWN_CAKE_0)) { 508 Slogf.e(TAG, "Motion event for passenger is only supported from %s", 509 UPSIDE_DOWN_CAKE_0); 510 return; 511 } 512 513 if (seat == VehicleAreaSeat.SEAT_UNKNOWN) { 514 throw new IllegalArgumentException("Unknown seat"); 515 } 516 517 notifyUserActivity(event, targetDisplayType, seat); 518 assignDisplayIdForSeat(event, targetDisplayType, seat); 519 mDefaultMotionHandler.onMotionEvent(event); 520 } 521 notifyKeyEventListener(KeyEvent event, int targetDisplay, int seat)522 private void notifyKeyEventListener(KeyEvent event, int targetDisplay, int seat) { 523 KeyEventListener keyEventListener; 524 synchronized (mLock) { 525 keyEventListener = mListeners.get(event.getKeyCode()); 526 } 527 if (keyEventListener == null) { 528 if (DBG) { 529 Slogf.d(TAG, "Key event listener not found for event %s", 530 KeyEvent.keyCodeToString(event.getKeyCode())); 531 } 532 // If there is no listener for the key event, it is injected into the core system. 533 keyEventListener = mDefaultKeyHandler; 534 } 535 assignDisplayIdForSeat(event, targetDisplay, seat); 536 keyEventListener.onKeyEvent(event, targetDisplay, seat); 537 } 538 assignDisplayId(KeyEvent event, @DisplayTypeEnum int targetDisplayType)539 private void assignDisplayId(KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 540 // Setting display id for driver user id (currently MAIN and CLUSTER display types are 541 // linked to driver user only) 542 int newDisplayId = mCarOccupantZoneService.getDisplayIdForDriver(targetDisplayType); 543 544 // Display id is overridden even if already set. 545 KeyEventHelper.setDisplayId(event, newDisplayId); 546 } 547 assignDisplayIdForSeat(InputEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)548 private void assignDisplayIdForSeat(InputEvent event, @DisplayTypeEnum int targetDisplayType, 549 @VehicleAreaSeat.Enum int seat) { 550 int newDisplayId = getDisplayIdForSeat(targetDisplayType, seat); 551 552 if (isPlatformVersionAtLeastU()) { 553 InputEventHelper.setDisplayId(event, newDisplayId); 554 } else if (event instanceof KeyEvent) { 555 KeyEventHelper.setDisplayId((KeyEvent) event, newDisplayId); 556 } else { 557 Slogf.e(TAG, "Assigning display id to motion event is only supported from %s.", 558 UPSIDE_DOWN_CAKE_0); 559 } 560 } 561 getDisplayIdForSeat(@isplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)562 private int getDisplayIdForSeat(@DisplayTypeEnum int targetDisplayType, 563 @VehicleAreaSeat.Enum int seat) { 564 int zoneId = mCarOccupantZoneService.getOccupantZoneIdForSeat(seat); 565 return mCarOccupantZoneService.getDisplayForOccupant(zoneId, targetDisplayType); 566 } 567 568 /** 569 * Notifies the car power manager that user activity happened. 570 */ notifyUserActivity(InputEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)571 private void notifyUserActivity(InputEvent event, @DisplayTypeEnum int targetDisplayType, 572 @VehicleAreaSeat.Enum int seat) { 573 int displayId = getDisplayIdForSeat(targetDisplayType, seat); 574 if (displayId == Display.INVALID_DISPLAY) { 575 return; 576 } 577 mCarPowerService.notifyUserActivity(displayId, event.getEventTime()); 578 } 579 580 @Override onRotaryEvent(RotaryEvent event, @DisplayTypeEnum int targetDisplay)581 public void onRotaryEvent(RotaryEvent event, @DisplayTypeEnum int targetDisplay) { 582 if (!mCaptureController.onRotaryEvent(targetDisplay, event)) { 583 List<KeyEvent> keyEvents = rotaryEventToKeyEvents(event); 584 for (KeyEvent keyEvent : keyEvents) { 585 onKeyEvent(keyEvent, targetDisplay); 586 } 587 } 588 } 589 590 @Override onCustomInputEvent(CustomInputEvent event)591 public void onCustomInputEvent(CustomInputEvent event) { 592 if (!mCaptureController.onCustomInputEvent(event)) { 593 Slogf.w(TAG, "Failed to propagate (%s)", event); 594 return; 595 } 596 Slogf.d(TAG, "Succeed injecting (%s)", event); 597 } 598 rotaryEventToKeyEvents(RotaryEvent event)599 private static List<KeyEvent> rotaryEventToKeyEvents(RotaryEvent event) { 600 int numClicks = event.getNumberOfClicks(); 601 int numEvents = numClicks * 2; // up / down per each click 602 boolean clockwise = event.isClockwise(); 603 int keyCode; 604 switch (event.getInputType()) { 605 case CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION: 606 keyCode = clockwise 607 ? KeyEvent.KEYCODE_NAVIGATE_NEXT 608 : KeyEvent.KEYCODE_NAVIGATE_PREVIOUS; 609 break; 610 case CarInputManager.INPUT_TYPE_ROTARY_VOLUME: 611 keyCode = clockwise 612 ? KeyEvent.KEYCODE_VOLUME_UP 613 : KeyEvent.KEYCODE_VOLUME_DOWN; 614 break; 615 default: 616 Slogf.e(TAG, "Unknown rotary input type: %d", event.getInputType()); 617 return Collections.EMPTY_LIST; 618 } 619 ArrayList<KeyEvent> keyEvents = new ArrayList<>(numEvents); 620 for (int i = 0; i < numClicks; i++) { 621 long uptime = event.getUptimeMillisForClick(i); 622 KeyEvent downEvent = createKeyEvent(/* down= */ true, uptime, uptime, keyCode); 623 KeyEvent upEvent = createKeyEvent(/* down= */ false, uptime, uptime, keyCode); 624 keyEvents.add(downEvent); 625 keyEvents.add(upEvent); 626 } 627 return keyEvents; 628 } 629 createKeyEvent(boolean down, long downTime, long eventTime, int keyCode)630 private static KeyEvent createKeyEvent(boolean down, long downTime, long eventTime, 631 int keyCode) { 632 return new KeyEvent( 633 downTime, 634 eventTime, 635 /* action= */ down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, 636 keyCode, 637 /* repeat= */ 0, 638 /* metaState= */ 0, 639 /* deviceId= */ 0, 640 /* scancode= */ 0, 641 /* flags= */ 0, 642 InputDevice.SOURCE_CLASS_BUTTON); 643 } 644 645 @Override requestInputEventCapture(ICarInputCallback callback, @DisplayTypeEnum int targetDisplayType, int[] inputTypes, int requestFlags)646 public int requestInputEventCapture(ICarInputCallback callback, 647 @DisplayTypeEnum int targetDisplayType, 648 int[] inputTypes, int requestFlags) { 649 return mCaptureController.requestInputEventCapture(callback, targetDisplayType, inputTypes, 650 requestFlags); 651 } 652 653 @Override releaseInputEventCapture(ICarInputCallback callback, @DisplayTypeEnum int targetDisplayType)654 public void releaseInputEventCapture(ICarInputCallback callback, 655 @DisplayTypeEnum int targetDisplayType) { 656 mCaptureController.releaseInputEventCapture(callback, targetDisplayType); 657 } 658 659 /** 660 * Injects the {@link KeyEvent} passed as parameter against Car Input API. 661 * <p> 662 * The event's display id will be overwritten accordingly to the display type (it will be 663 * retrieved from {@link CarOccupantZoneService}). 664 * 665 * @param event the event to inject 666 * @param targetDisplayType the display type associated with the event 667 * @throws SecurityException when caller doesn't have INJECT_EVENTS permission granted 668 */ 669 @Override injectKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType)670 public void injectKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 671 // Permission check 672 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( 673 android.Manifest.permission.INJECT_EVENTS)) { 674 throw new SecurityException("Injecting KeyEvent requires INJECT_EVENTS permission"); 675 } 676 677 long token = Binder.clearCallingIdentity(); 678 try { 679 // Redirect event to onKeyEvent 680 onKeyEvent(event, targetDisplayType); 681 } finally { 682 Binder.restoreCallingIdentity(token); 683 } 684 } 685 686 /** 687 * Injects the {@link KeyEvent} passed as parameter against Car Input API. 688 * <p> 689 * The event's display id will be overwritten accordingly to the display type (it will be 690 * retrieved from {@link CarOccupantZoneService}). 691 * 692 * @param event the event to inject 693 * @param targetDisplayType the display type associated with the event 694 * @param seat the seat associated with the event 695 * @throws SecurityException when caller doesn't have INJECT_EVENTS permission granted 696 */ injectKeyEventForSeat(KeyEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)697 public void injectKeyEventForSeat(KeyEvent event, @DisplayTypeEnum int targetDisplayType, 698 @VehicleAreaSeat.Enum int seat) { 699 // Permission check 700 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( 701 android.Manifest.permission.INJECT_EVENTS)) { 702 throw new SecurityException("Injecting KeyEvent requires INJECT_EVENTS permission"); 703 } 704 705 long token = Binder.clearCallingIdentity(); 706 try { 707 // Redirect event to onKeyEvent 708 onKeyEvent(event, targetDisplayType, seat); 709 } finally { 710 Binder.restoreCallingIdentity(token); 711 } 712 } 713 714 /** 715 * Injects the {@link MotionEvent} passed as parameter against Car Input API. 716 * <p> 717 * The event's display id will be overwritten accordingly to the display type (it will be 718 * retrieved from {@link CarOccupantZoneService}). 719 * 720 * @param event the event to inject 721 * @param targetDisplayType the display type associated with the event 722 * @param seat the seat associated with the event 723 * @throws SecurityException when caller doesn't have INJECT_EVENTS permission granted 724 */ injectMotionEventForSeat(MotionEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)725 public void injectMotionEventForSeat(MotionEvent event, @DisplayTypeEnum int targetDisplayType, 726 @VehicleAreaSeat.Enum int seat) { 727 // Permission check 728 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( 729 android.Manifest.permission.INJECT_EVENTS)) { 730 throw new SecurityException("Injecting MotionEvent requires INJECT_EVENTS permission"); 731 } 732 733 long token = Binder.clearCallingIdentity(); 734 try { 735 // Redirect event to onMotionEvent 736 onMotionEvent(event, targetDisplayType, seat); 737 } finally { 738 Binder.restoreCallingIdentity(token); 739 } 740 } 741 handleVoiceAssistKey(KeyEvent event)742 private void handleVoiceAssistKey(KeyEvent event) { 743 int action = event.getAction(); 744 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 745 mVoiceKeyTimer.keyDown(); 746 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN); 747 } else if (action == KeyEvent.ACTION_UP) { 748 if (mVoiceKeyTimer.keyUp()) { 749 // Long press already handled by handleVoiceAssistLongPress(), nothing more to do. 750 // Hand it off to projection, if it's interested, otherwise we're done. 751 dispatchProjectionKeyEvent( 752 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 753 return; 754 } 755 756 if (dispatchProjectionKeyEvent( 757 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP)) { 758 return; 759 } 760 761 launchDefaultVoiceAssistantHandler(); 762 } 763 } 764 handleVoiceAssistLongPress()765 private void handleVoiceAssistLongPress() { 766 // If projection wants this event, let it take it. 767 if (dispatchProjectionKeyEvent( 768 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN)) { 769 return; 770 } 771 // Otherwise, try to launch voice recognition on a BT device. 772 if (launchBluetoothVoiceRecognition()) { 773 return; 774 } 775 // Finally, fallback to the default voice assist handling. 776 launchDefaultVoiceAssistantHandler(); 777 } 778 handleCallKey(KeyEvent event)779 private void handleCallKey(KeyEvent event) { 780 int action = event.getAction(); 781 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 782 mCallKeyTimer.keyDown(); 783 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN); 784 } else if (action == KeyEvent.ACTION_UP) { 785 if (mCallKeyTimer.keyUp()) { 786 // Long press already handled by handleCallLongPress(), nothing more to do. 787 // Hand it off to projection, if it's interested, otherwise we're done. 788 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 789 return; 790 } 791 792 if (acceptCallIfRinging()) { 793 // Ringing call answered, nothing more to do. 794 return; 795 } 796 797 if (mShouldCallButtonEndOngoingCallSupplier.getAsBoolean() && endCall()) { 798 // On-going call ended, nothing more to do. 799 return; 800 } 801 802 if (dispatchProjectionKeyEvent( 803 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP)) { 804 return; 805 } 806 807 launchDialerHandler(); 808 } 809 } 810 handleCallLongPress()811 private void handleCallLongPress() { 812 // Long-press answers call if ringing, same as short-press. 813 if (acceptCallIfRinging()) { 814 return; 815 } 816 817 if (mShouldCallButtonEndOngoingCallSupplier.getAsBoolean() && endCall()) { 818 return; 819 } 820 821 if (dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN)) { 822 return; 823 } 824 825 dialLastCallHandler(); 826 } 827 handlePowerKey(KeyEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)828 private void handlePowerKey(KeyEvent event, @DisplayTypeEnum int targetDisplayType, 829 @VehicleAreaSeat.Enum int seat) { 830 if (DBG) { 831 Slogf.d(TAG, "called handlePowerKey: DisplayType=%d, VehicleAreaSeat=%d", 832 targetDisplayType, seat); 833 } 834 835 int displayId = getDisplayIdForSeat(targetDisplayType, seat); 836 if (displayId == Display.INVALID_DISPLAY) { 837 Slogf.e(TAG, "Failed to set display power state : Invalid display type=%d, seat=%d", 838 targetDisplayType, seat); 839 return; 840 } 841 842 boolean isOn = mSystemInterface.isDisplayEnabled(displayId); 843 844 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 845 if (!isOn) { 846 mCarPowerService.setDisplayPowerState(displayId, /* enable= */ true); 847 setPowerKeyHandled(seat, /* handled= */ true); 848 } 849 } else if (event.getAction() == KeyEvent.ACTION_UP) { 850 if (isOn && !isPowerKeyHandled(seat)) { 851 mCarPowerService.setDisplayPowerState(displayId, /* enable= */ false); 852 } 853 setPowerKeyHandled(seat, /* handled= */ false); 854 } 855 } 856 isPowerKeyHandled(@ehicleAreaSeat.Enum int seat)857 private boolean isPowerKeyHandled(@VehicleAreaSeat.Enum int seat) { 858 return mPowerKeyHandled.get(seat); 859 } 860 setPowerKeyHandled(@ehicleAreaSeat.Enum int seat, boolean handled)861 private void setPowerKeyHandled(@VehicleAreaSeat.Enum int seat, boolean handled) { 862 mPowerKeyHandled.put(seat, handled); 863 } 864 handleHomeKey(KeyEvent event, @DisplayTypeEnum int targetDisplayType, @VehicleAreaSeat.Enum int seat)865 private void handleHomeKey(KeyEvent event, @DisplayTypeEnum int targetDisplayType, 866 @VehicleAreaSeat.Enum int seat) { 867 if (DBG) { 868 Slogf.d(TAG, "called handleHomeKey: DisplayType=%d, VehicleAreaSeat=%d", 869 targetDisplayType, seat); 870 } 871 if (event.getAction() == KeyEvent.ACTION_UP) { 872 int zoneId = mCarOccupantZoneService.getOccupantZoneIdForSeat(seat); 873 if (zoneId == OccupantZoneInfo.INVALID_ZONE_ID) { 874 Slogf.w(TAG, "Failed to get occupant zone id : Invalid seat=%d", seat); 875 return; 876 } 877 878 int userId = mCarOccupantZoneService.getUserForOccupant(zoneId); 879 int displayId = mCarOccupantZoneService.getDisplayForOccupant(zoneId, 880 targetDisplayType); 881 CarServiceUtils.startHomeForUserAndDisplay(mContext, userId, displayId); 882 } 883 } 884 dispatchProjectionKeyEvent(@arProjectionManager.KeyEventNum int event)885 private boolean dispatchProjectionKeyEvent(@CarProjectionManager.KeyEventNum int event) { 886 CarProjectionManager.ProjectionKeyEventHandler projectionKeyEventHandler; 887 synchronized (mLock) { 888 projectionKeyEventHandler = mProjectionKeyEventHandler; 889 if (projectionKeyEventHandler == null || !mProjectionKeyEventsSubscribed.get(event)) { 890 // No event handler, or event handler doesn't want this event - we're done. 891 return false; 892 } 893 } 894 895 projectionKeyEventHandler.onKeyEvent(event); 896 return true; 897 } 898 launchDialerHandler()899 private void launchDialerHandler() { 900 Slogf.i(TAG, "call key, launch dialer intent"); 901 Intent dialerIntent = new Intent(Intent.ACTION_DIAL); 902 mContext.startActivityAsUser(dialerIntent, UserHandle.CURRENT); 903 } 904 dialLastCallHandler()905 private void dialLastCallHandler() { 906 Slogf.i(TAG, "call key, dialing last call"); 907 908 String lastNumber = mLastCalledNumberSupplier.get(); 909 if (!TextUtils.isEmpty(lastNumber)) { 910 Intent callLastNumberIntent = new Intent(Intent.ACTION_CALL) 911 .setData(Uri.fromParts("tel", lastNumber, /* fragment= */ null)) 912 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 913 mContext.startActivityAsUser(callLastNumberIntent, UserHandle.CURRENT); 914 } 915 } 916 acceptCallIfRinging()917 private boolean acceptCallIfRinging() { 918 if (mTelecomManager != null && mTelecomManager.isRinging()) { 919 Slogf.i(TAG, "call key while ringing. Answer the call!"); 920 mTelecomManager.acceptRingingCall(); 921 return true; 922 } 923 return false; 924 } 925 endCall()926 private boolean endCall() { 927 if (mTelecomManager != null && mTelecomManager.isInCall()) { 928 Slogf.i(TAG, "End the call!"); 929 mTelecomManager.endCall(); 930 return true; 931 } 932 return false; 933 } 934 isBluetoothVoiceRecognitionEnabled()935 private boolean isBluetoothVoiceRecognitionEnabled() { 936 Resources res = mContext.getResources(); 937 return res.getBoolean(R.bool.enableLongPressBluetoothVoiceRecognition); 938 } 939 launchBluetoothVoiceRecognition()940 private boolean launchBluetoothVoiceRecognition() { 941 if (isBluetoothVoiceRecognitionEnabled()) { 942 Slogf.d(TAG, "Attempting to start Bluetooth Voice Recognition."); 943 return mCarBluetoothService.startBluetoothVoiceRecognition(); 944 } 945 Slogf.d(TAG, "Unable to start Bluetooth Voice Recognition, it is not enabled."); 946 return false; 947 } 948 launchDefaultVoiceAssistantHandler()949 private void launchDefaultVoiceAssistantHandler() { 950 Slogf.d(TAG, "voice key, invoke AssistUtilsHelper"); 951 952 if (!AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, mShowCallback)) { 953 Slogf.w(TAG, "Unable to retrieve assist component for current user"); 954 } 955 } 956 957 /** 958 * @return false if the KeyEvent isn't consumed because there is no 959 * InstrumentClusterKeyListener. 960 */ handleInstrumentClusterKey(KeyEvent event)961 private boolean handleInstrumentClusterKey(KeyEvent event) { 962 KeyEventListener listener = null; 963 synchronized (mLock) { 964 listener = mInstrumentClusterKeyListener; 965 } 966 if (listener == null) { 967 return false; 968 } 969 listener.onKeyEvent(event); 970 return true; 971 } 972 getAccessibilityServicesToBeEnabled()973 private List<String> getAccessibilityServicesToBeEnabled() { 974 String carSafetyAccessibilityServiceComponentName = 975 BuiltinPackageDependency.getComponentName(CAR_ACCESSIBILITY_SERVICE_CLASS); 976 ArrayList<String> accessibilityServicesToBeEnabled = new ArrayList<>(); 977 accessibilityServicesToBeEnabled.add(carSafetyAccessibilityServiceComponentName); 978 if (!TextUtils.isEmpty(mRotaryServiceComponentName)) { 979 accessibilityServicesToBeEnabled.add(mRotaryServiceComponentName); 980 } 981 return accessibilityServicesToBeEnabled; 982 } 983 createServiceListFromSettingsString( String accessibilityServicesString)984 private static List<String> createServiceListFromSettingsString( 985 String accessibilityServicesString) { 986 return TextUtils.isEmpty(accessibilityServicesString) 987 ? new ArrayList<>() 988 : Arrays.asList(accessibilityServicesString.split( 989 ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR)); 990 } 991 992 @Override 993 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)994 public void dump(IndentingPrintWriter writer) { 995 writer.println("*Input Service*"); 996 writer.println("Long-press delay: " + mLongPressDelaySupplier.getAsInt() + "ms"); 997 writer.println("Call button ends ongoing call: " 998 + mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()); 999 mCaptureController.dump(writer); 1000 } 1001 updateCarAccessibilityServicesSettings(@serIdInt int userId)1002 private void updateCarAccessibilityServicesSettings(@UserIdInt int userId) { 1003 if (UserHelperLite.isHeadlessSystemUser(userId)) { 1004 return; 1005 } 1006 List<String> accessibilityServicesToBeEnabled = getAccessibilityServicesToBeEnabled(); 1007 ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId); 1008 List<String> alreadyEnabledServices = createServiceListFromSettingsString( 1009 Settings.Secure.getString(contentResolverForUser, 1010 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)); 1011 1012 int retry = 0; 1013 while (!alreadyEnabledServices.containsAll(accessibilityServicesToBeEnabled) 1014 && retry <= MAX_RETRIES_FOR_ENABLING_ACCESSIBILITY_SERVICES) { 1015 ArrayList<String> enabledServicesList = new ArrayList<>(alreadyEnabledServices); 1016 int numAccessibilityServicesToBeEnabled = accessibilityServicesToBeEnabled.size(); 1017 for (int i = 0; i < numAccessibilityServicesToBeEnabled; i++) { 1018 String serviceToBeEnabled = accessibilityServicesToBeEnabled.get(i); 1019 if (!enabledServicesList.contains(serviceToBeEnabled)) { 1020 enabledServicesList.add(serviceToBeEnabled); 1021 } 1022 } 1023 Settings.Secure.putString(contentResolverForUser, 1024 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 1025 String.join(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR, enabledServicesList)); 1026 // Read again to account for any race condition with other parts of the code that might 1027 // be enabling other accessibility services. 1028 alreadyEnabledServices = createServiceListFromSettingsString( 1029 Settings.Secure.getString(contentResolverForUser, 1030 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)); 1031 retry++; 1032 } 1033 if (!alreadyEnabledServices.containsAll(accessibilityServicesToBeEnabled)) { 1034 Slogf.e(TAG, "Failed to enable accessibility services"); 1035 } 1036 1037 Settings.Secure.putString(contentResolverForUser, Settings.Secure.ACCESSIBILITY_ENABLED, 1038 "1"); 1039 } 1040 } 1041