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