blob: e7ede3cec395be30d4140bbd4882dabc4b733d45 [file] [log] [blame]
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001/*
2 * Copyright (C) 2017 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
17package com.android.car;
18
Ram Periathiruvadie011a402018-03-22 18:54:33 -070019import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES;
20import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES;
21import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES;
22import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES;
23
Ram Periathiruvadi76a84892017-07-27 18:10:35 -070024import android.annotation.Nullable;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070025import android.app.ActivityManager;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080026import android.bluetooth.BluetoothA2dpSink;
27import android.bluetooth.BluetoothAdapter;
28import android.bluetooth.BluetoothDevice;
29import android.bluetooth.BluetoothHeadsetClient;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080030import android.bluetooth.BluetoothMapClient;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -080031import android.bluetooth.BluetoothPan;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080032import android.bluetooth.BluetoothPbapClient;
33import android.bluetooth.BluetoothProfile;
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -070034import android.bluetooth.BluetoothUuid;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070035import android.car.CarBluetoothManager;
36import android.car.ICarBluetoothUserService;
37import android.car.ICarUserService;
38import android.car.drivingstate.CarUxRestrictions;
39import android.car.drivingstate.ICarUxRestrictionsChangeListener;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080040import android.car.hardware.CarPropertyValue;
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -080041import android.car.hardware.CarSensorEvent;
42import android.car.hardware.CarSensorManager;
43import android.car.hardware.ICarSensorEventListener;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080044import android.car.hardware.cabin.CarCabinManager;
45import android.car.hardware.property.CarPropertyEvent;
46import android.car.hardware.property.ICarPropertyEventListener;
47import android.content.BroadcastReceiver;
48import android.content.Context;
49import android.content.Intent;
50import android.content.IntentFilter;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070051import android.os.ParcelUuid;
52import android.os.Parcelable;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080053import android.os.RemoteException;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070054import android.os.UserHandle;
55import android.provider.Settings;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080056import android.util.Log;
57
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -070058import com.android.internal.annotations.VisibleForTesting;
59
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080060import java.io.PrintWriter;
61import java.util.ArrayList;
62import java.util.Arrays;
63import java.util.HashMap;
Vasu Norie5fb9112017-11-17 10:23:02 -080064import java.util.HashSet;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080065import java.util.List;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080066import java.util.Map;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080067import java.util.Set;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070068import java.util.concurrent.locks.ReentrantLock;
69
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080070
71/**
72 * A Bluetooth Device Connection policy that is specific to the use cases of a Car. A car's
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070073 * bluetooth capabilities in terms of the profiles it supports and its use cases are unique.
74 * Hence the CarService manages the policy that drives when and what to connect to.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080075 *
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080076 * When to connect:
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070077 * The policy can be configured to listen to various vehicle events that are appropriate to
78 * trigger a connection attempt. Signals like door unlock/open, ignition state changes indicate
79 * user entry and there by attempt to connect to their devices. This removes the need for the user
80 * to manually connect his device everytime they get in a car.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080081 *
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080082 * Which device to connect:
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070083 * The policy also keeps track of the {Profile : DevicesThatCanConnectOnTheProfile} and when
84 * it is time to connect, picks the device that is appropriate and available.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080085 * For every profile, the policy attempts to connect to the last connected device first. The policy
86 * maintains a list of connect-able devices for every profile, in the order of how recently they
87 * connected. The device that successfully connects on a profile is moved to the top of the list
88 * of devices for that profile, so the next time a connection attempt is made, the policy starts
89 * with the last connected device first.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080090 */
91
92public class BluetoothDeviceConnectionPolicy {
93 private static final String TAG = "BTDevConnectionPolicy";
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070094 private static final String SETTINGS_DELIMITER = ",";
Vasu Norie5fb9112017-11-17 10:23:02 -080095 private static final boolean DBG = Utils.DBG;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070096 private final Context mContext;
Pavel Maltsevc9e86e82017-03-22 11:37:21 -070097 private boolean mInitialized = false;
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -070098 private boolean mUserSpecificInfoInitialized = false;
99 private final Object mSetupLock = new Object();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700100
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700101 // The main data structure that holds on to the {profile:list of known and connectible devices}
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800102 private HashMap<Integer, BluetoothDevicesInfo> mProfileToConnectableDevicesMap;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700103
Vasu Nori6b2789a2017-12-04 16:14:28 -0800104 /// TODO(vnori): fix this. b/70029056
105 private static final int NUM_SUPPORTED_PHONE_CONNECTIONS = 4; // num of HFP and PBAP connections
106 private static final int NUM_SUPPORTED_MSG_CONNECTIONS = 4; // num of MAP connections
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700107 private static final int NUM_SUPPORTED_MUSIC_CONNECTIONS = 1; // num of A2DP connections
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800108 private static final int NUM_SUPPORTED_NETWORK_CONNECTIONS = 1; // num of PAN connections
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700109 private Map<Integer, Integer> mNumSupportedActiveConnections;
110
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700111 private BluetoothAutoConnectStateMachine mBluetoothAutoConnectStateMachine;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800112 private final BluetoothAdapter mBluetoothAdapter;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700113 private BroadcastReceiver mBluetoothBroadcastReceiver;
114
115 private ICarUserService mCarUserService;
116 private PerUserCarServiceHelper mUserServiceHelper;
117 private ICarBluetoothUserService mCarBluetoothUserService;
118 private ReentrantLock mCarUserServiceAccessLock;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800119
120 // Events that are listened to for triggering an auto-connect:
121 // Cabin events like Door unlock coming from the Cabin Service.
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800122 private final CarCabinService mCarCabinService;
123 private final CarPropertyListener mCabinEventListener;
124 // Sensor events like Ignition switch ON from the Car Sensor Service
125 private final CarSensorService mCarSensorService;
126 private final CarSensorEventListener mCarSensorEventListener;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800127
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700128 // PerUserCarService related listeners
129 private final UserServiceConnectionCallback mServiceCallback;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800130
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700131 // Car Bluetooth Priority Settings Manager
132 private final CarBluetoothService mCarBluetoothService;
133
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700134 // Car UX Restrictions Manager Service to know when user restrictions are in place
135 private final CarUxRestrictionsManagerService mUxRService;
136 private final CarUxRServiceListener mUxRListener;
Joseph Pirozzoe5ac56c2018-01-10 15:05:57 -0800137 // Fast Pair Provider to allow discovery of new phones
138 private final FastPairProvider mFastPairProvider;
139
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700140 // The Bluetooth profiles that the CarService will try to auto-connect on.
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700141 private final List<Integer> mProfilesToConnect;
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700142 private final List<Integer> mPrioritiesSupported;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800143 private static final int MAX_CONNECT_RETRIES = 1;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800144 private static final int PROFILE_NOT_AVAILABLE = -1;
145
146 // Device & Profile currently being connected on
147 private ConnectionParams mConnectionInFlight;
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700148 // Allow write to Settings.Secure
149 private boolean mAllowReadWriteToSettings = true;
Vasu Norie5fb9112017-11-17 10:23:02 -0800150 // Maintain a list of Paired devices which haven't connected on any profiles yet.
151 private Set<BluetoothDevice> mPairedButUnconnectedDevices = new HashSet<>();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800152
153 public static BluetoothDeviceConnectionPolicy create(Context context,
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700154 CarCabinService carCabinService, CarSensorService carSensorService,
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700155 PerUserCarServiceHelper userServiceHelper, CarUxRestrictionsManagerService uxrService,
156 CarBluetoothService bluetoothService) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700157 return new BluetoothDeviceConnectionPolicy(context, carCabinService, carSensorService,
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700158 userServiceHelper, uxrService, bluetoothService);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800159 }
160
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800161 private BluetoothDeviceConnectionPolicy(Context context, CarCabinService carCabinService,
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700162 CarSensorService carSensorService, PerUserCarServiceHelper userServiceHelper,
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700163 CarUxRestrictionsManagerService uxrService, CarBluetoothService bluetoothService) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800164 mContext = context;
165 mCarCabinService = carCabinService;
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800166 mCarSensorService = carSensorService;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700167 mUserServiceHelper = userServiceHelper;
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700168 mUxRService = uxrService;
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700169 mCarBluetoothService = bluetoothService;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700170 mCarUserServiceAccessLock = new ReentrantLock();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700171 mProfilesToConnect = Arrays.asList(
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700172 BluetoothProfile.HEADSET_CLIENT, BluetoothProfile.A2DP_SINK,
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700173 BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_CLIENT, BluetoothProfile.PAN);
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700174 mPrioritiesSupported = Arrays.asList(
175 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0,
176 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1
177 );
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700178 // mNumSupportedActiveConnections is a HashMap of mProfilesToConnect and the number of
179 // connections each profile supports currently.
180 mNumSupportedActiveConnections = new HashMap<>(mProfilesToConnect.size());
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700181 for (Integer profile : mProfilesToConnect) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700182 switch (profile) {
183 case BluetoothProfile.HEADSET_CLIENT:
184 mNumSupportedActiveConnections.put(BluetoothProfile.HEADSET_CLIENT,
185 NUM_SUPPORTED_PHONE_CONNECTIONS);
186 break;
187 case BluetoothProfile.PBAP_CLIENT:
188 mNumSupportedActiveConnections.put(BluetoothProfile.PBAP_CLIENT,
189 NUM_SUPPORTED_PHONE_CONNECTIONS);
190 break;
191 case BluetoothProfile.A2DP_SINK:
192 mNumSupportedActiveConnections.put(BluetoothProfile.A2DP_SINK,
193 NUM_SUPPORTED_MUSIC_CONNECTIONS);
194 break;
195 case BluetoothProfile.MAP_CLIENT:
196 mNumSupportedActiveConnections.put(BluetoothProfile.MAP_CLIENT,
197 NUM_SUPPORTED_MSG_CONNECTIONS);
198 break;
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700199 case BluetoothProfile.PAN:
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800200 mNumSupportedActiveConnections.put(BluetoothProfile.PAN,
201 NUM_SUPPORTED_NETWORK_CONNECTIONS);
202 break;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700203 }
204 }
205
206 // Listen to Cabin events for triggering auto connect
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800207 mCabinEventListener = new CarPropertyListener();
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700208 // Listen to Sensor Events for triggering auto connect
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800209 mCarSensorEventListener = new CarSensorEventListener();
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700210 // Listen to UX Restrictions to know when to enable fast-pairing
211 mUxRListener = new CarUxRServiceListener();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700212 // Listen to User switching to connect to per User device.
213 mServiceCallback = new UserServiceConnectionCallback();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800214 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800215 if (mBluetoothAdapter == null) {
216 Log.w(TAG, "No Bluetooth Adapter Available");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800217 }
Joseph Pirozzoe5ac56c2018-01-10 15:05:57 -0800218 mFastPairProvider = new FastPairProvider(mContext);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800219 }
220
221 /**
222 * ConnectionParams - parameters/objects relevant to the bluetooth connection calls.
223 * This encapsulates the information that is passed around across different methods in the
224 * policy. Contains the bluetooth device {@link BluetoothDevice} and the list of profiles that
225 * we want that device to connect on.
226 * Used as the currency that methods use to talk to each other in the policy.
227 */
228 public static class ConnectionParams {
229 private BluetoothDevice mBluetoothDevice;
230 private Integer mBluetoothProfile;
231
232 public ConnectionParams() {
233 // default constructor
234 }
235
236 public ConnectionParams(Integer profile) {
237 mBluetoothProfile = profile;
238 }
239
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700240 public ConnectionParams(Integer profile, BluetoothDevice device) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800241 mBluetoothProfile = profile;
242 mBluetoothDevice = device;
243 }
244
245 // getters & Setters
246 public void setBluetoothDevice(BluetoothDevice device) {
247 mBluetoothDevice = device;
248 }
249
250 public void setBluetoothProfile(Integer profile) {
251 mBluetoothProfile = profile;
252 }
253
254 public BluetoothDevice getBluetoothDevice() {
255 return mBluetoothDevice;
256 }
257
258 public Integer getBluetoothProfile() {
259 return mBluetoothProfile;
260 }
261 }
262
263 /**
264 * BluetoothBroadcastReceiver receives the bluetooth related intents that are relevant to
265 * connection
266 * and bonding state changes. Reports the information to the {@link
267 * BluetoothDeviceConnectionPolicy}
268 * for it update its status.
269 */
270 public class BluetoothBroadcastReceiver extends BroadcastReceiver {
271 @Override
272 public void onReceive(Context context, Intent intent) {
273 String action = intent.getAction();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800274 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700275 if (DBG) {
276 if (device != null) {
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700277 Log.d(TAG, "Received Intent for device: " + device + " " + action);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700278 } else {
279 Log.d(TAG, "Received Intent no device: " + action);
280 }
281 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800282 ConnectionParams connectParams;
283 if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
284 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
285 BluetoothDevice.ERROR);
286 updateBondState(device, bondState);
287
288 } else if (BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700289 connectParams = new ConnectionParams(BluetoothProfile.A2DP_SINK, device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800290 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
291 BluetoothProfile.STATE_DISCONNECTED);
292 notifyConnectionStatus(connectParams, currState);
293
294 } else if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700295 connectParams = new ConnectionParams(BluetoothProfile.HEADSET_CLIENT, device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800296 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
297 BluetoothProfile.STATE_DISCONNECTED);
298 notifyConnectionStatus(connectParams, currState);
299
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800300 } else if (BluetoothPan.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
301 connectParams = new ConnectionParams(BluetoothProfile.PAN, device);
302 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
303 BluetoothProfile.STATE_DISCONNECTED);
304 notifyConnectionStatus(connectParams, currState);
305
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800306 } else if (BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700307 connectParams = new ConnectionParams(BluetoothProfile.PBAP_CLIENT, device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800308 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
309 BluetoothProfile.STATE_DISCONNECTED);
310 notifyConnectionStatus(connectParams, currState);
311
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800312 } else if (BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700313 connectParams = new ConnectionParams(BluetoothProfile.MAP_CLIENT, device);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800314 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
315 BluetoothProfile.STATE_DISCONNECTED);
316 notifyConnectionStatus(connectParams, currState);
317
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800318 } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700319 int currState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800320 if (DBG) {
321 Log.d(TAG, "Bluetooth Adapter State: " + currState);
322 }
323 if (currState == BluetoothAdapter.STATE_ON) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700324 // Read from Settings which devices to connect to and populate
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800325 // mProfileToConnectableDevicesMap
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700326 readAndRebuildDeviceMapFromSettings();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800327 initiateConnection();
328 } else if (currState == BluetoothAdapter.STATE_OFF) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800329 // Write currently connected device snapshot to file.
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700330 writeDeviceInfoToSettings();
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800331 resetBluetoothDevicesConnectionInfo();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800332 }
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700333 } else if (BluetoothDevice.ACTION_UUID.equals(action)) {
334 // Received during pairing with the UUIDs of the Bluetooth profiles supported by
335 // the remote device.
336 if (DBG) {
337 Log.d(TAG, "Received UUID intent for device " + device);
338 }
339 Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
340 if (uuids != null) {
341 ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
342 for (int i = 0; i < uuidsToSend.length; i++) {
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700343 uuidsToSend[i] = (ParcelUuid) uuids[i];
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700344 }
345 setProfilePriorities(device, uuidsToSend, BluetoothProfile.PRIORITY_ON);
346 }
347
348 }
349 }
350 }
351
352 /**
353 * Set priority for the Bluetooth profiles.
354 *
355 * The Bluetooth service stores the priority of a Bluetooth profile per device as a key value
356 * pair - BluetoothProfile_device:<Priority>.
357 * When we pair a device from the Settings App, the expected behavior is for the app to connect
358 * on all appropriate profiles after successful pairing automatically, without the user having
359 * to explicitly issue a connect. The settings app checks for the priority of the device from
360 * the above key-value pair and if the priority is set to PRIORITY_OFF or PRIORITY_UNDEFINED,
361 * the settings app will stop with just pairing and not connect.
362 * This scenario will happen when we pair a device, then unpair it and then pair it again. When
363 * the device is unpaired, the BT stack sets the priority for that device to PRIORITY_UNDEFINED
364 * ( as a way of resetting). So, the next time the same device is paired, the Settings app will
365 * stop with just pairing and not connect as explained above. Here, we register to receive the
366 * ACTION_UUID intent, which will broadcast the UUIDs corresponding to the profiles supported by
367 * the remote device which is successfully paired and we turn on the priority so when the
368 * Settings app tries to check before connecting, the priority is set to the expected value.
369 *
370 * @param device - Remote Bluetooth device
371 * @param uuids - UUIDs of the Bluetooth Profiles supported by the remote device
372 * @param priority - priority to set
373 */
374 private void setProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids, int priority) {
375 // need the BluetoothProfile proxy to be able to call the setPriority API
376 if (mCarBluetoothUserService == null) {
377 mCarBluetoothUserService = setupBluetoothUserService();
378 }
379 if (mCarBluetoothUserService != null) {
380 for (Integer profile : mProfilesToConnect) {
381 setBluetoothProfilePriorityIfUuidFound(uuids, profile, device, priority);
382 }
383 }
384 }
385
386 private void setBluetoothProfilePriorityIfUuidFound(ParcelUuid[] uuids, int profile,
387 BluetoothDevice device, int priority) {
388 if (mCarBluetoothUserService == null || device == null) {
389 return;
390 }
391 // Build a list of UUIDs that represent a profile.
392 List<ParcelUuid> uuidsToCheck = new ArrayList<>();
393 switch (profile) {
394 case BluetoothProfile.A2DP_SINK:
395 uuidsToCheck.add(BluetoothUuid.AudioSource);
396 break;
397 case BluetoothProfile.HEADSET_CLIENT:
398 uuidsToCheck.add(BluetoothUuid.Handsfree_AG);
399 uuidsToCheck.add(BluetoothUuid.HSP_AG);
400 break;
401 case BluetoothProfile.PBAP_CLIENT:
402 uuidsToCheck.add(BluetoothUuid.PBAP_PSE);
403 break;
404 case BluetoothProfile.MAP_CLIENT:
405 uuidsToCheck.add(BluetoothUuid.MAS);
406 break;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800407 case BluetoothProfile.PAN:
408 uuidsToCheck.add(BluetoothUuid.PANU);
409 break;
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700410 }
411
412 for (ParcelUuid uuid : uuidsToCheck) {
413 if (BluetoothUuid.isUuidPresent(uuids, uuid)) {
414 try {
415 mCarBluetoothUserService.setProfilePriority(profile, device, priority);
416 } catch (RemoteException e) {
417 Log.e(TAG, "RemoteException calling setProfilePriority");
418 }
419 // if any one of the uuid in uuidsTocheck is present, set the priority and break
420 break;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800421 }
422 }
423 }
424
425 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700426 * Cleanup state and reinitialize whenever we connect to the PerUserCarService.
427 * This happens in init() and whenever the PerUserCarService is restarted on User Switch Events
428 */
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700429 @VisibleForTesting
430 class UserServiceConnectionCallback implements PerUserCarServiceHelper.ServiceCallback {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700431 @Override
432 public void onServiceConnected(ICarUserService carUserService) {
433 if (mCarUserServiceAccessLock != null) {
434 mCarUserServiceAccessLock.lock();
435 try {
436 mCarUserService = carUserService;
437 } finally {
438 mCarUserServiceAccessLock.unlock();
439 }
440 }
441 if (DBG) {
442 Log.d(TAG, "Connected to PerUserCarService");
443 }
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700444 // Get the BluetoothUserService and also setup the Bluetooth Connection Proxy for
445 // all profiles.
446 mCarBluetoothUserService = setupBluetoothUserService();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700447 // re-initialize for current user.
448 initializeUserSpecificInfo();
449 }
450
451 @Override
452 public void onPreUnbind() {
453 if (DBG) {
454 Log.d(TAG, "Before Unbinding from UserService");
455 }
456 try {
457 if (mCarBluetoothUserService != null) {
458 mCarBluetoothUserService.closeBluetoothConnectionProxy();
459 }
460 } catch (RemoteException e) {
461 Log.e(TAG,
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700462 "Remote Exception during closeBluetoothConnectionProxy(): "
463 + e.getMessage());
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700464 }
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700465 // Clean up information related to user who went background.
466 cleanupUserSpecificInfo();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700467 }
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700468
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700469 @Override
470 public void onServiceDisconnected() {
471 if (DBG) {
472 Log.d(TAG, "Disconnected from PerUserCarService");
473 }
474 if (mCarUserServiceAccessLock != null) {
475 mCarUserServiceAccessLock.lock();
476 try {
477 mCarBluetoothUserService = null;
478 mCarUserService = null;
479 } finally {
480 mCarUserServiceAccessLock.unlock();
481 }
482 }
483 }
484 }
485
486 /**
487 * Gets the Per User Car Bluetooth Service (ICarBluetoothService) from the PerUserCarService
488 * which acts as a top level Service running in the current user context.
489 * Also sets up the connection proxy objects required to communicate with the Bluetooth
490 * Profile Services.
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700491 *
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700492 * @return ICarBluetoothUserService running in current user
493 */
494 private ICarBluetoothUserService setupBluetoothUserService() {
495 ICarBluetoothUserService carBluetoothUserService = null;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700496 if (mCarUserService != null) {
497 try {
498 carBluetoothUserService = mCarUserService.getBluetoothUserService();
499 if (carBluetoothUserService != null) {
500 if (DBG) {
501 Log.d(TAG, "Got CarBTUsrSvc");
502 }
503 carBluetoothUserService.setupBluetoothConnectionProxy();
504 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700505 } catch (RemoteException e) {
506 Log.e(TAG, "Remote Service Exception on ServiceConnection Callback: "
507 + e.getMessage());
508 }
509 } else {
510 if (DBG) {
511 Log.d(TAG, "PerUserCarService not connected");
512 }
513 }
514 return carBluetoothUserService;
515 }
516
517 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800518 * Setup the Bluetooth profile service connections and Vehicle Event listeners.
519 * and start the state machine -{@link BluetoothAutoConnectStateMachine}
520 */
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700521 public synchronized void init() {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800522 if (DBG) {
523 Log.d(TAG, "init()");
524 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700525 // Initialize information specific to current user.
526 initializeUserSpecificInfo();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800527 // Listen to various events coming from the vehicle.
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700528 setupEventListenersLocked();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700529 mInitialized = true;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800530 }
531
532 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700533 * Setup and initialize information that is specific per User account, which involves:
534 * 1. Reading the list of devices to connect for current user and initialize the deviceMap
535 * with that information.
536 * 2. Register a BroadcastReceiver for bluetooth related events for the current user.
537 * 3. Start and bind to {@link PerUserCarService} as current user.
538 * 4. Start the {@link BluetoothAutoConnectStateMachine}
539 */
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700540 private void initializeUserSpecificInfo() {
541 synchronized (mSetupLock) {
542 if (DBG) {
543 Log.d(TAG, "initializeUserSpecificInfo()");
544 }
545 if (mUserSpecificInfoInitialized) {
546 if (DBG) {
547 Log.d(TAG, "Already Initialized");
548 }
549 return;
550 }
551 mBluetoothAutoConnectStateMachine = BluetoothAutoConnectStateMachine.make(this);
552 readAndRebuildDeviceMapFromSettings();
553 setupBluetoothEventsIntentFilterLocked();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700554
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700555 mConnectionInFlight = new ConnectionParams();
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700556 mUserSpecificInfoInitialized = true;
557 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700558 }
559
560 /**
561 * Setting up the Intent filter for Bluetooth related broadcasts
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800562 * This includes knowing when the
563 * 1. Bluetooth Adapter turned on/off
564 * 2. Bonding State of a device changes
565 * 3. A specific profile's connection state changes.
566 */
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700567 private void setupBluetoothEventsIntentFilterLocked() {
568 mBluetoothBroadcastReceiver = new BluetoothBroadcastReceiver();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700569 IntentFilter profileFilter = new IntentFilter();
570 profileFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
571 profileFilter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
572 profileFilter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800573 profileFilter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700574 profileFilter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
575 profileFilter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
576 profileFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700577 profileFilter.addAction(BluetoothDevice.ACTION_UUID);
578 if (mContext != null) {
579 mContext.registerReceiverAsUser(mBluetoothBroadcastReceiver, UserHandle.CURRENT,
580 profileFilter, null, null);
581 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800582 }
583
584 /**
585 * Initialize the {@link #mProfileToConnectableDevicesMap}.
586 * {@link #mProfileToConnectableDevicesMap} stores the profile:DeviceList information. This
587 * method retrieves it from persistent memory.
588 */
589 private synchronized void initDeviceMap() {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700590 if (mProfileToConnectableDevicesMap == null) {
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700591 mProfileToConnectableDevicesMap = new HashMap<>();
592 for (Integer profile : mProfilesToConnect) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700593 // Build the BluetoothDevicesInfo for this profile.
594 BluetoothDevicesInfo devicesInfo = new BluetoothDevicesInfo(profile,
595 mNumSupportedActiveConnections.get(profile));
596 mProfileToConnectableDevicesMap.put(profile, devicesInfo);
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700597 }
598 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700599 Log.d(TAG, "Created a new empty Device Map");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800600 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800601 }
602 }
603
604 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800605 * Setting up Listeners to the various events we are interested in listening to for initiating
606 * Bluetooth connection attempts.
607 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700608 private void setupEventListenersLocked() {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800609 // Setting up a listener for events from CarCabinService
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800610 // For now, we listen to door unlock signal coming from {@link CarCabinService},
611 // and Ignition state START from {@link CarSensorService}
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800612 mCarCabinService.registerListener(mCabinEventListener);
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800613 mCarSensorService.registerOrUpdateSensorListener(
614 CarSensorManager.SENSOR_TYPE_IGNITION_STATE, 0, mCarSensorEventListener);
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700615 // Get Current restrictions and handle them
616 handleUxRestrictionsChanged(mUxRService.getCurrentUxRestrictions());
617 // Register for future changes to the UxRestrictions
618 mUxRService.registerUxRestrictionsChangeListener(mUxRListener);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700619 mUserServiceHelper.registerServiceCallback(mServiceCallback);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800620 }
621
622 /**
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800623 * Handles events coming in from the {@link CarCabinService}
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800624 * The events that can trigger Bluetooth Scanning from CarCabinService is Door Unlock.
625 * Upon receiving the event that is of interest, initiate a connection attempt by calling
626 * the policy {@link BluetoothDeviceConnectionPolicy}
627 */
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700628 @VisibleForTesting
629 class CarPropertyListener extends ICarPropertyEventListener.Stub {
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800630 @Override
631 public void onEvent(CarPropertyEvent event) throws RemoteException {
632 if (DBG) {
633 Log.d(TAG, "Cabin change Event : " + event.getEventType());
634 }
635 Boolean locked;
636 CarPropertyValue value = event.getCarPropertyValue();
637 Object o = value.getValue();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800638
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800639 if (value.getPropertyId() == CarCabinManager.ID_DOOR_LOCK) {
640 if (o instanceof Boolean) {
641 locked = (Boolean) o;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800642 if (DBG) {
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800643 Log.d(TAG, "Door Lock: " + locked);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800644 }
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800645 // Attempting a connection only on a door unlock
646 if (!locked) {
647 initiateConnection();
648 }
649 }
650 }
651 }
652 }
653
654 /**
655 * Handles events coming in from the {@link CarSensorService}
656 * The events that can trigger Bluetooth Scanning from CarSensorService is Ignition START.
657 * Upon receiving the event that is of interest, initiate a connection attempt by calling
658 * the policy {@link BluetoothDeviceConnectionPolicy}
659 */
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800660 private class CarSensorEventListener extends ICarSensorEventListener.Stub {
661 @Override
662 public void onSensorChanged(List<CarSensorEvent> events) throws RemoteException {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700663 if (events != null & !events.isEmpty()) {
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800664 CarSensorEvent event = events.get(0);
665 if (DBG) {
666 Log.d(TAG, "Sensor event Type : " + event.sensorType);
667 }
668 if (event.sensorType == CarSensorManager.SENSOR_TYPE_IGNITION_STATE) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800669 if (DBG) {
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800670 Log.d(TAG, "Sensor value : " + event.intValues[0]);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800671 }
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800672 if (event.intValues[0] == CarSensorEvent.IGNITION_STATE_START) {
673 initiateConnection();
674 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800675 }
676 }
677 }
678 }
679
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700680 private class CarUxRServiceListener extends ICarUxRestrictionsChangeListener.Stub {
681 @Override
682 public void onUxRestrictionsChanged(CarUxRestrictions restrictions) {
683 handleUxRestrictionsChanged(restrictions);
684 }
685 }
686
687 private void handleUxRestrictionsChanged(CarUxRestrictions restrictions) {
688 if (restrictions == null) {
689 return;
690 }
691 if ((restrictions.getActiveRestrictions() & CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP)
692 == CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP) {
693 mFastPairProvider.stopAdvertising();
694 } else {
695 mFastPairProvider.startAdvertising();
696 }
697 }
698
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800699 /**
700 * Clean up slate. Close the Bluetooth profile service connections and quit the state machine -
701 * {@link BluetoothAutoConnectStateMachine}
702 */
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700703 public synchronized void release() {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800704 if (DBG) {
705 Log.d(TAG, "release()");
706 }
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700707 mInitialized = false;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700708 writeDeviceInfoToSettings();
709 cleanupUserSpecificInfo();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800710 closeEventListeners();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700711 }
712
713 /**
714 * Clean up information related to user who went background.
715 */
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700716 private void cleanupUserSpecificInfo() {
717 synchronized (mSetupLock) {
718 if (DBG) {
719 Log.d(TAG, "cleanupUserSpecificInfo()");
720 }
721 if (!mUserSpecificInfoInitialized) {
722 if (DBG) {
723 Log.d(TAG, "User specific Info Not initialized..Not cleaning up");
724 }
725 return;
726 }
727 mUserSpecificInfoInitialized = false;
728 // quit the state machine
729 mBluetoothAutoConnectStateMachine.doQuit();
730 mProfileToConnectableDevicesMap = null;
731 mConnectionInFlight = null;
732 if (mBluetoothBroadcastReceiver != null) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700733 if (mContext != null) {
734 mContext.unregisterReceiver(mBluetoothBroadcastReceiver);
735 }
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700736 mBluetoothBroadcastReceiver = null;
737 }
738 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800739 }
740
741 /**
742 * Unregister the listeners to the various Vehicle events coming from other parts of the
743 * CarService
744 */
745 private void closeEventListeners() {
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700746 if (DBG) {
747 Log.d(TAG, "closeEventListeners()");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800748 }
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700749 mCarCabinService.unregisterListener(mCabinEventListener);
750 mCarSensorService.unregisterSensorListener(CarSensorManager.SENSOR_TYPE_IGNITION_STATE,
751 mCarSensorEventListener);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700752 mUserServiceHelper.unregisterServiceCallback(mServiceCallback);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800753 }
754
755 /**
756 * Resets the {@link BluetoothDevicesInfo#mConnectionInfo} of all the profiles to start from
757 * a clean slate. The ConnectionInfo has all the book keeping information regarding the state
758 * of connection attempts - like which device in the device list for the profile is the next
759 * to try connecting etc.
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700760 * This method does not clear the {@link BluetoothDevicesInfo#mDeviceInfoList} like the {@link
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800761 * #resetProfileToConnectableDevicesMap()} method does.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800762 */
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800763 private synchronized void resetBluetoothDevicesConnectionInfo() {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800764 if (DBG) {
765 Log.d(TAG, "Resetting ConnectionInfo for all profiles");
766 }
767 for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
768 devInfo.resetConnectionInfoLocked();
769 }
770 }
771
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700772 @VisibleForTesting
773 BroadcastReceiver getBluetoothBroadcastReceiver() {
774 return mBluetoothBroadcastReceiver;
775 }
776
777 @VisibleForTesting
778 UserServiceConnectionCallback getServiceCallback() {
779 return mServiceCallback;
780 }
781
782 @VisibleForTesting
783 CarPropertyListener getCarPropertyListener() {
784 return mCabinEventListener;
785 }
786
787 @VisibleForTesting
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700788 synchronized void setAllowReadWriteToSettings(boolean allowWrite) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700789 mAllowReadWriteToSettings = allowWrite;
790 }
791
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700792 @VisibleForTesting
793 BluetoothDevicesInfo getBluetoothDevicesInfo(int profile) {
794 return mProfileToConnectableDevicesMap.get(profile);
795 }
796
Vasu Norie5fb9112017-11-17 10:23:02 -0800797 @VisibleForTesting
798 String toDebugString() {
799 StringBuilder buf = new StringBuilder();
800 for (Integer profile : mProfileToConnectableDevicesMap.keySet()) {
801 BluetoothDevicesInfo info = mProfileToConnectableDevicesMap.get(profile);
802 buf.append(" \n**** profile = " + profile);
803 buf.append(", " + info.toDebugString());
804 }
805 return buf.toString();
806 }
807
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800808 /**
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800809 * Resets the {@link #mProfileToConnectableDevicesMap} to a clean and empty slate.
810 */
811 public synchronized void resetProfileToConnectableDevicesMap() {
812 if (DBG) {
813 Log.d(TAG, "Resetting the mProfilesToConnectableDevicesMap");
814 }
815 for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
816 devInfo.resetDeviceListLocked();
817 }
818 }
819
820 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800821 * Returns the list of profiles that the Autoconnection policy attempts to connect on
822 *
823 * @return profile list.
824 */
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700825 public synchronized List<Integer> getProfilesToConnect() {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800826 return mProfilesToConnect;
827 }
828
829 /**
830 * Add a new Profile to the list of To Be Connected profiles.
831 *
832 * @param profile - ProfileInfo of the new profile to be added.
833 */
834 public synchronized void addProfile(Integer profile) {
835 mProfilesToConnect.add(profile);
836 }
837
838 /**
839 * Add or remove a device based on the bonding state change.
840 *
841 * @param device - device to add/remove
842 * @param bondState - current bonding state
843 */
844 private void updateBondState(BluetoothDevice device, int bondState) {
845 if (device == null) {
846 Log.e(TAG, "updateBondState: device Null");
847 return;
848 }
849 if (DBG) {
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700850 Log.d(TAG, "BondState :" + bondState + " Device: " + device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800851 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800852 // Bonded devices are added to a profile's device list after the device CONNECTS on the
853 // profile. When unpaired, we remove the device from all of the profiles' device list.
854 if (bondState == BluetoothDevice.BOND_NONE) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800855 for (Integer profile : mProfilesToConnect) {
Ram Periathiruvadiee77dde2017-05-25 13:09:07 -0700856 if (DBG) {
857 Log.d(TAG, "Removing " + device + " from profile: " + profile);
858 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800859 removeDeviceFromProfile(device, profile);
860 }
Vasu Norie5fb9112017-11-17 10:23:02 -0800861 } else if (bondState == BluetoothDevice.BOND_BONDED) {
862 // A device just paired. When it connects on HFP profile, then immediately initiate
863 // connections on PBAP and MAP. for now, maintain this fact in a data structure
864 // to be looked up when HFP profile connects.
865 mPairedButUnconnectedDevices.add(device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800866 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800867 }
868
869 /**
870 * Add a new device to the list of devices connect-able on the given profile
871 *
872 * @param device - Bluetooth device to be added
873 * @param profile - profile to add the device to.
874 */
875 private synchronized void addDeviceToProfile(BluetoothDevice device, Integer profile) {
876 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
877 if (devInfo == null) {
878 if (DBG) {
879 Log.d(TAG, "Creating devInfo for profile: " + profile);
880 }
881 devInfo = new BluetoothDevicesInfo(profile);
882 mProfileToConnectableDevicesMap.put(profile, devInfo);
883 }
884 devInfo.addDeviceLocked(device);
885 }
886
887 /**
888 * Remove the device from the list of devices connect-able on the gievn profile.
889 *
890 * @param device - Bluetooth device to be removed
891 * @param profile - profile to remove the device from
892 */
893 private synchronized void removeDeviceFromProfile(BluetoothDevice device, Integer profile) {
894 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
895 if (devInfo != null) {
896 devInfo.removeDeviceLocked(device);
897 }
898 }
899
900 /**
901 * Initiate a bluetooth connection.
902 */
903 private void initiateConnection() {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800904 // Make sure the bluetooth adapter is available & enabled.
905 if (mBluetoothAdapter == null) {
906 Log.w(TAG, "Bluetooth Adapter null");
907 return;
908 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800909 if (mBluetoothAdapter.isEnabled()) {
910 if (isDeviceMapEmpty()) {
911 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700912 Log.d(TAG, "Device Map is empty. Nothing to connect to");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800913 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700914 return;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800915 }
916 resetDeviceAvailableToConnect();
917 if (DBG) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800918 Log.d(TAG, "initiateConnection() Reset Device Availability");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800919 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700920 mBluetoothAutoConnectStateMachine.sendMessage(BluetoothAutoConnectStateMachine
921 .CONNECT);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800922 } else {
923 if (DBG) {
924 Log.d(TAG, "Bluetooth Adapter not enabled.");
925 }
926 }
927 }
928
929 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800930 * Find an unconnected profile and find a device to connect on it.
931 * Finds the appropriate device for the profile from the information available in
932 * {@link #mProfileToConnectableDevicesMap}
933 *
934 * @return true - if we found a device to connect on for any of the {@link #mProfilesToConnect}
935 * false - if we cannot find a device to connect to or if we are not ready to connect yet.
936 */
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800937 public synchronized boolean findDeviceToConnect() {
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700938 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()
939 || mProfileToConnectableDevicesMap == null || !mInitialized) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700940 if (DBG) {
941 if (mProfileToConnectableDevicesMap == null) {
942 Log.d(TAG, "findDeviceToConnect(): Device Map null");
943 } else {
944 Log.d(TAG, "findDeviceToConnect(): BT Adapter not enabled");
945 }
946 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800947 return false;
948 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700949 boolean connectingToADevice = false;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800950 // Get the first unconnected profile that we can try to make a connection
951 Integer nextProfile = getNextProfileToConnectLocked();
952 // Keep going through the profiles until we find a device that we can connect to
953 while (nextProfile != PROFILE_NOT_AVAILABLE) {
954 if (DBG) {
955 Log.d(TAG, "connectToProfile(): " + nextProfile);
956 }
957 // find a device that is next in line for a connection attempt for that profile
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700958 // and try connecting to it.
959 connectingToADevice = connectToNextDeviceInQueueLocked(nextProfile);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800960 // If we found a device to connect, break out of the loop
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700961 if (connectingToADevice) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800962 if (DBG) {
963 Log.d(TAG, "Found device to connect to");
964 }
965 BluetoothDeviceConnectionPolicy.ConnectionParams btParams =
966 new BluetoothDeviceConnectionPolicy.ConnectionParams(
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700967 mConnectionInFlight.getBluetoothProfile(),
968 mConnectionInFlight.getBluetoothDevice());
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800969 // set up a time out
970 mBluetoothAutoConnectStateMachine.sendMessageDelayed(
971 BluetoothAutoConnectStateMachine.CONNECT_TIMEOUT, btParams,
972 BluetoothAutoConnectStateMachine.CONNECTION_TIMEOUT_MS);
973 break;
974 } else {
975 // result will be false, if there are no more devices to connect
976 // or if the ProfileProxy objects are null (ServiceConnection
977 // not yet established for this profile)
978 if (DBG) {
979 Log.d(TAG, "No more device to connect on Profile: " + nextProfile);
980 }
981 nextProfile = getNextProfileToConnectLocked();
982 }
983 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700984 return connectingToADevice;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800985 }
986
987 /**
988 * Get the first unconnected profile.
989 *
990 * @return profile to connect.
991 * Special return value 0 if
992 * 1. all profiles have been connected on.
993 * 2. no profile connected but no nearby known device that can be connected to
994 */
995 private Integer getNextProfileToConnectLocked() {
996 for (Integer profile : mProfilesToConnect) {
997 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
998 if (devInfo != null) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700999 if (devInfo.isProfileConnectableLocked()) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001000 return profile;
1001 }
1002 } else {
1003 Log.e(TAG, "Unexpected: devInfo null for profile: " + profile);
1004 }
1005 }
1006 // Reaching here denotes all profiles are connected or No devices available for any profile
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001007 if (DBG) {
1008 Log.d(TAG, "No disconnected profiles");
1009 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001010 return PROFILE_NOT_AVAILABLE;
1011 }
1012
1013 /**
Vasu Norie5fb9112017-11-17 10:23:02 -08001014 * Checks if the Bluetooth profile service's proxy object is available.
1015 * Proxy obj should be available before we can attempt to connect on that profile.
1016 *
1017 * @param profile The profile to check the presence of the proxy object
1018 * @return True if the proxy obj exists. False otherwise.
1019 */
1020 private boolean isProxyAvailable(Integer profile) {
1021 if (mCarBluetoothUserService == null) {
1022 mCarBluetoothUserService = setupBluetoothUserService();
1023 if (mCarBluetoothUserService == null) {
1024 Log.d(TAG, "CarBluetoothUserSvc null. Car service not bound to PerUserCarSvc.");
1025 return false;
1026 }
1027 }
1028 try {
1029 if (!mCarBluetoothUserService.isBluetoothConnectionProxyAvailable(profile)) {
1030 // proxy unavailable.
1031 if (DBG) {
1032 Log.d(TAG, "Proxy for Bluetooth Profile Service Unavailable: " + profile);
1033 }
1034 return false;
1035 }
1036 } catch (RemoteException e) {
1037 Log.e(TAG, "Car BT Service Remote Exception.");
1038 return false;
1039 }
1040 return true;
1041 }
1042
1043 /**
1044 * Creates a device connection for the given profile.
1045 *
1046 * @param profile The profile on which the connection is being setup
Ram Periathiruvadie011a402018-03-22 18:54:33 -07001047 * @param device The device for which the connection is to be made.
Vasu Norie5fb9112017-11-17 10:23:02 -08001048 * @return true if the connection is setup successfully. False otherwise.
1049 */
1050 boolean connectToDeviceOnProfile(Integer profile, BluetoothDevice device) {
1051 if (DBG) {
1052 Log.d(TAG, "in connectToDeviceOnProfile for Profile:" + profile +
1053 ", device: " + Utils.getDeviceDebugInfo(device));
1054 }
1055 if (!isProxyAvailable(profile)) {
1056 if (DBG) {
1057 Log.d(TAG, "No proxy available for Profile: " + profile);
1058 }
1059 return false;
1060 }
1061 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1062 if (devInfo == null) {
1063 if (DBG) {
1064 Log.d(TAG, "devInfo NULL on Profile:" + profile);
1065 }
1066 return false;
1067 }
1068 int state = devInfo.getCurrentConnectionStateLocked(device);
1069 if (state == BluetoothProfile.STATE_CONNECTED ||
1070 state == BluetoothProfile.STATE_CONNECTING) {
1071 if (DBG) {
1072 Log.d(TAG, "device " + Utils.getDeviceDebugInfo(device) +
1073 " is already connected/connecting on Profile:" + profile);
1074 }
1075 return true;
1076 }
1077 if (!devInfo.isProfileConnectableLocked()) {
1078 if (DBG) {
1079 Log.d(TAG, "isProfileConnectableLocked FALSE on Profile:" + profile +
1080 " this means number of connections on this profile max'ed out or " +
1081 " no more devices available to connect on this profile");
1082 }
1083 return false;
1084 }
1085 try {
1086 if (mCarBluetoothUserService != null) {
1087 mCarBluetoothUserService.bluetoothConnectToProfile((int) profile, device);
1088 devInfo.setConnectionStateLocked(device, BluetoothProfile.STATE_CONNECTING);
1089 // Increment the retry count & cache what is being connected to
1090 // This method is already called from a synchronized context.
1091 mConnectionInFlight.setBluetoothDevice(device);
1092 mConnectionInFlight.setBluetoothProfile(profile);
1093 devInfo.incrementRetryCountLocked();
1094 if (DBG) {
1095 Log.d(TAG, "Increment Retry to: " + devInfo.getRetryCountLocked() +
1096 ", for Profile: " + profile + ", on device: " +
1097 Utils.getDeviceDebugInfo(device));
1098 }
1099 return true;
1100 } else {
1101 Log.e(TAG, "CarBluetoothUserSvc null");
1102 }
1103 } catch (RemoteException e) {
1104 Log.e(TAG, "Remote User Service stopped responding: " + e.getMessage());
1105 }
1106 return false;
1107 }
1108
1109 /**
1110 * Helper method to reset info in {@link #mConnectionInFlight} in the given
1111 * {@link BluetoothDevicesInfo} object.
1112 *
1113 * @param devInfo the {@link BluetoothDevicesInfo} where the info is to be reset.
1114 */
1115 private void setProfileOnDeviceToUnavailable(BluetoothDevicesInfo devInfo) {
1116 mConnectionInFlight.setBluetoothProfile(0);
1117 mConnectionInFlight.setBluetoothDevice(null);
1118 devInfo.setDeviceAvailableToConnectLocked(false);
1119 }
1120
1121 boolean doesDeviceExistForProfile(Integer profile, BluetoothDevice device) {
1122 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1123 if (devInfo == null) {
1124 if (DBG) {
1125 Log.d(TAG, "devInfo NULL on Profile:" + profile);
1126 }
1127 return false;
1128 }
1129 boolean b = devInfo.checkDeviceInListLocked(device);
1130 if (DBG) {
1131 Log.d(TAG, "Is device " + Utils.getDeviceDebugInfo(device) +
1132 " listed for profile " + profile + ": " + b);
1133 }
1134 return b;
1135 }
1136
1137 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001138 * Try to connect to the next device in the device list for the given profile.
1139 *
1140 * @param profile - profile to connect on
1141 * @return - true if we found a device to connect on for this profile
1142 * false - if we cannot find a device to connect to.
1143 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001144 private boolean connectToNextDeviceInQueueLocked(Integer profile) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001145 // Get the Device Information for the given profile and find the next device to connect on
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001146 BluetoothDevice devToConnect = null;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001147 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1148 if (devInfo == null) {
1149 Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
1150 return false;
1151 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001152
Vasu Norie5fb9112017-11-17 10:23:02 -08001153 if (isProxyAvailable(profile)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001154 // Get the next device in the device list for this profile.
1155 devToConnect = devInfo.getNextDeviceInQueueLocked();
1156 if (devToConnect != null) {
1157 // deviceAvailable && proxyAvailable
Vasu Norie5fb9112017-11-17 10:23:02 -08001158 if (connectToDeviceOnProfile(profile, devToConnect)) return true;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001159 } else {
1160 // device unavailable
1161 if (DBG) {
1162 Log.d(TAG, "No paired nearby device to connect to for profile: " + profile);
1163 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001164 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001165 }
1166
Vasu Norie5fb9112017-11-17 10:23:02 -08001167 // reset the mConnectionInFlight
1168 setProfileOnDeviceToUnavailable(devInfo);
1169 return false;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001170 }
1171
1172 /**
1173 * Update the device connection status for a profile and also notify the state machine.
1174 * This gets called from {@link BluetoothBroadcastReceiver} when it receives a Profile's
1175 * CONNECTION_STATE_CHANGED intent.
1176 *
1177 * @param params - {@link ConnectionParams} device and profile list info
1178 * @param currentState - connection result to update
1179 */
1180 private void notifyConnectionStatus(ConnectionParams params, int currentState) {
1181 // Update the profile's BluetoothDevicesInfo.
1182 boolean isConnected;
1183 switch (currentState) {
1184 case BluetoothProfile.STATE_DISCONNECTED: {
1185 isConnected = false;
1186 break;
1187 }
1188
1189 case BluetoothProfile.STATE_CONNECTED: {
1190 isConnected = true;
1191 break;
1192 }
1193
1194 default: {
1195 if (DBG) {
1196 Log.d(TAG, "notifyConnectionStatus() Ignoring state: " + currentState);
1197 }
1198 return;
1199 }
1200
1201 }
1202
1203 boolean updateSuccessful = updateDeviceConnectionStatus(params, isConnected);
1204 if (updateSuccessful) {
1205 if (isConnected) {
1206 mBluetoothAutoConnectStateMachine.sendMessage(
1207 BluetoothAutoConnectStateMachine.DEVICE_CONNECTED,
1208 params);
1209 } else {
1210 mBluetoothAutoConnectStateMachine.sendMessage(
1211 BluetoothAutoConnectStateMachine.DEVICE_DISCONNECTED,
1212 params);
1213 }
1214 }
1215 }
1216
1217 /**
1218 * Update the profile's {@link BluetoothDevicesInfo} with the result of the connection
1219 * attempt. This gets called from the {@link BluetoothAutoConnectStateMachine} when the
1220 * connection attempt times out or from {@link BluetoothBroadcastReceiver} when it receives
1221 * a Profile's CONNECTION_STATE_CHANGED intent.
1222 *
1223 * @param params - {@link ConnectionParams} device and profile list info
1224 * @param didConnect - connection result to update
1225 */
1226 public synchronized boolean updateDeviceConnectionStatus(ConnectionParams params,
1227 boolean didConnect) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001228 if (params == null || params.getBluetoothDevice() == null) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001229 Log.e(TAG, "updateDeviceConnectionStatus: null params");
1230 return false;
1231 }
1232 // Get the profile to update
1233 Integer profileToUpdate = params.getBluetoothProfile();
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001234 BluetoothDevice deviceThatConnected = params.getBluetoothDevice();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001235 if (DBG) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001236 Log.d(TAG, "Profile: " + profileToUpdate + " Connected: " + didConnect + " on "
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001237 + deviceThatConnected);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001238 }
1239
Vasu Norie5fb9112017-11-17 10:23:02 -08001240 // If the device just connected to HEADSET_CLIENT profile, initiate
1241 // connections on PBAP & MAP profiles but let that begin after a timeout period.
1242 // If this is the first time after pairing, timeout is short. This allows A2DP profile
1243 // to complete its connection, so that there is no race condition between
1244 // Phone trying to connect on A2DP
1245 // and, Car trying to connect on PBAP & MAP.
1246 // If this is *not* the first time after pairing, then let timeout be a bit longer than
1247 // above. The longer wait allows the normal statemachine flow work, before jumping in with
1248 // this sidebar.
1249 // Normal flow of state machine handles tagging (Primary/Secondary devices) better. So
1250 // letting statemachine do its job is more desirable.
1251 if (didConnect && profileToUpdate == BluetoothProfile.HEADSET_CLIENT) {
Ram Periathiruvadie011a402018-03-22 18:54:33 -07001252 // Unlock the profiles PBAP, MAP in BluetoothDevicesInfo, so that they can be
1253 // connected on.
1254 for (Integer profile : Arrays.asList(BluetoothProfile.PBAP_CLIENT,
1255 BluetoothProfile.MAP_CLIENT)) {
1256 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1257 if (devInfo == null) {
1258 Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
1259 return false;
1260 }
1261 devInfo.setDeviceAvailableToConnectLocked(true);
1262 }
1263 boolean newlyPaired = mPairedButUnconnectedDevices.remove(params.getBluetoothDevice());
1264 if (newlyPaired) {
1265 if (DBG) {
1266 Log.d(TAG, "connect to PBAP/MAP after pairing: ");
1267 }
1268 mBluetoothAutoConnectStateMachine.sendMessageDelayed(
1269 BluetoothAutoConnectStateMachine.CHECK_CLIENT_PROFILES_AFTER_PAIRING,
1270 params,
1271 BluetoothAutoConnectStateMachine.CONNECT_MORE_PROFILES_SHORT_TIMEOUT_MS);
1272 } else {
1273 if (DBG) {
1274 Log.d(TAG, "connect to PBAP/MAP after disconnect: ");
1275 }
1276 mBluetoothAutoConnectStateMachine.sendMessageDelayed(
1277 BluetoothAutoConnectStateMachine.CHECK_CLIENT_PROFILES, params,
1278 BluetoothAutoConnectStateMachine.CONNECT_MORE_PROFILES_TIMEOUT_MS);
1279 }
Vasu Norie5fb9112017-11-17 10:23:02 -08001280
1281 }
1282
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001283 // If the connection update is on a different profile or device (a very rare possibility),
1284 // it is handled automatically. Just logging it here.
1285 if (DBG) {
1286 if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothProfile() != null) {
1287 if (profileToUpdate.equals(mConnectionInFlight.getBluetoothProfile()) == false) {
1288 Log.d(TAG, "Updating profile " + profileToUpdate
1289 + " different from connection in flight "
1290 + mConnectionInFlight.getBluetoothProfile());
1291 }
1292 }
1293
1294 if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothDevice() != null) {
1295 if (deviceThatConnected.equals(mConnectionInFlight.getBluetoothDevice()) == false) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001296 Log.d(TAG, "Updating device: " + deviceThatConnected
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001297 + " different from connection in flight: "
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001298 + mConnectionInFlight.getBluetoothDevice());
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001299
1300 }
1301 }
1302 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001303 BluetoothDevicesInfo devInfo = null;
1304 devInfo = mProfileToConnectableDevicesMap.get(profileToUpdate);
1305 if (devInfo == null) {
1306 Log.e(TAG, "Unexpected: devInfo null for profile: " + profileToUpdate);
1307 return false;
1308 }
1309
1310 boolean retry = canRetryConnection(profileToUpdate);
1311 // Update the status and also if a retry attempt can be made if the
1312 // connection timed out in the previous attempt.
1313 if (DBG) {
1314 Log.d(TAG, "Retry? : " + retry);
1315 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001316 devInfo.updateConnectionStatusLocked(deviceThatConnected, didConnect, retry);
1317 // Write to persistent memory to have the latest snapshot available
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001318 writeDeviceInfoToSettings(params);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001319 return true;
1320 }
1321
1322 /**
1323 * Returns if we can retry connection attempt on the given profile for the device that is
1324 * currently in the head of the queue.
1325 *
1326 * @param profile - Profile to check
1327 */
1328 private synchronized boolean canRetryConnection(Integer profile) {
1329 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1330 if (devInfo == null) {
1331 Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
1332 return false;
1333 }
1334 if (devInfo.getRetryCountLocked() < MAX_CONNECT_RETRIES) {
1335 return true;
1336 } else {
1337 return false;
1338 }
1339 }
1340
1341 /**
1342 * Helper method to see if there are any connect-able devices on any of the
1343 * profiles.
1344 *
1345 * @return true - if {@link #mProfileToConnectableDevicesMap} does not have any devices for any
1346 * profiles.
1347 * false - if {@link #mProfileToConnectableDevicesMap} has a device for at least one profile.
1348 */
1349 private synchronized boolean isDeviceMapEmpty() {
1350 boolean empty = true;
1351 for (Integer profile : mProfilesToConnect) {
1352 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1353 if (devInfo != null) {
1354 if (devInfo.getNumberOfPairedDevicesLocked() != 0) {
1355 if (DBG) {
1356 Log.d(TAG, "Device map not empty. Profile: " + profile + " has "
1357 + devInfo.getNumberOfPairedDevicesLocked() + " paired devices");
1358 }
1359 empty = false;
1360 break;
1361 }
1362 }
1363 }
1364 return empty;
1365 }
1366
1367 /**
1368 * Reset the Device Available to Connect information for all profiles to Available.
1369 * If in a previous connection attempt, we failed to connect on all devices for a profile,
1370 * we would update deviceAvailableToConnect for that profile to false. That information
1371 * is used to deduce if we should move to the next profile. If marked false, we will not
1372 * try to connect on that profile anymore as part of that connection attempt.
1373 * However, when we get another connection trigger from the vehicle, we need to reset the
1374 * deviceAvailableToConnect information so we can start the connection attempts all over
1375 * again.
1376 */
1377 private synchronized void resetDeviceAvailableToConnect() {
1378 for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
1379 devInfo.setDeviceAvailableToConnectLocked(true);
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001380 devInfo.resetDeviceIndex();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001381 }
1382 }
1383
1384 /**
1385 * Utility function - Prints the Profile: list of devices information to log
1386 * Caller should wrap a DBG around this, since this is for debugging purpose.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001387 *
1388 * @param writer - PrintWriter
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001389 */
1390 private synchronized void printDeviceMap(PrintWriter writer) {
1391 if (mProfileToConnectableDevicesMap == null) {
1392 return;
1393 }
Vasu Norie5fb9112017-11-17 10:23:02 -08001394 for (Integer profile : mProfileToConnectableDevicesMap.keySet()) {
1395 writer.print("Profile: " + Utils.getProfileName(profile) + "\t");
1396 writer.print("\n" + mProfileToConnectableDevicesMap.get(profile).toDebugString());
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001397 writer.println();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001398 }
1399 }
1400
1401 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001402 * Write the device list for all bluetooth profiles that connected.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001403 *
1404 * @return true if the write was successful, false otherwise
1405 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001406 private synchronized boolean writeDeviceInfoToSettings() {
1407 ConnectionParams params;
1408 boolean writeResult;
1409 for (Integer profile : mProfilesToConnect) {
1410 params = new ConnectionParams(profile);
1411 writeResult = writeDeviceInfoToSettings(params);
1412 if (!writeResult) {
1413 Log.e(TAG, "Error writing Device Info for profile:" + profile);
1414 return writeResult;
1415 }
1416 }
1417 return true;
1418 }
1419
1420 /**
1421 * Write information about which devices connected on which profile to Settings.Secure.
1422 * Essentially the list of devices that a profile can connect on the next auto-connect
1423 * attempt.
1424 *
1425 * @param params - ConnectionParams indicating which bluetooth profile to write this
1426 * information
1427 * for.
1428 * @return true if the write was successful, false otherwise
1429 */
1430 public synchronized boolean writeDeviceInfoToSettings(ConnectionParams params) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001431 if (!mAllowReadWriteToSettings) {
1432 return false;
1433 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001434 boolean writeSuccess = true;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001435 Integer profileToUpdate = params.getBluetoothProfile();
1436
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001437 if (mProfileToConnectableDevicesMap == null) {
1438 writeSuccess = false;
1439 } else {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001440 List<String> deviceNames = new ArrayList<>();
1441 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profileToUpdate);
1442 StringBuilder sb = new StringBuilder();
1443 String delimiter = ""; // start off with no delimiter.
1444
1445 // Iterate through the List<BluetoothDevice> and build a String that is
1446 // names of all devices to be connected for this profile joined together and
1447 // delimited by a delimiter (its a ',' here)
1448 if (devicesInfo != null && devicesInfo.getDeviceList() != null) {
1449 for (BluetoothDevice device : devicesInfo.getDeviceList()) {
1450 sb.append(delimiter);
1451 sb.append(device.getAddress());
1452 delimiter = SETTINGS_DELIMITER;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001453 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001454
1455 }
1456 // joinedDeviceNames has something like "22:22:33:44:55:AB,22:23:xx:xx:xx:xx"
1457 // mac addresses of connectable devices separated by a delimiter
1458 String joinedDeviceNames = sb.toString();
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001459 if (DBG) {
1460 Log.d(TAG, "Profile: " + profileToUpdate + " Writing: " + joinedDeviceNames);
1461 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001462 long userId = ActivityManager.getCurrentUser();
1463 switch (profileToUpdate) {
1464 case BluetoothProfile.A2DP_SINK:
1465 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1466 KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES,
1467 joinedDeviceNames, (int) userId);
1468 break;
1469
1470 case BluetoothProfile.HEADSET_CLIENT:
1471 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1472 KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES,
1473 joinedDeviceNames, (int) userId);
1474 break;
1475
1476 case BluetoothProfile.PBAP_CLIENT:
1477 // use the phone
1478 break;
1479
1480 case BluetoothProfile.MAP_CLIENT:
1481 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1482 KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES,
1483 joinedDeviceNames, (int) userId);
1484 break;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -08001485 case BluetoothProfile.PAN:
1486 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1487 KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES,
1488 joinedDeviceNames, (int) userId);
1489 break;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001490 }
1491 }
1492 return writeSuccess;
1493 }
1494
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001495 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001496 * Read the device information from Settings.Secure and populate the
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001497 * {@link #mProfileToConnectableDevicesMap}
1498 *
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001499 * Device MAC addresses are written to Settings.Secure delimited by a ','.
1500 * Ex: android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICES: xx:xx:xx:xx:xx:xx,yy:yy:yy:yy:yy
1501 * denotes that two devices with addresses xx:xx:xx:xx:xx:xx & yy:yy:yy:yy:yy:yy were connected
1502 * as phones (in HFP and PBAP profiles) the last time this user was logged in.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001503 *
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001504 * @return - true if the read was successful, false if 1. BT Adapter not enabled 2. No prior
1505 * bonded devices 3. No information stored in Settings for this user.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001506 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001507 public synchronized boolean readAndRebuildDeviceMapFromSettings() {
1508 List<String> deviceList;
1509 String devices = null;
1510 // Create and initialize mProfileToConnectableDevicesMap if needed.
1511 initDeviceMap();
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -08001512 if (mBluetoothAdapter != null) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001513 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001514 Log.d(TAG,
1515 "Number of Bonded devices:" + mBluetoothAdapter.getBondedDevices().size());
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001516 }
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -08001517 if (mBluetoothAdapter.getBondedDevices().isEmpty()) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001518 if (DBG) {
1519 Log.d(TAG, "No Bonded Devices available. Quit rebuilding");
1520 }
1521 return false;
1522 }
1523 }
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07001524 if (!mAllowReadWriteToSettings) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001525 return false;
1526 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001527 // Read from Settings.Secure for the current user. There are 3 keys 1 each for Phone
1528 // (HFP & PBAP), 1 for Music (A2DP) and 1 for Messaging device (MAP)
1529 long userId = ActivityManager.getCurrentUser();
1530 for (Integer profile : mProfilesToConnect) {
1531 switch (profile) {
1532 case BluetoothProfile.A2DP_SINK:
1533 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
1534 KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES, (int) userId);
1535 break;
1536 case BluetoothProfile.PBAP_CLIENT:
1537 // fall through
1538 case BluetoothProfile.HEADSET_CLIENT:
1539 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
1540 KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES, (int) userId);
1541 break;
1542 case BluetoothProfile.MAP_CLIENT:
1543 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
1544 KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES, (int) userId);
1545 break;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -08001546 case BluetoothProfile.PAN:
1547 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
1548 KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES, (int) userId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001549 default:
1550 Log.e(TAG, "Unexpected profile");
1551 break;
1552 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001553
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001554 if (devices == null) {
1555 if (DBG) {
1556 Log.d(TAG, "No device information stored in Settings");
1557 }
1558 return false;
1559 }
1560 if (DBG) {
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -07001561 Log.d(TAG, "Devices in Settings: " + devices);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001562 }
1563 // Get a list of Device Mac Addresses from the value
1564 deviceList = Arrays.asList(devices.split(SETTINGS_DELIMITER));
1565 if (deviceList == null) {
1566 return false;
1567 }
1568 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001569 // Do we have a bonded device with this name? If so, get it and populate the device
1570 // map.
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001571 for (String address : deviceList) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001572 BluetoothDevice deviceToAdd = getBondedDeviceWithGivenName(address);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001573 if (deviceToAdd != null) {
1574 devicesInfo.addDeviceLocked(deviceToAdd);
1575 } else {
1576 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001577 Log.d(TAG, "No device with name " + address + " found in bonded devices");
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001578 }
1579 }
1580 }
1581 mProfileToConnectableDevicesMap.put(profile, devicesInfo);
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07001582 // Check to see if there are any primary or secondary devices for this profile and
1583 // update BluetoothDevicesInfo with the priority information.
1584 for (int priority : mPrioritiesSupported) {
1585 readAndTagDeviceWithPriorityFromSettings(profile, priority);
1586 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001587 }
Pavel Maltsevc9e86e82017-03-22 11:37:21 -07001588 return true;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001589 }
1590
1591 /**
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07001592 * Read from Secure Settings if there are primary or secondary devices marked for this
1593 * Bluetooth profile. If there are tagged devices, update the BluetoothDevicesInfo so the
1594 * policy can prioritize those devices when making connection attempts.
1595 *
Ram Periathiruvadie011a402018-03-22 18:54:33 -07001596 * @param profile - Bluetooth Profile to check
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07001597 * @param priority - Priority to check
1598 */
1599 private void readAndTagDeviceWithPriorityFromSettings(int profile, int priority) {
1600 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
1601 if (devicesInfo == null) {
1602 return;
1603 }
1604 if (!mCarBluetoothService.isPriorityDevicePresent(profile, priority)) {
1605 // There is no device for this priority - either it hasn't been set or has been removed.
1606 // So check if the policy has a device associated with this priority and remove it.
1607 BluetoothDevice deviceToClear = devicesInfo.getBluetoothDeviceForPriorityLocked(
1608 priority);
1609 if (deviceToClear != null) {
1610 if (DBG) {
Vasu Norie5fb9112017-11-17 10:23:02 -08001611 Log.d(TAG, "Clearing priority for: " +
1612 Utils.getDeviceDebugInfo(deviceToClear));
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07001613 }
1614 devicesInfo.removeBluetoothDevicePriorityLocked(deviceToClear);
1615 }
1616 } else {
1617 // There is a device with the given priority for the given profile. Update the
1618 // policy's records.
1619 String deviceName = mCarBluetoothService.getDeviceNameWithPriority(profile,
1620 priority);
1621 if (deviceName != null) {
1622 BluetoothDevice bluetoothDevice = getBondedDeviceWithGivenName(deviceName);
1623 if (bluetoothDevice != null) {
1624 if (DBG) {
1625 Log.d(TAG, "Setting priority: " + priority + " for " + deviceName);
1626 }
1627 tagDeviceWithPriority(bluetoothDevice, profile, priority);
1628 }
1629 }
1630 }
1631 }
1632
1633 /**
1634 * Tag a Bluetooth device with priority - Primary or Secondary. This only updates the policy's
1635 * record (BluetoothDevicesInfo) of the priority information.
1636 *
1637 * @param device - BluetoothDevice to tag
1638 * @param profile - BluetoothProfile to tag
1639 * @param priority - Priority to tag with
1640 */
1641 @VisibleForTesting
1642 void tagDeviceWithPriority(BluetoothDevice device, int profile, int priority) {
1643 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
1644 if (device != null) {
1645 if (DBG) {
1646 Log.d(TAG, "Profile: " + profile + " : " + device + " Priority: " + priority);
1647 }
1648 devicesInfo.setBluetoothDevicePriorityLocked(device, priority);
1649 }
1650 }
1651
1652 /**
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001653 * Given the device name, find the corresponding {@link BluetoothDevice} from the list of
1654 * Bonded devices.
1655 *
1656 * @param name Bluetooth Device name
1657 */
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07001658 @Nullable
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001659 private BluetoothDevice getBondedDeviceWithGivenName(String name) {
1660 if (mBluetoothAdapter == null) {
1661 if (DBG) {
1662 Log.d(TAG, "Bluetooth Adapter Null");
1663 }
1664 return null;
1665 }
1666 if (name == null) {
1667 Log.w(TAG, "getBondedDeviceWithGivenName() Passing in a null name");
1668 return null;
1669 }
1670 if (DBG) {
1671 Log.d(TAG, "Looking for bonded device: " + name);
1672 }
1673 BluetoothDevice btDevice = null;
1674 Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
1675 for (BluetoothDevice bd : bondedDevices) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001676 if (name.equals(bd.getAddress())) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001677 btDevice = bd;
1678 break;
1679 }
1680 }
1681 return btDevice;
1682 }
1683
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001684
1685 public void dump(PrintWriter writer) {
1686 writer.println("*BluetoothDeviceConnectionPolicy*");
1687 printDeviceMap(writer);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001688 mBluetoothAutoConnectStateMachine.dump(writer);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001689 }
1690}