Home | History | Annotate | Download | only in car
      1 /*
      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 
     17 package com.android.car;
     18 
     19 import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES;
     20 import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES;
     21 import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES;
     22 import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES;
     23 
     24 import android.annotation.Nullable;
     25 import android.app.ActivityManager;
     26 import android.bluetooth.BluetoothA2dpSink;
     27 import android.bluetooth.BluetoothAdapter;
     28 import android.bluetooth.BluetoothDevice;
     29 import android.bluetooth.BluetoothHeadsetClient;
     30 import android.bluetooth.BluetoothMapClient;
     31 import android.bluetooth.BluetoothPan;
     32 import android.bluetooth.BluetoothPbapClient;
     33 import android.bluetooth.BluetoothProfile;
     34 import android.bluetooth.BluetoothUuid;
     35 import android.car.CarBluetoothManager;
     36 import android.car.ICarBluetoothUserService;
     37 import android.car.ICarUserService;
     38 import android.car.drivingstate.CarUxRestrictions;
     39 import android.car.drivingstate.ICarUxRestrictionsChangeListener;
     40 import android.car.hardware.CarPropertyValue;
     41 import android.car.hardware.property.CarPropertyEvent;
     42 import android.car.hardware.property.ICarPropertyEventListener;
     43 import android.content.BroadcastReceiver;
     44 import android.content.Context;
     45 import android.content.Intent;
     46 import android.content.IntentFilter;
     47 import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState;
     48 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
     49 import android.os.ParcelUuid;
     50 import android.os.Parcelable;
     51 import android.os.RemoteException;
     52 import android.os.UserHandle;
     53 import android.provider.Settings;
     54 import android.util.Log;
     55 
     56 import com.android.internal.annotations.VisibleForTesting;
     57 
     58 import java.io.PrintWriter;
     59 import java.util.ArrayList;
     60 import java.util.Arrays;
     61 import java.util.HashMap;
     62 import java.util.HashSet;
     63 import java.util.List;
     64 import java.util.Map;
     65 import java.util.Set;
     66 import java.util.concurrent.locks.ReentrantLock;
     67 
     68 
     69 /**
     70  * A Bluetooth Device Connection policy that is specific to the use cases of a Car.  A car's
     71  * bluetooth capabilities in terms of the profiles it supports and its use cases are unique.
     72  * Hence the CarService manages the policy that drives when and what to connect to.
     73  *
     74  * When to connect:
     75  * The policy can be configured to listen to various vehicle events that are appropriate to
     76  * trigger a connection attempt.  Signals like door unlock/open, ignition state changes indicate
     77  * user entry and there by attempt to connect to their devices. This removes the need for the user
     78  * to manually connect his device everytime they get in a car.
     79  *
     80  * Which device to connect:
     81  * The policy also keeps track of the {Profile : DevicesThatCanConnectOnTheProfile} and when
     82  * it is time to connect, picks the device that is appropriate and available.
     83  * For every profile, the policy attempts to connect to the last connected device first. The policy
     84  * maintains a list of connect-able devices for every profile, in the order of how recently they
     85  * connected.  The device that successfully connects on a profile is moved to the top of the list
     86  * of devices for that profile, so the next time a connection attempt is made, the policy starts
     87  * with the last connected device first.
     88  */
     89 
     90 public class BluetoothDeviceConnectionPolicy {
     91     private static final String TAG = "BTDevConnectionPolicy";
     92     private static final String SETTINGS_DELIMITER = ",";
     93     private static final boolean DBG = Utils.DBG;
     94     private final Context mContext;
     95     private boolean mInitialized = false;
     96     private boolean mUserSpecificInfoInitialized = false;
     97     private final Object mSetupLock = new Object();
     98 
     99     // The main data structure that holds on to the {profile:list of known and connectible devices}
    100     private HashMap<Integer, BluetoothDevicesInfo> mProfileToConnectableDevicesMap;
    101 
    102     /// TODO(vnori): fix this. b/70029056
    103     private static final int NUM_SUPPORTED_PHONE_CONNECTIONS = 4; // num of HFP and PBAP connections
    104     private static final int NUM_SUPPORTED_MSG_CONNECTIONS = 4; // num of MAP connections
    105     private static final int NUM_SUPPORTED_MUSIC_CONNECTIONS = 1; // num of A2DP connections
    106     private static final int NUM_SUPPORTED_NETWORK_CONNECTIONS = 1; // num of PAN connections
    107     private Map<Integer, Integer> mNumSupportedActiveConnections;
    108 
    109     private BluetoothAutoConnectStateMachine mBluetoothAutoConnectStateMachine;
    110     private final BluetoothAdapter mBluetoothAdapter;
    111     private BroadcastReceiver mBluetoothBroadcastReceiver;
    112 
    113     private ICarUserService mCarUserService;
    114     private PerUserCarServiceHelper mUserServiceHelper;
    115     private ICarBluetoothUserService mCarBluetoothUserService;
    116     private ReentrantLock mCarUserServiceAccessLock;
    117 
    118     // Events that are listened to for triggering an auto-connect:
    119     //  Door unlock and ignition switch ON come from Car Property Service
    120     private final CarPropertyService mCarPropertyService;
    121     private final CarPropertyListener mPropertyEventListener;
    122 
    123     // PerUserCarService related listeners
    124     private final UserServiceConnectionCallback mServiceCallback;
    125 
    126     // Car Bluetooth Priority Settings Manager
    127     private final CarBluetoothService mCarBluetoothService;
    128 
    129     // Car UX Restrictions Manager Service to know when user restrictions are in place
    130     private final CarUxRestrictionsManagerService mUxRService;
    131     private final CarUxRServiceListener mUxRListener;
    132     // Fast Pair Provider to allow discovery of new phones
    133     private final FastPairProvider mFastPairProvider;
    134 
    135     // The Bluetooth profiles that the CarService will try to auto-connect on.
    136     private final List<Integer> mProfilesToConnect;
    137     private final List<Integer> mPrioritiesSupported;
    138     private static final int MAX_CONNECT_RETRIES = 1;
    139     private static final int PROFILE_NOT_AVAILABLE = -1;
    140 
    141     // Device & Profile currently being connected on
    142     private ConnectionParams mConnectionInFlight;
    143     // Allow write to Settings.Secure
    144     private boolean mAllowReadWriteToSettings = true;
    145     // Maintain a list of Paired devices which haven't connected on any profiles yet.
    146     private Set<BluetoothDevice> mPairedButUnconnectedDevices = new HashSet<>();
    147 
    148     public static BluetoothDeviceConnectionPolicy create(Context context,
    149             CarPropertyService carPropertyService, PerUserCarServiceHelper userServiceHelper,
    150             CarUxRestrictionsManagerService uxrService, CarBluetoothService bluetoothService) {
    151         return new BluetoothDeviceConnectionPolicy(context, carPropertyService, userServiceHelper,
    152                 uxrService, bluetoothService);
    153     }
    154 
    155     private BluetoothDeviceConnectionPolicy(Context context, CarPropertyService carPropertyService,
    156             PerUserCarServiceHelper userServiceHelper, CarUxRestrictionsManagerService uxrService,
    157             CarBluetoothService bluetoothService) {
    158         mContext = context;
    159         mCarPropertyService = carPropertyService;
    160         mUserServiceHelper = userServiceHelper;
    161         mUxRService = uxrService;
    162         mCarBluetoothService = bluetoothService;
    163         mCarUserServiceAccessLock = new ReentrantLock();
    164         mProfilesToConnect = Arrays.asList(
    165                 BluetoothProfile.HEADSET_CLIENT, BluetoothProfile.A2DP_SINK,
    166                 BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_CLIENT, BluetoothProfile.PAN);
    167         mPrioritiesSupported = Arrays.asList(
    168                 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0,
    169                 CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1
    170         );
    171         // mNumSupportedActiveConnections is a HashMap of mProfilesToConnect and the number of
    172         // connections each profile supports currently.
    173         mNumSupportedActiveConnections = new HashMap<>(mProfilesToConnect.size());
    174         for (Integer profile : mProfilesToConnect) {
    175             switch (profile) {
    176                 case BluetoothProfile.HEADSET_CLIENT:
    177                     mNumSupportedActiveConnections.put(BluetoothProfile.HEADSET_CLIENT,
    178                             NUM_SUPPORTED_PHONE_CONNECTIONS);
    179                     break;
    180                 case BluetoothProfile.PBAP_CLIENT:
    181                     mNumSupportedActiveConnections.put(BluetoothProfile.PBAP_CLIENT,
    182                             NUM_SUPPORTED_PHONE_CONNECTIONS);
    183                     break;
    184                 case BluetoothProfile.A2DP_SINK:
    185                     mNumSupportedActiveConnections.put(BluetoothProfile.A2DP_SINK,
    186                             NUM_SUPPORTED_MUSIC_CONNECTIONS);
    187                     break;
    188                 case BluetoothProfile.MAP_CLIENT:
    189                     mNumSupportedActiveConnections.put(BluetoothProfile.MAP_CLIENT,
    190                             NUM_SUPPORTED_MSG_CONNECTIONS);
    191                     break;
    192                 case BluetoothProfile.PAN:
    193                     mNumSupportedActiveConnections.put(BluetoothProfile.PAN,
    194                             NUM_SUPPORTED_NETWORK_CONNECTIONS);
    195                     break;
    196             }
    197         }
    198 
    199         // Listen to events for triggering auto connect
    200         mPropertyEventListener = new CarPropertyListener();
    201         // Listen to UX Restrictions to know when to enable fast-pairing
    202         mUxRListener = new CarUxRServiceListener();
    203         // Listen to User switching to connect to per User device.
    204         mServiceCallback = new UserServiceConnectionCallback();
    205         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    206         if (mBluetoothAdapter == null) {
    207             Log.w(TAG, "No Bluetooth Adapter Available");
    208         }
    209         mFastPairProvider = new FastPairProvider(mContext);
    210     }
    211 
    212     /**
    213      * ConnectionParams - parameters/objects relevant to the bluetooth connection calls.
    214      * This encapsulates the information that is passed around across different methods in the
    215      * policy. Contains the bluetooth device {@link BluetoothDevice} and the list of profiles that
    216      * we want that device to connect on.
    217      * Used as the currency that methods use to talk to each other in the policy.
    218      */
    219     public static class ConnectionParams {
    220         private BluetoothDevice mBluetoothDevice;
    221         private Integer mBluetoothProfile;
    222 
    223         public ConnectionParams() {
    224             // default constructor
    225         }
    226 
    227         public ConnectionParams(Integer profile) {
    228             mBluetoothProfile = profile;
    229         }
    230 
    231         public ConnectionParams(Integer profile, BluetoothDevice device) {
    232             mBluetoothProfile = profile;
    233             mBluetoothDevice = device;
    234         }
    235 
    236         // getters & Setters
    237         public void setBluetoothDevice(BluetoothDevice device) {
    238             mBluetoothDevice = device;
    239         }
    240 
    241         public void setBluetoothProfile(Integer profile) {
    242             mBluetoothProfile = profile;
    243         }
    244 
    245         public BluetoothDevice getBluetoothDevice() {
    246             return mBluetoothDevice;
    247         }
    248 
    249         public Integer getBluetoothProfile() {
    250             return mBluetoothProfile;
    251         }
    252     }
    253 
    254     /**
    255      * BluetoothBroadcastReceiver receives the bluetooth related intents that are relevant to
    256      * connection
    257      * and bonding state changes.  Reports the information to the {@link
    258      * BluetoothDeviceConnectionPolicy}
    259      * for it update its status.
    260      */
    261     public class BluetoothBroadcastReceiver extends BroadcastReceiver {
    262         @Override
    263         public void onReceive(Context context, Intent intent) {
    264             String action = intent.getAction();
    265             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    266             if (DBG) {
    267                 if (device != null) {
    268                     Log.d(TAG, "Received Intent for device: " + device + " " + action);
    269                 } else {
    270                     Log.d(TAG, "Received Intent no device: " + action);
    271                 }
    272             }
    273             ConnectionParams connectParams;
    274             if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
    275                 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
    276                         BluetoothDevice.ERROR);
    277                 updateBondState(device, bondState);
    278 
    279             } else if (BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
    280                 connectParams = new ConnectionParams(BluetoothProfile.A2DP_SINK, device);
    281                 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
    282                         BluetoothProfile.STATE_DISCONNECTED);
    283                 notifyConnectionStatus(connectParams, currState);
    284 
    285             } else if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
    286                 connectParams = new ConnectionParams(BluetoothProfile.HEADSET_CLIENT, device);
    287                 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
    288                         BluetoothProfile.STATE_DISCONNECTED);
    289                 notifyConnectionStatus(connectParams, currState);
    290 
    291             } else if (BluetoothPan.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
    292                 connectParams = new ConnectionParams(BluetoothProfile.PAN, device);
    293                 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
    294                         BluetoothProfile.STATE_DISCONNECTED);
    295                 notifyConnectionStatus(connectParams, currState);
    296 
    297             } else if (BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
    298                 connectParams = new ConnectionParams(BluetoothProfile.PBAP_CLIENT, device);
    299                 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
    300                         BluetoothProfile.STATE_DISCONNECTED);
    301                 notifyConnectionStatus(connectParams, currState);
    302 
    303             } else if (BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
    304                 connectParams = new ConnectionParams(BluetoothProfile.MAP_CLIENT, device);
    305                 int currState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
    306                         BluetoothProfile.STATE_DISCONNECTED);
    307                 notifyConnectionStatus(connectParams, currState);
    308 
    309             } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
    310                 int currState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
    311                 if (DBG) {
    312                     Log.d(TAG, "Bluetooth Adapter State: " + currState);
    313                 }
    314                 if (currState == BluetoothAdapter.STATE_ON) {
    315                     // Read from Settings which devices to connect to and populate
    316                     // mProfileToConnectableDevicesMap
    317                     readAndRebuildDeviceMapFromSettings();
    318                     initiateConnection();
    319                 } else if (currState == BluetoothAdapter.STATE_OFF) {
    320                     // Write currently connected device snapshot to file.
    321                     writeDeviceInfoToSettings();
    322                     resetBluetoothDevicesConnectionInfo();
    323                 }
    324             } else if (BluetoothDevice.ACTION_UUID.equals(action)) {
    325                 // Received during pairing with the UUIDs of the Bluetooth profiles supported by
    326                 // the remote device.
    327                 if (DBG) {
    328                     Log.d(TAG, "Received UUID intent for device " + device);
    329                 }
    330                 Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
    331                 if (uuids != null) {
    332                     ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
    333                     for (int i = 0; i < uuidsToSend.length; i++) {
    334                         uuidsToSend[i] = (ParcelUuid) uuids[i];
    335                     }
    336                     setProfilePriorities(device, uuidsToSend, BluetoothProfile.PRIORITY_ON);
    337                 }
    338 
    339             }
    340         }
    341     }
    342 
    343     /**
    344      * Set priority for the Bluetooth profiles.
    345      *
    346      * The Bluetooth service stores the priority of a Bluetooth profile per device as a key value
    347      * pair - BluetoothProfile_device:<Priority>.
    348      * When we pair a device from the Settings App, the expected behavior is for the app to connect
    349      * on all appropriate profiles after successful pairing automatically, without the user having
    350      * to explicitly issue a connect. The settings app checks for the priority of the device from
    351      * the above key-value pair and if the priority is set to PRIORITY_OFF or PRIORITY_UNDEFINED,
    352      * the settings app will stop with just pairing and not connect.
    353      * This scenario will happen when we pair a device, then unpair it and then pair it again.  When
    354      * the device is unpaired, the BT stack sets the priority for that device to PRIORITY_UNDEFINED
    355      * ( as a way of resetting).  So, the next time the same device is paired, the Settings app will
    356      * stop with just pairing and not connect as explained above. Here, we register to receive the
    357      * ACTION_UUID intent, which will broadcast the UUIDs corresponding to the profiles supported by
    358      * the remote device which is successfully paired and we turn on the priority so when the
    359      * Settings app tries to check before connecting, the priority is set to the expected value.
    360      *
    361      * @param device   - Remote Bluetooth device
    362      * @param uuids    - UUIDs of the Bluetooth Profiles supported by the remote device
    363      * @param priority - priority to set
    364      */
    365     private void setProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids, int priority) {
    366         // need the BluetoothProfile proxy to be able to call the setPriority API
    367         if (mCarBluetoothUserService == null) {
    368             mCarBluetoothUserService = setupBluetoothUserService();
    369         }
    370         if (mCarBluetoothUserService != null) {
    371             for (Integer profile : mProfilesToConnect) {
    372                 setBluetoothProfilePriorityIfUuidFound(uuids, profile, device, priority);
    373             }
    374         }
    375     }
    376 
    377     private void setBluetoothProfilePriorityIfUuidFound(ParcelUuid[] uuids, int profile,
    378             BluetoothDevice device, int priority) {
    379         if (mCarBluetoothUserService == null || device == null) {
    380             return;
    381         }
    382         // Build a list of UUIDs that represent a profile.
    383         List<ParcelUuid> uuidsToCheck = new ArrayList<>();
    384         switch (profile) {
    385             case BluetoothProfile.A2DP_SINK:
    386                 uuidsToCheck.add(BluetoothUuid.AudioSource);
    387                 break;
    388             case BluetoothProfile.HEADSET_CLIENT:
    389                 uuidsToCheck.add(BluetoothUuid.Handsfree_AG);
    390                 uuidsToCheck.add(BluetoothUuid.HSP_AG);
    391                 break;
    392             case BluetoothProfile.PBAP_CLIENT:
    393                 uuidsToCheck.add(BluetoothUuid.PBAP_PSE);
    394                 break;
    395             case BluetoothProfile.MAP_CLIENT:
    396                 uuidsToCheck.add(BluetoothUuid.MAS);
    397                 break;
    398             case BluetoothProfile.PAN:
    399                 uuidsToCheck.add(BluetoothUuid.PANU);
    400                 break;
    401         }
    402 
    403         for (ParcelUuid uuid : uuidsToCheck) {
    404             if (BluetoothUuid.isUuidPresent(uuids, uuid)) {
    405                 try {
    406                     mCarBluetoothUserService.setProfilePriority(profile, device, priority);
    407                 } catch (RemoteException e) {
    408                     Log.e(TAG, "RemoteException calling setProfilePriority");
    409                 }
    410                 // if any one of the uuid in uuidsTocheck is present, set the priority and break
    411                 break;
    412             }
    413         }
    414     }
    415 
    416     /**
    417      * Cleanup state and reinitialize whenever we connect to the PerUserCarService.
    418      * This happens in init() and whenever the PerUserCarService is restarted on User Switch Events
    419      */
    420     @VisibleForTesting
    421     class UserServiceConnectionCallback implements PerUserCarServiceHelper.ServiceCallback {
    422         @Override
    423         public void onServiceConnected(ICarUserService carUserService) {
    424             if (mCarUserServiceAccessLock != null) {
    425                 mCarUserServiceAccessLock.lock();
    426                 try {
    427                     mCarUserService = carUserService;
    428                 } finally {
    429                     mCarUserServiceAccessLock.unlock();
    430                 }
    431             }
    432             if (DBG) {
    433                 Log.d(TAG, "Connected to PerUserCarService");
    434             }
    435             // Get the BluetoothUserService and also setup the Bluetooth Connection Proxy for
    436             // all profiles.
    437             mCarBluetoothUserService = setupBluetoothUserService();
    438             // re-initialize for current user.
    439             initializeUserSpecificInfo();
    440         }
    441 
    442         @Override
    443         public void onPreUnbind() {
    444             if (DBG) {
    445                 Log.d(TAG, "Before Unbinding from UserService");
    446             }
    447             try {
    448                 if (mCarBluetoothUserService != null) {
    449                     mCarBluetoothUserService.closeBluetoothConnectionProxy();
    450                 }
    451             } catch (RemoteException e) {
    452                 Log.e(TAG,
    453                         "Remote Exception during closeBluetoothConnectionProxy(): "
    454                                 + e.getMessage());
    455             }
    456             // Clean up information related to user who went background.
    457             cleanupUserSpecificInfo();
    458         }
    459 
    460         @Override
    461         public void onServiceDisconnected() {
    462             if (DBG) {
    463                 Log.d(TAG, "Disconnected from PerUserCarService");
    464             }
    465             if (mCarUserServiceAccessLock != null) {
    466                 mCarUserServiceAccessLock.lock();
    467                 try {
    468                     mCarBluetoothUserService = null;
    469                     mCarUserService = null;
    470                 } finally {
    471                     mCarUserServiceAccessLock.unlock();
    472                 }
    473             }
    474         }
    475     }
    476 
    477     /**
    478      * Gets the Per User Car Bluetooth Service (ICarBluetoothService) from the PerUserCarService
    479      * which acts as a top level Service running in the current user context.
    480      * Also sets up the connection proxy objects required to communicate with the Bluetooth
    481      * Profile Services.
    482      *
    483      * @return ICarBluetoothUserService running in current user
    484      */
    485     private ICarBluetoothUserService setupBluetoothUserService() {
    486         ICarBluetoothUserService carBluetoothUserService = null;
    487         if (mCarUserService != null) {
    488             try {
    489                 carBluetoothUserService = mCarUserService.getBluetoothUserService();
    490                 if (carBluetoothUserService != null) {
    491                     if (DBG) {
    492                         Log.d(TAG, "Got CarBTUsrSvc");
    493                     }
    494                     carBluetoothUserService.setupBluetoothConnectionProxy();
    495                 }
    496             } catch (RemoteException e) {
    497                 Log.e(TAG, "Remote Service Exception on ServiceConnection Callback: "
    498                         + e.getMessage());
    499             }
    500         } else {
    501             if (DBG) {
    502                 Log.d(TAG, "PerUserCarService not connected");
    503             }
    504         }
    505         return carBluetoothUserService;
    506     }
    507 
    508     /**
    509      * Setup the Bluetooth profile service connections and Vehicle Event listeners.
    510      * and start the state machine -{@link BluetoothAutoConnectStateMachine}
    511      */
    512     public synchronized void init() {
    513         if (DBG) {
    514             Log.d(TAG, "init()");
    515         }
    516         // Initialize information specific to current user.
    517         initializeUserSpecificInfo();
    518         // Listen to various events coming from the vehicle.
    519         setupEventListenersLocked();
    520         mInitialized = true;
    521     }
    522 
    523     /**
    524      * Setup and initialize information that is specific per User account, which involves:
    525      * 1. Reading the list of devices to connect for current user and initialize the deviceMap
    526      * with that information.
    527      * 2. Register a BroadcastReceiver for bluetooth related events for the current user.
    528      * 3. Start and bind to {@link PerUserCarService} as current user.
    529      * 4. Start the {@link BluetoothAutoConnectStateMachine}
    530      */
    531     private void initializeUserSpecificInfo() {
    532         synchronized (mSetupLock) {
    533             if (DBG) {
    534                 Log.d(TAG, "initializeUserSpecificInfo()");
    535             }
    536             if (mUserSpecificInfoInitialized) {
    537                 if (DBG) {
    538                     Log.d(TAG, "Already Initialized");
    539                 }
    540                 return;
    541             }
    542             mBluetoothAutoConnectStateMachine = BluetoothAutoConnectStateMachine.make(this);
    543             readAndRebuildDeviceMapFromSettings();
    544             setupBluetoothEventsIntentFilterLocked();
    545 
    546             mConnectionInFlight = new ConnectionParams();
    547             mUserSpecificInfoInitialized = true;
    548         }
    549     }
    550 
    551     /**
    552      * Setting up the Intent filter for Bluetooth related broadcasts
    553      * This includes knowing when the
    554      * 1. Bluetooth Adapter turned on/off
    555      * 2. Bonding State of a device changes
    556      * 3. A specific profile's connection state changes.
    557      */
    558     private void setupBluetoothEventsIntentFilterLocked() {
    559         mBluetoothBroadcastReceiver = new BluetoothBroadcastReceiver();
    560         IntentFilter profileFilter = new IntentFilter();
    561         profileFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
    562         profileFilter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
    563         profileFilter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
    564         profileFilter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
    565         profileFilter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
    566         profileFilter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
    567         profileFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    568         profileFilter.addAction(BluetoothDevice.ACTION_UUID);
    569         if (mContext != null) {
    570             mContext.registerReceiverAsUser(mBluetoothBroadcastReceiver, UserHandle.CURRENT,
    571                     profileFilter, null, null);
    572         }
    573     }
    574 
    575     /**
    576      * Initialize the {@link #mProfileToConnectableDevicesMap}.
    577      * {@link #mProfileToConnectableDevicesMap} stores the profile:DeviceList information.  This
    578      * method retrieves it from persistent memory.
    579      */
    580     private synchronized void initDeviceMap() {
    581         if (mProfileToConnectableDevicesMap == null) {
    582             mProfileToConnectableDevicesMap = new HashMap<>();
    583             for (Integer profile : mProfilesToConnect) {
    584                 // Build the BluetoothDevicesInfo for this profile.
    585                 BluetoothDevicesInfo devicesInfo = new BluetoothDevicesInfo(profile,
    586                         mNumSupportedActiveConnections.get(profile));
    587                 mProfileToConnectableDevicesMap.put(profile, devicesInfo);
    588             }
    589             if (DBG) {
    590                 Log.d(TAG, "Created a new empty Device Map");
    591             }
    592         }
    593     }
    594 
    595     /**
    596      * Setting up Listeners to the various events we are interested in listening to for initiating
    597      * Bluetooth connection attempts.
    598      */
    599     private void setupEventListenersLocked() {
    600         // Setting up a listener for events from CarPropertyService
    601         // For now, we listen to door unlock signal and Ignition state START coming from
    602         // {@link CarPropertyService}
    603         mCarPropertyService.registerListener(VehicleProperty.DOOR_LOCK, 0, mPropertyEventListener);
    604         mCarPropertyService.registerListener(VehicleProperty.IGNITION_STATE, 0,
    605                 mPropertyEventListener);
    606         // Get Current restrictions and handle them
    607         handleUxRestrictionsChanged(mUxRService.getCurrentUxRestrictions());
    608         // Register for future changes to the UxRestrictions
    609         mUxRService.registerUxRestrictionsChangeListener(mUxRListener);
    610         mUserServiceHelper.registerServiceCallback(mServiceCallback);
    611     }
    612 
    613     /**
    614      * Handles events coming in from the {@link CarPropertyService}
    615      * The events that can trigger Bluetooth Scanning from CarPropertyService are Door Unlock and
    616      * Igntion START.  Upon an event of interest, initiate a connection attempt by calling
    617      * the policy {@link BluetoothDeviceConnectionPolicy}
    618      */
    619     @VisibleForTesting
    620     class CarPropertyListener extends ICarPropertyEventListener.Stub {
    621         @Override
    622         public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
    623             for (CarPropertyEvent event : events) {
    624                 if (DBG) {
    625                     Log.d(TAG, "Cabin change Event : " + event.getEventType());
    626                 }
    627                 CarPropertyValue value = event.getCarPropertyValue();
    628                 Object o = value.getValue();
    629 
    630                 switch (value.getPropertyId()) {
    631                     case VehicleProperty.DOOR_LOCK:
    632                         if (o instanceof Boolean) {
    633                             Boolean locked = (Boolean) o;
    634                             if (DBG) {
    635                                 Log.d(TAG, "Door Lock: " + locked);
    636                             }
    637                             // Attempting a connection only on a door unlock
    638                             if (!locked) {
    639                                 initiateConnection();
    640                             }
    641                         }
    642                         break;
    643                     case VehicleProperty.IGNITION_STATE:
    644                         if (o instanceof Integer) {
    645                             Integer state = (Integer) o;
    646                             if (DBG) {
    647                                 Log.d(TAG, "Sensor value : " + state);
    648                             }
    649                             // Attempting a connection only on IgntionState START
    650                             if (state == VehicleIgnitionState.START) {
    651                                 initiateConnection();
    652                             }
    653                         }
    654                         break;
    655                 }
    656             }
    657         }
    658     }
    659 
    660     private class CarUxRServiceListener extends ICarUxRestrictionsChangeListener.Stub {
    661         @Override
    662         public void onUxRestrictionsChanged(CarUxRestrictions restrictions) {
    663             handleUxRestrictionsChanged(restrictions);
    664         }
    665     }
    666 
    667     private void handleUxRestrictionsChanged(CarUxRestrictions restrictions) {
    668         if (restrictions == null) {
    669             return;
    670         }
    671         if ((restrictions.getActiveRestrictions() & CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP)
    672                 == CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP) {
    673             mFastPairProvider.stopAdvertising();
    674         } else {
    675             mFastPairProvider.startAdvertising();
    676         }
    677     }
    678 
    679     /**
    680      * Clean up slate. Close the Bluetooth profile service connections and quit the state machine -
    681      * {@link BluetoothAutoConnectStateMachine}
    682      */
    683     public synchronized void release() {
    684         if (DBG) {
    685             Log.d(TAG, "release()");
    686         }
    687         mInitialized = false;
    688         writeDeviceInfoToSettings();
    689         cleanupUserSpecificInfo();
    690         closeEventListeners();
    691     }
    692 
    693     /**
    694      * Clean up information related to user who went background.
    695      */
    696     private void cleanupUserSpecificInfo() {
    697         synchronized (mSetupLock) {
    698             if (DBG) {
    699                 Log.d(TAG, "cleanupUserSpecificInfo()");
    700             }
    701             if (!mUserSpecificInfoInitialized) {
    702                 if (DBG) {
    703                     Log.d(TAG, "User specific Info Not initialized..Not cleaning up");
    704                 }
    705                 return;
    706             }
    707             mUserSpecificInfoInitialized = false;
    708             // quit the state machine
    709             mBluetoothAutoConnectStateMachine.doQuit();
    710             mProfileToConnectableDevicesMap = null;
    711             mConnectionInFlight = null;
    712             if (mBluetoothBroadcastReceiver != null) {
    713                 if (mContext != null) {
    714                     mContext.unregisterReceiver(mBluetoothBroadcastReceiver);
    715                 }
    716                 mBluetoothBroadcastReceiver = null;
    717             }
    718         }
    719     }
    720 
    721     /**
    722      * Unregister the listeners to the various Vehicle events coming from other parts of the
    723      * CarService
    724      */
    725     private void closeEventListeners() {
    726         if (DBG) {
    727             Log.d(TAG, "closeEventListeners()");
    728         }
    729         mCarPropertyService.unregisterListener(VehicleProperty.DOOR_LOCK, mPropertyEventListener);
    730         mCarPropertyService.unregisterListener(VehicleProperty.IGNITION_STATE,
    731                 mPropertyEventListener);
    732         mUserServiceHelper.unregisterServiceCallback(mServiceCallback);
    733     }
    734 
    735     /**
    736      * Resets the {@link BluetoothDevicesInfo#mConnectionInfo} of all the profiles to start from
    737      * a clean slate.  The ConnectionInfo has all the book keeping information regarding the state
    738      * of connection attempts - like which device in the device list for the profile is the next
    739      * to try connecting etc.
    740      * This method does not clear the {@link BluetoothDevicesInfo#mDeviceInfoList} like the {@link
    741      * #resetProfileToConnectableDevicesMap()} method does.
    742      */
    743     private synchronized void resetBluetoothDevicesConnectionInfo() {
    744         if (DBG) {
    745             Log.d(TAG, "Resetting ConnectionInfo for all profiles");
    746         }
    747         for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
    748             devInfo.resetConnectionInfoLocked();
    749         }
    750     }
    751 
    752     @VisibleForTesting
    753     BroadcastReceiver getBluetoothBroadcastReceiver() {
    754         return mBluetoothBroadcastReceiver;
    755     }
    756 
    757     @VisibleForTesting
    758     UserServiceConnectionCallback getServiceCallback() {
    759         return mServiceCallback;
    760     }
    761 
    762     @VisibleForTesting
    763     CarPropertyListener getCarPropertyListener() {
    764         return mPropertyEventListener;
    765     }
    766 
    767     @VisibleForTesting
    768     synchronized void setAllowReadWriteToSettings(boolean allowWrite) {
    769         mAllowReadWriteToSettings = allowWrite;
    770     }
    771 
    772     @VisibleForTesting
    773     BluetoothDevicesInfo getBluetoothDevicesInfo(int profile) {
    774         return mProfileToConnectableDevicesMap.get(profile);
    775     }
    776 
    777     @VisibleForTesting
    778     String toDebugString() {
    779         StringBuilder buf = new StringBuilder();
    780         for (Integer profile : mProfileToConnectableDevicesMap.keySet()) {
    781             BluetoothDevicesInfo info = mProfileToConnectableDevicesMap.get(profile);
    782             buf.append(" \n**** profile = " + profile);
    783             buf.append(", " + info.toDebugString());
    784         }
    785         return buf.toString();
    786     }
    787 
    788     /**
    789      * Resets the {@link #mProfileToConnectableDevicesMap} to a clean and empty slate.
    790      */
    791     public synchronized void resetProfileToConnectableDevicesMap() {
    792         if (DBG) {
    793             Log.d(TAG, "Resetting the mProfilesToConnectableDevicesMap");
    794         }
    795         for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
    796             devInfo.resetDeviceListLocked();
    797         }
    798     }
    799 
    800     /**
    801      * Returns the list of profiles that the Autoconnection policy attempts to connect on
    802      *
    803      * @return profile list.
    804      */
    805     public synchronized List<Integer> getProfilesToConnect() {
    806         return mProfilesToConnect;
    807     }
    808 
    809     /**
    810      * Add a new Profile to the list of To Be Connected profiles.
    811      *
    812      * @param profile - ProfileInfo of the new profile to be added.
    813      */
    814     public synchronized void addProfile(Integer profile) {
    815         mProfilesToConnect.add(profile);
    816     }
    817 
    818     /**
    819      * Add or remove a device based on the bonding state change.
    820      *
    821      * @param device    - device to add/remove
    822      * @param bondState - current bonding state
    823      */
    824     private void updateBondState(BluetoothDevice device, int bondState) {
    825         if (device == null) {
    826             Log.e(TAG, "updateBondState: device Null");
    827             return;
    828         }
    829         if (DBG) {
    830             Log.d(TAG, "BondState :" + bondState + " Device: " + device);
    831         }
    832         // Bonded devices are added to a profile's device list after the device CONNECTS on the
    833         // profile.  When unpaired, we remove the device from all of the profiles' device list.
    834         if (bondState == BluetoothDevice.BOND_NONE) {
    835             for (Integer profile : mProfilesToConnect) {
    836                 if (DBG) {
    837                     Log.d(TAG, "Removing " + device + " from profile: " + profile);
    838                 }
    839                 removeDeviceFromProfile(device, profile);
    840             }
    841         } else if (bondState == BluetoothDevice.BOND_BONDED) {
    842             // A device just paired. When it connects on HFP profile, then immediately initiate
    843             // connections on PBAP and MAP. for now, maintain this fact in a data structure
    844             // to be looked up when HFP profile connects.
    845             mPairedButUnconnectedDevices.add(device);
    846         }
    847     }
    848 
    849     /**
    850      * Add a new device to the list of devices connect-able on the given profile
    851      *
    852      * @param device  - Bluetooth device to be added
    853      * @param profile - profile to add the device to.
    854      */
    855     private synchronized void addDeviceToProfile(BluetoothDevice device, Integer profile) {
    856         BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
    857         if (devInfo == null) {
    858             if (DBG) {
    859                 Log.d(TAG, "Creating devInfo for profile: " + profile);
    860             }
    861             devInfo = new BluetoothDevicesInfo(profile);
    862             mProfileToConnectableDevicesMap.put(profile, devInfo);
    863         }
    864         devInfo.addDeviceLocked(device);
    865     }
    866 
    867     /**
    868      * Remove the device from the list of devices connect-able on the gievn profile.
    869      *
    870      * @param device  - Bluetooth device to be removed
    871      * @param profile - profile to remove the device from
    872      */
    873     private synchronized void removeDeviceFromProfile(BluetoothDevice device, Integer profile) {
    874         BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
    875         if (devInfo != null) {
    876             devInfo.removeDeviceLocked(device);
    877         }
    878     }
    879 
    880     /**
    881      * Initiate a bluetooth connection.
    882      */
    883     private void initiateConnection() {
    884         // Make sure the bluetooth adapter is available & enabled.
    885         if (mBluetoothAdapter == null) {
    886             Log.w(TAG, "Bluetooth Adapter null");
    887             return;
    888         }
    889         if (mBluetoothAdapter.isEnabled()) {
    890             if (isDeviceMapEmpty()) {
    891                 if (DBG) {
    892                     Log.d(TAG, "Device Map is empty. Nothing to connect to");
    893                 }
    894                 return;
    895             }
    896             resetDeviceAvailableToConnect();
    897             if (DBG) {
    898                 Log.d(TAG, "initiateConnection() Reset Device Availability");
    899             }
    900             mBluetoothAutoConnectStateMachine.sendMessage(BluetoothAutoConnectStateMachine
    901                     .CONNECT);
    902         } else {
    903             if (DBG) {
    904                 Log.d(TAG, "Bluetooth Adapter not enabled.");
    905             }
    906         }
    907     }
    908 
    909     /**
    910      * Find an unconnected profile and find a device to connect on it.
    911      * Finds the appropriate device for the profile from the information available in
    912      * {@link #mProfileToConnectableDevicesMap}
    913      *
    914      * @return true - if we found a device to connect on for any of the {@link #mProfilesToConnect}
    915      * false - if we cannot find a device to connect to or if we are not ready to connect yet.
    916      */
    917     public synchronized boolean findDeviceToConnect() {
    918         if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()
    919                 || mProfileToConnectableDevicesMap == null || !mInitialized) {
    920             if (DBG) {
    921                 if (mProfileToConnectableDevicesMap == null) {
    922                     Log.d(TAG, "findDeviceToConnect(): Device Map null");
    923                 } else {
    924                     Log.d(TAG, "findDeviceToConnect(): BT Adapter not enabled");
    925                 }
    926             }
    927             return false;
    928         }
    929         boolean connectingToADevice = false;
    930         // Get the first unconnected profile that we can try to make a connection
    931         Integer nextProfile = getNextProfileToConnectLocked();
    932         // Keep going through the profiles until we find a device that we can connect to
    933         while (nextProfile != PROFILE_NOT_AVAILABLE) {
    934             if (DBG) {
    935                 Log.d(TAG, "connectToProfile(): " + nextProfile);
    936             }
    937             // find a device that is next in line for a connection attempt for that profile
    938             // and try connecting to it.
    939             connectingToADevice = connectToNextDeviceInQueueLocked(nextProfile);
    940             // If we found a device to connect, break out of the loop
    941             if (connectingToADevice) {
    942                 if (DBG) {
    943                     Log.d(TAG, "Found device to connect to");
    944                 }
    945                 BluetoothDeviceConnectionPolicy.ConnectionParams btParams =
    946                         new BluetoothDeviceConnectionPolicy.ConnectionParams(
    947                                 mConnectionInFlight.getBluetoothProfile(),
    948                                 mConnectionInFlight.getBluetoothDevice());
    949                 // set up a time out
    950                 mBluetoothAutoConnectStateMachine.sendMessageDelayed(
    951                         BluetoothAutoConnectStateMachine.CONNECT_TIMEOUT, btParams,
    952                         BluetoothAutoConnectStateMachine.CONNECTION_TIMEOUT_MS);
    953                 break;
    954             } else {
    955                 // result will be false, if there are no more devices to connect
    956                 // or if the ProfileProxy objects are null (ServiceConnection
    957                 // not yet established for this profile)
    958                 if (DBG) {
    959                     Log.d(TAG, "No more device to connect on Profile: " + nextProfile);
    960                 }
    961                 nextProfile = getNextProfileToConnectLocked();
    962             }
    963         }
    964         return connectingToADevice;
    965     }
    966 
    967     /**
    968      * Get the first unconnected profile.
    969      *
    970      * @return profile to connect.
    971      * Special return value 0 if
    972      * 1. all profiles have been connected on.
    973      * 2. no profile connected but no nearby known device that can be connected to
    974      */
    975     private Integer getNextProfileToConnectLocked() {
    976         for (Integer profile : mProfilesToConnect) {
    977             BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
    978             if (devInfo != null) {
    979                 if (devInfo.isProfileConnectableLocked()) {
    980                     return profile;
    981                 }
    982             } else {
    983                 Log.e(TAG, "Unexpected: devInfo null for profile: " + profile);
    984             }
    985         }
    986         // Reaching here denotes all profiles are connected or No devices available for any profile
    987         if (DBG) {
    988             Log.d(TAG, "No disconnected profiles");
    989         }
    990         return PROFILE_NOT_AVAILABLE;
    991     }
    992 
    993     /**
    994      * Checks if the Bluetooth profile service's proxy object is available.
    995      * Proxy obj should be available before we can attempt to connect on that profile.
    996      *
    997      * @param profile The profile to check the presence of the proxy object
    998      * @return True if the proxy obj exists. False otherwise.
    999      */
   1000     private boolean isProxyAvailable(Integer profile) {
   1001         if (mCarBluetoothUserService == null) {
   1002             mCarBluetoothUserService = setupBluetoothUserService();
   1003             if (mCarBluetoothUserService == null) {
   1004                 Log.d(TAG, "CarBluetoothUserSvc null.  Car service not bound to PerUserCarSvc.");
   1005                 return false;
   1006             }
   1007         }
   1008         try {
   1009             if (!mCarBluetoothUserService.isBluetoothConnectionProxyAvailable(profile)) {
   1010                 // proxy unavailable.
   1011                 if (DBG) {
   1012                     Log.d(TAG, "Proxy for Bluetooth Profile Service Unavailable: " + profile);
   1013                 }
   1014                 return false;
   1015             }
   1016         } catch (RemoteException e) {
   1017             Log.e(TAG, "Car BT Service Remote Exception.");
   1018             return false;
   1019         }
   1020         return true;
   1021     }
   1022 
   1023     /**
   1024      * Creates a device connection for the given profile.
   1025      *
   1026      * @param profile The profile on which the connection is being setup
   1027      * @param device  The device for which the connection is to be made.
   1028      * @return true if the connection is setup successfully. False otherwise.
   1029      */
   1030     boolean connectToDeviceOnProfile(Integer profile, BluetoothDevice device) {
   1031         if (DBG) {
   1032             Log.d(TAG, "in connectToDeviceOnProfile for Profile:" + profile +
   1033                     ", device: " + Utils.getDeviceDebugInfo(device));
   1034         }
   1035         if (!isProxyAvailable(profile)) {
   1036             if (DBG) {
   1037                 Log.d(TAG, "No proxy available for Profile: " + profile);
   1038             }
   1039             return false;
   1040         }
   1041         BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
   1042         if (devInfo == null) {
   1043             if (DBG) {
   1044                 Log.d(TAG, "devInfo NULL on Profile:" + profile);
   1045             }
   1046             return false;
   1047         }
   1048         int state = devInfo.getCurrentConnectionStateLocked(device);
   1049         if (state == BluetoothProfile.STATE_CONNECTED ||
   1050                 state == BluetoothProfile.STATE_CONNECTING) {
   1051             if (DBG) {
   1052                 Log.d(TAG, "device " + Utils.getDeviceDebugInfo(device) +
   1053                         " is already connected/connecting on Profile:" + profile);
   1054             }
   1055             return true;
   1056         }
   1057         if (!devInfo.isProfileConnectableLocked()) {
   1058             if (DBG) {
   1059                 Log.d(TAG, "isProfileConnectableLocked FALSE on Profile:" + profile +
   1060                         "  this means number of connections on this profile max'ed out or " +
   1061                         " no more devices available to connect on this profile");
   1062             }
   1063             return false;
   1064         }
   1065         try {
   1066             if (mCarBluetoothUserService != null) {
   1067                 mCarBluetoothUserService.bluetoothConnectToProfile((int) profile, device);
   1068                 devInfo.setConnectionStateLocked(device, BluetoothProfile.STATE_CONNECTING);
   1069                 // Increment the retry count & cache what is being connected to
   1070                 // This method is already called from a synchronized context.
   1071                 mConnectionInFlight.setBluetoothDevice(device);
   1072                 mConnectionInFlight.setBluetoothProfile(profile);
   1073                 devInfo.incrementRetryCountLocked();
   1074                 if (DBG) {
   1075                     Log.d(TAG, "Increment Retry to: " + devInfo.getRetryCountLocked() +
   1076                             ", for Profile: " + profile + ", on device: " +
   1077                             Utils.getDeviceDebugInfo(device));
   1078                 }
   1079                 return true;
   1080             } else {
   1081                 Log.e(TAG, "CarBluetoothUserSvc null");
   1082             }
   1083         } catch (RemoteException e) {
   1084             Log.e(TAG, "Remote User Service stopped responding: " + e.getMessage());
   1085         }
   1086         return false;
   1087     }
   1088 
   1089     /**
   1090      * Helper method to reset info in {@link #mConnectionInFlight} in the given
   1091      * {@link BluetoothDevicesInfo} object.
   1092      *
   1093      * @param devInfo the {@link BluetoothDevicesInfo} where the info is to be reset.
   1094      */
   1095     private void setProfileOnDeviceToUnavailable(BluetoothDevicesInfo devInfo) {
   1096         mConnectionInFlight.setBluetoothProfile(0);
   1097         mConnectionInFlight.setBluetoothDevice(null);
   1098         devInfo.setDeviceAvailableToConnectLocked(false);
   1099     }
   1100 
   1101     boolean doesDeviceExistForProfile(Integer profile, BluetoothDevice device) {
   1102         BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
   1103         if (devInfo == null) {
   1104             if (DBG) {
   1105                 Log.d(TAG, "devInfo NULL on Profile:" + profile);
   1106             }
   1107             return false;
   1108         }
   1109         boolean b = devInfo.checkDeviceInListLocked(device);
   1110         if (DBG) {
   1111             Log.d(TAG, "Is device " + Utils.getDeviceDebugInfo(device) +
   1112                     " listed for profile " + profile + ": " + b);
   1113         }
   1114         return b;
   1115     }
   1116 
   1117     /**
   1118      * Try to connect to the next device in the device list for the given profile.
   1119      *
   1120      * @param profile - profile to connect on
   1121      * @return - true if we found a device to connect on for this profile
   1122      * false - if we cannot find a device to connect to.
   1123      */
   1124     private boolean connectToNextDeviceInQueueLocked(Integer profile) {
   1125         // Get the Device Information for the given profile and find the next device to connect on
   1126         BluetoothDevice devToConnect = null;
   1127         BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
   1128         if (devInfo == null) {
   1129             Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
   1130             return false;
   1131         }
   1132 
   1133         if (isProxyAvailable(profile)) {
   1134             // Get the next device in the device list for this profile.
   1135             devToConnect = devInfo.getNextDeviceInQueueLocked();
   1136             if (devToConnect != null) {
   1137                 // deviceAvailable && proxyAvailable
   1138                 if (connectToDeviceOnProfile(profile, devToConnect)) return true;
   1139             } else {
   1140                 // device unavailable
   1141                 if (DBG) {
   1142                     Log.d(TAG, "No paired nearby device to connect to for profile: " + profile);
   1143                 }
   1144             }
   1145         }
   1146 
   1147         // reset the mConnectionInFlight
   1148         setProfileOnDeviceToUnavailable(devInfo);
   1149         return false;
   1150     }
   1151 
   1152     /**
   1153      * Update the device connection status for a profile and also notify the state machine.
   1154      * This gets called from {@link BluetoothBroadcastReceiver} when it receives a Profile's
   1155      * CONNECTION_STATE_CHANGED intent.
   1156      *
   1157      * @param params       - {@link ConnectionParams} device and profile list info
   1158      * @param currentState - connection result to update
   1159      */
   1160     private void notifyConnectionStatus(ConnectionParams params, int currentState) {
   1161         // Update the profile's BluetoothDevicesInfo.
   1162         boolean isConnected;
   1163         switch (currentState) {
   1164             case BluetoothProfile.STATE_DISCONNECTED: {
   1165                 isConnected = false;
   1166                 break;
   1167             }
   1168 
   1169             case BluetoothProfile.STATE_CONNECTED: {
   1170                 isConnected = true;
   1171                 break;
   1172             }
   1173 
   1174             default: {
   1175                 if (DBG) {
   1176                     Log.d(TAG, "notifyConnectionStatus() Ignoring state: " + currentState);
   1177                 }
   1178                 return;
   1179             }
   1180 
   1181         }
   1182 
   1183         boolean updateSuccessful = updateDeviceConnectionStatus(params, isConnected);
   1184         if (updateSuccessful) {
   1185             if (isConnected) {
   1186                 mBluetoothAutoConnectStateMachine.sendMessage(
   1187                         BluetoothAutoConnectStateMachine.DEVICE_CONNECTED,
   1188                         params);
   1189             } else {
   1190                 mBluetoothAutoConnectStateMachine.sendMessage(
   1191                         BluetoothAutoConnectStateMachine.DEVICE_DISCONNECTED,
   1192                         params);
   1193             }
   1194         }
   1195     }
   1196 
   1197     /**
   1198      * Update the profile's {@link BluetoothDevicesInfo} with the result of the connection
   1199      * attempt.  This gets called from the {@link BluetoothAutoConnectStateMachine} when the
   1200      * connection attempt times out or from {@link BluetoothBroadcastReceiver} when it receives
   1201      * a Profile's CONNECTION_STATE_CHANGED intent.
   1202      *
   1203      * @param params     - {@link ConnectionParams} device and profile list info
   1204      * @param didConnect - connection result to update
   1205      */
   1206     public synchronized boolean updateDeviceConnectionStatus(ConnectionParams params,
   1207             boolean didConnect) {
   1208         if (params == null || params.getBluetoothDevice() == null) {
   1209             Log.e(TAG, "updateDeviceConnectionStatus: null params");
   1210             return false;
   1211         }
   1212         // Get the profile to update
   1213         Integer profileToUpdate = params.getBluetoothProfile();
   1214         BluetoothDevice deviceThatConnected = params.getBluetoothDevice();
   1215         if (DBG) {
   1216             Log.d(TAG, "Profile: " + profileToUpdate + " Connected: " + didConnect + " on "
   1217                     + deviceThatConnected);
   1218         }
   1219 
   1220         // If the device just connected to HEADSET_CLIENT profile, initiate
   1221         // connections on PBAP & MAP profiles but let that begin after a timeout period.
   1222         // timeout allows A2DP profile to complete its connection, so that there is no race
   1223         // condition between
   1224         //         Phone trying to connect on A2DP
   1225         //         and, Car trying to connect on PBAP & MAP.
   1226         if (didConnect && profileToUpdate == BluetoothProfile.HEADSET_CLIENT) {
   1227             // Unlock the profiles PBAP, MAP in BluetoothDevicesInfo, so that they can be
   1228             // connected on.
   1229             for (Integer profile : Arrays.asList(BluetoothProfile.PBAP_CLIENT,
   1230                     BluetoothProfile.MAP_CLIENT)) {
   1231                 BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
   1232                 if (devInfo == null) {
   1233                     Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
   1234                     return false;
   1235                 }
   1236                 devInfo.setDeviceAvailableToConnectLocked(true);
   1237             }
   1238             if (DBG) {
   1239                 Log.d(TAG, "connect to PBAP/MAP after disconnect: ");
   1240             }
   1241             mBluetoothAutoConnectStateMachine.sendMessageDelayed(
   1242                     BluetoothAutoConnectStateMachine.CHECK_CLIENT_PROFILES, params,
   1243                     BluetoothAutoConnectStateMachine.CONNECT_MORE_PROFILES_TIMEOUT_MS);
   1244         }
   1245 
   1246         // If the connection update is on a different profile or device (a very rare possibility),
   1247         // it is handled automatically.  Just logging it here.
   1248         if (DBG) {
   1249             if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothProfile() != null) {
   1250                 if (profileToUpdate.equals(mConnectionInFlight.getBluetoothProfile()) == false) {
   1251                     Log.d(TAG, "Updating profile " + profileToUpdate
   1252                             + " different from connection in flight "
   1253                             + mConnectionInFlight.getBluetoothProfile());
   1254                 }
   1255             }
   1256 
   1257             if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothDevice() != null) {
   1258                 if (deviceThatConnected.equals(mConnectionInFlight.getBluetoothDevice()) == false) {
   1259                     Log.d(TAG, "Updating device: " + deviceThatConnected
   1260                             + " different from connection in flight: "
   1261                             + mConnectionInFlight.getBluetoothDevice());
   1262 
   1263                 }
   1264             }
   1265         }
   1266         BluetoothDevicesInfo devInfo = null;
   1267         devInfo = mProfileToConnectableDevicesMap.get(profileToUpdate);
   1268         if (devInfo == null) {
   1269             Log.e(TAG, "Unexpected: devInfo null for profile: " + profileToUpdate);
   1270             return false;
   1271         }
   1272 
   1273         boolean retry = canRetryConnection(profileToUpdate);
   1274         // Update the status and also if a retry attempt can be made if the
   1275         // connection timed out in the previous attempt.
   1276         if (DBG) {
   1277             Log.d(TAG, "Retry? : " + retry);
   1278         }
   1279         devInfo.updateConnectionStatusLocked(deviceThatConnected, didConnect, retry);
   1280         // Write to persistent memory to have the latest snapshot available
   1281         writeDeviceInfoToSettings(params);
   1282         return true;
   1283     }
   1284 
   1285     /**
   1286      * Returns if we can retry connection attempt on the given profile for the device that is
   1287      * currently in the head of the queue.
   1288      *
   1289      * @param profile - Profile to check
   1290      */
   1291     private synchronized boolean canRetryConnection(Integer profile) {
   1292         BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
   1293         if (devInfo == null) {
   1294             Log.e(TAG, "Unexpected: No device Queue for this profile: " + profile);
   1295             return false;
   1296         }
   1297         if (devInfo.getRetryCountLocked() < MAX_CONNECT_RETRIES) {
   1298             return true;
   1299         } else {
   1300             return false;
   1301         }
   1302     }
   1303 
   1304     /**
   1305      * Helper method to see if there are any connect-able devices on any of the
   1306      * profiles.
   1307      *
   1308      * @return true - if {@link #mProfileToConnectableDevicesMap} does not have any devices for any
   1309      * profiles.
   1310      * false - if {@link #mProfileToConnectableDevicesMap} has a device for at least one profile.
   1311      */
   1312     private synchronized boolean isDeviceMapEmpty() {
   1313         boolean empty = true;
   1314         for (Integer profile : mProfilesToConnect) {
   1315             BluetoothDevicesInfo devInfo = mProfileToConnectableDevicesMap.get(profile);
   1316             if (devInfo != null) {
   1317                 if (devInfo.getNumberOfPairedDevicesLocked() != 0) {
   1318                     if (DBG) {
   1319                         Log.d(TAG, "Device map not empty. Profile: " + profile + " has "
   1320                                 + devInfo.getNumberOfPairedDevicesLocked() + " paired devices");
   1321                     }
   1322                     empty = false;
   1323                     break;
   1324                 }
   1325             }
   1326         }
   1327         return empty;
   1328     }
   1329 
   1330     /**
   1331      * Reset the Device Available to Connect information for all profiles to Available.
   1332      * If in a previous connection attempt, we failed to connect on all devices for a profile,
   1333      * we would update deviceAvailableToConnect for that profile to false.  That information
   1334      * is used to deduce if we should move to the next profile. If marked false, we will not
   1335      * try to connect on that profile anymore as part of that connection attempt.
   1336      * However, when we get another connection trigger from the vehicle, we need to reset the
   1337      * deviceAvailableToConnect information so we can start the connection attempts all over
   1338      * again.
   1339      */
   1340     private synchronized void resetDeviceAvailableToConnect() {
   1341         for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
   1342             devInfo.setDeviceAvailableToConnectLocked(true);
   1343             devInfo.resetDeviceIndex();
   1344         }
   1345     }
   1346 
   1347     /**
   1348      * Utility function - Prints the Profile: list of devices information to log
   1349      * Caller should wrap a DBG around this, since this is for debugging purpose.
   1350      *
   1351      * @param writer - PrintWriter
   1352      */
   1353     private synchronized void printDeviceMap(PrintWriter writer) {
   1354         if (mProfileToConnectableDevicesMap == null) {
   1355             return;
   1356         }
   1357         for (Integer profile : mProfileToConnectableDevicesMap.keySet()) {
   1358             writer.print("Profile: " + Utils.getProfileName(profile) + "\t");
   1359             writer.print("\n" + mProfileToConnectableDevicesMap.get(profile).toDebugString());
   1360             writer.println();
   1361         }
   1362     }
   1363 
   1364     /**
   1365      * Write the device list for all bluetooth profiles that connected.
   1366      *
   1367      * @return true if the write was successful, false otherwise
   1368      */
   1369     private synchronized boolean writeDeviceInfoToSettings() {
   1370         ConnectionParams params;
   1371         boolean writeResult;
   1372         for (Integer profile : mProfilesToConnect) {
   1373             params = new ConnectionParams(profile);
   1374             writeResult = writeDeviceInfoToSettings(params);
   1375             if (!writeResult) {
   1376                 Log.e(TAG, "Error writing Device Info for profile:" + profile);
   1377                 return writeResult;
   1378             }
   1379         }
   1380         return true;
   1381     }
   1382 
   1383     /**
   1384      * Write information about which devices connected on which profile to Settings.Secure.
   1385      * Essentially the list of devices that a profile can connect on the next auto-connect
   1386      * attempt.
   1387      *
   1388      * @param params - ConnectionParams indicating which bluetooth profile to write this
   1389      *               information
   1390      *               for.
   1391      * @return true if the write was successful, false otherwise
   1392      */
   1393     public synchronized boolean writeDeviceInfoToSettings(ConnectionParams params) {
   1394         if (!mAllowReadWriteToSettings) {
   1395             return false;
   1396         }
   1397         boolean writeSuccess = true;
   1398         Integer profileToUpdate = params.getBluetoothProfile();
   1399 
   1400         if (mProfileToConnectableDevicesMap == null) {
   1401             writeSuccess = false;
   1402         } else {
   1403             List<String> deviceNames = new ArrayList<>();
   1404             BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profileToUpdate);
   1405             StringBuilder sb = new StringBuilder();
   1406             String delimiter = ""; // start off with no delimiter.
   1407 
   1408             // Iterate through the List<BluetoothDevice> and build a String that is
   1409             // names of all devices to be connected for this profile joined together and
   1410             // delimited by a delimiter (its a ',' here)
   1411             if (devicesInfo != null && devicesInfo.getDeviceList() != null) {
   1412                 for (BluetoothDevice device : devicesInfo.getDeviceList()) {
   1413                     sb.append(delimiter);
   1414                     sb.append(device.getAddress());
   1415                     delimiter = SETTINGS_DELIMITER;
   1416                 }
   1417 
   1418             }
   1419             // joinedDeviceNames has something like "22:22:33:44:55:AB,22:23:xx:xx:xx:xx"
   1420             // mac addresses of connectable devices separated by a delimiter
   1421             String joinedDeviceNames = sb.toString();
   1422             if (DBG) {
   1423                 Log.d(TAG, "Profile: " + profileToUpdate + " Writing: " + joinedDeviceNames);
   1424             }
   1425             long userId = ActivityManager.getCurrentUser();
   1426             switch (profileToUpdate) {
   1427                 case BluetoothProfile.A2DP_SINK:
   1428                     Settings.Secure.putStringForUser(mContext.getContentResolver(),
   1429                             KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES,
   1430                             joinedDeviceNames, (int) userId);
   1431                     break;
   1432 
   1433                 case BluetoothProfile.HEADSET_CLIENT:
   1434                     Settings.Secure.putStringForUser(mContext.getContentResolver(),
   1435                             KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES,
   1436                             joinedDeviceNames, (int) userId);
   1437                     break;
   1438 
   1439                 case BluetoothProfile.PBAP_CLIENT:
   1440                     // use the phone
   1441                     break;
   1442 
   1443                 case BluetoothProfile.MAP_CLIENT:
   1444                     Settings.Secure.putStringForUser(mContext.getContentResolver(),
   1445                             KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES,
   1446                             joinedDeviceNames, (int) userId);
   1447                     break;
   1448                 case BluetoothProfile.PAN:
   1449                     Settings.Secure.putStringForUser(mContext.getContentResolver(),
   1450                             KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES,
   1451                             joinedDeviceNames, (int) userId);
   1452                     break;
   1453             }
   1454         }
   1455         return writeSuccess;
   1456     }
   1457 
   1458     /**
   1459      * Read the device information from Settings.Secure and populate the
   1460      * {@link #mProfileToConnectableDevicesMap}
   1461      *
   1462      * Device MAC addresses are written to Settings.Secure delimited by a ','.
   1463      * Ex: android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICES: xx:xx:xx:xx:xx:xx,yy:yy:yy:yy:yy
   1464      * denotes that two devices with addresses xx:xx:xx:xx:xx:xx & yy:yy:yy:yy:yy:yy were connected
   1465      * as phones (in HFP and PBAP profiles) the last time this user was logged in.
   1466      *
   1467      * @return - true if the read was successful, false if 1. BT Adapter not enabled 2. No prior
   1468      * bonded devices 3. No information stored in Settings for this user.
   1469      */
   1470     public synchronized boolean readAndRebuildDeviceMapFromSettings() {
   1471         List<String> deviceList;
   1472         String devices = null;
   1473         // Create and initialize mProfileToConnectableDevicesMap if needed.
   1474         initDeviceMap();
   1475         if (mBluetoothAdapter != null) {
   1476             if (DBG) {
   1477                 Log.d(TAG,
   1478                         "Number of Bonded devices:" + mBluetoothAdapter.getBondedDevices().size());
   1479             }
   1480             if (mBluetoothAdapter.getBondedDevices().isEmpty()) {
   1481                 if (DBG) {
   1482                     Log.d(TAG, "No Bonded Devices available. Quit rebuilding");
   1483                 }
   1484                 return false;
   1485             }
   1486         }
   1487         if (!mAllowReadWriteToSettings) {
   1488             return false;
   1489         }
   1490         // Read from Settings.Secure for the current user.  There are 3 keys 1 each for Phone
   1491         // (HFP & PBAP), 1 for Music (A2DP) and 1 for Messaging device (MAP)
   1492         long userId = ActivityManager.getCurrentUser();
   1493         for (Integer profile : mProfilesToConnect) {
   1494             switch (profile) {
   1495                 case BluetoothProfile.A2DP_SINK:
   1496                     devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
   1497                             KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES, (int) userId);
   1498                     break;
   1499                 case BluetoothProfile.PBAP_CLIENT:
   1500                     // fall through
   1501                 case BluetoothProfile.HEADSET_CLIENT:
   1502                     devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
   1503                             KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES, (int) userId);
   1504                     break;
   1505                 case BluetoothProfile.MAP_CLIENT:
   1506                     devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
   1507                             KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES, (int) userId);
   1508                     break;
   1509                 case BluetoothProfile.PAN:
   1510                     devices = Settings.Secure.getStringForUser(mContext.getContentResolver(),
   1511                             KEY_BLUETOOTH_AUTOCONNECT_NETWORK_DEVICES, (int) userId);
   1512                 default:
   1513                     Log.e(TAG, "Unexpected profile");
   1514                     break;
   1515             }
   1516 
   1517             if (devices == null) {
   1518                 if (DBG) {
   1519                     Log.d(TAG, "No device information stored in Settings");
   1520                 }
   1521                 return false;
   1522             }
   1523             if (DBG) {
   1524                 Log.d(TAG, "Devices in Settings: " + devices);
   1525             }
   1526             // Get a list of Device Mac Addresses from the value
   1527             deviceList = Arrays.asList(devices.split(SETTINGS_DELIMITER));
   1528             if (deviceList == null) {
   1529                 return false;
   1530             }
   1531             BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
   1532             // Do we have a bonded device with this name?  If so, get it and populate the device
   1533             // map.
   1534             for (String address : deviceList) {
   1535                 BluetoothDevice deviceToAdd = getBondedDeviceWithGivenName(address);
   1536                 if (deviceToAdd != null) {
   1537                     devicesInfo.addDeviceLocked(deviceToAdd);
   1538                 } else {
   1539                     if (DBG) {
   1540                         Log.d(TAG, "No device with name " + address + " found in bonded devices");
   1541                     }
   1542                 }
   1543             }
   1544             mProfileToConnectableDevicesMap.put(profile, devicesInfo);
   1545             // Check to see if there are any  primary or secondary devices for this profile and
   1546             // update BluetoothDevicesInfo with the priority information.
   1547             for (int priority : mPrioritiesSupported) {
   1548                 readAndTagDeviceWithPriorityFromSettings(profile, priority);
   1549             }
   1550         }
   1551         return true;
   1552     }
   1553 
   1554     /**
   1555      * Read from Secure Settings if there are primary or secondary devices marked for this
   1556      * Bluetooth profile.  If there are tagged devices, update the BluetoothDevicesInfo so the
   1557      * policy can prioritize those devices when making connection attempts.
   1558      *
   1559      * @param profile  - Bluetooth Profile to check
   1560      * @param priority - Priority to check
   1561      */
   1562     private void readAndTagDeviceWithPriorityFromSettings(int profile, int priority) {
   1563         BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
   1564         if (devicesInfo == null) {
   1565             return;
   1566         }
   1567         if (!mCarBluetoothService.isPriorityDevicePresent(profile, priority)) {
   1568             // There is no device for this priority - either it hasn't been set or has been removed.
   1569             // So check if the policy has a device associated with this priority and remove it.
   1570             BluetoothDevice deviceToClear = devicesInfo.getBluetoothDeviceForPriorityLocked(
   1571                     priority);
   1572             if (deviceToClear != null) {
   1573                 if (DBG) {
   1574                     Log.d(TAG, "Clearing priority for: " +
   1575                             Utils.getDeviceDebugInfo(deviceToClear));
   1576                 }
   1577                 devicesInfo.removeBluetoothDevicePriorityLocked(deviceToClear);
   1578             }
   1579         } else {
   1580             // There is a device with the given priority for the given profile.  Update the
   1581             // policy's records.
   1582             String deviceName = mCarBluetoothService.getDeviceNameWithPriority(profile,
   1583                     priority);
   1584             if (deviceName != null) {
   1585                 BluetoothDevice bluetoothDevice = getBondedDeviceWithGivenName(deviceName);
   1586                 if (bluetoothDevice != null) {
   1587                     if (DBG) {
   1588                         Log.d(TAG, "Setting priority: " + priority + " for " + deviceName);
   1589                     }
   1590                     tagDeviceWithPriority(bluetoothDevice, profile, priority);
   1591                 }
   1592             }
   1593         }
   1594     }
   1595 
   1596     /**
   1597      * Tag a Bluetooth device with priority - Primary or Secondary.  This only updates the policy's
   1598      * record (BluetoothDevicesInfo) of the priority information.
   1599      *
   1600      * @param device   - BluetoothDevice to tag
   1601      * @param profile  - BluetoothProfile to tag
   1602      * @param priority - Priority to tag with
   1603      */
   1604     @VisibleForTesting
   1605     void tagDeviceWithPriority(BluetoothDevice device, int profile, int priority) {
   1606         BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
   1607         if (device != null) {
   1608             if (DBG) {
   1609                 Log.d(TAG, "Profile: " + profile + " : " + device + " Priority: " + priority);
   1610             }
   1611             devicesInfo.setBluetoothDevicePriorityLocked(device, priority);
   1612         }
   1613     }
   1614 
   1615     /**
   1616      * Given the device name, find the corresponding {@link BluetoothDevice} from the list of
   1617      * Bonded devices.
   1618      *
   1619      * @param name Bluetooth Device name
   1620      */
   1621     @Nullable
   1622     private BluetoothDevice getBondedDeviceWithGivenName(String name) {
   1623         if (mBluetoothAdapter == null) {
   1624             if (DBG) {
   1625                 Log.d(TAG, "Bluetooth Adapter Null");
   1626             }
   1627             return null;
   1628         }
   1629         if (name == null) {
   1630             Log.w(TAG, "getBondedDeviceWithGivenName() Passing in a null name");
   1631             return null;
   1632         }
   1633         if (DBG) {
   1634             Log.d(TAG, "Looking for bonded device: " + name);
   1635         }
   1636         BluetoothDevice btDevice = null;
   1637         Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
   1638         for (BluetoothDevice bd : bondedDevices) {
   1639             if (name.equals(bd.getAddress())) {
   1640                 btDevice = bd;
   1641                 break;
   1642             }
   1643         }
   1644         return btDevice;
   1645     }
   1646 
   1647 
   1648     public void dump(PrintWriter writer) {
   1649         writer.println("*BluetoothDeviceConnectionPolicy*");
   1650         printDeviceMap(writer);
   1651         mBluetoothAutoConnectStateMachine.dump(writer);
   1652     }
   1653 }
   1654