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