• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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