blob: f63a6e00705ec3548f50a9a0ccffbc40c31870d3 [file] [log] [blame]
Keun-young Parka28d7b22016-02-29 16:54:29 -08001/*
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 */
16package com.android.car;
17
kanant49fc8d42020-10-19 16:34:01 -070018import static android.car.CarOccupantZoneManager.DisplayTypeEnum;
Pavel Maltsev338cef82016-07-26 16:04:15 -070019import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
Nicholas Sauer3e2b20c2018-06-28 18:55:15 -070020import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK;
Pavel Maltsev338cef82016-07-26 16:04:15 -070021
Antonio Kantek3a2c5a22021-06-04 16:02:24 -070022import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
23
Justin Pauporefcd39fe2019-01-30 17:13:15 -080024import android.annotation.Nullable;
Yabin Huang9cbc7582020-05-27 13:04:15 -070025import android.annotation.UserIdInt;
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -070026import android.app.ActivityManager;
George Luabb9a962019-02-15 14:45:52 -080027import android.bluetooth.BluetoothAdapter;
28import android.bluetooth.BluetoothDevice;
29import android.bluetooth.BluetoothHeadsetClient;
30import android.bluetooth.BluetoothProfile;
kanant49fc8d42020-10-19 16:34:01 -070031import android.car.CarOccupantZoneManager;
Justin Pauporea057d052019-03-29 15:04:00 -070032import android.car.CarProjectionManager;
Keun young Park401479c2020-02-19 14:15:51 -080033import android.car.input.CarInputManager;
kananted82e292020-07-23 14:10:17 -070034import android.car.input.CustomInputEvent;
Keun young Park401479c2020-02-19 14:15:51 -080035import android.car.input.ICarInput;
36import android.car.input.ICarInputCallback;
Keun young Park401479c2020-02-19 14:15:51 -080037import android.car.input.RotaryEvent;
Yabin Huang9cbc7582020-05-27 13:04:15 -070038import android.car.user.CarUserManager;
Justin Paupored6c8e702019-04-23 18:22:08 -070039import android.content.ContentResolver;
Keun-young Parka28d7b22016-02-29 16:54:29 -080040import android.content.Context;
41import android.content.Intent;
kanant20c2f3d2020-10-01 11:39:12 -070042import android.content.pm.PackageManager;
Babak Bostana2fc9112020-02-12 11:44:47 -080043import android.content.res.Resources;
Pavel Maltsev338cef82016-07-26 16:04:15 -070044import android.hardware.input.InputManager;
Vitalii Tomkive2142e52016-04-29 11:35:26 -070045import android.net.Uri;
Antonio Kantekf3861912021-05-13 18:01:34 -070046import android.os.Binder;
Vitalii Tomkiv2bd06922016-07-26 11:07:48 -070047import android.os.Bundle;
Justin Pauporefcd39fe2019-01-30 17:13:15 -080048import android.os.Handler;
Justin Pauporefcd39fe2019-01-30 17:13:15 -080049import android.os.Looper;
Keun-young Parka28d7b22016-02-29 16:54:29 -080050import android.os.UserHandle;
Vitalii Tomkive2142e52016-04-29 11:35:26 -070051import android.provider.CallLog.Calls;
Justin Paupored6c8e702019-04-23 18:22:08 -070052import android.provider.Settings;
Vitalii Tomkive2142e52016-04-29 11:35:26 -070053import android.telecom.TelecomManager;
Vitalii Tomkiv2bd06922016-07-26 11:07:48 -070054import android.text.TextUtils;
Felipe Leme176a5fd2021-01-20 15:48:33 -080055import android.util.IndentingPrintWriter;
Keun young Park401479c2020-02-19 14:15:51 -080056import android.view.InputDevice;
Keun-young Parka28d7b22016-02-29 16:54:29 -080057import android.view.KeyEvent;
Justin Paupored6c8e702019-04-23 18:22:08 -070058import android.view.ViewConfiguration;
Keun-young Parka28d7b22016-02-29 16:54:29 -080059
60import com.android.car.hal.InputHalService;
Antonio Kantek3a2c5a22021-06-04 16:02:24 -070061import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
Mayank Garg801ea6a2020-09-29 15:43:49 -070062import com.android.car.internal.common.UserHelperLite;
Gaurav Bhola53fef5c2021-07-07 19:50:34 +000063import com.android.car.pm.CarSafetyAccessibilityService;
Yabin Huang9cbc7582020-05-27 13:04:15 -070064import com.android.car.user.CarUserService;
Justin Pauporefcd39fe2019-01-30 17:13:15 -080065import com.android.internal.annotations.GuardedBy;
66import com.android.internal.annotations.VisibleForTesting;
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -070067import com.android.internal.app.AssistUtils;
68import com.android.internal.app.IVoiceInteractionSessionShowCallback;
Antonio Kantekaa8a7cd2021-03-11 11:23:00 -080069import com.android.internal.os.BackgroundThread;
Antonio Kantekabf133e2021-04-21 10:24:51 -070070import com.android.server.utils.Slogf;
Keun-young Parka28d7b22016-02-29 16:54:29 -080071
Keun young Park401479c2020-02-19 14:15:51 -080072import java.util.ArrayList;
Gaurav Bhola53fef5c2021-07-07 19:50:34 +000073import java.util.Arrays;
Justin Pauporea057d052019-03-29 15:04:00 -070074import java.util.BitSet;
Keun young Park401479c2020-02-19 14:15:51 -080075import java.util.Collections;
George Luabb9a962019-02-15 14:45:52 -080076import java.util.List;
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +010077import java.util.function.BooleanSupplier;
Justin Paupored6c8e702019-04-23 18:22:08 -070078import java.util.function.IntSupplier;
Justin Pauporefcd39fe2019-01-30 17:13:15 -080079import java.util.function.Supplier;
Keun-young Parka28d7b22016-02-29 16:54:29 -080080
Antonio Kantek82394312019-12-03 17:00:13 -080081/**
82 * CarInputService monitors and handles input event through vehicle HAL.
83 */
Keun young Park401479c2020-02-19 14:15:51 -080084public class CarInputService extends ICarInput.Stub
85 implements CarServiceBase, InputHalService.InputListener {
Gaurav Bhola53fef5c2021-07-07 19:50:34 +000086 public static final String ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR = ":";
87 private static final int MAX_RETRIES_FOR_ENABLING_ACCESSIBILITY_SERVICES = 5;
Antonio Kantekabf133e2021-04-21 10:24:51 -070088 private static final String TAG = CarLog.TAG_INPUT;
89
Justin Pauporefcd39fe2019-01-30 17:13:15 -080090 /** An interface to receive {@link KeyEvent}s as they occur. */
Keun-young Parka28d7b22016-02-29 16:54:29 -080091 public interface KeyEventListener {
Justin Pauporefcd39fe2019-01-30 17:13:15 -080092 /** Called when a key event occurs. */
93 void onKeyEvent(KeyEvent event);
Keun-young Parka28d7b22016-02-29 16:54:29 -080094 }
95
Antonio Kantek82394312019-12-03 17:00:13 -080096 private final class KeyPressTimer {
Justin Pauporefcd39fe2019-01-30 17:13:15 -080097 private final Runnable mLongPressRunnable;
98 private final Runnable mCallback = this::onTimerExpired;
Justin Paupored6c8e702019-04-23 18:22:08 -070099 private final IntSupplier mLongPressDelaySupplier;
100
Antonio Kantek82394312019-12-03 17:00:13 -0800101 @GuardedBy("CarInputService.this.mLock")
102 private final Handler mHandler;
103 @GuardedBy("CarInputService.this.mLock")
104 private boolean mDown;
105 @GuardedBy("CarInputService.this.mLock")
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800106 private boolean mLongPress = false;
Enrico Granata755dc572017-11-15 11:49:08 -0800107
Justin Paupored6c8e702019-04-23 18:22:08 -0700108 KeyPressTimer(
109 Handler handler, IntSupplier longPressDelaySupplier, Runnable longPressRunnable) {
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800110 mHandler = handler;
111 mLongPressRunnable = longPressRunnable;
Justin Paupored6c8e702019-04-23 18:22:08 -0700112 mLongPressDelaySupplier = longPressDelaySupplier;
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800113 }
114
115 /** Marks that a key was pressed, and starts the long-press timer. */
Antonio Kantek82394312019-12-03 17:00:13 -0800116 void keyDown() {
117 synchronized (mLock) {
118 mDown = true;
119 mLongPress = false;
120 mHandler.removeCallbacks(mCallback);
121 mHandler.postDelayed(mCallback, mLongPressDelaySupplier.getAsInt());
122 }
Enrico Granata755dc572017-11-15 11:49:08 -0800123 }
124
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800125 /**
126 * Marks that a key was released, and stops the long-press timer.
127 *
128 * Returns true if the press was a long-press.
129 */
Antonio Kantek82394312019-12-03 17:00:13 -0800130 boolean keyUp() {
131 synchronized (mLock) {
132 mHandler.removeCallbacks(mCallback);
133 mDown = false;
134 return mLongPress;
135 }
Enrico Granata755dc572017-11-15 11:49:08 -0800136 }
137
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800138 private void onTimerExpired() {
Antonio Kantek82394312019-12-03 17:00:13 -0800139 synchronized (mLock) {
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800140 // If the timer expires after key-up, don't retroactively make the press long.
141 if (!mDown) {
142 return;
143 }
144 mLongPress = true;
Enrico Granata755dc572017-11-15 11:49:08 -0800145 }
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800146 mLongPressRunnable.run();
Enrico Granata755dc572017-11-15 11:49:08 -0800147 }
148 }
149
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800150 private final IVoiceInteractionSessionShowCallback mShowCallback =
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -0700151 new IVoiceInteractionSessionShowCallback.Stub() {
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800152 @Override
153 public void onFailed() {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700154 Slogf.w(TAG, "Failed to show VoiceInteractionSession");
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800155 }
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -0700156
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800157 @Override
158 public void onShown() {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700159 Slogf.d(TAG, "IVoiceInteractionSessionShowCallback onShown()");
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800160 }
161 };
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -0700162
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800163 @VisibleForTesting
164 static final String EXTRA_CAR_PUSH_TO_TALK =
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -0700165 "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK";
Keun-young Parka28d7b22016-02-29 16:54:29 -0800166
167 private final Context mContext;
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700168 private final InputHalService mInputHalService;
Yabin Huang9cbc7582020-05-27 13:04:15 -0700169 private final CarUserService mUserService;
kanant20c2f3d2020-10-01 11:39:12 -0700170 private final CarOccupantZoneService mCarOccupantZoneService;
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700171 private final TelecomManager mTelecomManager;
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -0700172 private final AssistUtils mAssistUtils;
kanant36553a02020-07-16 13:51:44 -0700173
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800174 // The default handler for main-display input events. By default, injects the events into
175 // the input queue via InputManager, but can be overridden for testing.
176 private final KeyEventListener mMainDisplayHandler;
177 // The supplier for the last-called number. By default, gets the number from the call log.
178 // May be overridden for testing.
179 private final Supplier<String> mLastCalledNumberSupplier;
Justin Paupored6c8e702019-04-23 18:22:08 -0700180 // The supplier for the system long-press delay, in milliseconds. By default, gets the value
181 // from Settings.Secure for the current user, falling back to the system-wide default
182 // long-press delay defined in ViewConfiguration. May be overridden for testing.
183 private final IntSupplier mLongPressDelaySupplier;
Yabin Huang9cbc7582020-05-27 13:04:15 -0700184 // ComponentName of the RotaryService.
185 private final String mRotaryServiceComponentName;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800186
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100187 private final BooleanSupplier mShouldCallButtonEndOngoingCallSupplier;
188
Antonio Kantek82394312019-12-03 17:00:13 -0800189 private final Object mLock = new Object();
190
191 @GuardedBy("mLock")
Justin Pauporea057d052019-03-29 15:04:00 -0700192 private CarProjectionManager.ProjectionKeyEventHandler mProjectionKeyEventHandler;
Antonio Kantek82394312019-12-03 17:00:13 -0800193
194 @GuardedBy("mLock")
Justin Pauporea057d052019-03-29 15:04:00 -0700195 private final BitSet mProjectionKeyEventsSubscribed = new BitSet();
Keun-young Parka28d7b22016-02-29 16:54:29 -0800196
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800197 private final KeyPressTimer mVoiceKeyTimer;
198 private final KeyPressTimer mCallKeyTimer;
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700199
Antonio Kantek82394312019-12-03 17:00:13 -0800200 @GuardedBy("mLock")
Pavel Maltseve11ad3a2016-03-25 15:40:24 -0700201 private KeyEventListener mInstrumentClusterKeyListener;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800202
Keun young Park401479c2020-02-19 14:15:51 -0800203 private final InputCaptureClientController mCaptureController;
204
Antonio Kantek3d6174b2021-03-24 20:08:27 -0700205 private final BluetoothAdapter mBluetoothAdapter;
George Luabb9a962019-02-15 14:45:52 -0800206
207 // BluetoothHeadsetClient set through mBluetoothProfileServiceListener, and used by
208 // launchBluetoothVoiceRecognition().
Antonio Kantek82394312019-12-03 17:00:13 -0800209 @GuardedBy("mLock")
George Luabb9a962019-02-15 14:45:52 -0800210 private BluetoothHeadsetClient mBluetoothHeadsetClient;
211
212 private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
213 new BluetoothProfile.ServiceListener() {
214 @Override
215 public void onServiceConnected(int profile, BluetoothProfile proxy) {
216 if (profile == BluetoothProfile.HEADSET_CLIENT) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700217 Slogf.d(TAG, "Bluetooth proxy connected for HEADSET_CLIENT profile");
Antonio Kantek82394312019-12-03 17:00:13 -0800218 synchronized (mLock) {
George Luabb9a962019-02-15 14:45:52 -0800219 mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
220 }
221 }
222 }
223
224 @Override
225 public void onServiceDisconnected(int profile) {
226 if (profile == BluetoothProfile.HEADSET_CLIENT) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700227 Slogf.d(TAG, "Bluetooth proxy disconnected for HEADSET_CLIENT profile");
Antonio Kantek82394312019-12-03 17:00:13 -0800228 synchronized (mLock) {
George Luabb9a962019-02-15 14:45:52 -0800229 mBluetoothHeadsetClient = null;
230 }
231 }
232 }
233 };
234
Yabin Huang9cbc7582020-05-27 13:04:15 -0700235 private final CarUserManager.UserLifecycleListener mUserLifecycleListener = event -> {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700236 Slogf.d(TAG, "CarInputService.onEvent(%s)", event);
Yabin Huang9cbc7582020-05-27 13:04:15 -0700237 if (CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING == event.getEventType()) {
Gaurav Bhola53fef5c2021-07-07 19:50:34 +0000238 updateCarAccessibilityServicesSettings(event.getUserId());
Yabin Huang9cbc7582020-05-27 13:04:15 -0700239 }
240 };
241
Justin Paupored6c8e702019-04-23 18:22:08 -0700242 private static int getViewLongPressDelay(ContentResolver cr) {
243 return Settings.Secure.getIntForUser(
244 cr,
245 Settings.Secure.LONG_PRESS_TIMEOUT,
246 ViewConfiguration.getLongPressTimeout(),
247 UserHandle.USER_CURRENT);
248 }
249
Yabin Huang9cbc7582020-05-27 13:04:15 -0700250 public CarInputService(Context context, InputHalService inputHalService,
kanant20c2f3d2020-10-01 11:39:12 -0700251 CarUserService userService, CarOccupantZoneService occupantZoneService) {
252 this(context, inputHalService, userService, occupantZoneService,
253 new Handler(Looper.getMainLooper()),
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800254 context.getSystemService(TelecomManager.class), new AssistUtils(context),
255 event ->
256 context.getSystemService(InputManager.class)
257 .injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC),
258 () -> Calls.getLastOutgoingCall(context),
kanantd3f36592020-07-30 16:52:23 -0700259 () -> getViewLongPressDelay(context.getContentResolver()),
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100260 () -> context.getResources().getBoolean(R.bool.config_callButtonEndsOngoingCall),
Antonio Kantek3d6174b2021-03-24 20:08:27 -0700261 new InputCaptureClientController(context),
262 BluetoothAdapter.getDefaultAdapter());
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800263 }
264
265 @VisibleForTesting
Yabin Huang9cbc7582020-05-27 13:04:15 -0700266 CarInputService(Context context, InputHalService inputHalService, CarUserService userService,
kanant20c2f3d2020-10-01 11:39:12 -0700267 CarOccupantZoneService occupantZoneService, Handler handler,
268 TelecomManager telecomManager, AssistUtils assistUtils,
269 KeyEventListener mainDisplayHandler,
270 Supplier<String> lastCalledNumberSupplier, IntSupplier longPressDelaySupplier,
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100271 BooleanSupplier shouldCallButtonEndOngoingCallSupplier,
Antonio Kantek3d6174b2021-03-24 20:08:27 -0700272 InputCaptureClientController captureController, BluetoothAdapter bluetoothAdapter) {
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800273 mContext = context;
kanantd3f36592020-07-30 16:52:23 -0700274 mCaptureController = captureController;
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800275 mInputHalService = inputHalService;
Yabin Huang9cbc7582020-05-27 13:04:15 -0700276 mUserService = userService;
kanant20c2f3d2020-10-01 11:39:12 -0700277 mCarOccupantZoneService = occupantZoneService;
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800278 mTelecomManager = telecomManager;
279 mAssistUtils = assistUtils;
280 mMainDisplayHandler = mainDisplayHandler;
281 mLastCalledNumberSupplier = lastCalledNumberSupplier;
Justin Paupored6c8e702019-04-23 18:22:08 -0700282 mLongPressDelaySupplier = longPressDelaySupplier;
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800283
Justin Paupored6c8e702019-04-23 18:22:08 -0700284 mVoiceKeyTimer =
285 new KeyPressTimer(
286 handler, longPressDelaySupplier, this::handleVoiceAssistLongPress);
287 mCallKeyTimer =
288 new KeyPressTimer(handler, longPressDelaySupplier, this::handleCallLongPress);
Yabin Huang9cbc7582020-05-27 13:04:15 -0700289
290 mRotaryServiceComponentName = mContext.getString(R.string.rotaryService);
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100291 mShouldCallButtonEndOngoingCallSupplier = shouldCallButtonEndOngoingCallSupplier;
Antonio Kantek3d6174b2021-03-24 20:08:27 -0700292 mBluetoothAdapter = bluetoothAdapter;
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800293 }
294
Keun-young Parka28d7b22016-02-29 16:54:29 -0800295 /**
Justin Pauporea057d052019-03-29 15:04:00 -0700296 * Set projection key event listener. If null, unregister listener.
Vitalii Tomkiv61e2c232016-03-08 10:48:51 -0800297 */
Justin Pauporea057d052019-03-29 15:04:00 -0700298 public void setProjectionKeyEventHandler(
299 @Nullable CarProjectionManager.ProjectionKeyEventHandler listener,
300 @Nullable BitSet events) {
Antonio Kantek82394312019-12-03 17:00:13 -0800301 synchronized (mLock) {
Justin Pauporea057d052019-03-29 15:04:00 -0700302 mProjectionKeyEventHandler = listener;
303 mProjectionKeyEventsSubscribed.clear();
304 if (events != null) {
305 mProjectionKeyEventsSubscribed.or(events);
306 }
Keun-young Parka28d7b22016-02-29 16:54:29 -0800307 }
308 }
309
Antonio Kantek040c28f2020-07-08 10:35:41 -0700310 /**
311 * Sets the instrument cluster key event listener.
312 */
Keun-young Parka28d7b22016-02-29 16:54:29 -0800313 public void setInstrumentClusterKeyListener(KeyEventListener listener) {
Antonio Kantek82394312019-12-03 17:00:13 -0800314 synchronized (mLock) {
Pavel Maltseve11ad3a2016-03-25 15:40:24 -0700315 mInstrumentClusterKeyListener = listener;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800316 }
317 }
318
319 @Override
320 public void init() {
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700321 if (!mInputHalService.isKeyInputSupported()) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700322 Slogf.w(TAG, "Hal does not support key input.");
Keun-young Parka28d7b22016-02-29 16:54:29 -0800323 return;
324 }
Antonio Kantekabf133e2021-04-21 10:24:51 -0700325 Slogf.d(TAG, "Hal supports key input.");
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700326 mInputHalService.setInputListener(this);
George Luabb9a962019-02-15 14:45:52 -0800327 if (mBluetoothAdapter != null) {
Antonio Kantekaa8a7cd2021-03-11 11:23:00 -0800328 BackgroundThread.getHandler().post(() -> {
329 mBluetoothAdapter.getProfileProxy(mContext,
330 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET_CLIENT);
331 });
George Luabb9a962019-02-15 14:45:52 -0800332 }
Gaurav Bhola53fef5c2021-07-07 19:50:34 +0000333 mUserService.addUserLifecycleListener(mUserLifecycleListener);
Keun-young Parka28d7b22016-02-29 16:54:29 -0800334 }
335
336 @Override
337 public void release() {
Antonio Kantek82394312019-12-03 17:00:13 -0800338 synchronized (mLock) {
Justin Pauporea057d052019-03-29 15:04:00 -0700339 mProjectionKeyEventHandler = null;
340 mProjectionKeyEventsSubscribed.clear();
Pavel Maltseve11ad3a2016-03-25 15:40:24 -0700341 mInstrumentClusterKeyListener = null;
George Luabb9a962019-02-15 14:45:52 -0800342 if (mBluetoothHeadsetClient != null) {
343 mBluetoothAdapter.closeProfileProxy(
344 BluetoothProfile.HEADSET_CLIENT, mBluetoothHeadsetClient);
345 mBluetoothHeadsetClient = null;
346 }
347 }
Gaurav Bhola53fef5c2021-07-07 19:50:34 +0000348 mUserService.removeUserLifecycleListener(mUserLifecycleListener);
Keun-young Parka28d7b22016-02-29 16:54:29 -0800349 }
350
351 @Override
Antonio Kantek65b9bc32021-03-16 12:01:25 -0700352 public void onKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType) {
Scott Randolph64e88822018-03-15 11:00:05 -0700353 // Special case key code that have special "long press" handling for automotive
Vitalii Tomkiv2bd06922016-07-26 11:07:48 -0700354 switch (event.getKeyCode()) {
Keun-young Parka28d7b22016-02-29 16:54:29 -0800355 case KeyEvent.KEYCODE_VOICE_ASSIST:
356 handleVoiceAssistKey(event);
357 return;
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700358 case KeyEvent.KEYCODE_CALL:
359 handleCallKey(event);
360 return;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800361 default:
362 break;
363 }
Scott Randolph64e88822018-03-15 11:00:05 -0700364
Antonio Kantek65b9bc32021-03-16 12:01:25 -0700365 assignDisplayId(event, targetDisplayType);
366
Scott Randolph64e88822018-03-15 11:00:05 -0700367 // Allow specifically targeted keys to be routed to the cluster
Antonio Kantek65b9bc32021-03-16 12:01:25 -0700368 if (targetDisplayType == CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER
Yuncheol Heo34ba7dd2021-03-12 20:27:19 -0800369 && handleInstrumentClusterKey(event)) {
370 return;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800371 }
Antonio Kantek65b9bc32021-03-16 12:01:25 -0700372 if (mCaptureController.onKeyEvent(targetDisplayType, event)) {
Yuncheol Heo34ba7dd2021-03-12 20:27:19 -0800373 return;
374 }
375 mMainDisplayHandler.onKeyEvent(event);
Keun-young Parka28d7b22016-02-29 16:54:29 -0800376 }
377
Antonio Kantek65b9bc32021-03-16 12:01:25 -0700378 private void assignDisplayId(KeyEvent event, @DisplayTypeEnum int targetDisplayType) {
379 // Setting display id for driver user id (currently MAIN and CLUSTER display types are
380 // linked to driver user only)
381 int newDisplayId = mCarOccupantZoneService.getDisplayIdForDriver(targetDisplayType);
382
383 // Display id is overridden even if already set.
384 event.setDisplayId(newDisplayId);
385 }
386
Keun young Park401479c2020-02-19 14:15:51 -0800387 @Override
kanant49fc8d42020-10-19 16:34:01 -0700388 public void onRotaryEvent(RotaryEvent event, @DisplayTypeEnum int targetDisplay) {
Keun young Park401479c2020-02-19 14:15:51 -0800389 if (!mCaptureController.onRotaryEvent(targetDisplay, event)) {
390 List<KeyEvent> keyEvents = rotaryEventToKeyEvents(event);
391 for (KeyEvent keyEvent : keyEvents) {
392 onKeyEvent(keyEvent, targetDisplay);
393 }
394 }
395 }
396
kananted82e292020-07-23 14:10:17 -0700397 @Override
398 public void onCustomInputEvent(CustomInputEvent event) {
399 if (!mCaptureController.onCustomInputEvent(event)) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700400 Slogf.w(TAG, "Failed to propagate (%s)", event);
kananted82e292020-07-23 14:10:17 -0700401 return;
402 }
Antonio Kantekabf133e2021-04-21 10:24:51 -0700403 Slogf.d(TAG, "Succeed injecting (%s)", event);
kananted82e292020-07-23 14:10:17 -0700404 }
405
Keun young Park401479c2020-02-19 14:15:51 -0800406 private static List<KeyEvent> rotaryEventToKeyEvents(RotaryEvent event) {
407 int numClicks = event.getNumberOfClicks();
408 int numEvents = numClicks * 2; // up / down per each click
409 boolean clockwise = event.isClockwise();
410 int keyCode;
411 switch (event.getInputType()) {
412 case CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION:
413 keyCode = clockwise
414 ? KeyEvent.KEYCODE_NAVIGATE_NEXT
415 : KeyEvent.KEYCODE_NAVIGATE_PREVIOUS;
416 break;
417 case CarInputManager.INPUT_TYPE_ROTARY_VOLUME:
418 keyCode = clockwise
419 ? KeyEvent.KEYCODE_VOLUME_UP
420 : KeyEvent.KEYCODE_VOLUME_DOWN;
421 break;
422 default:
Antonio Kantekabf133e2021-04-21 10:24:51 -0700423 Slogf.e(TAG, "Unknown rotary input type: %d", event.getInputType());
Keun young Park401479c2020-02-19 14:15:51 -0800424 return Collections.EMPTY_LIST;
425 }
426 ArrayList<KeyEvent> keyEvents = new ArrayList<>(numEvents);
427 for (int i = 0; i < numClicks; i++) {
428 long uptime = event.getUptimeMillisForClick(i);
429 KeyEvent downEvent = createKeyEvent(/* down= */ true, uptime, uptime, keyCode);
430 KeyEvent upEvent = createKeyEvent(/* down= */ false, uptime, uptime, keyCode);
431 keyEvents.add(downEvent);
432 keyEvents.add(upEvent);
433 }
434 return keyEvents;
435 }
436
437 private static KeyEvent createKeyEvent(boolean down, long downTime, long eventTime,
438 int keyCode) {
439 return new KeyEvent(
440 downTime,
441 eventTime,
442 /* action= */ down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
443 keyCode,
444 /* repeat= */ 0,
445 /* metaState= */ 0,
446 /* deviceId= */ 0,
447 /* scancode= */ 0,
448 /* flags= */ 0,
449 InputDevice.SOURCE_CLASS_BUTTON);
450 }
451
Antonio Kantek48e16af2021-08-05 22:50:43 -0700452 /**
453 * Requests capturing of input event for the specified display for all requested input types.
454 *
455 * Currently this method requires {@code android.car.permission.CAR_MONITOR_INPUT} or
456 * {@code android.permission.MONITOR_INPUT} permissions (any of them will be acceptable).
457 */
Keun young Park401479c2020-02-19 14:15:51 -0800458 @Override
kanant49fc8d42020-10-19 16:34:01 -0700459 public int requestInputEventCapture(ICarInputCallback callback,
460 @DisplayTypeEnum int targetDisplayType,
Keun young Park401479c2020-02-19 14:15:51 -0800461 int[] inputTypes, int requestFlags) {
462 return mCaptureController.requestInputEventCapture(callback, targetDisplayType, inputTypes,
463 requestFlags);
464 }
465
Antonio Kantek48e16af2021-08-05 22:50:43 -0700466 /**
467 * Overloads #requestInputEventCapture(int, int[], int, CarInputCaptureCallback) by providing
468 * a {@link java.util.concurrent.Executor} to be used when invoking the callback argument.
469 *
470 * Currently this method requires {@code android.car.permission.CAR_MONITOR_INPUT} or
471 * {@code android.permission.MONITOR_INPUT} permissions (any of them will be acceptable).
472 */
Keun young Park401479c2020-02-19 14:15:51 -0800473 @Override
kanant49fc8d42020-10-19 16:34:01 -0700474 public void releaseInputEventCapture(ICarInputCallback callback,
475 @DisplayTypeEnum int targetDisplayType) {
Keun young Park401479c2020-02-19 14:15:51 -0800476 mCaptureController.releaseInputEventCapture(callback, targetDisplayType);
477 }
478
kanant20c2f3d2020-10-01 11:39:12 -0700479 /**
480 * Injects the {@link KeyEvent} passed as parameter against Car Input API.
481 * <p>
482 * The event's display id will be overridden accordingly to the display type (it will be
483 * retrieved from {@link CarOccupantZoneService}).
484 *
kanant49fc8d42020-10-19 16:34:01 -0700485 * @param event the event to inject
kanant20c2f3d2020-10-01 11:39:12 -0700486 * @param targetDisplayType the display type associated with the event
487 * @throws SecurityException when caller doesn't have INJECT_EVENTS permission granted
488 */
489 @Override
kanant49fc8d42020-10-19 16:34:01 -0700490 public void injectKeyEvent(KeyEvent event, @DisplayTypeEnum int targetDisplayType) {
kanant20c2f3d2020-10-01 11:39:12 -0700491 // Permission check
492 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
493 android.Manifest.permission.INJECT_EVENTS)) {
494 throw new SecurityException("Injecting KeyEvent requires INJECT_EVENTS permission");
495 }
496
Antonio Kantekf3861912021-05-13 18:01:34 -0700497 long token = Binder.clearCallingIdentity();
498 try {
499 // Redirect event to onKeyEvent
500 onKeyEvent(event, targetDisplayType);
501 } finally {
502 Binder.restoreCallingIdentity(token);
503 }
kanant20c2f3d2020-10-01 11:39:12 -0700504 }
505
Keun-young Parka28d7b22016-02-29 16:54:29 -0800506 private void handleVoiceAssistKey(KeyEvent event) {
507 int action = event.getAction();
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800508 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
Enrico Granata755dc572017-11-15 11:49:08 -0800509 mVoiceKeyTimer.keyDown();
Justin Pauporea057d052019-03-29 15:04:00 -0700510 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN);
Keun-young Parka28d7b22016-02-29 16:54:29 -0800511 } else if (action == KeyEvent.ACTION_UP) {
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800512 if (mVoiceKeyTimer.keyUp()) {
513 // Long press already handled by handleVoiceAssistLongPress(), nothing more to do.
Justin Pauporea057d052019-03-29 15:04:00 -0700514 // Hand it off to projection, if it's interested, otherwise we're done.
515 dispatchProjectionKeyEvent(
516 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP);
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800517 return;
518 }
Enrico Granata755dc572017-11-15 11:49:08 -0800519
Justin Pauporea057d052019-03-29 15:04:00 -0700520 if (dispatchProjectionKeyEvent(
521 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP)) {
522 return;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800523 }
Enrico Granata755dc572017-11-15 11:49:08 -0800524
Justin Pauporea057d052019-03-29 15:04:00 -0700525 launchDefaultVoiceAssistantHandler();
Keun-young Parka28d7b22016-02-29 16:54:29 -0800526 }
527 }
528
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800529 private void handleVoiceAssistLongPress() {
Justin Pauporea057d052019-03-29 15:04:00 -0700530 // If projection wants this event, let it take it.
531 if (dispatchProjectionKeyEvent(
532 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN)) {
George Luabb9a962019-02-15 14:45:52 -0800533 return;
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800534 }
George Luabb9a962019-02-15 14:45:52 -0800535 // Otherwise, try to launch voice recognition on a BT device.
536 if (launchBluetoothVoiceRecognition()) {
537 return;
538 }
539 // Finally, fallback to the default voice assist handling.
540 launchDefaultVoiceAssistantHandler();
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800541 }
542
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700543 private void handleCallKey(KeyEvent event) {
544 int action = event.getAction();
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800545 if (action == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
Enrico Granata755dc572017-11-15 11:49:08 -0800546 mCallKeyTimer.keyDown();
Justin Pauporea057d052019-03-29 15:04:00 -0700547 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN);
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700548 } else if (action == KeyEvent.ACTION_UP) {
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800549 if (mCallKeyTimer.keyUp()) {
550 // Long press already handled by handleCallLongPress(), nothing more to do.
Justin Pauporea057d052019-03-29 15:04:00 -0700551 // Hand it off to projection, if it's interested, otherwise we're done.
552 dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP);
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800553 return;
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700554 }
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800555
556 if (acceptCallIfRinging()) {
557 // Ringing call answered, nothing more to do.
558 return;
559 }
560
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100561 if (mShouldCallButtonEndOngoingCallSupplier.getAsBoolean() && endCall()) {
562 // On-going call ended, nothing more to do.
563 return;
564 }
565
Justin Pauporea057d052019-03-29 15:04:00 -0700566 if (dispatchProjectionKeyEvent(
567 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP)) {
568 return;
569 }
570
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800571 launchDialerHandler();
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700572 }
573 }
574
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800575 private void handleCallLongPress() {
576 // Long-press answers call if ringing, same as short-press.
577 if (acceptCallIfRinging()) {
578 return;
579 }
580
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100581 if (mShouldCallButtonEndOngoingCallSupplier.getAsBoolean() && endCall()) {
582 return;
583 }
584
Justin Pauporea057d052019-03-29 15:04:00 -0700585 if (dispatchProjectionKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN)) {
586 return;
587 }
588
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800589 dialLastCallHandler();
590 }
591
Justin Pauporea057d052019-03-29 15:04:00 -0700592 private boolean dispatchProjectionKeyEvent(@CarProjectionManager.KeyEventNum int event) {
593 CarProjectionManager.ProjectionKeyEventHandler projectionKeyEventHandler;
Antonio Kantek82394312019-12-03 17:00:13 -0800594 synchronized (mLock) {
Justin Pauporea057d052019-03-29 15:04:00 -0700595 projectionKeyEventHandler = mProjectionKeyEventHandler;
596 if (projectionKeyEventHandler == null || !mProjectionKeyEventsSubscribed.get(event)) {
597 // No event handler, or event handler doesn't want this event - we're done.
598 return false;
599 }
600 }
601
602 projectionKeyEventHandler.onKeyEvent(event);
603 return true;
604 }
605
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700606 private void launchDialerHandler() {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700607 Slogf.i(TAG, "call key, launch dialer intent");
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700608 Intent dialerIntent = new Intent(Intent.ACTION_DIAL);
609 mContext.startActivityAsUser(dialerIntent, null, UserHandle.CURRENT_OR_SELF);
610 }
611
612 private void dialLastCallHandler() {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700613 Slogf.i(TAG, "call key, dialing last call");
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700614
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800615 String lastNumber = mLastCalledNumberSupplier.get();
616 if (!TextUtils.isEmpty(lastNumber)) {
Vitalii Tomkive2142e52016-04-29 11:35:26 -0700617 Intent callLastNumberIntent = new Intent(Intent.ACTION_CALL)
618 .setData(Uri.fromParts("tel", lastNumber, null))
619 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
620 mContext.startActivityAsUser(callLastNumberIntent, null, UserHandle.CURRENT_OR_SELF);
621 }
622 }
623
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800624 private boolean acceptCallIfRinging() {
625 if (mTelecomManager != null && mTelecomManager.isRinging()) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700626 Slogf.i(TAG, "call key while ringing. Answer the call!");
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800627 mTelecomManager.acceptRingingCall();
628 return true;
629 }
Justin Pauporefcd39fe2019-01-30 17:13:15 -0800630 return false;
631 }
632
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100633 private boolean endCall() {
634 if (mTelecomManager != null && mTelecomManager.isInCall()) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700635 Slogf.i(TAG, "End the call!");
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100636 mTelecomManager.endCall();
637 return true;
638 }
639 return false;
640 }
641
Babak Bostana2fc9112020-02-12 11:44:47 -0800642 private boolean isBluetoothVoiceRecognitionEnabled() {
643 Resources res = mContext.getResources();
644 return res.getBoolean(R.bool.enableLongPressBluetoothVoiceRecognition);
645 }
646
George Luabb9a962019-02-15 14:45:52 -0800647 private boolean launchBluetoothVoiceRecognition() {
Antonio Kantek82394312019-12-03 17:00:13 -0800648 synchronized (mLock) {
Babak Bostana2fc9112020-02-12 11:44:47 -0800649 if (mBluetoothHeadsetClient == null || !isBluetoothVoiceRecognitionEnabled()) {
George Luabb9a962019-02-15 14:45:52 -0800650 return false;
651 }
652 // getConnectedDevices() does not make any guarantees about the order of the returned
653 // list. As of 2019-02-26, this code is only triggered through a long-press of the
654 // voice recognition key, so handling of multiple connected devices that support voice
655 // recognition is not expected to be a primary use case.
656 List<BluetoothDevice> devices = mBluetoothHeadsetClient.getConnectedDevices();
657 if (devices != null) {
658 for (BluetoothDevice device : devices) {
659 Bundle bundle = mBluetoothHeadsetClient.getCurrentAgFeatures(device);
660 if (bundle == null || !bundle.getBoolean(
661 BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION)) {
662 continue;
663 }
664 if (mBluetoothHeadsetClient.startVoiceRecognition(device)) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700665 Slogf.d(TAG, "started voice recognition on BT device at (%s)",
666 device.getAddress());
George Luabb9a962019-02-15 14:45:52 -0800667 return true;
668 }
669 }
670 }
671 }
672 return false;
673 }
674
Pavel Maltseve11ad3a2016-03-25 15:40:24 -0700675 private void launchDefaultVoiceAssistantHandler() {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700676 Slogf.i(TAG, "voice key, invoke AssistUtils");
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -0700677
678 if (mAssistUtils.getAssistComponentForUser(ActivityManager.getCurrentUser()) == null) {
Antonio Kantekabf133e2021-04-21 10:24:51 -0700679 Slogf.w(TAG, "Unable to retrieve assist component for current user");
Nicholas Sauerdcb3caa2018-06-19 15:04:04 -0700680 return;
681 }
682
683 final Bundle args = new Bundle();
684 args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true);
685
686 mAssistUtils.showSessionForActiveService(args,
Nicholas Sauer3e2b20c2018-06-28 18:55:15 -0700687 SHOW_SOURCE_PUSH_TO_TALK, mShowCallback, null /*activityToken*/);
Keun-young Parka28d7b22016-02-29 16:54:29 -0800688 }
689
Yuncheol Heo34ba7dd2021-03-12 20:27:19 -0800690 /**
691 * @return false if the KeyEvent isn't consumed because there is no
692 * InstrumentClusterKeyListener.
693 */
694 private boolean handleInstrumentClusterKey(KeyEvent event) {
Keun-young Parka28d7b22016-02-29 16:54:29 -0800695 KeyEventListener listener = null;
Antonio Kantek82394312019-12-03 17:00:13 -0800696 synchronized (mLock) {
Pavel Maltseve11ad3a2016-03-25 15:40:24 -0700697 listener = mInstrumentClusterKeyListener;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800698 }
699 if (listener == null) {
Yuncheol Heo34ba7dd2021-03-12 20:27:19 -0800700 return false;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800701 }
702 listener.onKeyEvent(event);
Yuncheol Heo34ba7dd2021-03-12 20:27:19 -0800703 return true;
Keun-young Parka28d7b22016-02-29 16:54:29 -0800704 }
705
Gaurav Bhola53fef5c2021-07-07 19:50:34 +0000706 private List<String> getAccessibilityServicesToBeEnabled() {
707 String carSafetyAccessibilityServiceComponentName = mContext.getPackageName()
708 + "/"
709 + CarSafetyAccessibilityService.class.getName();
710 ArrayList<String> accessibilityServicesToBeEnabled = new ArrayList<>();
711 accessibilityServicesToBeEnabled.add(carSafetyAccessibilityServiceComponentName);
712 if (!TextUtils.isEmpty(mRotaryServiceComponentName)) {
713 accessibilityServicesToBeEnabled.add(mRotaryServiceComponentName);
714 }
715 return accessibilityServicesToBeEnabled;
716 }
717
718 private static List<String> createServiceListFromSettingsString(
719 String accessibilityServicesString) {
720 return TextUtils.isEmpty(accessibilityServicesString)
721 ? new ArrayList<>()
722 : Arrays.asList(accessibilityServicesString.split(
723 ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR));
724 }
725
Keun-young Parka28d7b22016-02-29 16:54:29 -0800726 @Override
Antonio Kantek3a2c5a22021-06-04 16:02:24 -0700727 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
Felipe Leme176a5fd2021-01-20 15:48:33 -0800728 public void dump(IndentingPrintWriter writer) {
Keun-young Parka28d7b22016-02-29 16:54:29 -0800729 writer.println("*Input Service*");
Justin Paupored6c8e702019-04-23 18:22:08 -0700730 writer.println("Long-press delay: " + mLongPressDelaySupplier.getAsInt() + "ms");
Alexander Mishkovetsde539cd2020-11-10 15:41:58 +0100731 writer.println("Call button ends ongoing call: "
732 + mShouldCallButtonEndOngoingCallSupplier.getAsBoolean());
Keun young Park401479c2020-02-19 14:15:51 -0800733 mCaptureController.dump(writer);
Keun-young Parka28d7b22016-02-29 16:54:29 -0800734 }
735
Gaurav Bhola53fef5c2021-07-07 19:50:34 +0000736 private void updateCarAccessibilityServicesSettings(@UserIdInt int userId) {
Mayank Garg94f3eb92020-08-12 12:38:58 -0700737 if (UserHelperLite.isHeadlessSystemUser(userId)) {
Yabin Huang9cbc7582020-05-27 13:04:15 -0700738 return;
739 }
Gaurav Bhola53fef5c2021-07-07 19:50:34 +0000740 List<String> accessibilityServicesToBeEnabled = getAccessibilityServicesToBeEnabled();
Yabin Huang9cbc7582020-05-27 13:04:15 -0700741 ContentResolver contentResolver = mContext.getContentResolver();
Gaurav Bhola53fef5c2021-07-07 19:50:34 +0000742 List<String> alreadyEnabledServices = createServiceListFromSettingsString(
743 Settings.Secure.getStringForUser(contentResolver,
744 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
745 userId));
746
747 int retry = 0;
748 while (!alreadyEnabledServices.containsAll(accessibilityServicesToBeEnabled)
749 && retry <= MAX_RETRIES_FOR_ENABLING_ACCESSIBILITY_SERVICES) {
750 ArrayList<String> enabledServicesList = new ArrayList<>(alreadyEnabledServices);
751 int numAccessibilityServicesToBeEnabled = accessibilityServicesToBeEnabled.size();
752 for (int i = 0; i < numAccessibilityServicesToBeEnabled; i++) {
753 String serviceToBeEnabled = accessibilityServicesToBeEnabled.get(i);
754 if (!enabledServicesList.contains(serviceToBeEnabled)) {
755 enabledServicesList.add(serviceToBeEnabled);
756 }
757 }
758 Settings.Secure.putStringForUser(contentResolver,
759 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
760 String.join(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR, enabledServicesList),
761 userId);
762 // Read again to account for any race condition with other parts of the code that might
763 // be enabling other accessibility services.
764 alreadyEnabledServices = createServiceListFromSettingsString(
765 Settings.Secure.getStringForUser(contentResolver,
766 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
767 userId));
768 retry++;
769 }
770 if (!alreadyEnabledServices.containsAll(accessibilityServicesToBeEnabled)) {
771 Slogf.e(TAG, "Failed to enable accessibility services");
772 }
773
Yabin Huang9cbc7582020-05-27 13:04:15 -0700774 Settings.Secure.putStringForUser(contentResolver,
775 Settings.Secure.ACCESSIBILITY_ENABLED,
776 "1",
777 userId);
778 }
Keun-young Parka28d7b22016-02-29 16:54:29 -0800779}