• 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 
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