blob: 6b2169aae99104856f5bcb8bcac0a0a81dbb1382 [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;
Justin Paupore44985ba2019-01-30 18:53:41 -080023import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_PROFILES_INHIBITED;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070024
Ram Periathiruvadi76a84892017-07-27 18:10:35 -070025import android.annotation.Nullable;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070026import android.app.ActivityManager;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080027import android.bluetooth.BluetoothA2dpSink;
28import android.bluetooth.BluetoothAdapter;
29import android.bluetooth.BluetoothDevice;
30import android.bluetooth.BluetoothHeadsetClient;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080031import android.bluetooth.BluetoothMapClient;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -080032import android.bluetooth.BluetoothPan;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080033import android.bluetooth.BluetoothPbapClient;
34import android.bluetooth.BluetoothProfile;
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -070035import android.bluetooth.BluetoothUuid;
Sal Savageed6b04e2019-03-15 13:27:27 -070036import android.car.Car;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070037import android.car.CarBluetoothManager;
Sal Savageed6b04e2019-03-15 13:27:27 -070038import android.car.CarNotConnectedException;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070039import android.car.ICarBluetoothUserService;
40import android.car.ICarUserService;
41import android.car.drivingstate.CarUxRestrictions;
42import android.car.drivingstate.ICarUxRestrictionsChangeListener;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080043import android.car.hardware.CarPropertyValue;
Sal Savageed6b04e2019-03-15 13:27:27 -070044import android.car.hardware.power.CarPowerManager;
45import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080046import android.car.hardware.property.CarPropertyEvent;
47import android.car.hardware.property.ICarPropertyEventListener;
48import android.content.BroadcastReceiver;
Sal Savageed6b04e2019-03-15 13:27:27 -070049import android.content.ComponentName;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080050import android.content.Context;
51import android.content.Intent;
52import android.content.IntentFilter;
Sal Savageed6b04e2019-03-15 13:27:27 -070053import android.content.ServiceConnection;
Steve Paik9ec53d72018-04-27 13:28:31 -070054import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState;
55import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
Justin Paupore7b17ea62018-12-28 20:27:33 -080056import android.os.Binder;
57import android.os.Handler;
58import android.os.IBinder;
59import android.os.Looper;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070060import android.os.ParcelUuid;
61import android.os.Parcelable;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080062import android.os.RemoteException;
Ram Periathiruvadie011a402018-03-22 18:54:33 -070063import android.os.UserHandle;
64import android.provider.Settings;
Justin Paupore7b17ea62018-12-28 20:27:33 -080065import android.text.TextUtils;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080066import android.util.Log;
67
Justin Paupore44985ba2019-01-30 18:53:41 -080068import com.android.internal.annotations.GuardedBy;
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -070069import com.android.internal.annotations.VisibleForTesting;
70
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080071import java.io.PrintWriter;
72import java.util.ArrayList;
73import java.util.Arrays;
74import java.util.HashMap;
Vasu Norie5fb9112017-11-17 10:23:02 -080075import java.util.HashSet;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080076import java.util.List;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080077import java.util.Map;
Justin Pauporec813ead2018-12-28 20:18:25 -080078import java.util.Objects;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080079import java.util.Set;
Sal Savageed6b04e2019-03-15 13:27:27 -070080import java.util.concurrent.CompletableFuture;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070081import java.util.concurrent.locks.ReentrantLock;
Justin Paupore7b17ea62018-12-28 20:27:33 -080082import java.util.stream.Collectors;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070083
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080084/**
85 * 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 -070086 * bluetooth capabilities in terms of the profiles it supports and its use cases are unique.
87 * Hence the CarService manages the policy that drives when and what to connect to.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080088 *
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080089 * When to connect:
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070090 * The policy can be configured to listen to various vehicle events that are appropriate to
91 * trigger a connection attempt. Signals like door unlock/open, ignition state changes indicate
92 * user entry and there by attempt to connect to their devices. This removes the need for the user
93 * to manually connect his device everytime they get in a car.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -080094 *
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080095 * Which device to connect:
Ram Periathiruvadiacb60242017-04-13 16:19:09 -070096 * The policy also keeps track of the {Profile : DevicesThatCanConnectOnTheProfile} and when
97 * it is time to connect, picks the device that is appropriate and available.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -080098 * For every profile, the policy attempts to connect to the last connected device first. The policy
99 * maintains a list of connect-able devices for every profile, in the order of how recently they
100 * connected. The device that successfully connects on a profile is moved to the top of the list
101 * of devices for that profile, so the next time a connection attempt is made, the policy starts
102 * with the last connected device first.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800103 */
104
105public class BluetoothDeviceConnectionPolicy {
106 private static final String TAG = "BTDevConnectionPolicy";
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700107 private static final String SETTINGS_DELIMITER = ",";
Vasu Norie5fb9112017-11-17 10:23:02 -0800108 private static final boolean DBG = Utils.DBG;
Justin Paupore7b17ea62018-12-28 20:27:33 -0800109
Justin Paupore44985ba2019-01-30 18:53:41 -0800110 private static final Binder RESTORED_PROFILE_INHIBIT_TOKEN = new Binder();
Justin Paupore7b17ea62018-12-28 20:27:33 -0800111 private static final long RESTORE_BACKOFF_MILLIS = 1000L;
112
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700113 private final Context mContext;
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700114 private boolean mInitialized = false;
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700115 private boolean mUserSpecificInfoInitialized = false;
116 private final Object mSetupLock = new Object();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700117
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700118 // The main data structure that holds on to the {profile:list of known and connectible devices}
Joseph Pirozzoffb78982018-10-31 08:28:46 -0700119 HashMap<Integer, BluetoothDevicesInfo> mProfileToConnectableDevicesMap;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700120
Sal Savageed6b04e2019-03-15 13:27:27 -0700121 // Keep a map of the maximum number of connections allowed for any profile we plan to support.
122 private static final Map<Integer, Integer> sNumSupportedActiveConnections = new HashMap<>();
123 static {
124 sNumSupportedActiveConnections.put(BluetoothProfile.HEADSET_CLIENT, 4);
125 sNumSupportedActiveConnections.put(BluetoothProfile.PBAP_CLIENT, 4);
126 sNumSupportedActiveConnections.put(BluetoothProfile.A2DP_SINK, 1);
127 sNumSupportedActiveConnections.put(BluetoothProfile.MAP_CLIENT, 4);
128 sNumSupportedActiveConnections.put(BluetoothProfile.PAN, 1);
129 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700130
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700131 private BluetoothAutoConnectStateMachine mBluetoothAutoConnectStateMachine;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800132 private final BluetoothAdapter mBluetoothAdapter;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700133 private BroadcastReceiver mBluetoothBroadcastReceiver;
134
135 private ICarUserService mCarUserService;
136 private PerUserCarServiceHelper mUserServiceHelper;
137 private ICarBluetoothUserService mCarBluetoothUserService;
138 private ReentrantLock mCarUserServiceAccessLock;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800139
140 // Events that are listened to for triggering an auto-connect:
Steve Paik9ec53d72018-04-27 13:28:31 -0700141 // Door unlock and ignition switch ON come from Car Property Service
142 private final CarPropertyService mCarPropertyService;
143 private final CarPropertyListener mPropertyEventListener;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800144
Sal Savageed6b04e2019-03-15 13:27:27 -0700145 // Car service binder to setup listening for power manager updates
146 private final Car mCar;
147 private CarPowerManager mCarPowerManager;
148 private final CarPowerStateListener mCarPowerStateListener = new CarPowerStateListener() {
149 @Override
150 public void onStateChanged(int state, CompletableFuture<Void> future) {
151 if (DBG) Log.d(TAG, "Car power state has changed to " + state);
152
153 // ON is the state when user turned on the car (it can be either ignition or
154 // door unlock) the policy for ON is defined by OEMs and we can rely on that.
155 if (state == CarPowerManager.CarPowerStateListener.ON) {
156 Log.i(TAG, "Car is powering on. Enable Bluetooth and auto-connect to devices.");
157 if (isBluetoothPersistedOn()) {
158 enabledBluetooth();
159 }
160 initiateConnection();
161 return;
162 }
163
164 // Since we're appearing to be off after shutdown prepare, but may stay on in idle mode,
165 // we'll turn off Bluetooth to disconnect devices and better the "off" illusion
166 if (state == CarPowerManager.CarPowerStateListener.SHUTDOWN_PREPARE) {
167 Log.i(TAG, "Car is preparing for shutdown. Disable bluetooth adapter.");
168 disableBluetooth();
169
170 // Let CPMS know we're ready to shutdown. Otherwise, CPMS will get stuck for
171 // up to an hour.
172 if (future != null) {
173 future.complete(null);
174 }
175 return;
176 }
177 }
178 };
179
180
181 private final ServiceConnection mCarServiceConnection = new ServiceConnection() {
182 @Override
183 public void onServiceConnected(ComponentName name, IBinder service) {
184 Log.i(TAG, "Car is now connected, getting CarPowerManager service");
185 try {
186 mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE);
187 mCarPowerManager.setListener(mCarPowerStateListener);
188 } catch (CarNotConnectedException e) {
189 Log.e(TAG, "Failed to get CarPowerManager instance", e);
190 }
191 }
192
193 @Override
194 public void onServiceDisconnected(ComponentName name) {
195 Log.i(TAG, "Car is now disconnected");
196 if (mCarPowerManager != null) {
197 mCarPowerManager.clearListener();
198 }
199 }
200 };
201
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700202 // PerUserCarService related listeners
203 private final UserServiceConnectionCallback mServiceCallback;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800204
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700205 // Car Bluetooth Priority Settings Manager
206 private final CarBluetoothService mCarBluetoothService;
207
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700208 // Car UX Restrictions Manager Service to know when user restrictions are in place
209 private final CarUxRestrictionsManagerService mUxRService;
210 private final CarUxRServiceListener mUxRListener;
Joseph Pirozzoe5ac56c2018-01-10 15:05:57 -0800211 // Fast Pair Provider to allow discovery of new phones
212 private final FastPairProvider mFastPairProvider;
213
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700214 // The Bluetooth profiles that the CarService will try to auto-connect on.
Joseph Pirozzoffb78982018-10-31 08:28:46 -0700215 final List<Integer> mProfilesToConnect;
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700216 private final List<Integer> mPrioritiesSupported;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800217 private static final int MAX_CONNECT_RETRIES = 1;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800218 private static final int PROFILE_NOT_AVAILABLE = -1;
Joseph Pirozzoffb78982018-10-31 08:28:46 -0700219 private int mUserId;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800220
221 // Device & Profile currently being connected on
222 private ConnectionParams mConnectionInFlight;
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700223 // Allow write to Settings.Secure
224 private boolean mAllowReadWriteToSettings = true;
Vasu Norie5fb9112017-11-17 10:23:02 -0800225 // Maintain a list of Paired devices which haven't connected on any profiles yet.
226 private Set<BluetoothDevice> mPairedButUnconnectedDevices = new HashSet<>();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800227
Justin Paupore44985ba2019-01-30 18:53:41 -0800228 // State for profile inhibits.
229 @GuardedBy("this")
230 private final SetMultimap<ConnectionParams, InhibitRecord> mProfileInhibits =
231 new SetMultimap<>();
232 @GuardedBy("this")
233 private final HashSet<InhibitRecord> mRestoredInhibits = new HashSet<>();
234 @GuardedBy("this")
Justin Paupore7b17ea62018-12-28 20:27:33 -0800235 private final HashSet<ConnectionParams> mAlreadyDisabledProfiles = new HashSet<>();
236
237 private final Handler mHandler = new Handler(Looper.getMainLooper());
238
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800239 public static BluetoothDeviceConnectionPolicy create(Context context,
Steve Paik9ec53d72018-04-27 13:28:31 -0700240 CarPropertyService carPropertyService, PerUserCarServiceHelper userServiceHelper,
241 CarUxRestrictionsManagerService uxrService, CarBluetoothService bluetoothService) {
242 return new BluetoothDeviceConnectionPolicy(context, carPropertyService, userServiceHelper,
243 uxrService, bluetoothService);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800244 }
245
Steve Paik9ec53d72018-04-27 13:28:31 -0700246 private BluetoothDeviceConnectionPolicy(Context context, CarPropertyService carPropertyService,
247 PerUserCarServiceHelper userServiceHelper, CarUxRestrictionsManagerService uxrService,
248 CarBluetoothService bluetoothService) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800249 mContext = context;
Steve Paik9ec53d72018-04-27 13:28:31 -0700250 mCarPropertyService = carPropertyService;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700251 mUserServiceHelper = userServiceHelper;
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700252 mUxRService = uxrService;
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700253 mCarBluetoothService = bluetoothService;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700254 mCarUserServiceAccessLock = new ReentrantLock();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700255 mProfilesToConnect = Arrays.asList(
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700256 BluetoothProfile.HEADSET_CLIENT, BluetoothProfile.A2DP_SINK,
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700257 BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_CLIENT, BluetoothProfile.PAN);
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700258 mPrioritiesSupported = Arrays.asList(
259 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0,
260 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1
261 );
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700262
Steve Paik9ec53d72018-04-27 13:28:31 -0700263 // Listen to events for triggering auto connect
264 mPropertyEventListener = new CarPropertyListener();
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700265 // Listen to UX Restrictions to know when to enable fast-pairing
266 mUxRListener = new CarUxRServiceListener();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700267 // Listen to User switching to connect to per User device.
268 mServiceCallback = new UserServiceConnectionCallback();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800269 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800270 if (mBluetoothAdapter == null) {
271 Log.w(TAG, "No Bluetooth Adapter Available");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800272 }
Joseph Pirozzoe5ac56c2018-01-10 15:05:57 -0800273 mFastPairProvider = new FastPairProvider(mContext);
Sal Savageed6b04e2019-03-15 13:27:27 -0700274
275 // Connect to car
276 mCar = Car.createCar(context, mCarServiceConnection);
277 mCar.connect();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800278 }
279
280 /**
281 * ConnectionParams - parameters/objects relevant to the bluetooth connection calls.
282 * This encapsulates the information that is passed around across different methods in the
283 * policy. Contains the bluetooth device {@link BluetoothDevice} and the list of profiles that
284 * we want that device to connect on.
285 * Used as the currency that methods use to talk to each other in the policy.
286 */
287 public static class ConnectionParams {
Justin Paupore7b17ea62018-12-28 20:27:33 -0800288 // Examples:
289 // 01:23:45:67:89:AB/9
290 // null/0
291 // null/null
292 private static final String FLATTENED_PATTERN =
293 "^(([0-9A-F]{2}:){5}[0-9A-F]{2}|null)/([0-9]+|null)$";
294
295 @Nullable private final BluetoothDevice mBluetoothDevice;
296 @Nullable private final Integer mBluetoothProfile;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800297
298 public ConnectionParams() {
Justin Pauporec813ead2018-12-28 20:18:25 -0800299 this(null, null);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800300 }
301
Justin Paupore7b17ea62018-12-28 20:27:33 -0800302 public ConnectionParams(@Nullable Integer profile) {
Justin Pauporec813ead2018-12-28 20:18:25 -0800303 this(profile, null);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800304 }
305
Justin Paupore7b17ea62018-12-28 20:27:33 -0800306 public ConnectionParams(@Nullable Integer profile, @Nullable BluetoothDevice device) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800307 mBluetoothProfile = profile;
308 mBluetoothDevice = device;
309 }
310
Justin Paupore7b17ea62018-12-28 20:27:33 -0800311 @Nullable
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800312 public BluetoothDevice getBluetoothDevice() {
313 return mBluetoothDevice;
314 }
315
Justin Paupore7b17ea62018-12-28 20:27:33 -0800316 @Nullable
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800317 public Integer getBluetoothProfile() {
318 return mBluetoothProfile;
319 }
Justin Pauporec813ead2018-12-28 20:18:25 -0800320
321 @Override
322 public boolean equals(Object other) {
323 if (this == other) {
324 return true;
325 }
326 if (!(other instanceof ConnectionParams)) {
327 return false;
328 }
329 ConnectionParams otherParams = (ConnectionParams) other;
330 return Objects.equals(mBluetoothDevice, otherParams.mBluetoothDevice)
331 && Objects.equals(mBluetoothProfile, otherParams.mBluetoothProfile);
332 }
333
334 @Override
335 public int hashCode() {
336 return Objects.hash(mBluetoothDevice, mBluetoothProfile);
337 }
Justin Paupore7b17ea62018-12-28 20:27:33 -0800338
339 @Override
340 public String toString() {
341 return flattenToString();
342 }
343
344 /** Converts these {@link ConnectionParams} to a parseable string representation. */
345 public String flattenToString() {
346 return mBluetoothDevice + "/" + mBluetoothProfile;
347 }
348
349 /**
350 * Creates a {@link ConnectionParams} from a previous output of {@link #flattenToString()}.
351 *
352 * @param flattenedParams A flattened string representation of a {@link ConnectionParams}.
353 * @param adapter A {@link BluetoothAdapter} used to convert Bluetooth addresses into
354 * {@link BluetoothDevice} objects.
355 */
356 public static ConnectionParams parse(String flattenedParams, BluetoothAdapter adapter) {
357 if (!flattenedParams.matches(FLATTENED_PATTERN)) {
358 throw new IllegalArgumentException("Bad format for flattened ConnectionParams");
359 }
360 String[] parts = flattenedParams.split("/");
361
362 BluetoothDevice device;
363 if (!"null".equals(parts[0])) {
364 device = adapter.getRemoteDevice(parts[0]);
365 } else {
366 device = null;
367 }
368
369 Integer profile;
370 if (!"null".equals(parts[1])) {
371 profile = Integer.valueOf(parts[1]);
372 } else {
373 profile = null;
374 }
375
376 return new ConnectionParams(profile, device);
377 }
378 }
379
Justin Paupore44985ba2019-01-30 18:53:41 -0800380 private class InhibitRecord implements IBinder.DeathRecipient {
Justin Paupore7b17ea62018-12-28 20:27:33 -0800381 private final ConnectionParams mParams;
382 private final IBinder mToken;
383
384 private boolean mRemoved = false;
385
Justin Paupore44985ba2019-01-30 18:53:41 -0800386 InhibitRecord(ConnectionParams params, IBinder token) {
Justin Paupore7b17ea62018-12-28 20:27:33 -0800387 this.mParams = params;
388 this.mToken = token;
389 }
390
391 public ConnectionParams getParams() {
392 return mParams;
393 }
394
395 public IBinder getToken() {
396 return mToken;
397 }
398
399 public boolean removeSelf() {
400 synchronized (BluetoothDeviceConnectionPolicy.this) {
401 if (mRemoved) {
402 return true;
403 }
404
Justin Paupore44985ba2019-01-30 18:53:41 -0800405 if (removeInhibitRecord(this)) {
Justin Paupore7b17ea62018-12-28 20:27:33 -0800406 mRemoved = true;
407 return true;
408 } else {
409 return false;
410 }
411 }
412 }
413
414 @Override
415 public void binderDied() {
416 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -0800417 Log.d(TAG, "Releasing inhibit request on profile "
Justin Paupore7b17ea62018-12-28 20:27:33 -0800418 + Utils.getProfileName(mParams.getBluetoothProfile())
419 + " for device " + mParams.getBluetoothDevice()
420 + ": requesting process died");
421 }
422 removeSelf();
423 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800424 }
425
426 /**
427 * BluetoothBroadcastReceiver receives the bluetooth related intents that are relevant to
428 * connection
429 * and bonding state changes. Reports the information to the {@link
430 * BluetoothDeviceConnectionPolicy}
431 * for it update its status.
432 */
433 public class BluetoothBroadcastReceiver extends BroadcastReceiver {
434 @Override
435 public void onReceive(Context context, Intent intent) {
436 String action = intent.getAction();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800437 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700438 if (DBG) {
439 if (device != null) {
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700440 Log.d(TAG, "Received Intent for device: " + device + " " + action);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700441 } else {
442 Log.d(TAG, "Received Intent no device: " + action);
443 }
444 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800445 ConnectionParams connectParams;
446 if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
447 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
448 BluetoothDevice.ERROR);
449 updateBondState(device, bondState);
450
451 } else if (BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700452 connectParams = new ConnectionParams(BluetoothProfile.A2DP_SINK, device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800453 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
454 BluetoothProfile.STATE_DISCONNECTED);
455 notifyConnectionStatus(connectParams, currState);
456
457 } else if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700458 connectParams = new ConnectionParams(BluetoothProfile.HEADSET_CLIENT, device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800459 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
460 BluetoothProfile.STATE_DISCONNECTED);
461 notifyConnectionStatus(connectParams, currState);
462
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800463 } else if (BluetoothPan.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
464 connectParams = new ConnectionParams(BluetoothProfile.PAN, device);
465 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
466 BluetoothProfile.STATE_DISCONNECTED);
467 notifyConnectionStatus(connectParams, currState);
468
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800469 } else if (BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700470 connectParams = new ConnectionParams(BluetoothProfile.PBAP_CLIENT, device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800471 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
472 BluetoothProfile.STATE_DISCONNECTED);
473 notifyConnectionStatus(connectParams, currState);
474
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800475 } else if (BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700476 connectParams = new ConnectionParams(BluetoothProfile.MAP_CLIENT, device);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800477 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
478 BluetoothProfile.STATE_DISCONNECTED);
479 notifyConnectionStatus(connectParams, currState);
480
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800481 } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700482 int currState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800483 if (DBG) {
484 Log.d(TAG, "Bluetooth Adapter State: " + currState);
485 }
486 if (currState == BluetoothAdapter.STATE_ON) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700487 // Read from Settings which devices to connect to and populate
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800488 // mProfileToConnectableDevicesMap
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700489 readAndRebuildDeviceMapFromSettings();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800490 initiateConnection();
491 } else if (currState == BluetoothAdapter.STATE_OFF) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800492 // Write currently connected device snapshot to file.
Joseph Pirozzoffb78982018-10-31 08:28:46 -0700493 mBluetoothAutoConnectStateMachine.sendMessage(
494 BluetoothAutoConnectStateMachine.ADAPTER_OFF);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700495 writeDeviceInfoToSettings();
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800496 resetBluetoothDevicesConnectionInfo();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800497 }
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700498 } else if (BluetoothDevice.ACTION_UUID.equals(action)) {
499 // Received during pairing with the UUIDs of the Bluetooth profiles supported by
500 // the remote device.
501 if (DBG) {
502 Log.d(TAG, "Received UUID intent for device " + device);
503 }
504 Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
505 if (uuids != null) {
506 ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
507 for (int i = 0; i < uuidsToSend.length; i++) {
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700508 uuidsToSend[i] = (ParcelUuid) uuids[i];
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700509 }
510 setProfilePriorities(device, uuidsToSend, BluetoothProfile.PRIORITY_ON);
511 }
512
513 }
514 }
515 }
516
517 /**
518 * Set priority for the Bluetooth profiles.
519 *
520 * The Bluetooth service stores the priority of a Bluetooth profile per device as a key value
521 * pair - BluetoothProfile_device:<Priority>.
522 * When we pair a device from the Settings App, the expected behavior is for the app to connect
523 * on all appropriate profiles after successful pairing automatically, without the user having
524 * to explicitly issue a connect. The settings app checks for the priority of the device from
525 * the above key-value pair and if the priority is set to PRIORITY_OFF or PRIORITY_UNDEFINED,
526 * the settings app will stop with just pairing and not connect.
527 * This scenario will happen when we pair a device, then unpair it and then pair it again. When
528 * the device is unpaired, the BT stack sets the priority for that device to PRIORITY_UNDEFINED
529 * ( as a way of resetting). So, the next time the same device is paired, the Settings app will
530 * stop with just pairing and not connect as explained above. Here, we register to receive the
531 * ACTION_UUID intent, which will broadcast the UUIDs corresponding to the profiles supported by
532 * the remote device which is successfully paired and we turn on the priority so when the
533 * Settings app tries to check before connecting, the priority is set to the expected value.
534 *
535 * @param device - Remote Bluetooth device
536 * @param uuids - UUIDs of the Bluetooth Profiles supported by the remote device
537 * @param priority - priority to set
538 */
539 private void setProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids, int priority) {
540 // need the BluetoothProfile proxy to be able to call the setPriority API
541 if (mCarBluetoothUserService == null) {
542 mCarBluetoothUserService = setupBluetoothUserService();
543 }
544 if (mCarBluetoothUserService != null) {
545 for (Integer profile : mProfilesToConnect) {
Justin Paupore7b17ea62018-12-28 20:27:33 -0800546 synchronized (this) {
547 ConnectionParams params = new ConnectionParams(profile, device);
Justin Paupore44985ba2019-01-30 18:53:41 -0800548 // If this profile is inhibited, don't try to change its priority until the
549 // inhibit is released. Instead, if the profile is being enabled, take it off of
550 // the "previously disabled profiles" list, so it will be restored when all
551 // inhibits are removed.
552 if (mProfileInhibits.keySet().contains(params)) {
Justin Paupore7b17ea62018-12-28 20:27:33 -0800553 if (DBG) {
554 Log.i(TAG, "Not setting profile " + profile + " priority of "
Justin Paupore44985ba2019-01-30 18:53:41 -0800555 + device.getAddress() + " to " + priority + ": inhibited");
556 }
557 if (priority == BluetoothProfile.PRIORITY_ON) {
558 mAlreadyDisabledProfiles.remove(params);
Justin Paupore7b17ea62018-12-28 20:27:33 -0800559 }
560 continue;
561 }
562 }
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700563 setBluetoothProfilePriorityIfUuidFound(uuids, profile, device, priority);
564 }
565 }
566 }
567
568 private void setBluetoothProfilePriorityIfUuidFound(ParcelUuid[] uuids, int profile,
569 BluetoothDevice device, int priority) {
570 if (mCarBluetoothUserService == null || device == null) {
571 return;
572 }
573 // Build a list of UUIDs that represent a profile.
574 List<ParcelUuid> uuidsToCheck = new ArrayList<>();
575 switch (profile) {
576 case BluetoothProfile.A2DP_SINK:
577 uuidsToCheck.add(BluetoothUuid.AudioSource);
578 break;
579 case BluetoothProfile.HEADSET_CLIENT:
580 uuidsToCheck.add(BluetoothUuid.Handsfree_AG);
581 uuidsToCheck.add(BluetoothUuid.HSP_AG);
582 break;
583 case BluetoothProfile.PBAP_CLIENT:
584 uuidsToCheck.add(BluetoothUuid.PBAP_PSE);
585 break;
586 case BluetoothProfile.MAP_CLIENT:
587 uuidsToCheck.add(BluetoothUuid.MAS);
588 break;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800589 case BluetoothProfile.PAN:
590 uuidsToCheck.add(BluetoothUuid.PANU);
591 break;
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700592 }
593
594 for (ParcelUuid uuid : uuidsToCheck) {
595 if (BluetoothUuid.isUuidPresent(uuids, uuid)) {
596 try {
597 mCarBluetoothUserService.setProfilePriority(profile, device, priority);
598 } catch (RemoteException e) {
599 Log.e(TAG, "RemoteException calling setProfilePriority");
600 }
601 // if any one of the uuid in uuidsTocheck is present, set the priority and break
602 break;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800603 }
604 }
605 }
606
607 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700608 * Cleanup state and reinitialize whenever we connect to the PerUserCarService.
609 * This happens in init() and whenever the PerUserCarService is restarted on User Switch Events
610 */
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700611 @VisibleForTesting
612 class UserServiceConnectionCallback implements PerUserCarServiceHelper.ServiceCallback {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700613 @Override
614 public void onServiceConnected(ICarUserService carUserService) {
615 if (mCarUserServiceAccessLock != null) {
616 mCarUserServiceAccessLock.lock();
617 try {
618 mCarUserService = carUserService;
619 } finally {
620 mCarUserServiceAccessLock.unlock();
621 }
622 }
623 if (DBG) {
624 Log.d(TAG, "Connected to PerUserCarService");
625 }
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700626 // Get the BluetoothUserService and also setup the Bluetooth Connection Proxy for
627 // all profiles.
628 mCarBluetoothUserService = setupBluetoothUserService();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700629 // re-initialize for current user.
630 initializeUserSpecificInfo();
Justin Paupore44985ba2019-01-30 18:53:41 -0800631 // Restore profile inhibits, if any, that were saved from last run...
632 restoreProfileInhibitsFromSettings();
Justin Paupore7b17ea62018-12-28 20:27:33 -0800633 // ... and start trying to remove them.
Justin Paupore44985ba2019-01-30 18:53:41 -0800634 removeRestoredProfileInhibits();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700635 }
636
637 @Override
638 public void onPreUnbind() {
639 if (DBG) {
640 Log.d(TAG, "Before Unbinding from UserService");
641 }
Justin Paupore7b17ea62018-12-28 20:27:33 -0800642
Justin Paupore44985ba2019-01-30 18:53:41 -0800643 // Try to release profile inhibits now, before CarBluetoothUserService goes away.
644 // This also stops any active attempts to remove restored inhibits.
Justin Paupore7b17ea62018-12-28 20:27:33 -0800645 //
646 // If any can't be released, they'll persist in settings and will be cleaned up
647 // next time this user starts. This can happen if the Bluetooth profile proxies in
648 // CarBluetoothUserService unbind before we get the chance to make calls on them.
Justin Paupore44985ba2019-01-30 18:53:41 -0800649 releaseAllInhibitsBeforeUnbind();
Justin Paupore7b17ea62018-12-28 20:27:33 -0800650
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700651 try {
652 if (mCarBluetoothUserService != null) {
653 mCarBluetoothUserService.closeBluetoothConnectionProxy();
654 }
655 } catch (RemoteException e) {
656 Log.e(TAG,
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700657 "Remote Exception during closeBluetoothConnectionProxy(): "
658 + e.getMessage());
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700659 }
Justin Paupore7b17ea62018-12-28 20:27:33 -0800660
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700661 // Clean up information related to user who went background.
662 cleanupUserSpecificInfo();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700663 }
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700664
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700665 @Override
666 public void onServiceDisconnected() {
667 if (DBG) {
668 Log.d(TAG, "Disconnected from PerUserCarService");
669 }
670 if (mCarUserServiceAccessLock != null) {
671 mCarUserServiceAccessLock.lock();
672 try {
673 mCarBluetoothUserService = null;
674 mCarUserService = null;
675 } finally {
676 mCarUserServiceAccessLock.unlock();
677 }
678 }
679 }
680 }
681
682 /**
683 * Gets the Per User Car Bluetooth Service (ICarBluetoothService) from the PerUserCarService
684 * which acts as a top level Service running in the current user context.
685 * Also sets up the connection proxy objects required to communicate with the Bluetooth
686 * Profile Services.
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700687 *
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700688 * @return ICarBluetoothUserService running in current user
689 */
690 private ICarBluetoothUserService setupBluetoothUserService() {
691 ICarBluetoothUserService carBluetoothUserService = null;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700692 if (mCarUserService != null) {
693 try {
694 carBluetoothUserService = mCarUserService.getBluetoothUserService();
695 if (carBluetoothUserService != null) {
696 if (DBG) {
697 Log.d(TAG, "Got CarBTUsrSvc");
698 }
699 carBluetoothUserService.setupBluetoothConnectionProxy();
700 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700701 } catch (RemoteException e) {
702 Log.e(TAG, "Remote Service Exception on ServiceConnection Callback: "
703 + e.getMessage());
704 }
705 } else {
706 if (DBG) {
707 Log.d(TAG, "PerUserCarService not connected");
708 }
709 }
710 return carBluetoothUserService;
711 }
712
713 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800714 * Setup the Bluetooth profile service connections and Vehicle Event listeners.
715 * and start the state machine -{@link BluetoothAutoConnectStateMachine}
716 */
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700717 public synchronized void init() {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800718 if (DBG) {
719 Log.d(TAG, "init()");
720 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700721 // Initialize information specific to current user.
722 initializeUserSpecificInfo();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800723 // Listen to various events coming from the vehicle.
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700724 setupEventListenersLocked();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700725 mInitialized = true;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800726 }
727
728 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700729 * Setup and initialize information that is specific per User account, which involves:
730 * 1. Reading the list of devices to connect for current user and initialize the deviceMap
731 * with that information.
732 * 2. Register a BroadcastReceiver for bluetooth related events for the current user.
733 * 3. Start and bind to {@link PerUserCarService} as current user.
734 * 4. Start the {@link BluetoothAutoConnectStateMachine}
735 */
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700736 private void initializeUserSpecificInfo() {
737 synchronized (mSetupLock) {
738 if (DBG) {
739 Log.d(TAG, "initializeUserSpecificInfo()");
740 }
741 if (mUserSpecificInfoInitialized) {
742 if (DBG) {
743 Log.d(TAG, "Already Initialized");
744 }
745 return;
746 }
Joseph Pirozzoffb78982018-10-31 08:28:46 -0700747 mUserId = ActivityManager.getCurrentUser();
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700748 mBluetoothAutoConnectStateMachine = BluetoothAutoConnectStateMachine.make(this);
749 readAndRebuildDeviceMapFromSettings();
750 setupBluetoothEventsIntentFilterLocked();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700751
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700752 mConnectionInFlight = new ConnectionParams();
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700753 mUserSpecificInfoInitialized = true;
754 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700755 }
756
757 /**
758 * Setting up the Intent filter for Bluetooth related broadcasts
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800759 * This includes knowing when the
760 * 1. Bluetooth Adapter turned on/off
761 * 2. Bonding State of a device changes
762 * 3. A specific profile's connection state changes.
763 */
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700764 private void setupBluetoothEventsIntentFilterLocked() {
765 mBluetoothBroadcastReceiver = new BluetoothBroadcastReceiver();
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700766 IntentFilter profileFilter = new IntentFilter();
767 profileFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
768 profileFilter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
769 profileFilter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
Joseph Pirozzo99aeba92018-02-05 14:48:49 -0800770 profileFilter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700771 profileFilter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
772 profileFilter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
773 profileFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -0700774 profileFilter.addAction(BluetoothDevice.ACTION_UUID);
775 if (mContext != null) {
776 mContext.registerReceiverAsUser(mBluetoothBroadcastReceiver, UserHandle.CURRENT,
777 profileFilter, null, null);
778 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800779 }
780
781 /**
782 * Initialize the {@link #mProfileToConnectableDevicesMap}.
783 * {@link #mProfileToConnectableDevicesMap} stores the profile:DeviceList information. This
784 * method retrieves it from persistent memory.
785 */
786 private synchronized void initDeviceMap() {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700787 if (mProfileToConnectableDevicesMap == null) {
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700788 mProfileToConnectableDevicesMap = new HashMap<>();
789 for (Integer profile : mProfilesToConnect) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700790 // Build the BluetoothDevicesInfo for this profile.
791 BluetoothDevicesInfo devicesInfo = new BluetoothDevicesInfo(profile,
Sal Savageed6b04e2019-03-15 13:27:27 -0700792 sNumSupportedActiveConnections.get(profile));
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700793 mProfileToConnectableDevicesMap.put(profile, devicesInfo);
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700794 }
795 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700796 Log.d(TAG, "Created a new empty Device Map");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800797 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800798 }
799 }
800
801 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800802 * Setting up Listeners to the various events we are interested in listening to for initiating
803 * Bluetooth connection attempts.
804 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700805 private void setupEventListenersLocked() {
Steve Paik9ec53d72018-04-27 13:28:31 -0700806 // Setting up a listener for events from CarPropertyService
807 // For now, we listen to door unlock signal and Ignition state START coming from
808 // {@link CarPropertyService}
809 mCarPropertyService.registerListener(VehicleProperty.DOOR_LOCK, 0, mPropertyEventListener);
810 mCarPropertyService.registerListener(VehicleProperty.IGNITION_STATE, 0,
811 mPropertyEventListener);
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700812 // Get Current restrictions and handle them
813 handleUxRestrictionsChanged(mUxRService.getCurrentUxRestrictions());
814 // Register for future changes to the UxRestrictions
815 mUxRService.registerUxRestrictionsChangeListener(mUxRListener);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700816 mUserServiceHelper.registerServiceCallback(mServiceCallback);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800817 }
818
819 /**
Steve Paik9ec53d72018-04-27 13:28:31 -0700820 * Handles events coming in from the {@link CarPropertyService}
821 * The events that can trigger Bluetooth Scanning from CarPropertyService are Door Unlock and
822 * Igntion START. Upon an event of interest, initiate a connection attempt by calling
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800823 * the policy {@link BluetoothDeviceConnectionPolicy}
824 */
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700825 @VisibleForTesting
826 class CarPropertyListener extends ICarPropertyEventListener.Stub {
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800827 @Override
Steve Paik9ec53d72018-04-27 13:28:31 -0700828 public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
829 for (CarPropertyEvent event : events) {
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800830 if (DBG) {
Steve Paik9ec53d72018-04-27 13:28:31 -0700831 Log.d(TAG, "Cabin change Event : " + event.getEventType());
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -0800832 }
Steve Paik9ec53d72018-04-27 13:28:31 -0700833 CarPropertyValue value = event.getCarPropertyValue();
834 Object o = value.getValue();
835
836 switch (value.getPropertyId()) {
837 case VehicleProperty.DOOR_LOCK:
838 if (o instanceof Boolean) {
839 Boolean locked = (Boolean) o;
840 if (DBG) {
841 Log.d(TAG, "Door Lock: " + locked);
842 }
843 // Attempting a connection only on a door unlock
844 if (!locked) {
845 initiateConnection();
846 }
847 }
848 break;
849 case VehicleProperty.IGNITION_STATE:
850 if (o instanceof Integer) {
851 Integer state = (Integer) o;
852 if (DBG) {
853 Log.d(TAG, "Sensor value : " + state);
854 }
855 // Attempting a connection only on IgntionState START
856 if (state == VehicleIgnitionState.START) {
857 initiateConnection();
858 }
859 }
860 break;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800861 }
862 }
863 }
864 }
865
Ram Periathiruvadie011a402018-03-22 18:54:33 -0700866 private class CarUxRServiceListener extends ICarUxRestrictionsChangeListener.Stub {
867 @Override
868 public void onUxRestrictionsChanged(CarUxRestrictions restrictions) {
869 handleUxRestrictionsChanged(restrictions);
870 }
871 }
872
873 private void handleUxRestrictionsChanged(CarUxRestrictions restrictions) {
874 if (restrictions == null) {
875 return;
876 }
877 if ((restrictions.getActiveRestrictions() & CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP)
878 == CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP) {
879 mFastPairProvider.stopAdvertising();
880 } else {
881 mFastPairProvider.startAdvertising();
882 }
883 }
884
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800885 /**
886 * Clean up slate. Close the Bluetooth profile service connections and quit the state machine -
887 * {@link BluetoothAutoConnectStateMachine}
888 */
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700889 public synchronized void release() {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800890 if (DBG) {
891 Log.d(TAG, "release()");
892 }
Pavel Maltsevc9e86e82017-03-22 11:37:21 -0700893 mInitialized = false;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700894 writeDeviceInfoToSettings();
895 cleanupUserSpecificInfo();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800896 closeEventListeners();
Sal Savageed6b04e2019-03-15 13:27:27 -0700897 mCar.disconnect();
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700898 }
899
900 /**
901 * Clean up information related to user who went background.
902 */
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700903 private void cleanupUserSpecificInfo() {
904 synchronized (mSetupLock) {
905 if (DBG) {
906 Log.d(TAG, "cleanupUserSpecificInfo()");
907 }
908 if (!mUserSpecificInfoInitialized) {
909 if (DBG) {
910 Log.d(TAG, "User specific Info Not initialized..Not cleaning up");
911 }
912 return;
913 }
914 mUserSpecificInfoInitialized = false;
915 // quit the state machine
916 mBluetoothAutoConnectStateMachine.doQuit();
917 mProfileToConnectableDevicesMap = null;
918 mConnectionInFlight = null;
919 if (mBluetoothBroadcastReceiver != null) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700920 if (mContext != null) {
921 mContext.unregisterReceiver(mBluetoothBroadcastReceiver);
922 }
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700923 mBluetoothBroadcastReceiver = null;
924 }
925 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800926 }
927
928 /**
929 * Unregister the listeners to the various Vehicle events coming from other parts of the
930 * CarService
931 */
932 private void closeEventListeners() {
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -0700933 if (DBG) {
934 Log.d(TAG, "closeEventListeners()");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800935 }
Steve Paik9ec53d72018-04-27 13:28:31 -0700936 mCarPropertyService.unregisterListener(VehicleProperty.DOOR_LOCK, mPropertyEventListener);
937 mCarPropertyService.unregisterListener(VehicleProperty.IGNITION_STATE,
938 mPropertyEventListener);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700939 mUserServiceHelper.unregisterServiceCallback(mServiceCallback);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800940 }
941
942 /**
943 * Resets the {@link BluetoothDevicesInfo#mConnectionInfo} of all the profiles to start from
944 * a clean slate. The ConnectionInfo has all the book keeping information regarding the state
945 * of connection attempts - like which device in the device list for the profile is the next
946 * to try connecting etc.
Ram Periathiruvadiacb60242017-04-13 16:19:09 -0700947 * This method does not clear the {@link BluetoothDevicesInfo#mDeviceInfoList} like the {@link
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800948 * #resetProfileToConnectableDevicesMap()} method does.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800949 */
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800950 private synchronized void resetBluetoothDevicesConnectionInfo() {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800951 if (DBG) {
952 Log.d(TAG, "Resetting ConnectionInfo for all profiles");
953 }
954 for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
955 devInfo.resetConnectionInfoLocked();
956 }
957 }
958
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700959 @VisibleForTesting
960 BroadcastReceiver getBluetoothBroadcastReceiver() {
961 return mBluetoothBroadcastReceiver;
962 }
963
964 @VisibleForTesting
965 UserServiceConnectionCallback getServiceCallback() {
966 return mServiceCallback;
967 }
968
969 @VisibleForTesting
970 CarPropertyListener getCarPropertyListener() {
Steve Paik9ec53d72018-04-27 13:28:31 -0700971 return mPropertyEventListener;
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700972 }
973
974 @VisibleForTesting
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700975 synchronized void setAllowReadWriteToSettings(boolean allowWrite) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -0700976 mAllowReadWriteToSettings = allowWrite;
977 }
978
Ram Periathiruvadi76a84892017-07-27 18:10:35 -0700979 @VisibleForTesting
980 BluetoothDevicesInfo getBluetoothDevicesInfo(int profile) {
981 return mProfileToConnectableDevicesMap.get(profile);
982 }
983
Vasu Norie5fb9112017-11-17 10:23:02 -0800984 @VisibleForTesting
985 String toDebugString() {
986 StringBuilder buf = new StringBuilder();
987 for (Integer profile : mProfileToConnectableDevicesMap.keySet()) {
988 BluetoothDevicesInfo info = mProfileToConnectableDevicesMap.get(profile);
989 buf.append(" \n**** profile = " + profile);
990 buf.append(", " + info.toDebugString());
991 }
992 return buf.toString();
993 }
994
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -0800995 /**
Ram Periathiruvadiee28c002017-02-07 21:35:01 -0800996 * Resets the {@link #mProfileToConnectableDevicesMap} to a clean and empty slate.
997 */
998 public synchronized void resetProfileToConnectableDevicesMap() {
999 if (DBG) {
1000 Log.d(TAG, "Resetting the mProfilesToConnectableDevicesMap");
1001 }
1002 for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
1003 devInfo.resetDeviceListLocked();
1004 }
1005 }
1006
1007 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001008 * Returns the list of profiles that the Autoconnection policy attempts to connect on
1009 *
1010 * @return profile list.
1011 */
Pavel Maltsevc9e86e82017-03-22 11:37:21 -07001012 public synchronized List<Integer> getProfilesToConnect() {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001013 return mProfilesToConnect;
1014 }
1015
1016 /**
1017 * Add a new Profile to the list of To Be Connected profiles.
1018 *
1019 * @param profile - ProfileInfo of the new profile to be added.
1020 */
1021 public synchronized void addProfile(Integer profile) {
1022 mProfilesToConnect.add(profile);
1023 }
1024
1025 /**
Justin Paupore7b17ea62018-12-28 20:27:33 -08001026 * Request to disconnect the given profile on the given device, and prevent it from reconnecting
1027 * until either the request is released, or the process owning the given token dies.
Justin Paupore44985ba2019-01-30 18:53:41 -08001028 * @return True if the profile was successfully inhibited, false if an error occurred.
Justin Paupore7b17ea62018-12-28 20:27:33 -08001029 */
Justin Paupore44985ba2019-01-30 18:53:41 -08001030 boolean requestProfileInhibit(BluetoothDevice device, int profile, IBinder token) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001031 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001032 Log.d(TAG, "Request profile inhibit: profile " + Utils.getProfileName(profile)
Justin Paupore7b17ea62018-12-28 20:27:33 -08001033 + ", device " + device.getAddress());
1034 }
1035 ConnectionParams params = new ConnectionParams(profile, device);
Justin Paupore44985ba2019-01-30 18:53:41 -08001036 InhibitRecord record = new InhibitRecord(params, token);
1037 return addInhibitRecord(record);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001038 }
1039
1040 /**
Justin Paupore44985ba2019-01-30 18:53:41 -08001041 * Undo a previous call to {@link #requestProfileInhibit} with the same parameters,
Justin Paupore7b17ea62018-12-28 20:27:33 -08001042 * and reconnect the profile if no other requests are active.
1043 *
1044 * @return True if the request was released, false if an error occurred.
1045 */
Justin Paupore44985ba2019-01-30 18:53:41 -08001046 boolean releaseProfileInhibit(BluetoothDevice device, int profile, IBinder token) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001047 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001048 Log.d(TAG, "Release profile inhibit: profile " + Utils.getProfileName(profile)
Justin Paupore7b17ea62018-12-28 20:27:33 -08001049 + ", device " + device.getAddress());
1050 }
1051
1052 ConnectionParams params = new ConnectionParams(profile, device);
Justin Paupore44985ba2019-01-30 18:53:41 -08001053 InhibitRecord record;
Justin Paupore7b17ea62018-12-28 20:27:33 -08001054 synchronized (this) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001055 record = findInhibitRecordLocked(params, token);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001056 }
1057
1058 if (record == null) {
1059 Log.e(TAG, "Record not found");
1060 return false;
1061 }
1062
1063 return record.removeSelf();
1064 }
1065
Justin Paupore44985ba2019-01-30 18:53:41 -08001066 /** Add a profile inhibit record, disabling the profile if necessary. */
1067 private synchronized boolean addInhibitRecord(InhibitRecord record) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001068 ConnectionParams params = record.getParams();
1069 if (!isProxyAvailable(params.getBluetoothProfile())) {
1070 return false;
1071 }
1072
Justin Paupore44985ba2019-01-30 18:53:41 -08001073 Set<InhibitRecord> previousRecords = mProfileInhibits.get(params);
1074 if (findInhibitRecordLocked(params, record.getToken()) != null) {
1075 Log.e(TAG, "Inhibit request already registered - skipping duplicate");
Justin Paupore7b17ea62018-12-28 20:27:33 -08001076 return false;
1077 }
1078
1079 try {
1080 record.getToken().linkToDeath(record, 0);
1081 } catch (RemoteException e) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001082 Log.e(TAG, "Could not link to death on inhibit token (already dead?)", e);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001083 return false;
1084 }
1085
1086 boolean isNewlyAdded = previousRecords.isEmpty();
Justin Paupore44985ba2019-01-30 18:53:41 -08001087 mProfileInhibits.put(params, record);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001088
1089 if (isNewlyAdded) {
1090 try {
1091 int priority =
1092 mCarBluetoothUserService.getProfilePriority(
1093 params.getBluetoothProfile(),
1094 params.getBluetoothDevice());
1095 if (priority == BluetoothProfile.PRIORITY_OFF) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001096 // This profile was already disabled (and not as the result of an inhibit).
1097 // Add it to the already-disabled list, and do nothing else.
Justin Paupore7b17ea62018-12-28 20:27:33 -08001098 mAlreadyDisabledProfiles.add(params);
1099
1100 if (DBG) {
1101 Log.d(TAG, "Profile " + Utils.getProfileName(params.getBluetoothProfile())
1102 + " already disabled for device " + params.getBluetoothDevice()
1103 + " - suppressing re-enable");
1104 }
1105 } else {
1106 mCarBluetoothUserService.setProfilePriority(
1107 params.getBluetoothProfile(),
1108 params.getBluetoothDevice(),
1109 BluetoothProfile.PRIORITY_OFF);
1110 mCarBluetoothUserService.bluetoothDisconnectFromProfile(
1111 params.getBluetoothProfile(),
1112 params.getBluetoothDevice());
1113 if (DBG) {
1114 Log.d(TAG, "Disabled profile "
1115 + Utils.getProfileName(params.getBluetoothProfile())
1116 + " for device " + params.getBluetoothDevice());
1117 }
1118 }
1119 } catch (RemoteException e) {
1120 Log.e(TAG, "Could not disable profile", e);
1121 record.getToken().unlinkToDeath(record, 0);
Justin Paupore44985ba2019-01-30 18:53:41 -08001122 mProfileInhibits.remove(params, record);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001123 return false;
1124 }
1125 }
1126
Justin Paupore44985ba2019-01-30 18:53:41 -08001127 saveProfileInhibitsToSettingsLocked();
Justin Paupore7b17ea62018-12-28 20:27:33 -08001128 return true;
1129 }
1130
Justin Paupore44985ba2019-01-30 18:53:41 -08001131 /** Remove a given profile inhibit record, reconnecting if necessary. */
1132 private synchronized boolean removeInhibitRecord(InhibitRecord record) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001133 ConnectionParams params = record.getParams();
1134 if (!isProxyAvailable(params.getBluetoothProfile())) {
1135 return false;
1136 }
Justin Paupore44985ba2019-01-30 18:53:41 -08001137 if (!mProfileInhibits.containsEntry(params, record)) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001138 Log.e(TAG, "Record already removed");
1139 // Removing something a second time vacuously succeeds.
1140 return true;
1141 }
1142
1143 // Re-enable profile before unlinking and removing the record, in case of error.
1144 // The profile should be re-enabled if this record is the only one left for that
1145 // device and profile combination.
Justin Paupore44985ba2019-01-30 18:53:41 -08001146 if (mProfileInhibits.get(params).size() == 1) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001147 if (!restoreProfilePriority(params)) {
1148 return false;
1149 }
1150 }
1151
1152 record.getToken().unlinkToDeath(record, 0);
Justin Paupore44985ba2019-01-30 18:53:41 -08001153 mProfileInhibits.remove(params, record);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001154
Justin Paupore44985ba2019-01-30 18:53:41 -08001155 saveProfileInhibitsToSettingsLocked();
Justin Paupore7b17ea62018-12-28 20:27:33 -08001156 return true;
1157 }
1158
Justin Paupore44985ba2019-01-30 18:53:41 -08001159 /** Find the inhibit record, if any, corresponding to the given parameters and token. */
Justin Paupore7b17ea62018-12-28 20:27:33 -08001160 @Nullable
Justin Paupore44985ba2019-01-30 18:53:41 -08001161 private InhibitRecord findInhibitRecordLocked(ConnectionParams params, IBinder token) {
1162 return mProfileInhibits.get(params)
Justin Paupore7b17ea62018-12-28 20:27:33 -08001163 .stream()
1164 .filter(r -> r.getToken() == token)
1165 .findAny()
1166 .orElse(null);
1167 }
1168
1169 /** Re-enable and reconnect a given profile for a device. */
1170 private boolean restoreProfilePriority(ConnectionParams params) {
1171 if (!isProxyAvailable(params.getBluetoothProfile())) {
1172 return false;
1173 }
1174
1175 if (mAlreadyDisabledProfiles.remove(params)) {
1176 // The profile does not need any state changes, since it was disabled
Justin Paupore44985ba2019-01-30 18:53:41 -08001177 // before it was inhibited. Leave it disabled.
Justin Paupore7b17ea62018-12-28 20:27:33 -08001178 if (DBG) {
1179 Log.d(TAG, "Not restoring profile "
1180 + Utils.getProfileName(params.getBluetoothProfile()) + " for device "
1181 + params.getBluetoothDevice() + " - was manually disabled");
1182 }
1183 return true;
1184 }
1185
1186 try {
1187 mCarBluetoothUserService.setProfilePriority(
1188 params.getBluetoothProfile(),
1189 params.getBluetoothDevice(),
1190 BluetoothProfile.PRIORITY_ON);
1191 mCarBluetoothUserService.bluetoothConnectToProfile(
1192 params.getBluetoothProfile(),
1193 params.getBluetoothDevice());
1194 if (DBG) {
1195 Log.d(TAG, "Restored profile " + Utils.getProfileName(params.getBluetoothProfile())
1196 + " for device " + params.getBluetoothDevice());
1197 }
1198 return true;
1199 } catch (RemoteException e) {
1200 Log.e(TAG, "Could not enable profile", e);
1201 return false;
1202 }
1203 }
1204
Justin Paupore44985ba2019-01-30 18:53:41 -08001205 /** Dump all currently-active profile inhibits to {@link Settings.Secure}. */
1206 private void saveProfileInhibitsToSettingsLocked() {
1207 Set<ConnectionParams> inhibitedProfiles = new HashSet<>(mProfileInhibits.keySet());
1208 // Don't write out profiles that were disabled before a request was made, since
Justin Paupore7b17ea62018-12-28 20:27:33 -08001209 // restoring those profiles is a no-op.
Justin Paupore44985ba2019-01-30 18:53:41 -08001210 inhibitedProfiles.removeAll(mAlreadyDisabledProfiles);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001211 String savedDisconnects =
Justin Paupore44985ba2019-01-30 18:53:41 -08001212 inhibitedProfiles
Justin Paupore7b17ea62018-12-28 20:27:33 -08001213 .stream()
1214 .map(ConnectionParams::flattenToString)
1215 .collect(Collectors.joining(SETTINGS_DELIMITER));
1216
1217 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001218 Log.d(TAG, "Saving inhibits to settings for u" + mUserId + ": " + savedDisconnects);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001219 }
1220
1221 Settings.Secure.putStringForUser(
Justin Paupore44985ba2019-01-30 18:53:41 -08001222 mContext.getContentResolver(), KEY_BLUETOOTH_PROFILES_INHIBITED,
Justin Paupore7b17ea62018-12-28 20:27:33 -08001223 savedDisconnects, mUserId);
1224 }
1225
Justin Paupore44985ba2019-01-30 18:53:41 -08001226 /** Create {@link InhibitRecord}s for all profile inhibits written to settings. */
1227 private synchronized void restoreProfileInhibitsFromSettings() {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001228 if (mBluetoothAdapter == null) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001229 Log.e(TAG, "Cannot restore inhibit records - Bluetooth not available");
Justin Paupore7b17ea62018-12-28 20:27:33 -08001230 return;
1231 }
1232
1233 String savedConnectionParams = Settings.Secure.getStringForUser(
1234 mContext.getContentResolver(),
Justin Paupore44985ba2019-01-30 18:53:41 -08001235 KEY_BLUETOOTH_PROFILES_INHIBITED,
Justin Paupore7b17ea62018-12-28 20:27:33 -08001236 mUserId);
1237
1238 if (TextUtils.isEmpty(savedConnectionParams)) {
1239 return;
1240 }
1241
1242 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001243 Log.d(TAG, "Restoring profile inhibits: " + savedConnectionParams);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001244 }
1245
1246 for (String paramsStr : savedConnectionParams.split(SETTINGS_DELIMITER)) {
1247 try {
1248 ConnectionParams params = ConnectionParams.parse(paramsStr, mBluetoothAdapter);
Justin Paupore44985ba2019-01-30 18:53:41 -08001249 InhibitRecord record =
1250 new InhibitRecord(params, RESTORED_PROFILE_INHIBIT_TOKEN);
1251 mProfileInhibits.put(params, record);
1252 mRestoredInhibits.add(record);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001253 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001254 Log.d(TAG, "Restored profile inhibits for " + params);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001255 }
1256 } catch (IllegalArgumentException e) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001257 Log.e(TAG, "Bad format for saved profile inhibit: " + paramsStr, e);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001258 // We won't ever be able to fix a bad parse, so skip it and move on.
1259 }
1260 }
1261 }
1262
1263 /**
Justin Paupore44985ba2019-01-30 18:53:41 -08001264 * Try once to remove all restored profile inhibits.
Justin Paupore7b17ea62018-12-28 20:27:33 -08001265 *
1266 * If the CarBluetoothUserService is not yet available, or it hasn't yet bound its profile
1267 * proxies, the removal will fail, and will need to be retried later.
1268 */
Justin Paupore44985ba2019-01-30 18:53:41 -08001269 private void tryRemoveRestoredProfileInhibitsLocked() {
1270 HashSet<InhibitRecord> successfullyRemoved = new HashSet<>();
Justin Paupore7b17ea62018-12-28 20:27:33 -08001271
Justin Paupore44985ba2019-01-30 18:53:41 -08001272 for (InhibitRecord record : mRestoredInhibits) {
1273 if (removeInhibitRecord(record)) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001274 successfullyRemoved.add(record);
1275 }
1276 }
1277
Justin Paupore44985ba2019-01-30 18:53:41 -08001278 mRestoredInhibits.removeAll(successfullyRemoved);
Justin Paupore7b17ea62018-12-28 20:27:33 -08001279 }
1280
1281 /**
Justin Paupore44985ba2019-01-30 18:53:41 -08001282 * Keep trying to remove all profile inhibits that were restored from settings
1283 * until all such inhibits have been removed.
Justin Paupore7b17ea62018-12-28 20:27:33 -08001284 */
Justin Paupore44985ba2019-01-30 18:53:41 -08001285 private synchronized void removeRestoredProfileInhibits() {
1286 tryRemoveRestoredProfileInhibitsLocked();
Justin Paupore7b17ea62018-12-28 20:27:33 -08001287
Justin Paupore44985ba2019-01-30 18:53:41 -08001288 if (!mRestoredInhibits.isEmpty()) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001289 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001290 Log.d(TAG, "Could not remove all restored profile inhibits - "
Justin Paupore7b17ea62018-12-28 20:27:33 -08001291 + "trying again in " + RESTORE_BACKOFF_MILLIS + "ms");
1292 }
1293 mHandler.postDelayed(
Justin Paupore44985ba2019-01-30 18:53:41 -08001294 this::removeRestoredProfileInhibits,
1295 RESTORED_PROFILE_INHIBIT_TOKEN,
Justin Paupore7b17ea62018-12-28 20:27:33 -08001296 RESTORE_BACKOFF_MILLIS);
1297 }
1298 }
1299
Justin Paupore44985ba2019-01-30 18:53:41 -08001300 /** Release all active inhibit records prior to user switch or shutdown. */
1301 private synchronized void releaseAllInhibitsBeforeUnbind() {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001302 if (DBG) {
Justin Paupore44985ba2019-01-30 18:53:41 -08001303 Log.d(TAG, "Unbinding CarBluetoothUserService - releasing all profile inhibits");
Justin Paupore7b17ea62018-12-28 20:27:33 -08001304 }
Justin Paupore44985ba2019-01-30 18:53:41 -08001305 for (ConnectionParams params : mProfileInhibits.keySet()) {
1306 for (InhibitRecord record : mProfileInhibits.get(params)) {
Justin Paupore7b17ea62018-12-28 20:27:33 -08001307 record.removeSelf();
1308 }
1309 }
1310
Justin Paupore44985ba2019-01-30 18:53:41 -08001311 // Some inhibits might be hanging around because they couldn't be cleaned up.
Justin Paupore7b17ea62018-12-28 20:27:33 -08001312 // Make sure they get persisted...
Justin Paupore44985ba2019-01-30 18:53:41 -08001313 saveProfileInhibitsToSettingsLocked();
Justin Paupore7b17ea62018-12-28 20:27:33 -08001314 // ...then clear them from the map.
Justin Paupore44985ba2019-01-30 18:53:41 -08001315 mProfileInhibits.clear();
Justin Paupore7b17ea62018-12-28 20:27:33 -08001316
Justin Paupore44985ba2019-01-30 18:53:41 -08001317 // We don't need to maintain previously-disabled profiles any more - they were already
1318 // skipped in saveProfileInhibitsToSettingsLocked() above, and they don't need any
Justin Paupore7b17ea62018-12-28 20:27:33 -08001319 // further handling when the user resumes.
1320 mAlreadyDisabledProfiles.clear();
1321
Justin Paupore44985ba2019-01-30 18:53:41 -08001322 // Clean up bookkeeping for restored inhibits. (If any are still around, they'll be
Justin Paupore7b17ea62018-12-28 20:27:33 -08001323 // restored again when this user restarts.)
Justin Paupore44985ba2019-01-30 18:53:41 -08001324 mHandler.removeCallbacksAndMessages(RESTORED_PROFILE_INHIBIT_TOKEN);
1325 mRestoredInhibits.clear();
Justin Paupore7b17ea62018-12-28 20:27:33 -08001326 }
1327
1328 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001329 * Add or remove a device based on the bonding state change.
1330 *
1331 * @param device - device to add/remove
1332 * @param bondState - current bonding state
1333 */
1334 private void updateBondState(BluetoothDevice device, int bondState) {
1335 if (device == null) {
1336 Log.e(TAG, "updateBondState: device Null");
1337 return;
1338 }
1339 if (DBG) {
Ram Periathiruvadibe7ea0fe2017-05-31 23:31:40 -07001340 Log.d(TAG, "BondState :" + bondState + " Device: " + device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001341 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001342 // Bonded devices are added to a profile's device list after the device CONNECTS on the
1343 // profile. When unpaired, we remove the device from all of the profiles' device list.
1344 if (bondState == BluetoothDevice.BOND_NONE) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001345 for (Integer profile : mProfilesToConnect) {
Ram Periathiruvadiee77dde2017-05-25 13:09:07 -07001346 if (DBG) {
1347 Log.d(TAG, "Removing " + device + " from profile: " + profile);
1348 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001349 removeDeviceFromProfile(device, profile);
1350 }
Vasu Norie5fb9112017-11-17 10:23:02 -08001351 } else if (bondState == BluetoothDevice.BOND_BONDED) {
1352 // A device just paired. When it connects on HFP profile, then immediately initiate
1353 // connections on PBAP and MAP. for now, maintain this fact in a data structure
1354 // to be looked up when HFP profile connects.
1355 mPairedButUnconnectedDevices.add(device);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001356 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001357 }
1358
1359 /**
1360 * Add a new device to the list of devices connect-able on the given profile
1361 *
1362 * @param device - Bluetooth device to be added
1363 * @param profile - profile to add the device to.
1364 */
1365 private synchronized void addDeviceToProfile(BluetoothDevice device, Integer profile) {
1366 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1367 if (devInfo == null) {
1368 if (DBG) {
1369 Log.d(TAG, "Creating devInfo for profile: " + profile);
1370 }
1371 devInfo = new BluetoothDevicesInfo(profile);
1372 mProfileToConnectableDevicesMap.put(profile, devInfo);
1373 }
1374 devInfo.addDeviceLocked(device);
1375 }
1376
1377 /**
1378 * Remove the device from the list of devices connect-able on the gievn profile.
1379 *
1380 * @param device - Bluetooth device to be removed
1381 * @param profile - profile to remove the device from
1382 */
1383 private synchronized void removeDeviceFromProfile(BluetoothDevice device, Integer profile) {
1384 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1385 if (devInfo != null) {
1386 devInfo.removeDeviceLocked(device);
1387 }
1388 }
1389
1390 /**
1391 * Initiate a bluetooth connection.
1392 */
1393 private void initiateConnection() {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001394 // Make sure the bluetooth adapter is available & enabled.
1395 if (mBluetoothAdapter == null) {
1396 Log.w(TAG, "Bluetooth Adapter null");
1397 return;
1398 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001399 if (mBluetoothAdapter.isEnabled()) {
1400 if (isDeviceMapEmpty()) {
1401 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001402 Log.d(TAG, "Device Map is empty. Nothing to connect to");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001403 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001404 return;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001405 }
1406 resetDeviceAvailableToConnect();
1407 if (DBG) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001408 Log.d(TAG, "initiateConnection() Reset Device Availability");
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001409 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001410 mBluetoothAutoConnectStateMachine.sendMessage(BluetoothAutoConnectStateMachine
1411 .CONNECT);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001412 } else {
1413 if (DBG) {
1414 Log.d(TAG, "Bluetooth Adapter not enabled.");
1415 }
1416 }
1417 }
1418
1419 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001420 * Find an unconnected profile and find a device to connect on it.
1421 * Finds the appropriate device for the profile from the information available in
1422 * {@link #mProfileToConnectableDevicesMap}
1423 *
1424 * @return true - if we found a device to connect on for any of the {@link #mProfilesToConnect}
1425 * false - if we cannot find a device to connect to or if we are not ready to connect yet.
1426 */
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001427 public synchronized boolean findDeviceToConnect() {
Pavel Maltsevc9e86e82017-03-22 11:37:21 -07001428 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()
1429 || mProfileToConnectableDevicesMap == null || !mInitialized) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001430 if (DBG) {
1431 if (mProfileToConnectableDevicesMap == null) {
1432 Log.d(TAG, "findDeviceToConnect(): Device Map null");
1433 } else {
1434 Log.d(TAG, "findDeviceToConnect(): BT Adapter not enabled");
1435 }
1436 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001437 return false;
1438 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001439 boolean connectingToADevice = false;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001440 // Get the first unconnected profile that we can try to make a connection
1441 Integer nextProfile = getNextProfileToConnectLocked();
1442 // Keep going through the profiles until we find a device that we can connect to
1443 while (nextProfile != PROFILE_NOT_AVAILABLE) {
1444 if (DBG) {
1445 Log.d(TAG, "connectToProfile(): " + nextProfile);
1446 }
1447 // find a device that is next in line for a connection attempt for that profile
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001448 // and try connecting to it.
1449 connectingToADevice = connectToNextDeviceInQueueLocked(nextProfile);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001450 // If we found a device to connect, break out of the loop
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001451 if (connectingToADevice) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001452 if (DBG) {
1453 Log.d(TAG, "Found device to connect to");
1454 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001455 // set up a time out
1456 mBluetoothAutoConnectStateMachine.sendMessageDelayed(
Justin Pauporec813ead2018-12-28 20:18:25 -08001457 BluetoothAutoConnectStateMachine.CONNECT_TIMEOUT, mConnectionInFlight,
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001458 BluetoothAutoConnectStateMachine.CONNECTION_TIMEOUT_MS);
1459 break;
1460 } else {
1461 // result will be false, if there are no more devices to connect
1462 // or if the ProfileProxy objects are null (ServiceConnection
1463 // not yet established for this profile)
1464 if (DBG) {
1465 Log.d(TAG, "No more device to connect on Profile: " + nextProfile);
1466 }
1467 nextProfile = getNextProfileToConnectLocked();
1468 }
1469 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001470 return connectingToADevice;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001471 }
1472
1473 /**
1474 * Get the first unconnected profile.
1475 *
1476 * @return profile to connect.
1477 * Special return value 0 if
1478 * 1. all profiles have been connected on.
1479 * 2. no profile connected but no nearby known device that can be connected to
1480 */
1481 private Integer getNextProfileToConnectLocked() {
1482 for (Integer profile : mProfilesToConnect) {
1483 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1484 if (devInfo != null) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001485 if (devInfo.isProfileConnectableLocked()) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001486 return profile;
1487 }
1488 } else {
1489 Log.e(TAG, "Unexpected: devInfo null for profile: " + profile);
1490 }
1491 }
1492 // Reaching here denotes all profiles are connected or No devices available for any profile
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001493 if (DBG) {
1494 Log.d(TAG, "No disconnected profiles");
1495 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001496 return PROFILE_NOT_AVAILABLE;
1497 }
1498
1499 /**
Vasu Norie5fb9112017-11-17 10:23:02 -08001500 * Checks if the Bluetooth profile service's proxy object is available.
1501 * Proxy obj should be available before we can attempt to connect on that profile.
1502 *
1503 * @param profile The profile to check the presence of the proxy object
1504 * @return True if the proxy obj exists. False otherwise.
1505 */
1506 private boolean isProxyAvailable(Integer profile) {
1507 if (mCarBluetoothUserService == null) {
1508 mCarBluetoothUserService = setupBluetoothUserService();
1509 if (mCarBluetoothUserService == null) {
1510 Log.d(TAG, "CarBluetoothUserSvc null. Car service not bound to PerUserCarSvc.");
1511 return false;
1512 }
1513 }
1514 try {
1515 if (!mCarBluetoothUserService.isBluetoothConnectionProxyAvailable(profile)) {
1516 // proxy unavailable.
1517 if (DBG) {
1518 Log.d(TAG, "Proxy for Bluetooth Profile Service Unavailable: " + profile);
1519 }
1520 return false;
1521 }
1522 } catch (RemoteException e) {
1523 Log.e(TAG, "Car BT Service Remote Exception.");
1524 return false;
1525 }
1526 return true;
1527 }
1528
1529 /**
1530 * Creates a device connection for the given profile.
1531 *
1532 * @param profile The profile on which the connection is being setup
Ram Periathiruvadie011a402018-03-22 18:54:33 -07001533 * @param device The device for which the connection is to be made.
Vasu Norie5fb9112017-11-17 10:23:02 -08001534 * @return true if the connection is setup successfully. False otherwise.
1535 */
1536 boolean connectToDeviceOnProfile(Integer profile, BluetoothDevice device) {
1537 if (DBG) {
1538 Log.d(TAG, "in connectToDeviceOnProfile for Profile:" + profile +
1539 ", device: " + Utils.getDeviceDebugInfo(device));
1540 }
1541 if (!isProxyAvailable(profile)) {
1542 if (DBG) {
1543 Log.d(TAG, "No proxy available for Profile: " + profile);
1544 }
1545 return false;
1546 }
1547 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1548 if (devInfo == null) {
1549 if (DBG) {
1550 Log.d(TAG, "devInfo NULL on Profile:" + profile);
1551 }
1552 return false;
1553 }
1554 int state = devInfo.getCurrentConnectionStateLocked(device);
1555 if (state == BluetoothProfile.STATE_CONNECTED ||
1556 state == BluetoothProfile.STATE_CONNECTING) {
1557 if (DBG) {
1558 Log.d(TAG, "device " + Utils.getDeviceDebugInfo(device) +
1559 " is already connected/connecting on Profile:" + profile);
1560 }
1561 return true;
1562 }
1563 if (!devInfo.isProfileConnectableLocked()) {
1564 if (DBG) {
1565 Log.d(TAG, "isProfileConnectableLocked FALSE on Profile:" + profile +
1566 " this means number of connections on this profile max'ed out or " +
1567 " no more devices available to connect on this profile");
1568 }
1569 return false;
1570 }
1571 try {
1572 if (mCarBluetoothUserService != null) {
1573 mCarBluetoothUserService.bluetoothConnectToProfile((int) profile, device);
1574 devInfo.setConnectionStateLocked(device, BluetoothProfile.STATE_CONNECTING);
1575 // Increment the retry count & cache what is being connected to
1576 // This method is already called from a synchronized context.
Justin Pauporec813ead2018-12-28 20:18:25 -08001577 mConnectionInFlight = new ConnectionParams(profile, device);
Vasu Norie5fb9112017-11-17 10:23:02 -08001578 devInfo.incrementRetryCountLocked();
1579 if (DBG) {
1580 Log.d(TAG, "Increment Retry to: " + devInfo.getRetryCountLocked() +
1581 ", for Profile: " + profile + ", on device: " +
1582 Utils.getDeviceDebugInfo(device));
1583 }
1584 return true;
1585 } else {
1586 Log.e(TAG, "CarBluetoothUserSvc null");
1587 }
1588 } catch (RemoteException e) {
1589 Log.e(TAG, "Remote User Service stopped responding: " + e.getMessage());
1590 }
1591 return false;
1592 }
1593
1594 /**
1595 * Helper method to reset info in {@link #mConnectionInFlight} in the given
1596 * {@link BluetoothDevicesInfo} object.
1597 *
1598 * @param devInfo the {@link BluetoothDevicesInfo} where the info is to be reset.
1599 */
1600 private void setProfileOnDeviceToUnavailable(BluetoothDevicesInfo devInfo) {
Justin Pauporec813ead2018-12-28 20:18:25 -08001601 mConnectionInFlight = new ConnectionParams(0, null);
Vasu Norie5fb9112017-11-17 10:23:02 -08001602 devInfo.setDeviceAvailableToConnectLocked(false);
1603 }
1604
1605 boolean doesDeviceExistForProfile(Integer profile, BluetoothDevice device) {
1606 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1607 if (devInfo == null) {
1608 if (DBG) {
1609 Log.d(TAG, "devInfo NULL on Profile:" + profile);
1610 }
1611 return false;
1612 }
1613 boolean b = devInfo.checkDeviceInListLocked(device);
1614 if (DBG) {
1615 Log.d(TAG, "Is device " + Utils.getDeviceDebugInfo(device) +
1616 " listed for profile " + profile + ": " + b);
1617 }
1618 return b;
1619 }
1620
1621 /**
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001622 * Try to connect to the next device in the device list for the given profile.
1623 *
1624 * @param profile - profile to connect on
1625 * @return - true if we found a device to connect on for this profile
1626 * false - if we cannot find a device to connect to.
1627 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001628 private boolean connectToNextDeviceInQueueLocked(Integer profile) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001629 // Get the Device Information for the given profile and find the next device to connect on
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001630 BluetoothDevice devToConnect = null;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001631 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1632 if (devInfo == null) {
1633 Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
1634 return false;
1635 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001636
Vasu Norie5fb9112017-11-17 10:23:02 -08001637 if (isProxyAvailable(profile)) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001638 // Get the next device in the device list for this profile.
1639 devToConnect = devInfo.getNextDeviceInQueueLocked();
1640 if (devToConnect != null) {
1641 // deviceAvailable && proxyAvailable
Vasu Norie5fb9112017-11-17 10:23:02 -08001642 if (connectToDeviceOnProfile(profile, devToConnect)) return true;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001643 } else {
1644 // device unavailable
1645 if (DBG) {
1646 Log.d(TAG, "No paired nearby device to connect to for profile: " + profile);
1647 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001648 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001649 }
1650
Vasu Norie5fb9112017-11-17 10:23:02 -08001651 // reset the mConnectionInFlight
1652 setProfileOnDeviceToUnavailable(devInfo);
1653 return false;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001654 }
1655
1656 /**
1657 * Update the device connection status for a profile and also notify the state machine.
1658 * This gets called from {@link BluetoothBroadcastReceiver} when it receives a Profile's
1659 * CONNECTION_STATE_CHANGED intent.
1660 *
1661 * @param params - {@link ConnectionParams} device and profile list info
1662 * @param currentState - connection result to update
1663 */
1664 private void notifyConnectionStatus(ConnectionParams params, int currentState) {
1665 // Update the profile's BluetoothDevicesInfo.
1666 boolean isConnected;
1667 switch (currentState) {
1668 case BluetoothProfile.STATE_DISCONNECTED: {
1669 isConnected = false;
1670 break;
1671 }
1672
1673 case BluetoothProfile.STATE_CONNECTED: {
1674 isConnected = true;
1675 break;
1676 }
1677
1678 default: {
1679 if (DBG) {
1680 Log.d(TAG, "notifyConnectionStatus() Ignoring state: " + currentState);
1681 }
1682 return;
1683 }
1684
1685 }
1686
1687 boolean updateSuccessful = updateDeviceConnectionStatus(params, isConnected);
1688 if (updateSuccessful) {
1689 if (isConnected) {
1690 mBluetoothAutoConnectStateMachine.sendMessage(
1691 BluetoothAutoConnectStateMachine.DEVICE_CONNECTED,
1692 params);
1693 } else {
1694 mBluetoothAutoConnectStateMachine.sendMessage(
1695 BluetoothAutoConnectStateMachine.DEVICE_DISCONNECTED,
1696 params);
1697 }
1698 }
1699 }
1700
1701 /**
1702 * Update the profile's {@link BluetoothDevicesInfo} with the result of the connection
1703 * attempt. This gets called from the {@link BluetoothAutoConnectStateMachine} when the
1704 * connection attempt times out or from {@link BluetoothBroadcastReceiver} when it receives
1705 * a Profile's CONNECTION_STATE_CHANGED intent.
1706 *
1707 * @param params - {@link ConnectionParams} device and profile list info
1708 * @param didConnect - connection result to update
1709 */
1710 public synchronized boolean updateDeviceConnectionStatus(ConnectionParams params,
1711 boolean didConnect) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001712 if (params == null || params.getBluetoothDevice() == null) {
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001713 Log.e(TAG, "updateDeviceConnectionStatus: null params");
1714 return false;
1715 }
1716 // Get the profile to update
1717 Integer profileToUpdate = params.getBluetoothProfile();
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001718 BluetoothDevice deviceThatConnected = params.getBluetoothDevice();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001719 if (DBG) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001720 Log.d(TAG, "Profile: " + profileToUpdate + " Connected: " + didConnect + " on "
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001721 + deviceThatConnected);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001722 }
1723
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001724 // If the connection update is on a different profile or device (a very rare possibility),
1725 // it is handled automatically. Just logging it here.
1726 if (DBG) {
1727 if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothProfile() != null) {
1728 if (profileToUpdate.equals(mConnectionInFlight.getBluetoothProfile()) == false) {
1729 Log.d(TAG, "Updating profile " + profileToUpdate
1730 + " different from connection in flight "
1731 + mConnectionInFlight.getBluetoothProfile());
1732 }
1733 }
1734
1735 if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothDevice() != null) {
1736 if (deviceThatConnected.equals(mConnectionInFlight.getBluetoothDevice()) == false) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001737 Log.d(TAG, "Updating device: " + deviceThatConnected
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001738 + " different from connection in flight: "
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001739 + mConnectionInFlight.getBluetoothDevice());
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001740
1741 }
1742 }
1743 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001744 BluetoothDevicesInfo devInfo = null;
1745 devInfo = mProfileToConnectableDevicesMap.get(profileToUpdate);
1746 if (devInfo == null) {
1747 Log.e(TAG, "Unexpected: devInfo null for profile: " + profileToUpdate);
1748 return false;
1749 }
1750
1751 boolean retry = canRetryConnection(profileToUpdate);
1752 // Update the status and also if a retry attempt can be made if the
1753 // connection timed out in the previous attempt.
1754 if (DBG) {
1755 Log.d(TAG, "Retry? : " + retry);
1756 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001757 devInfo.updateConnectionStatusLocked(deviceThatConnected, didConnect, retry);
1758 // Write to persistent memory to have the latest snapshot available
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001759 writeDeviceInfoToSettings(params);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001760 return true;
1761 }
1762
1763 /**
1764 * Returns if we can retry connection attempt on the given profile for the device that is
1765 * currently in the head of the queue.
1766 *
1767 * @param profile - Profile to check
1768 */
1769 private synchronized boolean canRetryConnection(Integer profile) {
1770 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1771 if (devInfo == null) {
1772 Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
1773 return false;
1774 }
1775 if (devInfo.getRetryCountLocked() < MAX_CONNECT_RETRIES) {
1776 return true;
1777 } else {
1778 return false;
1779 }
1780 }
1781
1782 /**
1783 * Helper method to see if there are any connect-able devices on any of the
1784 * profiles.
1785 *
1786 * @return true - if {@link #mProfileToConnectableDevicesMap} does not have any devices for any
1787 * profiles.
1788 * false - if {@link #mProfileToConnectableDevicesMap} has a device for at least one profile.
1789 */
1790 private synchronized boolean isDeviceMapEmpty() {
1791 boolean empty = true;
1792 for (Integer profile : mProfilesToConnect) {
1793 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
1794 if (devInfo != null) {
1795 if (devInfo.getNumberOfPairedDevicesLocked() != 0) {
1796 if (DBG) {
1797 Log.d(TAG, "Device map not empty. Profile: " + profile + " has "
1798 + devInfo.getNumberOfPairedDevicesLocked() + " paired devices");
1799 }
1800 empty = false;
1801 break;
1802 }
1803 }
1804 }
1805 return empty;
1806 }
1807
1808 /**
1809 * Reset the Device Available to Connect information for all profiles to Available.
1810 * If in a previous connection attempt, we failed to connect on all devices for a profile,
1811 * we would update deviceAvailableToConnect for that profile to false. That information
1812 * is used to deduce if we should move to the next profile. If marked false, we will not
1813 * try to connect on that profile anymore as part of that connection attempt.
1814 * However, when we get another connection trigger from the vehicle, we need to reset the
1815 * deviceAvailableToConnect information so we can start the connection attempts all over
1816 * again.
1817 */
1818 private synchronized void resetDeviceAvailableToConnect() {
1819 for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
1820 devInfo.setDeviceAvailableToConnectLocked(true);
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001821 devInfo.resetDeviceIndex();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001822 }
1823 }
1824
1825 /**
1826 * Utility function - Prints the Profile: list of devices information to log
1827 * Caller should wrap a DBG around this, since this is for debugging purpose.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001828 *
1829 * @param writer - PrintWriter
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001830 */
1831 private synchronized void printDeviceMap(PrintWriter writer) {
1832 if (mProfileToConnectableDevicesMap == null) {
1833 return;
1834 }
Vasu Norie5fb9112017-11-17 10:23:02 -08001835 for (Integer profile : mProfileToConnectableDevicesMap.keySet()) {
1836 writer.print("Profile: " + Utils.getProfileName(profile) + "\t");
1837 writer.print("\n" + mProfileToConnectableDevicesMap.get(profile).toDebugString());
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001838 writer.println();
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001839 }
1840 }
1841
1842 /**
Sal Savageed6b04e2019-03-15 13:27:27 -07001843 * Get the persisted Bluetooth state from Settings
1844 */
1845 private boolean isBluetoothPersistedOn() {
1846 return (Settings.Global.getInt(
1847 mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, -1) != 0);
1848 }
1849
1850 /**
1851 * Turn on the Bluetooth Adapter.
1852 */
1853 private void enabledBluetooth() {
1854 if (DBG) Log.d(TAG, "Enable bluetooth adapter");
1855 if (mBluetoothAdapter == null) {
1856 Log.e(TAG, "Cannot enable Bluetooth adapter. The object is null.");
1857 return;
1858 }
1859 mBluetoothAdapter.enable();
1860 }
1861
1862 /**
1863 * Turn off the Bluetooth Adapter.
1864 *
1865 * Tells BluetoothAdapter to shut down _without_ persisting the off state as the desired state
1866 * of the Bluetooth adapter for next start up.
1867 */
1868 private void disableBluetooth() {
1869 if (DBG) Log.d(TAG, "Disable bluetooth, do not persist state across reboot");
1870 if (mBluetoothAdapter == null) {
1871 Log.e(TAG, "Cannot disable Bluetooth adapter. The object is null.");
1872 return;
1873 }
1874 mBluetoothAdapter.disable(false);
1875 }
1876
1877 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001878 * Write the device list for all bluetooth profiles that connected.
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001879 *
1880 * @return true if the write was successful, false otherwise
1881 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001882 private synchronized boolean writeDeviceInfoToSettings() {
1883 ConnectionParams params;
1884 boolean writeResult;
1885 for (Integer profile : mProfilesToConnect) {
1886 params = new ConnectionParams(profile);
1887 writeResult = writeDeviceInfoToSettings(params);
1888 if (!writeResult) {
1889 Log.e(TAG, "Error writing Device Info for profile:" + profile);
1890 return writeResult;
1891 }
1892 }
1893 return true;
1894 }
1895
1896 /**
1897 * Write information about which devices connected on which profile to Settings.Secure.
1898 * Essentially the list of devices that a profile can connect on the next auto-connect
1899 * attempt.
1900 *
1901 * @param params - ConnectionParams indicating which bluetooth profile to write this
1902 * information
1903 * for.
1904 * @return true if the write was successful, false otherwise
1905 */
1906 public synchronized boolean writeDeviceInfoToSettings(ConnectionParams params) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001907 if (!mAllowReadWriteToSettings) {
1908 return false;
1909 }
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001910 boolean writeSuccess = true;
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001911 Integer profileToUpdate = params.getBluetoothProfile();
1912
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001913 if (mProfileToConnectableDevicesMap == null) {
1914 writeSuccess = false;
1915 } else {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001916 List<String> deviceNames = new ArrayList<>();
1917 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profileToUpdate);
1918 StringBuilder sb = new StringBuilder();
1919 String delimiter = ""; // start off with no delimiter.
1920
1921 // Iterate through the List<BluetoothDevice> and build a String that is
1922 // names of all devices to be connected for this profile joined together and
1923 // delimited by a delimiter (its a ',' here)
1924 if (devicesInfo != null && devicesInfo.getDeviceList() != null) {
1925 for (BluetoothDevice device : devicesInfo.getDeviceList()) {
1926 sb.append(delimiter);
1927 sb.append(device.getAddress());
1928 delimiter = SETTINGS_DELIMITER;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001929 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001930
1931 }
1932 // joinedDeviceNames has something like "22:22:33:44:55:AB,22:23:xx:xx:xx:xx"
1933 // mac addresses of connectable devices separated by a delimiter
1934 String joinedDeviceNames = sb.toString();
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07001935 if (DBG) {
1936 Log.d(TAG, "Profile: " + profileToUpdate + " Writing: " + joinedDeviceNames);
1937 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001938 switch (profileToUpdate) {
1939 case BluetoothProfile.A2DP_SINK:
1940 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1941 KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES,
Joseph Pirozzoffb78982018-10-31 08:28:46 -07001942 joinedDeviceNames, mUserId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001943 break;
1944
1945 case BluetoothProfile.HEADSET_CLIENT:
1946 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1947 KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES,
Joseph Pirozzoffb78982018-10-31 08:28:46 -07001948 joinedDeviceNames, mUserId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001949 break;
1950
1951 case BluetoothProfile.PBAP_CLIENT:
1952 // use the phone
1953 break;
1954
1955 case BluetoothProfile.MAP_CLIENT:
1956 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1957 KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES,
Joseph Pirozzoffb78982018-10-31 08:28:46 -07001958 joinedDeviceNames, mUserId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001959 break;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -08001960 case BluetoothProfile.PAN:
1961 Settings.Secure.putStringForUser(mContext.getContentResolver(),
1962 KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES,
Joseph Pirozzoffb78982018-10-31 08:28:46 -07001963 joinedDeviceNames, mUserId);
Joseph Pirozzo99aeba92018-02-05 14:48:49 -08001964 break;
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001965 }
1966 }
1967 return writeSuccess;
1968 }
1969
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001970 /**
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001971 * Read the device information from Settings.Secure and populate the
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08001972 * {@link #mProfileToConnectableDevicesMap}
1973 *
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001974 * Device MAC addresses are written to Settings.Secure delimited by a ','.
1975 * Ex: android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICES: xx:xx:xx:xx:xx:xx,yy:yy:yy:yy:yy
1976 * denotes that two devices with addresses xx:xx:xx:xx:xx:xx & yy:yy:yy:yy:yy:yy were connected
1977 * as phones (in HFP and PBAP profiles) the last time this user was logged in.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001978 *
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001979 * @return - true if the read was successful, false if 1. BT Adapter not enabled 2. No prior
1980 * bonded devices 3. No information stored in Settings for this user.
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001981 */
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001982 public synchronized boolean readAndRebuildDeviceMapFromSettings() {
1983 List<String> deviceList;
1984 String devices = null;
1985 // Create and initialize mProfileToConnectableDevicesMap if needed.
1986 initDeviceMap();
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -08001987 if (mBluetoothAdapter != null) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001988 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07001989 Log.d(TAG,
1990 "Number of Bonded devices:" + mBluetoothAdapter.getBondedDevices().size());
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001991 }
Ram Periathiruvadi49d5a5a2017-02-17 18:50:09 -08001992 if (mBluetoothAdapter.getBondedDevices().isEmpty()) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08001993 if (DBG) {
1994 Log.d(TAG, "No Bonded Devices available. Quit rebuilding");
1995 }
1996 return false;
1997 }
1998 }
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07001999 if (!mAllowReadWriteToSettings) {
Ram Periathiruvadi83ee9012017-07-18 19:21:06 -07002000 return false;
2001 }
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002002 // Read from Settings.Secure for the current user. There are 3 keys 1 each for Phone
2003 // (HFP & PBAP), 1 for Music (A2DP) and 1 for Messaging device (MAP)
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002004 for (Integer profile : mProfilesToConnect) {
2005 switch (profile) {
2006 case BluetoothProfile.A2DP_SINK:
2007 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Joseph Pirozzoffb78982018-10-31 08:28:46 -07002008 KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES, mUserId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002009 break;
2010 case BluetoothProfile.PBAP_CLIENT:
2011 // fall through
2012 case BluetoothProfile.HEADSET_CLIENT:
2013 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Joseph Pirozzoffb78982018-10-31 08:28:46 -07002014 KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES, mUserId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002015 break;
2016 case BluetoothProfile.MAP_CLIENT:
2017 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Joseph Pirozzoffb78982018-10-31 08:28:46 -07002018 KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES, mUserId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002019 break;
Joseph Pirozzo99aeba92018-02-05 14:48:49 -08002020 case BluetoothProfile.PAN:
2021 devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Joseph Pirozzoffb78982018-10-31 08:28:46 -07002022 KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES, mUserId);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002023 default:
2024 Log.e(TAG, "Unexpected profile");
2025 break;
2026 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002027
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002028 if (devices == null) {
2029 if (DBG) {
2030 Log.d(TAG, "No device information stored in Settings");
2031 }
2032 return false;
2033 }
2034 if (DBG) {
Ram Periathiruvadia048c0a2017-05-09 07:35:03 -07002035 Log.d(TAG, "Devices in Settings: " + devices);
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002036 }
2037 // Get a list of Device Mac Addresses from the value
2038 deviceList = Arrays.asList(devices.split(SETTINGS_DELIMITER));
2039 if (deviceList == null) {
2040 return false;
2041 }
2042 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002043 // Do we have a bonded device with this name? If so, get it and populate the device
2044 // map.
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002045 for (String address : deviceList) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002046 BluetoothDevice deviceToAdd = getBondedDeviceWithGivenName(address);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002047 if (deviceToAdd != null) {
2048 devicesInfo.addDeviceLocked(deviceToAdd);
2049 } else {
2050 if (DBG) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002051 Log.d(TAG, "No device with name " + address + " found in bonded devices");
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002052 }
2053 }
2054 }
2055 mProfileToConnectableDevicesMap.put(profile, devicesInfo);
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07002056 // Check to see if there are any primary or secondary devices for this profile and
2057 // update BluetoothDevicesInfo with the priority information.
2058 for (int priority : mPrioritiesSupported) {
2059 readAndTagDeviceWithPriorityFromSettings(profile, priority);
2060 }
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002061 }
Pavel Maltsevc9e86e82017-03-22 11:37:21 -07002062 return true;
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002063 }
2064
2065 /**
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07002066 * Read from Secure Settings if there are primary or secondary devices marked for this
2067 * Bluetooth profile. If there are tagged devices, update the BluetoothDevicesInfo so the
2068 * policy can prioritize those devices when making connection attempts.
2069 *
Ram Periathiruvadie011a402018-03-22 18:54:33 -07002070 * @param profile - Bluetooth Profile to check
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07002071 * @param priority - Priority to check
2072 */
2073 private void readAndTagDeviceWithPriorityFromSettings(int profile, int priority) {
2074 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
2075 if (devicesInfo == null) {
2076 return;
2077 }
2078 if (!mCarBluetoothService.isPriorityDevicePresent(profile, priority)) {
2079 // There is no device for this priority - either it hasn't been set or has been removed.
2080 // So check if the policy has a device associated with this priority and remove it.
2081 BluetoothDevice deviceToClear = devicesInfo.getBluetoothDeviceForPriorityLocked(
2082 priority);
2083 if (deviceToClear != null) {
2084 if (DBG) {
Vasu Norie5fb9112017-11-17 10:23:02 -08002085 Log.d(TAG, "Clearing priority for: " +
2086 Utils.getDeviceDebugInfo(deviceToClear));
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07002087 }
2088 devicesInfo.removeBluetoothDevicePriorityLocked(deviceToClear);
2089 }
2090 } else {
2091 // There is a device with the given priority for the given profile. Update the
2092 // policy's records.
2093 String deviceName = mCarBluetoothService.getDeviceNameWithPriority(profile,
2094 priority);
2095 if (deviceName != null) {
2096 BluetoothDevice bluetoothDevice = getBondedDeviceWithGivenName(deviceName);
2097 if (bluetoothDevice != null) {
2098 if (DBG) {
2099 Log.d(TAG, "Setting priority: " + priority + " for " + deviceName);
2100 }
2101 tagDeviceWithPriority(bluetoothDevice, profile, priority);
2102 }
2103 }
2104 }
2105 }
2106
2107 /**
2108 * Tag a Bluetooth device with priority - Primary or Secondary. This only updates the policy's
2109 * record (BluetoothDevicesInfo) of the priority information.
2110 *
2111 * @param device - BluetoothDevice to tag
2112 * @param profile - BluetoothProfile to tag
2113 * @param priority - Priority to tag with
2114 */
2115 @VisibleForTesting
2116 void tagDeviceWithPriority(BluetoothDevice device, int profile, int priority) {
2117 BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
2118 if (device != null) {
2119 if (DBG) {
2120 Log.d(TAG, "Profile: " + profile + " : " + device + " Priority: " + priority);
2121 }
2122 devicesInfo.setBluetoothDevicePriorityLocked(device, priority);
2123 }
2124 }
2125
2126 /**
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002127 * Given the device name, find the corresponding {@link BluetoothDevice} from the list of
2128 * Bonded devices.
2129 *
2130 * @param name Bluetooth Device name
2131 */
Ram Periathiruvadi76a84892017-07-27 18:10:35 -07002132 @Nullable
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002133 private BluetoothDevice getBondedDeviceWithGivenName(String name) {
2134 if (mBluetoothAdapter == null) {
2135 if (DBG) {
2136 Log.d(TAG, "Bluetooth Adapter Null");
2137 }
2138 return null;
2139 }
2140 if (name == null) {
2141 Log.w(TAG, "getBondedDeviceWithGivenName() Passing in a null name");
2142 return null;
2143 }
2144 if (DBG) {
2145 Log.d(TAG, "Looking for bonded device: " + name);
2146 }
2147 BluetoothDevice btDevice = null;
2148 Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
2149 for (BluetoothDevice bd : bondedDevices) {
Ram Periathiruvadiacb60242017-04-13 16:19:09 -07002150 if (name.equals(bd.getAddress())) {
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002151 btDevice = bd;
2152 break;
2153 }
2154 }
2155 return btDevice;
2156 }
2157
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08002158
2159 public void dump(PrintWriter writer) {
2160 writer.println("*BluetoothDeviceConnectionPolicy*");
2161 printDeviceMap(writer);
Ram Periathiruvadiee28c002017-02-07 21:35:01 -08002162 mBluetoothAutoConnectStateMachine.dump(writer);
Justin Paupore44985ba2019-01-30 18:53:41 -08002163 String inhibits;
Justin Paupore7b17ea62018-12-28 20:27:33 -08002164 synchronized (this) {
Justin Paupore44985ba2019-01-30 18:53:41 -08002165 inhibits = mProfileInhibits.keySet().toString();
Justin Paupore7b17ea62018-12-28 20:27:33 -08002166 }
Justin Paupore44985ba2019-01-30 18:53:41 -08002167 writer.println("Inhibited profiles: " + inhibits);
Ram Periathiruvadi7ed84182017-01-20 15:18:08 -08002168 }
2169}