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