• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.bluetooth.btservice;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 import static android.Manifest.permission.BLUETOOTH_SCAN;
21 
22 import android.bluetooth.BluetoothA2dp;
23 import android.bluetooth.BluetoothA2dpSink;
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothAvrcpController;
26 import android.bluetooth.BluetoothClass;
27 import android.bluetooth.BluetoothDevice;
28 import android.bluetooth.BluetoothHeadset;
29 import android.bluetooth.BluetoothHeadsetClient;
30 import android.bluetooth.BluetoothHearingAid;
31 import android.bluetooth.BluetoothHidDevice;
32 import android.bluetooth.BluetoothHidHost;
33 import android.bluetooth.BluetoothMap;
34 import android.bluetooth.BluetoothMapClient;
35 import android.bluetooth.BluetoothPan;
36 import android.bluetooth.BluetoothPbap;
37 import android.bluetooth.BluetoothPbapClient;
38 import android.bluetooth.BluetoothProfile;
39 import android.bluetooth.BluetoothSap;
40 import android.bluetooth.BufferConstraint;
41 import android.bluetooth.BufferConstraints;
42 import android.content.BroadcastReceiver;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.os.ParcelUuid;
47 import android.os.SystemProperties;
48 import android.os.UserHandle;
49 import android.util.Log;
50 import android.util.Pair;
51 
52 import androidx.annotation.VisibleForTesting;
53 
54 import com.android.bluetooth.BluetoothStatsLog;
55 import com.android.bluetooth.Utils;
56 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
57 
58 import java.io.FileDescriptor;
59 import java.io.PrintWriter;
60 import java.util.ArrayList;
61 import java.util.HashMap;
62 import java.util.List;
63 import java.util.concurrent.CopyOnWriteArrayList;
64 
65 class AdapterProperties {
66     private static final boolean DBG = true;
67     private static final boolean VDBG = false;
68     private static final String TAG = "AdapterProperties";
69 
70     private static final String MAX_CONNECTED_AUDIO_DEVICES_PROPERTY =
71             "persist.bluetooth.maxconnectedaudiodevices";
72     static final int MAX_CONNECTED_AUDIO_DEVICES_LOWER_BOND = 1;
73     private static final int MAX_CONNECTED_AUDIO_DEVICES_UPPER_BOUND = 5;
74     private static final String A2DP_OFFLOAD_SUPPORTED_PROPERTY =
75             "ro.bluetooth.a2dp_offload.supported";
76     private static final String A2DP_OFFLOAD_DISABLED_PROPERTY =
77             "persist.bluetooth.a2dp_offload.disabled";
78 
79     private static final long DEFAULT_DISCOVERY_TIMEOUT_MS = 12800;
80     private static final int BD_ADDR_LEN = 6; // in bytes
81 
82     private volatile String mName;
83     private volatile byte[] mAddress;
84     private volatile BluetoothClass mBluetoothClass;
85     private volatile int mScanMode;
86     private volatile int mDiscoverableTimeout;
87     private volatile ParcelUuid[] mUuids;
88     private volatile int mLocalIOCapability = BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
89     private volatile int mLocalIOCapabilityBLE = BluetoothAdapter.IO_CAPABILITY_UNKNOWN;
90 
91     private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices =
92             new CopyOnWriteArrayList<BluetoothDevice>();
93 
94     private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
95     private final HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState =
96             new HashMap<>();
97 
98     private volatile int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
99     private volatile int mState = BluetoothAdapter.STATE_OFF;
100     private int mMaxConnectedAudioDevices = 1;
101     private boolean mA2dpOffloadEnabled = false;
102 
103     private AdapterService mService;
104     private boolean mDiscovering;
105     private long mDiscoveryEndMs; //< Time (ms since epoch) that discovery ended or will end.
106     private RemoteDevices mRemoteDevices;
107     private BluetoothAdapter mAdapter;
108     //TODO - all hw capabilities to be exposed as a class
109     private int mNumOfAdvertisementInstancesSupported;
110     private boolean mRpaOffloadSupported;
111     private int mNumOfOffloadedIrkSupported;
112     private int mNumOfOffloadedScanFilterSupported;
113     private int mOffloadedScanResultStorageBytes;
114     private int mVersSupported;
115     private int mTotNumOfTrackableAdv;
116     private boolean mIsExtendedScanSupported;
117     private boolean mIsDebugLogSupported;
118     private boolean mIsActivityAndEnergyReporting;
119     private boolean mIsLe2MPhySupported;
120     private boolean mIsLeCodedPhySupported;
121     private boolean mIsLeExtendedAdvertisingSupported;
122     private boolean mIsLePeriodicAdvertisingSupported;
123     private int mLeMaximumAdvertisingDataLength;
124 
125     private int mIsDynamicAudioBufferSizeSupported;
126     private int mDynamicAudioBufferSizeSupportedCodecsGroup1;
127     private int mDynamicAudioBufferSizeSupportedCodecsGroup2;
128     private List<BufferConstraint> mBufferConstraintList;
129 
130     private boolean mReceiverRegistered;
131     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
132         @Override
133         public void onReceive(Context context, Intent intent) {
134             String action = intent.getAction();
135             if (action == null) {
136                 Log.w(TAG, "Received intent with null action");
137                 return;
138             }
139             switch (action) {
140                 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
141                     sendConnectionStateChange(BluetoothProfile.HEADSET, intent);
142                     break;
143                 case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
144                     sendConnectionStateChange(BluetoothProfile.A2DP, intent);
145                     break;
146                 case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED:
147                     sendConnectionStateChange(BluetoothProfile.HEADSET_CLIENT, intent);
148                     break;
149                 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
150                     sendConnectionStateChange(BluetoothProfile.HEARING_AID, intent);
151                     break;
152                 case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED:
153                     sendConnectionStateChange(BluetoothProfile.A2DP_SINK, intent);
154                     break;
155                 case BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED:
156                     sendConnectionStateChange(BluetoothProfile.HID_DEVICE, intent);
157                     break;
158                 case BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED:
159                     sendConnectionStateChange(BluetoothProfile.HID_HOST, intent);
160                     break;
161                 case BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED:
162                     sendConnectionStateChange(BluetoothProfile.AVRCP_CONTROLLER, intent);
163                     break;
164                 case BluetoothPan.ACTION_CONNECTION_STATE_CHANGED:
165                     sendConnectionStateChange(BluetoothProfile.PAN, intent);
166                     break;
167                 case BluetoothMap.ACTION_CONNECTION_STATE_CHANGED:
168                     sendConnectionStateChange(BluetoothProfile.MAP, intent);
169                     break;
170                 case BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED:
171                     sendConnectionStateChange(BluetoothProfile.MAP_CLIENT, intent);
172                     break;
173                 case BluetoothSap.ACTION_CONNECTION_STATE_CHANGED:
174                     sendConnectionStateChange(BluetoothProfile.SAP, intent);
175                     break;
176                 case BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED:
177                     sendConnectionStateChange(BluetoothProfile.PBAP_CLIENT, intent);
178                     break;
179                 case BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED:
180                     sendConnectionStateChange(BluetoothProfile.PBAP, intent);
181                     break;
182                 default:
183                     Log.w(TAG, "Received unknown intent " + intent);
184                     break;
185             }
186         }
187     };
188 
189     // Lock for all getters and setters.
190     // If finer grained locking is needer, more locks
191     // can be added here.
192     private final Object mObject = new Object();
193 
AdapterProperties(AdapterService service)194     AdapterProperties(AdapterService service) {
195         mService = service;
196         mAdapter = BluetoothAdapter.getDefaultAdapter();
197         invalidateBluetoothCaches();
198     }
199 
init(RemoteDevices remoteDevices)200     public void init(RemoteDevices remoteDevices) {
201         mProfileConnectionState.clear();
202         mRemoteDevices = remoteDevices;
203 
204         // Get default max connected audio devices from config.xml in frameworks/base/core
205         int configDefaultMaxConnectedAudioDevices = mService.getResources().getInteger(
206                 com.android.internal.R.integer.config_bluetooth_max_connected_audio_devices);
207         // Override max connected audio devices if MAX_CONNECTED_AUDIO_DEVICES_PROPERTY is set
208         int propertyOverlayedMaxConnectedAudioDevices =
209                 SystemProperties.getInt(MAX_CONNECTED_AUDIO_DEVICES_PROPERTY,
210                         configDefaultMaxConnectedAudioDevices);
211         // Make sure the final value of max connected audio devices is within allowed range
212         mMaxConnectedAudioDevices = Math.min(Math.max(propertyOverlayedMaxConnectedAudioDevices,
213                 MAX_CONNECTED_AUDIO_DEVICES_LOWER_BOND), MAX_CONNECTED_AUDIO_DEVICES_UPPER_BOUND);
214         Log.i(TAG, "init(), maxConnectedAudioDevices, default="
215                 + configDefaultMaxConnectedAudioDevices + ", propertyOverlayed="
216                 + propertyOverlayedMaxConnectedAudioDevices + ", finalValue="
217                 + mMaxConnectedAudioDevices);
218 
219         mA2dpOffloadEnabled =
220                 SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false)
221                 && !SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false);
222 
223         IntentFilter filter = new IntentFilter();
224         filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
225         filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
226         filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
227         filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
228         filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
229         filter.addAction(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
230         filter.addAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
231         filter.addAction(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);
232         filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
233         filter.addAction(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
234         filter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
235         filter.addAction(BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
236         filter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
237         mService.registerReceiver(mReceiver, filter);
238         mReceiverRegistered = true;
239         invalidateBluetoothCaches();
240     }
241 
cleanup()242     public void cleanup() {
243         mRemoteDevices = null;
244         mProfileConnectionState.clear();
245         if (mReceiverRegistered) {
246             mService.unregisterReceiver(mReceiver);
247             mReceiverRegistered = false;
248         }
249         mService = null;
250         mBondedDevices.clear();
251         invalidateBluetoothCaches();
252     }
invalidateGetProfileConnectionStateCache()253     private static void invalidateGetProfileConnectionStateCache() {
254         BluetoothAdapter.invalidateGetProfileConnectionStateCache();
255     }
invalidateIsOffloadedFilteringSupportedCache()256     private static void invalidateIsOffloadedFilteringSupportedCache() {
257         BluetoothAdapter.invalidateIsOffloadedFilteringSupportedCache();
258     }
invalidateGetConnectionStateCache()259     private static void invalidateGetConnectionStateCache() {
260         BluetoothAdapter.invalidateGetAdapterConnectionStateCache();
261     }
invalidateGetBondStateCache()262     private static void invalidateGetBondStateCache() {
263         BluetoothDevice.invalidateBluetoothGetBondStateCache();
264     }
invalidateBluetoothCaches()265     private static void invalidateBluetoothCaches() {
266         invalidateGetProfileConnectionStateCache();
267         invalidateIsOffloadedFilteringSupportedCache();
268         invalidateGetConnectionStateCache();
269         invalidateGetBondStateCache();
270     }
271 
272     @Override
clone()273     public Object clone() throws CloneNotSupportedException {
274         throw new CloneNotSupportedException();
275     }
276 
277     /**
278      * @return the mName
279      */
getName()280     String getName() {
281         return mName;
282     }
283 
284     /**
285      * Set the local adapter property - name
286      * @param name the name to set
287      */
setName(String name)288     boolean setName(String name) {
289         synchronized (mObject) {
290             return mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME,
291                     name.getBytes());
292         }
293     }
294 
295     /**
296      * Set the Bluetooth Class of Device (CoD) of the adapter.
297      *
298      * <p>Bluetooth stack stores some adapter properties in native BT stack storage and some in the
299      * Java Android stack. Bluetooth CoD is stored in the Android layer through
300      * {@link android.provider.Settings.Global#BLUETOOTH_CLASS_OF_DEVICE}.
301      *
302      * <p>Due to this, the getAdapterPropertyNative and adapterPropertyChangedCallback methods don't
303      * actually update mBluetoothClass. Hence, we update the field mBluetoothClass every time we
304      * successfully update BluetoothClass.
305      *
306      * @param bluetoothClass BluetoothClass of the device
307      */
setBluetoothClass(BluetoothClass bluetoothClass)308     boolean setBluetoothClass(BluetoothClass bluetoothClass) {
309         synchronized (mObject) {
310             boolean result =
311                     mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE,
312                             bluetoothClass.getClassOfDeviceBytes());
313 
314             if (result) {
315                 mBluetoothClass = bluetoothClass;
316             }
317 
318             return result;
319         }
320     }
321 
322     /**
323      * @return the BluetoothClass of the Bluetooth adapter.
324      */
getBluetoothClass()325     BluetoothClass getBluetoothClass() {
326         synchronized (mObject) {
327             return mBluetoothClass;
328         }
329     }
330 
setIoCapability(int capability)331     boolean setIoCapability(int capability) {
332         synchronized (mObject) {
333             boolean result = mService.setAdapterPropertyNative(
334                     AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS, Utils.intToByteArray(capability));
335 
336             if (result) {
337                 mLocalIOCapability = capability;
338             }
339 
340             return result;
341         }
342     }
343 
getIoCapability()344     int getIoCapability() {
345         synchronized (mObject) {
346             return mLocalIOCapability;
347         }
348     }
349 
setLeIoCapability(int capability)350     boolean setLeIoCapability(int capability) {
351         synchronized (mObject) {
352             boolean result = mService.setAdapterPropertyNative(
353                     AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS_BLE,
354                     Utils.intToByteArray(capability));
355 
356             if (result) {
357                 mLocalIOCapabilityBLE = capability;
358             }
359 
360             return result;
361         }
362     }
363 
getLeIoCapability()364     int getLeIoCapability() {
365         synchronized (mObject) {
366             return mLocalIOCapabilityBLE;
367         }
368     }
369 
370     /**
371      * @return the mScanMode
372      */
getScanMode()373     int getScanMode() {
374         return mScanMode;
375     }
376 
377     /**
378      * Set the local adapter property - scanMode
379      *
380      * @param scanMode the ScanMode to set
381      */
setScanMode(int scanMode)382     boolean setScanMode(int scanMode) {
383         synchronized (mObject) {
384             return mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE,
385                     Utils.intToByteArray(scanMode));
386         }
387     }
388 
389     /**
390      * @return the mUuids
391      */
getUuids()392     ParcelUuid[] getUuids() {
393         return mUuids;
394     }
395 
396     /**
397      * @return the mAddress
398      */
getAddress()399     byte[] getAddress() {
400         return mAddress;
401     }
402 
403     /**
404      * @param connectionState the mConnectionState to set
405      */
setConnectionState(int connectionState)406     void setConnectionState(int connectionState) {
407         mConnectionState = connectionState;
408         invalidateGetConnectionStateCache();
409     }
410 
411     /**
412      * @return the mConnectionState
413      */
getConnectionState()414     int getConnectionState() {
415         return mConnectionState;
416     }
417 
418     /**
419      * @param mState the mState to set
420      */
setState(int state)421     void setState(int state) {
422         debugLog("Setting state to " + BluetoothAdapter.nameForState(state));
423         mState = state;
424     }
425 
426     /**
427      * @return the mState
428      */
getState()429     int getState() {
430         return mState;
431     }
432 
433     /**
434      * @return the mNumOfAdvertisementInstancesSupported
435      */
getNumOfAdvertisementInstancesSupported()436     int getNumOfAdvertisementInstancesSupported() {
437         return mNumOfAdvertisementInstancesSupported;
438     }
439 
440     /**
441      * @return the mRpaOffloadSupported
442      */
isRpaOffloadSupported()443     boolean isRpaOffloadSupported() {
444         return mRpaOffloadSupported;
445     }
446 
447     /**
448      * @return the mNumOfOffloadedIrkSupported
449      */
getNumOfOffloadedIrkSupported()450     int getNumOfOffloadedIrkSupported() {
451         return mNumOfOffloadedIrkSupported;
452     }
453 
454     /**
455      * @return the mNumOfOffloadedScanFilterSupported
456      */
getNumOfOffloadedScanFilterSupported()457     int getNumOfOffloadedScanFilterSupported() {
458         return mNumOfOffloadedScanFilterSupported;
459     }
460 
461     /**
462      * @return the mOffloadedScanResultStorageBytes
463      */
getOffloadedScanResultStorage()464     int getOffloadedScanResultStorage() {
465         return mOffloadedScanResultStorageBytes;
466     }
467 
468     /**
469      * @return tx/rx/idle activity and energy info
470      */
isActivityAndEnergyReportingSupported()471     boolean isActivityAndEnergyReportingSupported() {
472         return mIsActivityAndEnergyReporting;
473     }
474 
475     /**
476      * @return the mIsLe2MPhySupported
477      */
isLe2MPhySupported()478     boolean isLe2MPhySupported() {
479         return mIsLe2MPhySupported;
480     }
481 
482     /**
483      * @return the mIsLeCodedPhySupported
484      */
isLeCodedPhySupported()485     boolean isLeCodedPhySupported() {
486         return mIsLeCodedPhySupported;
487     }
488 
489     /**
490      * @return the mIsLeExtendedAdvertisingSupported
491      */
isLeExtendedAdvertisingSupported()492     boolean isLeExtendedAdvertisingSupported() {
493         return mIsLeExtendedAdvertisingSupported;
494     }
495 
496     /**
497      * @return the mIsLePeriodicAdvertisingSupported
498      */
isLePeriodicAdvertisingSupported()499     boolean isLePeriodicAdvertisingSupported() {
500         return mIsLePeriodicAdvertisingSupported;
501     }
502 
503     /**
504      * @return the getLeMaximumAdvertisingDataLength
505      */
getLeMaximumAdvertisingDataLength()506     int getLeMaximumAdvertisingDataLength() {
507         return mLeMaximumAdvertisingDataLength;
508     }
509 
510     /**
511      * @return total number of trackable advertisements
512      */
getTotalNumOfTrackableAdvertisements()513     int getTotalNumOfTrackableAdvertisements() {
514         return mTotNumOfTrackableAdv;
515     }
516 
517     /**
518      * @return the maximum number of connected audio devices
519      */
getMaxConnectedAudioDevices()520     int getMaxConnectedAudioDevices() {
521         return mMaxConnectedAudioDevices;
522     }
523 
524     /**
525      * @return A2DP offload support
526      */
isA2dpOffloadEnabled()527     boolean isA2dpOffloadEnabled() {
528         return mA2dpOffloadEnabled;
529     }
530 
531     /**
532      * @return Dynamic Audio Buffer support
533      */
getDynamicBufferSupport()534     int getDynamicBufferSupport() {
535         if (!mA2dpOffloadEnabled) {
536             // TODO: Enable Dynamic Audio Buffer for A2DP software encoding when ready.
537             mIsDynamicAudioBufferSizeSupported =
538                 BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
539         } else {
540             if ((mDynamicAudioBufferSizeSupportedCodecsGroup1 != 0)
541                     || (mDynamicAudioBufferSizeSupportedCodecsGroup2 != 0)) {
542                 mIsDynamicAudioBufferSizeSupported =
543                     BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD;
544             } else {
545                 mIsDynamicAudioBufferSizeSupported =
546                     BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
547             }
548         }
549         return mIsDynamicAudioBufferSizeSupported;
550     }
551 
552     /**
553      * @return Dynamic Audio Buffer Capability
554      */
getBufferConstraints()555     BufferConstraints getBufferConstraints() {
556         return new BufferConstraints(mBufferConstraintList);
557     }
558 
559     /**
560      * Set the dynamic audio buffer size
561      *
562      * @param codec the codecs to set
563      * @param size the size to set
564      */
setBufferLengthMillis(int codec, int value)565     boolean setBufferLengthMillis(int codec, int value) {
566         return mService.setBufferLengthMillisNative(codec, value);
567     }
568 
569     /**
570      * @return the mBondedDevices
571      */
getBondedDevices()572     BluetoothDevice[] getBondedDevices() {
573         BluetoothDevice[] bondedDeviceList = new BluetoothDevice[0];
574         try {
575             bondedDeviceList = mBondedDevices.toArray(bondedDeviceList);
576         } catch (ArrayStoreException ee) {
577             errorLog("Error retrieving bonded device array");
578         }
579         infoLog("getBondedDevices: length=" + bondedDeviceList.length);
580         return bondedDeviceList;
581     }
582 
583     // This function shall be invoked from BondStateMachine whenever the bond
584     // state changes.
585     @VisibleForTesting
onBondStateChanged(BluetoothDevice device, int state)586     void onBondStateChanged(BluetoothDevice device, int state) {
587         if (device == null) {
588             Log.w(TAG, "onBondStateChanged, device is null");
589             return;
590         }
591         try {
592             byte[] addrByte = Utils.getByteAddress(device);
593             DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
594             if (prop == null) {
595                 prop = mRemoteDevices.addDeviceProperties(addrByte);
596             }
597             prop.setBondState(state);
598 
599             if (state == BluetoothDevice.BOND_BONDED) {
600                 // add if not already in list
601                 if (!mBondedDevices.contains(device)) {
602                     debugLog("Adding bonded device:" + device);
603                     mBondedDevices.add(device);
604                 }
605             } else if (state == BluetoothDevice.BOND_NONE) {
606                 // remove device from list
607                 if (mBondedDevices.remove(device)) {
608                     debugLog("Removing bonded device:" + device);
609                 } else {
610                     debugLog("Failed to remove device: " + device);
611                 }
612             }
613             invalidateGetBondStateCache();
614         } catch (Exception ee) {
615             Log.w(TAG, "onBondStateChanged: Exception ", ee);
616         }
617     }
618 
getDiscoverableTimeout()619     int getDiscoverableTimeout() {
620         return mDiscoverableTimeout;
621     }
622 
setDiscoverableTimeout(int timeout)623     boolean setDiscoverableTimeout(int timeout) {
624         synchronized (mObject) {
625             return mService.setAdapterPropertyNative(
626                     AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
627                     Utils.intToByteArray(timeout));
628         }
629     }
630 
getProfileConnectionState(int profile)631     int getProfileConnectionState(int profile) {
632         synchronized (mObject) {
633             Pair<Integer, Integer> p = mProfileConnectionState.get(profile);
634             if (p != null) {
635                 return p.first;
636             }
637             return BluetoothProfile.STATE_DISCONNECTED;
638         }
639     }
640 
discoveryEndMillis()641     long discoveryEndMillis() {
642         return mDiscoveryEndMs;
643     }
644 
isDiscovering()645     boolean isDiscovering() {
646         return mDiscovering;
647     }
648 
sendConnectionStateChange(int profile, Intent connIntent)649     private void sendConnectionStateChange(int profile, Intent connIntent) {
650         BluetoothDevice device = connIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
651         int prevState = connIntent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
652         int state = connIntent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
653         Log.d(TAG,
654                 "PROFILE_CONNECTION_STATE_CHANGE: profile=" + profile + ", device=" + device + ", "
655                         + prevState + " -> " + state);
656         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CONNECTION_STATE_CHANGED, state,
657                 0 /* deprecated */, profile, mService.obfuscateAddress(device),
658                 mService.getMetricId(device));
659 
660         if (!isNormalStateTransition(prevState, state)) {
661             Log.w(TAG,
662                     "PROFILE_CONNECTION_STATE_CHANGE: unexpected transition for profile=" + profile
663                             + ", device=" + device + ", " + prevState + " -> " + state);
664         }
665         sendConnectionStateChange(device, profile, state, prevState);
666     }
667 
sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState)668     void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) {
669         if (!validateProfileConnectionState(state) || !validateProfileConnectionState(prevState)) {
670             // Previously, an invalid state was broadcast anyway,
671             // with the invalid state converted to -1 in the intent.
672             // Better to log an error and not send an intent with
673             // invalid contents or set mAdapterConnectionState to -1.
674             errorLog("sendConnectionStateChange: invalid state transition " + prevState + " -> "
675                     + state);
676             return;
677         }
678 
679         synchronized (mObject) {
680             updateProfileConnectionState(profile, state, prevState);
681 
682             if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
683                 int newAdapterState = convertToAdapterState(state);
684                 int prevAdapterState = convertToAdapterState(prevState);
685                 setConnectionState(newAdapterState);
686 
687                 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
688                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
689                 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, newAdapterState);
690                 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, prevAdapterState);
691                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
692                 Log.d(TAG, "ADAPTER_CONNECTION_STATE_CHANGE: " + device + ": " + prevAdapterState
693                         + " -> " + newAdapterState);
694                 if (!isNormalStateTransition(prevState, state)) {
695                     Log.w(TAG, "ADAPTER_CONNECTION_STATE_CHANGE: unexpected transition for profile="
696                             + profile + ", device=" + device + ", " + prevState + " -> " + state);
697                 }
698                 mService.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT,
699                         Utils.getTempAllowlistBroadcastOptions());
700             }
701         }
702     }
703 
validateProfileConnectionState(int state)704     private boolean validateProfileConnectionState(int state) {
705         return (state == BluetoothProfile.STATE_DISCONNECTED
706                 || state == BluetoothProfile.STATE_CONNECTING
707                 || state == BluetoothProfile.STATE_CONNECTED
708                 || state == BluetoothProfile.STATE_DISCONNECTING);
709     }
710 
convertToAdapterState(int state)711     private static int convertToAdapterState(int state) {
712         switch (state) {
713             case BluetoothProfile.STATE_DISCONNECTED:
714                 return BluetoothAdapter.STATE_DISCONNECTED;
715             case BluetoothProfile.STATE_DISCONNECTING:
716                 return BluetoothAdapter.STATE_DISCONNECTING;
717             case BluetoothProfile.STATE_CONNECTED:
718                 return BluetoothAdapter.STATE_CONNECTED;
719             case BluetoothProfile.STATE_CONNECTING:
720                 return BluetoothAdapter.STATE_CONNECTING;
721         }
722         Log.e(TAG, "convertToAdapterState, unknow state " + state);
723         return -1;
724     }
725 
isNormalStateTransition(int prevState, int nextState)726     private static boolean isNormalStateTransition(int prevState, int nextState) {
727         switch (prevState) {
728             case BluetoothProfile.STATE_DISCONNECTED:
729                 return nextState == BluetoothProfile.STATE_CONNECTING;
730             case BluetoothProfile.STATE_CONNECTED:
731                 return nextState == BluetoothProfile.STATE_DISCONNECTING;
732             case BluetoothProfile.STATE_DISCONNECTING:
733             case BluetoothProfile.STATE_CONNECTING:
734                 return (nextState == BluetoothProfile.STATE_DISCONNECTED) || (nextState
735                         == BluetoothProfile.STATE_CONNECTED);
736             default:
737                 return false;
738         }
739     }
740 
updateCountersAndCheckForConnectionStateChange(int state, int prevState)741     private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
742         switch (prevState) {
743             case BluetoothProfile.STATE_CONNECTING:
744                 if (mProfilesConnecting > 0) {
745                     mProfilesConnecting--;
746                 } else {
747                     Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting);
748                     throw new IllegalStateException(
749                             "Invalid state transition, " + prevState + " -> " + state);
750                 }
751                 break;
752 
753             case BluetoothProfile.STATE_CONNECTED:
754                 if (mProfilesConnected > 0) {
755                     mProfilesConnected--;
756                 } else {
757                     Log.e(TAG, "mProfilesConnected " + mProfilesConnected);
758                     throw new IllegalStateException(
759                             "Invalid state transition, " + prevState + " -> " + state);
760                 }
761                 break;
762 
763             case BluetoothProfile.STATE_DISCONNECTING:
764                 if (mProfilesDisconnecting > 0) {
765                     mProfilesDisconnecting--;
766                 } else {
767                     Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting);
768                     throw new IllegalStateException(
769                             "Invalid state transition, " + prevState + " -> " + state);
770                 }
771                 break;
772         }
773 
774         switch (state) {
775             case BluetoothProfile.STATE_CONNECTING:
776                 mProfilesConnecting++;
777                 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
778 
779             case BluetoothProfile.STATE_CONNECTED:
780                 mProfilesConnected++;
781                 return (mProfilesConnected == 1);
782 
783             case BluetoothProfile.STATE_DISCONNECTING:
784                 mProfilesDisconnecting++;
785                 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
786 
787             case BluetoothProfile.STATE_DISCONNECTED:
788                 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
789 
790             default:
791                 return true;
792         }
793     }
794 
updateProfileConnectionState(int profile, int newState, int oldState)795     private void updateProfileConnectionState(int profile, int newState, int oldState) {
796         // mProfileConnectionState is a hashmap -
797         // <Integer, Pair<Integer, Integer>>
798         // The key is the profile, the value is a pair. first element
799         // is the state and the second element is the number of devices
800         // in that state.
801         int numDev = 1;
802         int newHashState = newState;
803         boolean update = true;
804 
805         // The following conditions are considered in this function:
806         // 1. If there is no record of profile and state - update
807         // 2. If a new device's state is current hash state - increment
808         //    number of devices in the state.
809         // 3. If a state change has happened to Connected or Connecting
810         //    (if current state is not connected), update.
811         // 4. If numDevices is 1 and that device state is being updated, update
812         // 5. If numDevices is > 1 and one of the devices is changing state,
813         //    decrement numDevices but maintain oldState if it is Connected or
814         //    Connecting
815         Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
816         if (stateNumDev != null) {
817             int currHashState = stateNumDev.first;
818             numDev = stateNumDev.second;
819 
820             if (newState == currHashState) {
821                 numDev++;
822             } else if (newState == BluetoothProfile.STATE_CONNECTED || (
823                     newState == BluetoothProfile.STATE_CONNECTING
824                             && currHashState != BluetoothProfile.STATE_CONNECTED)) {
825                 numDev = 1;
826             } else if (numDev == 1 && oldState == currHashState) {
827                 update = true;
828             } else if (numDev > 1 && oldState == currHashState) {
829                 numDev--;
830 
831                 if (currHashState == BluetoothProfile.STATE_CONNECTED
832                         || currHashState == BluetoothProfile.STATE_CONNECTING) {
833                     newHashState = currHashState;
834                 }
835             } else {
836                 update = false;
837             }
838         }
839 
840         if (update) {
841             mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState, numDev));
842             invalidateGetProfileConnectionStateCache();
843         }
844     }
845 
adapterPropertyChangedCallback(int[] types, byte[][] values)846     void adapterPropertyChangedCallback(int[] types, byte[][] values) {
847         Intent intent;
848         int type;
849         byte[] val;
850         for (int i = 0; i < types.length; i++) {
851             val = values[i];
852             type = types[i];
853             infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length);
854             synchronized (mObject) {
855                 switch (type) {
856                     case AbstractionLayer.BT_PROPERTY_BDNAME:
857                         mName = new String(val);
858                         intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
859                         intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName);
860                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
861                         mService.sendBroadcastAsUser(intent, UserHandle.ALL,
862                                 BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
863                         debugLog("Name is: " + mName);
864                         break;
865                     case AbstractionLayer.BT_PROPERTY_BDADDR:
866                         mAddress = val;
867                         String address = Utils.getAddressStringFromByte(mAddress);
868                         intent = new Intent(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
869                         intent.putExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS, address);
870                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
871                         mService.sendBroadcastAsUser(intent, UserHandle.ALL,
872                                 BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions());
873                         break;
874                     case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
875                         if (val == null || val.length != 3) {
876                             debugLog("Invalid BT CoD value from stack.");
877                             return;
878                         }
879                         int bluetoothClass =
880                                 ((int) val[0] << 16) + ((int) val[1] << 8) + (int) val[2];
881                         if (bluetoothClass != 0) {
882                             mBluetoothClass = new BluetoothClass(bluetoothClass);
883                         }
884                         debugLog("BT Class:" + mBluetoothClass);
885                         break;
886                     case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE:
887                         int mode = Utils.byteArrayToInt(val, 0);
888                         mScanMode = AdapterService.convertScanModeFromHal(mode);
889                         intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
890                         intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode);
891                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
892                         mService.sendBroadcast(intent, BLUETOOTH_SCAN,
893                                 Utils.getTempAllowlistBroadcastOptions());
894                         debugLog("Scan Mode:" + mScanMode);
895                         break;
896                     case AbstractionLayer.BT_PROPERTY_UUIDS:
897                         mUuids = Utils.byteArrayToUuid(val);
898                         break;
899                     case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES:
900                         int number = val.length / BD_ADDR_LEN;
901                         byte[] addrByte = new byte[BD_ADDR_LEN];
902                         for (int j = 0; j < number; j++) {
903                             System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN);
904                             onBondStateChanged(mAdapter.getRemoteDevice(
905                                     Utils.getAddressStringFromByte(addrByte)),
906                                     BluetoothDevice.BOND_BONDED);
907                         }
908                         break;
909                     case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
910                         mDiscoverableTimeout = Utils.byteArrayToInt(val, 0);
911                         debugLog("Discoverable Timeout:" + mDiscoverableTimeout);
912                         break;
913 
914                     case AbstractionLayer.BT_PROPERTY_LOCAL_LE_FEATURES:
915                         updateFeatureSupport(val);
916                         break;
917 
918                     case AbstractionLayer.BT_PROPERTY_DYNAMIC_AUDIO_BUFFER:
919                         updateDynamicAudioBufferSupport(val);
920                         break;
921 
922                     case AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS:
923                         mLocalIOCapability = Utils.byteArrayToInt(val);
924                         debugLog("mLocalIOCapability set to " + mLocalIOCapability);
925                         break;
926 
927                     case AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS_BLE:
928                         mLocalIOCapabilityBLE = Utils.byteArrayToInt(val);
929                         debugLog("mLocalIOCapabilityBLE set to " + mLocalIOCapabilityBLE);
930                         break;
931 
932                     default:
933                         errorLog("Property change not handled in Java land:" + type);
934                 }
935             }
936         }
937     }
938 
updateFeatureSupport(byte[] val)939     private void updateFeatureSupport(byte[] val) {
940         mVersSupported = ((0xFF & ((int) val[1])) << 8) + (0xFF & ((int) val[0]));
941         mNumOfAdvertisementInstancesSupported = (0xFF & ((int) val[3]));
942         mRpaOffloadSupported = ((0xFF & ((int) val[4])) != 0);
943         mNumOfOffloadedIrkSupported = (0xFF & ((int) val[5]));
944         mNumOfOffloadedScanFilterSupported = (0xFF & ((int) val[6]));
945         mIsActivityAndEnergyReporting = ((0xFF & ((int) val[7])) != 0);
946         mOffloadedScanResultStorageBytes = ((0xFF & ((int) val[9])) << 8) + (0xFF & ((int) val[8]));
947         mTotNumOfTrackableAdv = ((0xFF & ((int) val[11])) << 8) + (0xFF & ((int) val[10]));
948         mIsExtendedScanSupported = ((0xFF & ((int) val[12])) != 0);
949         mIsDebugLogSupported = ((0xFF & ((int) val[13])) != 0);
950         mIsLe2MPhySupported = ((0xFF & ((int) val[14])) != 0);
951         mIsLeCodedPhySupported = ((0xFF & ((int) val[15])) != 0);
952         mIsLeExtendedAdvertisingSupported = ((0xFF & ((int) val[16])) != 0);
953         mIsLePeriodicAdvertisingSupported = ((0xFF & ((int) val[17])) != 0);
954         mLeMaximumAdvertisingDataLength =
955                 (0xFF & ((int) val[18])) + ((0xFF & ((int) val[19])) << 8);
956         mDynamicAudioBufferSizeSupportedCodecsGroup1 =
957                 ((0xFF & ((int) val[21])) << 8) + (0xFF & ((int) val[20]));
958         mDynamicAudioBufferSizeSupportedCodecsGroup2 =
959                 ((0xFF & ((int) val[23])) << 8) + (0xFF & ((int) val[22]));
960 
961         Log.d(TAG, "BT_PROPERTY_LOCAL_LE_FEATURES: update from BT controller"
962                 + " mNumOfAdvertisementInstancesSupported = "
963                 + mNumOfAdvertisementInstancesSupported + " mRpaOffloadSupported = "
964                 + mRpaOffloadSupported + " mNumOfOffloadedIrkSupported = "
965                 + mNumOfOffloadedIrkSupported + " mNumOfOffloadedScanFilterSupported = "
966                 + mNumOfOffloadedScanFilterSupported + " mOffloadedScanResultStorageBytes= "
967                 + mOffloadedScanResultStorageBytes + " mIsActivityAndEnergyReporting = "
968                 + mIsActivityAndEnergyReporting + " mVersSupported = " + mVersSupported
969                 + " mTotNumOfTrackableAdv = " + mTotNumOfTrackableAdv
970                 + " mIsExtendedScanSupported = " + mIsExtendedScanSupported
971                 + " mIsDebugLogSupported = " + mIsDebugLogSupported + " mIsLe2MPhySupported = "
972                 + mIsLe2MPhySupported + " mIsLeCodedPhySupported = " + mIsLeCodedPhySupported
973                 + " mIsLeExtendedAdvertisingSupported = " + mIsLeExtendedAdvertisingSupported
974                 + " mIsLePeriodicAdvertisingSupported = " + mIsLePeriodicAdvertisingSupported
975                 + " mLeMaximumAdvertisingDataLength = " + mLeMaximumAdvertisingDataLength
976                 + " mDynamicAudioBufferSizeSupportedCodecsGroup1 = "
977                 + mDynamicAudioBufferSizeSupportedCodecsGroup1
978                 + " mDynamicAudioBufferSizeSupportedCodecsGroup2 = "
979                 + mDynamicAudioBufferSizeSupportedCodecsGroup2);
980         invalidateIsOffloadedFilteringSupportedCache();
981     }
982 
updateDynamicAudioBufferSupport(byte[] val)983     private void updateDynamicAudioBufferSupport(byte[] val) {
984         // bufferConstraints is the table indicates the capability of all the codecs
985         // with buffer time. The raw is codec number, and the column is buffer type. There are 3
986         // buffer types - default/maximum/minimum.
987         // The maximum number of raw is BUFFER_CODEC_MAX_NUM(32).
988         // The maximum number of column is BUFFER_TYPE_MAX(3).
989         // The array element indicates the buffer time, the size is two octet.
990         mBufferConstraintList = new ArrayList<BufferConstraint>();
991 
992         for (int i = 0; i < BufferConstraints.BUFFER_CODEC_MAX_NUM; i++) {
993             int defaultBufferTime = ((0xFF & ((int) val[i * 6 + 1])) << 8)
994                     + (0xFF & ((int) val[i * 6]));
995             int maximumBufferTime = ((0xFF & ((int) val[i * 6 + 3])) << 8)
996                     + (0xFF & ((int) val[i * 6 + 2]));
997             int minimumBufferTime = ((0xFF & ((int) val[i * 6 + 5])) << 8)
998                     + (0xFF & ((int) val[i * 6 + 4]));
999             BufferConstraint bufferConstraint = new BufferConstraint(defaultBufferTime,
1000                     maximumBufferTime, minimumBufferTime);
1001             mBufferConstraintList.add(bufferConstraint);
1002         }
1003     }
1004 
onBluetoothReady()1005     void onBluetoothReady() {
1006         debugLog("onBluetoothReady, state=" + BluetoothAdapter.nameForState(getState())
1007                 + ", ScanMode=" + mScanMode);
1008 
1009         synchronized (mObject) {
1010             // Reset adapter and profile connection states
1011             setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
1012             mProfileConnectionState.clear();
1013             invalidateGetProfileConnectionStateCache();
1014             mProfilesConnected = 0;
1015             mProfilesConnecting = 0;
1016             mProfilesDisconnecting = 0;
1017             // adapterPropertyChangedCallback has already been received.  Set the scan mode.
1018             setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
1019             // This keeps NV up-to date on first-boot after flash.
1020             setDiscoverableTimeout(mDiscoverableTimeout);
1021         }
1022     }
1023 
onBleDisable()1024     void onBleDisable() {
1025         // Sequence BLE_ON to STATE_OFF - that is _complete_ OFF state.
1026         debugLog("onBleDisable");
1027         // Set the scan_mode to NONE (no incoming connections).
1028         setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
1029     }
1030 
discoveryStateChangeCallback(int state)1031     void discoveryStateChangeCallback(int state) {
1032         infoLog("Callback:discoveryStateChangeCallback with state:" + state);
1033         synchronized (mObject) {
1034             Intent intent;
1035             if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
1036                 mDiscovering = false;
1037                 mService.clearDiscoveringPackages();
1038                 mDiscoveryEndMs = System.currentTimeMillis();
1039                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
1040                 mService.sendBroadcast(intent, BLUETOOTH_SCAN,
1041                         Utils.getTempAllowlistBroadcastOptions());
1042             } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
1043                 mDiscovering = true;
1044                 mDiscoveryEndMs = System.currentTimeMillis() + DEFAULT_DISCOVERY_TIMEOUT_MS;
1045                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
1046                 mService.sendBroadcast(intent, BLUETOOTH_SCAN,
1047                         Utils.getTempAllowlistBroadcastOptions());
1048             }
1049         }
1050     }
1051 
dump(FileDescriptor fd, PrintWriter writer, String[] args)1052     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
1053         writer.println(TAG);
1054         writer.println("  " + "Name: " + getName());
1055         writer.println("  " + "Address: " + Utils.getAddressStringFromByte(mAddress));
1056         writer.println("  " + "BluetoothClass: " + getBluetoothClass());
1057         writer.println("  " + "ScanMode: " + dumpScanMode(getScanMode()));
1058         writer.println("  " + "ConnectionState: " + dumpConnectionState(getConnectionState()));
1059         writer.println("  " + "State: " + BluetoothAdapter.nameForState(getState()));
1060         writer.println("  " + "MaxConnectedAudioDevices: " + getMaxConnectedAudioDevices());
1061         writer.println("  " + "A2dpOffloadEnabled: " + mA2dpOffloadEnabled);
1062         writer.println("  " + "Discovering: " + mDiscovering);
1063         writer.println("  " + "DiscoveryEndMs: " + mDiscoveryEndMs);
1064 
1065         writer.println("  " + "Bonded devices:");
1066         for (BluetoothDevice device : mBondedDevices) {
1067             writer.println(
1068                     "    " + device.getAddress() + " [" + dumpDeviceType(device.getType()) + "] "
1069                             + Utils.getName(device));
1070         }
1071     }
1072 
dumpDeviceType(int deviceType)1073     private String dumpDeviceType(int deviceType) {
1074         switch (deviceType) {
1075             case BluetoothDevice.DEVICE_TYPE_UNKNOWN:
1076                 return " ???? ";
1077             case BluetoothDevice.DEVICE_TYPE_CLASSIC:
1078                 return "BR/EDR";
1079             case BluetoothDevice.DEVICE_TYPE_LE:
1080                 return "  LE  ";
1081             case BluetoothDevice.DEVICE_TYPE_DUAL:
1082                 return " DUAL ";
1083             default:
1084                 return "Invalid device type: " + deviceType;
1085         }
1086     }
1087 
dumpConnectionState(int state)1088     private String dumpConnectionState(int state) {
1089         switch (state) {
1090             case BluetoothAdapter.STATE_DISCONNECTED:
1091                 return "STATE_DISCONNECTED";
1092             case BluetoothAdapter.STATE_DISCONNECTING:
1093                 return "STATE_DISCONNECTING";
1094             case BluetoothAdapter.STATE_CONNECTING:
1095                 return "STATE_CONNECTING";
1096             case BluetoothAdapter.STATE_CONNECTED:
1097                 return "STATE_CONNECTED";
1098             default:
1099                 return "Unknown Connection State " + state;
1100         }
1101     }
1102 
dumpScanMode(int scanMode)1103     private String dumpScanMode(int scanMode) {
1104         switch (scanMode) {
1105             case BluetoothAdapter.SCAN_MODE_NONE:
1106                 return "SCAN_MODE_NONE";
1107             case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
1108                 return "SCAN_MODE_CONNECTABLE";
1109             case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
1110                 return "SCAN_MODE_CONNECTABLE_DISCOVERABLE";
1111             default:
1112                 return "Unknown Scan Mode " + scanMode;
1113         }
1114     }
1115 
infoLog(String msg)1116     private static void infoLog(String msg) {
1117         if (VDBG) {
1118             Log.i(TAG, msg);
1119         }
1120     }
1121 
debugLog(String msg)1122     private static void debugLog(String msg) {
1123         if (DBG) {
1124             Log.d(TAG, msg);
1125         }
1126     }
1127 
errorLog(String msg)1128     private static void errorLog(String msg) {
1129         Log.e(TAG, msg);
1130     }
1131 }
1132