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.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 20 21 import static com.android.car.BuiltinPackageDependency.CAR_ACCESSIBILITY_SERVICE_CLASS; 22 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 23 import static com.android.car.util.Utils.getContentResolverForUser; 24 import static com.android.car.util.Utils.isEventOfType; 25 26 import android.annotation.Nullable; 27 import android.annotation.UserIdInt; 28 import android.car.CarOccupantZoneManager; 29 import android.car.CarProjectionManager; 30 import android.car.builtin.input.InputManagerHelper; 31 import android.car.builtin.util.AssistUtilsHelper; 32 import android.car.builtin.util.AssistUtilsHelper.VoiceInteractionSessionShowCallbackHelper; 33 import android.car.builtin.util.Slogf; 34 import android.car.builtin.view.KeyEventHelper; 35 import android.car.input.CarInputManager; 36 import android.car.input.CustomInputEvent; 37 import android.car.input.ICarInput; 38 import android.car.input.ICarInputCallback; 39 import android.car.input.RotaryEvent; 40 import android.car.user.CarUserManager.UserLifecycleListener; 41 import android.car.user.UserLifecycleEventFilter; 42 import android.content.ContentResolver; 43 import android.content.Context; 44 import android.content.Intent; 45 import android.content.pm.PackageManager; 46 import android.content.res.Resources; 47 import android.hardware.input.InputManager; 48 import android.net.Uri; 49 import android.os.Binder; 50 import android.os.Handler; 51 import android.os.UserHandle; 52 import android.provider.CallLog.Calls; 53 import android.provider.Settings; 54 import android.telecom.TelecomManager; 55 import android.text.TextUtils; 56 import android.view.InputDevice; 57 import android.view.KeyEvent; 58 import android.view.ViewConfiguration; 59 60 import com.android.car.bluetooth.CarBluetoothService; 61 import com.android.car.hal.InputHalService; 62 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 63 import com.android.car.internal.common.UserHelperLite; 64 import com.android.car.internal.util.IndentingPrintWriter; 65 import com.android.car.user.CarUserService; 66 import com.android.internal.annotations.GuardedBy; 67 import com.android.internal.annotations.VisibleForTesting; 68 69 import java.util.ArrayList; 70 import java.util.Arrays; 71 import java.util.BitSet; 72 import java.util.Collections; 73 import java.util.List; 74 import java.util.function.BooleanSupplier; 75 import java.util.function.IntSupplier; 76 import java.util.function.Supplier; 77 78 /** 79 * CarInputService monitors and handles input event through vehicle HAL. 80 */ 81 public class CarInputService extends ICarInput.Stub 82 implements CarServiceBase, InputHalService.InputListener { 83 public static final String ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ":"; 84 85 private static final int MAX_RETRIES_FOR_ENABLING_ACCESSIBILITY_SERVICES = 5; 86 87 @VisibleForTesting 88 static final String TAG = CarLog.TAG_INPUT; 89 90 @VisibleForTesting 91 static final String LONG_PRESS_TIMEOUT = "long_press_timeout"; 92 93 /** An interface to receive {@link KeyEvent}s as they occur. */ 94 public interface KeyEventListener { 95 /** Called when a key event occurs. */ onKeyEvent(KeyEvent event)96 void onKeyEvent(KeyEvent event); 97 } 98 99 private final class KeyPressTimer { 100 private final Runnable mLongPressRunnable; 101 private final Runnable mCallback = this::onTimerExpired; 102 private final IntSupplier mLongPressDelaySupplier; 103 104 @GuardedBy("CarInputService.this.mLock") 105 private final Handler mHandler; 106 @GuardedBy("CarInputService.this.mLock") 107 private boolean mDown; 108 @GuardedBy("CarInputService.this.mLock") 109 private boolean mLongPress = false; 110 KeyPressTimer( Handler handler, IntSupplier longPressDelaySupplier, Runnable longPressRunnable)111 KeyPressTimer( 112 Handler handler, IntSupplier longPressDelaySupplier, Runnable longPressRunnable) { 113 mHandler = handler; 114 mLongPressRunnable = longPressRunnable; 115 mLongPressDelaySupplier = longPressDelaySupplier; 116 } 117 118 /** Marks that a key was pressed, and starts the long-press timer. */ keyDown()119 void keyDown() { 120 synchronized (mLock) { 121 mDown = true; 122 mLongPress = false; 123 mHandler.removeCallbacks(mCallback); 124 mHandler.postDelayed(mCallback, mLongPressDelaySupplier.getAsInt()); 125 } 126 } 127 128 /** 129 * Marks that a key was released, and stops the long-press timer. 130 * <p> 131 * Returns true if the press was a long-press. 132 */ keyUp()133 boolean keyUp() { 134 synchronized (mLock) { 135 mHandler.removeCallbacks(mCallback); 136 mDown = false; 137 return mLongPress; 138 } 139 } 140 onTimerExpired()141 private void onTimerExpired() { 142 synchronized (mLock) { 143 // If the timer expires after key-up, don't retroactively make the press long. 144 if (!mDown) { 145 return; 146 } 147 mLongPress = true; 148 } 149 mLongPressRunnable.run(); 150 } 151 } 152 153 private final VoiceInteractionSessionShowCallbackHelper mShowCallback = 154 new VoiceInteractionSessionShowCallbackHelper() { 155 @Override 156 public void onFailed() { 157 Slogf.w(TAG, "Failed to show VoiceInteractionSession"); 158 } 159 160 @Override 161 public void onShown() { 162 Slogf.d(TAG, "VoiceInteractionSessionShowCallbackHelper onShown()"); 163 } 164 }; 165 166 private final Context mContext; 167 private final InputHalService mInputHalService; 168 private final CarUserService mUserService; 169 private final CarOccupantZoneService mCarOccupantZoneService; 170 private final CarBluetoothService mCarBluetoothService; 171 private final TelecomManager mTelecomManager; 172 173 // The default handler for main-display input events. By default, injects the events into 174 // the input queue via InputManager, but can be overridden for testing. 175 private final KeyEventListener mMainDisplayHandler; 176 // The supplier for the last-called number. By default, gets the number from the call log. 177 // May be overridden for testing. 178 private final Supplier<String> mLastCalledNumberSupplier; 179 // The supplier for the system long-press delay, in milliseconds. By default, gets the value 180 // from Settings.Secure for the current user, falling back to the system-wide default 181 // long-press delay defined in ViewConfiguration. May be overridden for testing. 182 private final IntSupplier mLongPressDelaySupplier; 183 // ComponentName of the RotaryService. 184 private final String mRotaryServiceComponentName; 185 186 private final BooleanSupplier mShouldCallButtonEndOngoingCallSupplier; 187 188 private final Object mLock = new Object(); 189 190 @GuardedBy("mLock") 191 private CarProjectionManager.ProjectionKeyEventHandler mProjectionKeyEventHandler; 192 193 @GuardedBy("mLock") 194 private final BitSet mProjectionKeyEventsSubscribed = new BitSet(); 195 196 private final KeyPressTimer mVoiceKeyTimer; 197 private final KeyPressTimer mCallKeyTimer; 198 199 @GuardedBy("mLock") 200 private KeyEventListener mInstrumentClusterKeyListener; 201 202 private final InputCaptureClientController mCaptureController; 203 204 private final UserLifecycleListener mUserLifecycleListener = event -> { 205 if (!isEventOfType(TAG, event, USER_LIFECYCLE_EVENT_TYPE_SWITCHING)) { 206 return; 207 } 208 Slogf.d(TAG, "CarInputService.onEvent(%s)", event); 209 210 updateCarAccessibilityServicesSettings(event.getUserId()); 211 }; 212 getViewLongPressDelay(Context context)213 private static int getViewLongPressDelay(Context context) { 214 return Settings.Secure.getInt( 215 getContentResolverForUser(context, UserHandle.CURRENT.getIdentifier()), 216 LONG_PRESS_TIMEOUT, 217 ViewConfiguration.getLongPressTimeout()); 218 } 219 CarInputService(Context context, InputHalService inputHalService, CarUserService userService, CarOccupantZoneService occupantZoneService, CarBluetoothService bluetoothService)220 public CarInputService(Context context, InputHalService inputHalService, 221 CarUserService userService, CarOccupantZoneService occupantZoneService, 222 CarBluetoothService bluetoothService) { 223 this(context, inputHalService, userService, occupantZoneService, bluetoothService, 224 new Handler(CarServiceUtils.getCommonHandlerThread().getLooper()), 225 context.getSystemService(TelecomManager.class), 226 event -> InputManagerHelper.injectInputEvent( 227 context.getSystemService(InputManager.class), event), 228 () -> Calls.getLastOutgoingCall(context), 229 () -> getViewLongPressDelay(context), 230 () -> context.getResources().getBoolean(R.bool.config_callButtonEndsOngoingCall), 231 new InputCaptureClientController(context)); 232 } 233 234 @VisibleForTesting CarInputService(Context context, InputHalService inputHalService, CarUserService userService, CarOccupantZoneService occupantZoneService, CarBluetoothService bluetoothService, Handler handler, TelecomManager telecomManager, KeyEventListener mainDisplayHandler, Supplier<String> lastCalledNumberSupplier, IntSupplier longPressDelaySupplier, BooleanSupplier shouldCallButtonEndOngoingCallSupplier, InputCaptureClientController captureController)235 CarInputService(Context context, InputHalService inputHalService, CarUserService userService, 236 CarOccupantZoneService occupantZoneService, CarBluetoothService bluetoothService, 237 Handler handler, TelecomManager telecomManager, KeyEventListener mainDisplayHandler, 238 Supplier<String> lastCalledNumberSupplier, IntSupplier longPressDelaySupplier, 239 BooleanSupplier shouldCallButtonEndOngoingCallSupplier, 240 InputCaptureClientController captureController) { 241 mContext = context; 242 mCaptureController = captureController; 243 mInputHalService = inputHalService; 244 mUserService = userService; 245 mCarOccupantZoneService = occupantZoneService; 246 mCarBluetoothService = bluetoothService; 247 mTelecomManager = telecomManager; 248 mMainDisplayHandler = mainDisplayHandler; 249 mLastCalledNumberSupplier = lastCalledNumberSupplier; 250 mLongPressDelaySupplier = longPressDelaySupplier; 251 252 mVoiceKeyTimer = 253 new KeyPressTimer( 254 handler, longPressDelaySupplier, this::handleVoiceAssistLongPress); 255 mCallKeyTimer = 256 new KeyPressTimer(handler, longPressDelaySupplier, this::handleCallLongPress); 257 258 mRotaryServiceComponentName = mContext.getString(R.string.rotaryService); 259 mShouldCallButtonEndOngoingCallSupplier = shouldCallButtonEndOngoingCallSupplier; 260 } 261 262 /** 263 * Set projection key event listener. If null, unregister listener. 264 */ setProjectionKeyEventHandler( @ullable CarProjectionManager.ProjectionKeyEventHandler listener, @Nullable BitSet events)265 public void setProjectionKeyEventHandler( 266 @Nullable CarProjectionManager.ProjectionKeyEventHandler listener, 267 @Nullable BitSet events) { 268 synchronized (mLock) { 269 mProjectionKeyEventHandler = listener; 270 mProjectionKeyEventsSubscribed.clear(); 271 if (events != null) { 272 mProjectionKeyEventsSubscribed.or(events); 273 } 274 } 275 } 276 277 /** 278 * Sets the instrument cluster key event listener. 279 */ setInstrumentClusterKeyListener(KeyEventListener listener)280 public void setInstrumentClusterKeyListener(KeyEventListener listener) { 281 synchronized (mLock) { 282 mInstrumentClusterKeyListener = listener; 283 } 284 } 285 286 @Override init()287 public void init() { 288 if (!mInputHalService.isKeyInputSupported()) { 289 Slogf.w(TAG, "Hal does not support key input."); 290 return; 291 } 292 Slogf.d(TAG, "Hal supports key input."); 293 mInputHalService.setInputListener(this); 294 UserLifecycleEventFilter userSwitchingEventFilter = new UserLifecycleEventFilter.Builder() 295 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build(); 296 mUserService.addUserLifecycleListener(userSwitchingEventFilter, mUserLifecycleListener); 297 } 298 299 @Override release()300 public void release() { 301 synchronized (mLock) { 302 mProjectionKeyEventHandler = null; 303 mProjectionKeyEventsSubscribed.clear(); 304 mInstrumentClusterKeyListener = null; 305 } 306 mUserService.removeUserLifecycleListener(mUserLifecycleListener); 307 } 308 309 @Override onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType)310 public void onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 311 // Special case key code that have special "long press" handling for automotive 312 switch (event.getKeyCode()) { 313 case KeyEvent.KEYCODE_VOICE_ASSIST: 314 handleVoiceAssistKey(event); 315 return; 316 case KeyEvent.KEYCODE_CALL: 317 handleCallKey(event); 318 return; 319 default: 320 break; 321 } 322 323 assignDisplayId(event, targetDisplayType); 324 325 // Allow specifically targeted keys to be routed to the cluster 326 if (targetDisplayType == CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER 327 && handleInstrumentClusterKey(event)) { 328 return; 329 } 330 if (mCaptureController.onKeyEvent(targetDisplayType, event)) { 331 return; 332 } 333 mMainDisplayHandler.onKeyEvent(event); 334 } 335 assignDisplayId(KeyEvent event, @DisplayTypeEnum int targetDisplayType)336 private void assignDisplayId(KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 337 // Setting display id for driver user id (currently MAIN and CLUSTER display types are 338 // linked to driver user only) 339 int newDisplayId = mCarOccupantZoneService.getDisplayIdForDriver(targetDisplayType); 340 341 // Display id is overridden even if already set. 342 KeyEventHelper.setDisplayId(event, newDisplayId); 343 } 344 345 @Override onRotaryEvent(RotaryEvent event, @DisplayTypeEnum int targetDisplay)346 public void onRotaryEvent(RotaryEvent event, @DisplayTypeEnum int targetDisplay) { 347 if (!mCaptureController.onRotaryEvent(targetDisplay, event)) { 348 List<KeyEvent> keyEvents = rotaryEventToKeyEvents(event); 349 for (KeyEvent keyEvent : keyEvents) { 350 onKeyEvent(keyEvent, targetDisplay); 351 } 352 } 353 } 354 355 @Override onCustomInputEvent(CustomInputEvent event)356 public void onCustomInputEvent(CustomInputEvent event) { 357 if (!mCaptureController.onCustomInputEvent(event)) { 358 Slogf.w(TAG, "Failed to propagate (%s)", event); 359 return; 360 } 361 Slogf.d(TAG, "Succeed injecting (%s)", event); 362 } 363 rotaryEventToKeyEvents(RotaryEvent event)364 private static List<KeyEvent> rotaryEventToKeyEvents(RotaryEvent event) { 365 int numClicks = event.getNumberOfClicks(); 366 int numEvents = numClicks * 2; // up / down per each click 367 boolean clockwise = event.isClockwise(); 368 int keyCode; 369 switch (event.getInputType()) { 370 case CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION: 371 keyCode = clockwise 372 ? KeyEvent.KEYCODE_NAVIGATE_NEXT 373 : KeyEvent.KEYCODE_NAVIGATE_PREVIOUS; 374 break; 375 case CarInputManager.INPUT_TYPE_ROTARY_VOLUME: 376 keyCode = clockwise 377 ? KeyEvent.KEYCODE_VOLUME_UP 378 : KeyEvent.KEYCODE_VOLUME_DOWN; 379 break; 380 default: 381 Slogf.e(TAG, "Unknown rotary input type: %d", event.getInputType()); 382 return Collections.EMPTY_LIST; 383 } 384 ArrayList<KeyEvent> keyEvents = new ArrayList<>(numEvents); 385 for (int i = 0; i < numClicks; i++) { 386 long uptime = event.getUptimeMillisForClick(i); 387 KeyEvent downEvent = createKeyEvent(/* down= */ true, uptime, uptime, keyCode); 388 KeyEvent upEvent = createKeyEvent(/* down= */ false, uptime, uptime, keyCode); 389 keyEvents.add(downEvent); 390 keyEvents.add(upEvent); 391 } 392 return keyEvents; 393 } 394 createKeyEvent(boolean down, long downTime, long eventTime, int keyCode)395 private static KeyEvent createKeyEvent(boolean down, long downTime, long eventTime, 396 int keyCode) { 397 return new KeyEvent( 398 downTime, 399 eventTime, 400 /* action= */ down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, 401 keyCode, 402 /* repeat= */ 0, 403 /* metaState= */ 0, 404 /* deviceId= */ 0, 405 /* scancode= */ 0, 406 /* flags= */ 0, 407 InputDevice.SOURCE_CLASS_BUTTON); 408 } 409 410 @Override requestInputEventCapture(ICarInputCallback callback, @DisplayTypeEnum int targetDisplayType, int[] inputTypes, int requestFlags)411 public int requestInputEventCapture(ICarInputCallback callback, 412 @DisplayTypeEnum int targetDisplayType, 413 int[] inputTypes, int requestFlags) { 414 return mCaptureController.requestInputEventCapture(callback, targetDisplayType, inputTypes, 415 requestFlags); 416 } 417 418 @Override releaseInputEventCapture(ICarInputCallback callback, @DisplayTypeEnum int targetDisplayType)419 public void releaseInputEventCapture(ICarInputCallback callback, 420 @DisplayTypeEnum int targetDisplayType) { 421 mCaptureController.releaseInputEventCapture(callback, targetDisplayType); 422 } 423 424 /** 425 * Injects the {@link KeyEvent} passed as parameter against Car Input API. 426 * <p> 427 * The event's display id will be overridden accordingly to the display type (it will be 428 * retrieved from {@link CarOccupantZoneService}). 429 * 430 * @param event the event to inject 431 * @param targetDisplayType the display type associated with the event 432 * @throws SecurityException when caller doesn't have INJECT_EVENTS permission granted 433 */ 434 @Override injectKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType)435 public void injectKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType) { 436 // Permission check 437 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( 438 android.Manifest.permission.INJECT_EVENTS)) { 439 throw new SecurityException("Injecting KeyEvent requires INJECT_EVENTS permission"); 440 } 441 442 long token = Binder.clearCallingIdentity(); 443 try { 444 // Redirect event to onKeyEvent 445 onKeyEvent(event, targetDisplayType); 446 } finally { 447 Binder.restoreCallingIdentity(token); 448 } 449 } 450 handleVoiceAssistKey(KeyEvent event)451 private void handleVoiceAssistKey(KeyEvent event) { 452 int action = event.getAction(); 453 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 454 mVoiceKeyTimer.keyDown(); 455 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN); 456 } else if (action == KeyEvent.ACTION_UP) { 457 if (mVoiceKeyTimer.keyUp()) { 458 // Long press already handled by handleVoiceAssistLongPress(), nothing more to do. 459 // Hand it off to projection, if it's interested, otherwise we're done. 460 dispatchProjectionKeyEvent( 461 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 462 return; 463 } 464 465 if (dispatchProjectionKeyEvent( 466 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP)) { 467 return; 468 } 469 470 launchDefaultVoiceAssistantHandler(); 471 } 472 } 473 handleVoiceAssistLongPress()474 private void handleVoiceAssistLongPress() { 475 // If projection wants this event, let it take it. 476 if (dispatchProjectionKeyEvent( 477 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN)) { 478 return; 479 } 480 // Otherwise, try to launch voice recognition on a BT device. 481 if (launchBluetoothVoiceRecognition()) { 482 return; 483 } 484 // Finally, fallback to the default voice assist handling. 485 launchDefaultVoiceAssistantHandler(); 486 } 487 handleCallKey(KeyEvent event)488 private void handleCallKey(KeyEvent event) { 489 int action = event.getAction(); 490 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 491 mCallKeyTimer.keyDown(); 492 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN); 493 } else if (action == KeyEvent.ACTION_UP) { 494 if (mCallKeyTimer.keyUp()) { 495 // Long press already handled by handleCallLongPress(), nothing more to do. 496 // Hand it off to projection, if it's interested, otherwise we're done. 497 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 498 return; 499 } 500 501 if (acceptCallIfRinging()) { 502 // Ringing call answered, nothing more to do. 503 return; 504 } 505 506 if (mShouldCallButtonEndOngoingCallSupplier.getAsBoolean() && endCall()) { 507 // On-going call ended, nothing more to do. 508 return; 509 } 510 511 if (dispatchProjectionKeyEvent( 512 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP)) { 513 return; 514 } 515 516 launchDialerHandler(); 517 } 518 } 519 handleCallLongPress()520 private void handleCallLongPress() { 521 // Long-press answers call if ringing, same as short-press. 522 if (acceptCallIfRinging()) { 523 return; 524 } 525 526 if (mShouldCallButtonEndOngoingCallSupplier.getAsBoolean() && endCall()) { 527 return; 528 } 529 530 if (dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN)) { 531 return; 532 } 533 534 dialLastCallHandler(); 535 } 536 dispatchProjectionKeyEvent(@arProjectionManager.KeyEventNum int event)537 private boolean dispatchProjectionKeyEvent(@CarProjectionManager.KeyEventNum int event) { 538 CarProjectionManager.ProjectionKeyEventHandler projectionKeyEventHandler; 539 synchronized (mLock) { 540 projectionKeyEventHandler = mProjectionKeyEventHandler; 541 if (projectionKeyEventHandler == null || !mProjectionKeyEventsSubscribed.get(event)) { 542 // No event handler, or event handler doesn't want this event - we're done. 543 return false; 544 } 545 } 546 547 projectionKeyEventHandler.onKeyEvent(event); 548 return true; 549 } 550 launchDialerHandler()551 private void launchDialerHandler() { 552 Slogf.i(TAG, "call key, launch dialer intent"); 553 Intent dialerIntent = new Intent(Intent.ACTION_DIAL); 554 mContext.startActivityAsUser(dialerIntent, UserHandle.CURRENT); 555 } 556 dialLastCallHandler()557 private void dialLastCallHandler() { 558 Slogf.i(TAG, "call key, dialing last call"); 559 560 String lastNumber = mLastCalledNumberSupplier.get(); 561 if (!TextUtils.isEmpty(lastNumber)) { 562 Intent callLastNumberIntent = new Intent(Intent.ACTION_CALL) 563 .setData(Uri.fromParts("tel", lastNumber, /* fragment= */ null)) 564 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 565 mContext.startActivityAsUser(callLastNumberIntent, UserHandle.CURRENT); 566 } 567 } 568 acceptCallIfRinging()569 private boolean acceptCallIfRinging() { 570 if (mTelecomManager != null && mTelecomManager.isRinging()) { 571 Slogf.i(TAG, "call key while ringing. Answer the call!"); 572 mTelecomManager.acceptRingingCall(); 573 return true; 574 } 575 return false; 576 } 577 endCall()578 private boolean endCall() { 579 if (mTelecomManager != null && mTelecomManager.isInCall()) { 580 Slogf.i(TAG, "End the call!"); 581 mTelecomManager.endCall(); 582 return true; 583 } 584 return false; 585 } 586 isBluetoothVoiceRecognitionEnabled()587 private boolean isBluetoothVoiceRecognitionEnabled() { 588 Resources res = mContext.getResources(); 589 return res.getBoolean(R.bool.enableLongPressBluetoothVoiceRecognition); 590 } 591 launchBluetoothVoiceRecognition()592 private boolean launchBluetoothVoiceRecognition() { 593 if (isBluetoothVoiceRecognitionEnabled()) { 594 Slogf.d(TAG, "Attempting to start Bluetooth Voice Recognition."); 595 return mCarBluetoothService.startBluetoothVoiceRecognition(); 596 } 597 Slogf.d(TAG, "Unable to start Bluetooth Voice Recognition, it is not enabled."); 598 return false; 599 } 600 launchDefaultVoiceAssistantHandler()601 private void launchDefaultVoiceAssistantHandler() { 602 Slogf.d(TAG, "voice key, invoke AssistUtilsHelper"); 603 604 if (!AssistUtilsHelper.showPushToTalkSessionForActiveService(mContext, mShowCallback)) { 605 Slogf.w(TAG, "Unable to retrieve assist component for current user"); 606 } 607 } 608 609 /** 610 * @return false if the KeyEvent isn't consumed because there is no 611 * InstrumentClusterKeyListener. 612 */ handleInstrumentClusterKey(KeyEvent event)613 private boolean handleInstrumentClusterKey(KeyEvent event) { 614 KeyEventListener listener = null; 615 synchronized (mLock) { 616 listener = mInstrumentClusterKeyListener; 617 } 618 if (listener == null) { 619 return false; 620 } 621 listener.onKeyEvent(event); 622 return true; 623 } 624 getAccessibilityServicesToBeEnabled()625 private List<String> getAccessibilityServicesToBeEnabled() { 626 String carSafetyAccessibilityServiceComponentName = 627 BuiltinPackageDependency.getComponentName(CAR_ACCESSIBILITY_SERVICE_CLASS); 628 ArrayList<String> accessibilityServicesToBeEnabled = new ArrayList<>(); 629 accessibilityServicesToBeEnabled.add(carSafetyAccessibilityServiceComponentName); 630 if (!TextUtils.isEmpty(mRotaryServiceComponentName)) { 631 accessibilityServicesToBeEnabled.add(mRotaryServiceComponentName); 632 } 633 return accessibilityServicesToBeEnabled; 634 } 635 createServiceListFromSettingsString( String accessibilityServicesString)636 private static List<String> createServiceListFromSettingsString( 637 String accessibilityServicesString) { 638 return TextUtils.isEmpty(accessibilityServicesString) 639 ? new ArrayList<>() 640 : Arrays.asList(accessibilityServicesString.split( 641 ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR)); 642 } 643 644 @Override 645 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)646 public void dump(IndentingPrintWriter writer) { 647 writer.println("*Input Service*"); 648 writer.println("Long-press delay: " + mLongPressDelaySupplier.getAsInt() + "ms"); 649 writer.println("Call button ends ongoing call: " 650 + mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()); 651 mCaptureController.dump(writer); 652 } 653 updateCarAccessibilityServicesSettings(@serIdInt int userId)654 private void updateCarAccessibilityServicesSettings(@UserIdInt int userId) { 655 if (UserHelperLite.isHeadlessSystemUser(userId)) { 656 return; 657 } 658 List<String> accessibilityServicesToBeEnabled = getAccessibilityServicesToBeEnabled(); 659 ContentResolver contentResolverForUser = getContentResolverForUser(mContext, userId); 660 List<String> alreadyEnabledServices = createServiceListFromSettingsString( 661 Settings.Secure.getString(contentResolverForUser, 662 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)); 663 664 int retry = 0; 665 while (!alreadyEnabledServices.containsAll(accessibilityServicesToBeEnabled) 666 && retry <= MAX_RETRIES_FOR_ENABLING_ACCESSIBILITY_SERVICES) { 667 ArrayList<String> enabledServicesList = new ArrayList<>(alreadyEnabledServices); 668 int numAccessibilityServicesToBeEnabled = accessibilityServicesToBeEnabled.size(); 669 for (int i = 0; i < numAccessibilityServicesToBeEnabled; i++) { 670 String serviceToBeEnabled = accessibilityServicesToBeEnabled.get(i); 671 if (!enabledServicesList.contains(serviceToBeEnabled)) { 672 enabledServicesList.add(serviceToBeEnabled); 673 } 674 } 675 Settings.Secure.putString(contentResolverForUser, 676 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 677 String.join(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR, enabledServicesList)); 678 // Read again to account for any race condition with other parts of the code that might 679 // be enabling other accessibility services. 680 alreadyEnabledServices = createServiceListFromSettingsString( 681 Settings.Secure.getString(contentResolverForUser, 682 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)); 683 retry++; 684 } 685 if (!alreadyEnabledServices.containsAll(accessibilityServicesToBeEnabled)) { 686 Slogf.e(TAG, "Failed to enable accessibility services"); 687 } 688 689 Settings.Secure.putString(contentResolverForUser, Settings.Secure.ACCESSIBILITY_ENABLED, 690 "1"); 691 } 692 } 693