• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012-2014 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.app.ActivityThread;
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothAssignedNumbers;
25 import android.bluetooth.BluetoothClass;
26 import android.bluetooth.BluetoothDevice;
27 import android.bluetooth.BluetoothHeadset;
28 import android.bluetooth.BluetoothProfile;
29 import android.bluetooth.IBluetoothConnectionCallback;
30 import android.content.Attributable;
31 import android.content.BroadcastReceiver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.net.MacAddress;
36 import android.os.Handler;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.ParcelUuid;
40 import android.os.RemoteException;
41 import android.util.Log;
42 
43 import com.android.bluetooth.BluetoothStatsLog;
44 import com.android.bluetooth.R;
45 import com.android.bluetooth.Utils;
46 import com.android.bluetooth.hfp.HeadsetHalConstants;
47 import com.android.internal.annotations.VisibleForTesting;
48 
49 import java.util.ArrayList;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.LinkedList;
53 import java.util.Queue;
54 import java.util.Set;
55 import java.util.function.Predicate;
56 
57 final class RemoteDevices {
58     private static final boolean DBG = false;
59     private static final String TAG = "BluetoothRemoteDevices";
60 
61     // Maximum number of device properties to remember
62     private static final int MAX_DEVICE_QUEUE_SIZE = 200;
63 
64     private static BluetoothAdapter sAdapter;
65     private static AdapterService sAdapterService;
66     private static ArrayList<BluetoothDevice> sSdpTracker;
67     private final Object mObject = new Object();
68 
69     private static final int UUID_INTENT_DELAY = 6000;
70     private static final int MESSAGE_UUID_INTENT = 1;
71 
72     private final HashMap<String, DeviceProperties> mDevices;
73     private Queue<String> mDeviceQueue;
74 
75     private final Handler mHandler;
76     private class RemoteDevicesHandler extends Handler {
77 
78         /**
79          * Handler must be created from an explicit looper to avoid threading ambiguity
80          * @param looper The looper that this handler should be executed on
81          */
RemoteDevicesHandler(Looper looper)82         RemoteDevicesHandler(Looper looper) {
83             super(looper);
84         }
85 
86         @Override
handleMessage(Message msg)87         public void handleMessage(Message msg) {
88             switch (msg.what) {
89                 case MESSAGE_UUID_INTENT:
90                     BluetoothDevice device = (BluetoothDevice) msg.obj;
91                     Attributable.setAttributionSource(device,
92                             ActivityThread.currentAttributionSource());
93                     if (device != null) {
94                         DeviceProperties prop = getDeviceProperties(device);
95                         sendUuidIntent(device, prop);
96                     }
97                     break;
98             }
99         }
100     }
101 
102     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
103         @Override
104         public void onReceive(Context context, Intent intent) {
105             String action = intent.getAction();
106             switch (action) {
107                 case BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED:
108                     onHfIndicatorValueChanged(intent);
109                     break;
110                 case BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT:
111                     onVendorSpecificHeadsetEvent(intent);
112                     break;
113                 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
114                     onHeadsetConnectionStateChanged(intent);
115                     break;
116                 default:
117                     Log.w(TAG, "Unhandled intent: " + intent);
118                     break;
119             }
120         }
121     };
122 
123     /**
124      * Predicate that tests if the given {@link BluetoothDevice} is well-known
125      * to be used for physical location.
126      */
127     private final Predicate<BluetoothDevice> mLocationDenylistPredicate = (device) -> {
128         final MacAddress parsedAddress = MacAddress.fromString(device.getAddress());
129         if (sAdapterService.getLocationDenylistMac().test(parsedAddress.toByteArray())) {
130             Log.v(TAG, "Skipping device matching denylist: " + parsedAddress);
131             return true;
132         }
133         final String name = Utils.getName(device);
134         if (sAdapterService.getLocationDenylistName().test(name)) {
135             Log.v(TAG, "Skipping name matching denylist: " + name);
136             return true;
137         }
138         return false;
139     };
140 
RemoteDevices(AdapterService service, Looper looper)141     RemoteDevices(AdapterService service, Looper looper) {
142         sAdapter = BluetoothAdapter.getDefaultAdapter();
143         sAdapterService = service;
144         sSdpTracker = new ArrayList<BluetoothDevice>();
145         mDevices = new HashMap<String, DeviceProperties>();
146         mDeviceQueue = new LinkedList<String>();
147         mHandler = new RemoteDevicesHandler(looper);
148     }
149 
150     /**
151      * Init should be called before using this RemoteDevices object
152      */
init()153     void init() {
154         IntentFilter filter = new IntentFilter();
155         filter.addAction(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
156         filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
157         filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
158                 + BluetoothAssignedNumbers.PLANTRONICS);
159         filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
160                 + BluetoothAssignedNumbers.APPLE);
161         filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
162         sAdapterService.registerReceiver(mReceiver, filter);
163     }
164 
165     /**
166      * Clean up should be called when this object is no longer needed, must be called after init()
167      */
cleanup()168     void cleanup() {
169         // Unregister receiver first, mAdapterService is never null
170         sAdapterService.unregisterReceiver(mReceiver);
171         reset();
172     }
173 
174     /**
175      * Reset should be called when the state of this object needs to be cleared
176      * RemoteDevices is still usable after reset
177      */
reset()178     void reset() {
179         if (sSdpTracker != null) {
180             sSdpTracker.clear();
181         }
182 
183         if (mDevices != null) {
184             mDevices.clear();
185         }
186 
187         if (mDeviceQueue != null) {
188             mDeviceQueue.clear();
189         }
190     }
191 
192     @Override
clone()193     public Object clone() throws CloneNotSupportedException {
194         throw new CloneNotSupportedException();
195     }
196 
getDeviceProperties(BluetoothDevice device)197     DeviceProperties getDeviceProperties(BluetoothDevice device) {
198         synchronized (mDevices) {
199             return mDevices.get(device.getAddress());
200         }
201     }
202 
getDevice(byte[] address)203     BluetoothDevice getDevice(byte[] address) {
204         DeviceProperties prop = mDevices.get(Utils.getAddressStringFromByte(address));
205         if (prop == null) {
206             return null;
207         }
208         return prop.getDevice();
209     }
210 
211     @VisibleForTesting
addDeviceProperties(byte[] address)212     DeviceProperties addDeviceProperties(byte[] address) {
213         synchronized (mDevices) {
214             DeviceProperties prop = new DeviceProperties();
215             prop.mDevice = sAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
216             prop.mAddress = address;
217             String key = Utils.getAddressStringFromByte(address);
218             DeviceProperties pv = mDevices.put(key, prop);
219 
220             if (pv == null) {
221                 mDeviceQueue.offer(key);
222                 if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) {
223                     String deleteKey = mDeviceQueue.poll();
224                     for (BluetoothDevice device : sAdapterService.getBondedDevices()) {
225                         if (device.getAddress().equals(deleteKey)) {
226                             return prop;
227                         }
228                     }
229                     debugLog("Removing device " + deleteKey + " from property map");
230                     mDevices.remove(deleteKey);
231                 }
232             }
233             return prop;
234         }
235     }
236 
237     class DeviceProperties {
238         private String mName;
239         private byte[] mAddress;
240         private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED;
241         private short mRssi;
242         private String mAlias;
243         private BluetoothDevice mDevice;
244         private boolean mIsBondingInitiatedLocally;
245         private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
246         @VisibleForTesting int mBondState;
247         @VisibleForTesting int mDeviceType;
248         @VisibleForTesting ParcelUuid[] mUuids;
249 
DeviceProperties()250         DeviceProperties() {
251             mBondState = BluetoothDevice.BOND_NONE;
252         }
253 
254         /**
255          * @return the mName
256          */
getName()257         String getName() {
258             synchronized (mObject) {
259                 return mName;
260             }
261         }
262 
263         /**
264          * @return the mClass
265          */
getBluetoothClass()266         int getBluetoothClass() {
267             synchronized (mObject) {
268                 return mBluetoothClass;
269             }
270         }
271 
272         /**
273          * @return the mUuids
274          */
getUuids()275         ParcelUuid[] getUuids() {
276             synchronized (mObject) {
277                 return mUuids;
278             }
279         }
280 
281         /**
282          * @return the mAddress
283          */
getAddress()284         byte[] getAddress() {
285             synchronized (mObject) {
286                 return mAddress;
287             }
288         }
289 
290         /**
291          * @return the mDevice
292          */
getDevice()293         BluetoothDevice getDevice() {
294             synchronized (mObject) {
295                 return mDevice;
296             }
297         }
298 
299         /**
300          * @return mRssi
301          */
getRssi()302         short getRssi() {
303             synchronized (mObject) {
304                 return mRssi;
305             }
306         }
307         /**
308          * @return mDeviceType
309          */
getDeviceType()310         int getDeviceType() {
311             synchronized (mObject) {
312                 return mDeviceType;
313             }
314         }
315 
316         /**
317          * @return the mAlias
318          */
getAlias()319         String getAlias() {
320             synchronized (mObject) {
321                 return mAlias;
322             }
323         }
324 
325         /**
326          * @param mAlias the mAlias to set
327          */
setAlias(BluetoothDevice device, String mAlias)328         void setAlias(BluetoothDevice device, String mAlias) {
329             synchronized (mObject) {
330                 this.mAlias = mAlias;
331                 sAdapterService.setDevicePropertyNative(mAddress,
332                         AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
333                 Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
334                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
335                 intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias);
336                 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
337                         Utils.getTempAllowlistBroadcastOptions());
338             }
339         }
340 
341         /**
342          * @param mBondState the mBondState to set
343          */
setBondState(int newBondState)344         void setBondState(int newBondState) {
345             synchronized (mObject) {
346                 if ((mBondState == BluetoothDevice.BOND_BONDED
347                         && newBondState == BluetoothDevice.BOND_BONDING)
348                         || newBondState == BluetoothDevice.BOND_NONE) {
349                     /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
350                     cachedBluetoothDevice issued a connect using the local cached copy of uuids,
351                     without waiting for the ACTION_UUID intent.
352                     This was resulting in multiple calls to connect().*/
353                     mUuids = null;
354                     mAlias = null;
355                 }
356                 mBondState = newBondState;
357             }
358         }
359 
360         /**
361          * @return the mBondState
362          */
getBondState()363         int getBondState() {
364             synchronized (mObject) {
365                 return mBondState;
366             }
367         }
368 
isBonding()369         boolean isBonding() {
370             return getBondState() == BluetoothDevice.BOND_BONDING;
371         }
372 
isBondingOrBonded()373         boolean isBondingOrBonded() {
374             return isBonding() || getBondState() == BluetoothDevice.BOND_BONDED;
375         }
376 
377         /**
378          * @param isBondingInitiatedLocally wether bonding is initiated locally
379          */
setBondingInitiatedLocally(boolean isBondingInitiatedLocally)380         void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) {
381             synchronized (mObject) {
382                 this.mIsBondingInitiatedLocally = isBondingInitiatedLocally;
383             }
384         }
385 
386         /**
387          * @return the isBondingInitiatedLocally
388          */
isBondingInitiatedLocally()389         boolean isBondingInitiatedLocally() {
390             synchronized (mObject) {
391                 return mIsBondingInitiatedLocally;
392             }
393         }
394 
getBatteryLevel()395         int getBatteryLevel() {
396             synchronized (mObject) {
397                 return mBatteryLevel;
398             }
399         }
400 
401         /**
402          * @param batteryLevel the mBatteryLevel to set
403          */
setBatteryLevel(int batteryLevel)404         void setBatteryLevel(int batteryLevel) {
405             synchronized (mObject) {
406                 this.mBatteryLevel = batteryLevel;
407             }
408         }
409     }
410 
sendUuidIntent(BluetoothDevice device, DeviceProperties prop)411     private void sendUuidIntent(BluetoothDevice device, DeviceProperties prop) {
412         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
413         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
414         intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null ? null : prop.mUuids);
415         sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
416                 Utils.getTempAllowlistBroadcastOptions());
417 
418         //Remove the outstanding UUID request
419         sSdpTracker.remove(device);
420     }
421 
422     /**
423      * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing,
424      * we must add device first before setting it's properties. This is a helper method for doing
425      * that.
426      */
setBondingInitiatedLocally(byte[] address)427     void setBondingInitiatedLocally(byte[] address) {
428         DeviceProperties properties;
429 
430         BluetoothDevice device = getDevice(address);
431         if (device == null) {
432             properties = addDeviceProperties(address);
433         } else {
434             properties = getDeviceProperties(device);
435         }
436 
437         properties.setBondingInitiatedLocally(true);
438     }
439 
440     /**
441      * Update battery level in device properties
442      * @param device The remote device to be updated
443      * @param batteryLevel Battery level Indicator between 0-100,
444      *                    {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} is error
445      */
446     @VisibleForTesting
updateBatteryLevel(BluetoothDevice device, int batteryLevel)447     void updateBatteryLevel(BluetoothDevice device, int batteryLevel) {
448         if (device == null || batteryLevel < 0 || batteryLevel > 100) {
449             warnLog("Invalid parameters device=" + String.valueOf(device == null)
450                     + ", batteryLevel=" + String.valueOf(batteryLevel));
451             return;
452         }
453         DeviceProperties deviceProperties = getDeviceProperties(device);
454         if (deviceProperties == null) {
455             deviceProperties = addDeviceProperties(Utils.getByteAddress(device));
456         }
457         synchronized (mObject) {
458             int currentBatteryLevel = deviceProperties.getBatteryLevel();
459             if (batteryLevel == currentBatteryLevel) {
460                 debugLog("Same battery level for device " + device + " received " + String.valueOf(
461                         batteryLevel) + "%");
462                 return;
463             }
464             deviceProperties.setBatteryLevel(batteryLevel);
465         }
466         sendBatteryLevelChangedBroadcast(device, batteryLevel);
467         Log.d(TAG, "Updated device " + device + " battery level to " + batteryLevel + "%");
468     }
469 
470     /**
471      * Reset battery level property to {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} for a device
472      * @param device device whose battery level property needs to be reset
473      */
474     @VisibleForTesting
resetBatteryLevel(BluetoothDevice device)475     void resetBatteryLevel(BluetoothDevice device) {
476         if (device == null) {
477             warnLog("Device is null");
478             return;
479         }
480         DeviceProperties deviceProperties = getDeviceProperties(device);
481         if (deviceProperties == null) {
482             return;
483         }
484         synchronized (mObject) {
485             if (deviceProperties.getBatteryLevel() == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
486                 debugLog("Battery level was never set or is already reset, device=" + device);
487                 return;
488             }
489             deviceProperties.setBatteryLevel(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
490         }
491         sendBatteryLevelChangedBroadcast(device, BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
492         Log.d(TAG, "Reset battery level, device=" + device);
493     }
494 
sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel)495     private void sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel) {
496         Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED);
497         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
498         intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel);
499         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
500         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
501         sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
502                 Utils.getTempAllowlistBroadcastOptions());
503     }
504 
areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2)505     private static boolean areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2) {
506         final int length1 = uuids1 == null ? 0 : uuids1.length;
507         final int length2 = uuids2 == null ? 0 : uuids2.length;
508         if (length1 != length2) {
509             return false;
510         }
511         Set<ParcelUuid> set = new HashSet<>();
512         for (int i = 0; i < length1; ++i) {
513             set.add(uuids1[i]);
514         }
515         for (int i = 0; i < length2; ++i) {
516             set.remove(uuids2[i]);
517         }
518         return set.isEmpty();
519     }
520 
devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values)521     void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
522         Intent intent;
523         byte[] val;
524         int type;
525         BluetoothDevice bdDevice = getDevice(address);
526         DeviceProperties device;
527         if (bdDevice == null) {
528             debugLog("Added new device property");
529             device = addDeviceProperties(address);
530             bdDevice = getDevice(address);
531         } else {
532             device = getDeviceProperties(bdDevice);
533         }
534 
535         if (types.length <= 0) {
536             errorLog("No properties to update");
537             return;
538         }
539 
540         for (int j = 0; j < types.length; j++) {
541             type = types[j];
542             val = values[j];
543             if (val.length > 0) {
544                 synchronized (mObject) {
545                     debugLog("Property type: " + type);
546                     switch (type) {
547                         case AbstractionLayer.BT_PROPERTY_BDNAME:
548                             final String newName = new String(val);
549                             if (newName.equals(device.mName)) {
550                                 debugLog("Skip name update for " + bdDevice);
551                                 break;
552                             }
553                             device.mName = newName;
554                             intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
555                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
556                             intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
557                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
558                             sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
559                                     Utils.getTempAllowlistBroadcastOptions());
560                             debugLog("Remote Device name is: " + device.mName);
561                             break;
562                         case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
563                             device.mAlias = new String(val);
564                             debugLog("Remote device alias is: " + device.mAlias);
565                             break;
566                         case AbstractionLayer.BT_PROPERTY_BDADDR:
567                             device.mAddress = val;
568                             debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
569                             break;
570                         case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
571                             final int newClass = Utils.byteArrayToInt(val);
572                             if (newClass == device.mBluetoothClass) {
573                                 debugLog("Skip class update for " + bdDevice);
574                                 break;
575                             }
576                             device.mBluetoothClass = Utils.byteArrayToInt(val);
577                             intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
578                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
579                             intent.putExtra(BluetoothDevice.EXTRA_CLASS,
580                                     new BluetoothClass(device.mBluetoothClass));
581                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
582                             sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
583                                     Utils.getTempAllowlistBroadcastOptions());
584                             debugLog("Remote class is:" + device.mBluetoothClass);
585                             break;
586                         case AbstractionLayer.BT_PROPERTY_UUIDS:
587                             int numUuids = val.length / AbstractionLayer.BT_UUID_SIZE;
588                             final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val);
589                             if (areUuidsEqual(newUuids, device.mUuids)) {
590                                 debugLog( "Skip uuids update for " + bdDevice.getAddress());
591                                 break;
592                             }
593                             device.mUuids = newUuids;
594                             if (sAdapterService.getState() == BluetoothAdapter.STATE_ON) {
595                                 sAdapterService.deviceUuidUpdated(bdDevice);
596                                 sendUuidIntent(bdDevice, device);
597                             }
598                             break;
599                         case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
600                             // The device type from hal layer, defined in bluetooth.h,
601                             // matches the type defined in BluetoothDevice.java
602                             device.mDeviceType = Utils.byteArrayToInt(val);
603                             break;
604                         case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
605                             // RSSI from hal is in one byte
606                             device.mRssi = val[0];
607                             break;
608                     }
609                 }
610             }
611         }
612     }
613 
deviceFoundCallback(byte[] address)614     void deviceFoundCallback(byte[] address) {
615         // The device properties are already registered - we can send the intent
616         // now
617         BluetoothDevice device = getDevice(address);
618         debugLog("deviceFoundCallback: Remote Address is:" + device);
619         DeviceProperties deviceProp = getDeviceProperties(device);
620         if (deviceProp == null) {
621             errorLog("Device Properties is null for Device:" + device);
622             return;
623         }
624 
625         Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
626         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
627         intent.putExtra(BluetoothDevice.EXTRA_CLASS,
628                 new BluetoothClass(deviceProp.mBluetoothClass));
629         intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
630         intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
631 
632         final ArrayList<DiscoveringPackage> packages = sAdapterService.getDiscoveringPackages();
633         synchronized (packages) {
634             for (DiscoveringPackage pkg : packages) {
635                 if (pkg.hasDisavowedLocation()) {
636                     if (mLocationDenylistPredicate.test(device)) {
637                         continue;
638                     }
639                 }
640 
641                 intent.setPackage(pkg.getPackageName());
642 
643                 if (pkg.getPermission() != null) {
644                     sAdapterService.sendBroadcastMultiplePermissions(intent,
645                             new String[] { BLUETOOTH_SCAN, pkg.getPermission() },
646                             Utils.getTempAllowlistBroadcastOptions());
647                 } else {
648                     sAdapterService.sendBroadcastMultiplePermissions(intent,
649                             new String[] { BLUETOOTH_SCAN },
650                             Utils.getTempAllowlistBroadcastOptions());
651                 }
652             }
653         }
654     }
655 
aclStateChangeCallback(int status, byte[] address, int newState, int hciReason)656     void aclStateChangeCallback(int status, byte[] address, int newState, int hciReason) {
657         BluetoothDevice device = getDevice(address);
658 
659         if (device == null) {
660             errorLog("aclStateChangeCallback: device is NULL, address="
661                     + Utils.getAddressStringFromByte(address) + ", newState=" + newState);
662             return;
663         }
664         int state = sAdapterService.getState();
665 
666         Intent intent = null;
667         if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
668             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) {
669                 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
670             } else if (state == BluetoothAdapter.STATE_BLE_ON
671                     || state == BluetoothAdapter.STATE_BLE_TURNING_ON) {
672                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED);
673             }
674             debugLog(
675                     "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state)
676                             + " Connected: " + device);
677         } else {
678             if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
679                 // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding.
680                 intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
681                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
682                 intent.setPackage(sAdapterService.getString(R.string.pairing_ui_package));
683                 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
684                         Utils.getTempAllowlistBroadcastOptions());
685             }
686             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) {
687                 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
688             } else if (state == BluetoothAdapter.STATE_BLE_ON
689                     || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
690                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED);
691             }
692             // Reset battery level on complete disconnection
693             if (sAdapterService.getConnectionState(device) == 0) {
694                 resetBatteryLevel(device);
695             }
696             debugLog(
697                     "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state)
698                             + " Disconnected: " + device);
699         }
700 
701         int connectionState = newState == AbstractionLayer.BT_ACL_STATE_CONNECTED
702                 ? BluetoothAdapter.STATE_CONNECTED : BluetoothAdapter.STATE_DISCONNECTED;
703         int metricId = sAdapterService.getMetricId(device);
704         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACL_CONNECTION_STATE_CHANGED,
705                 sAdapterService.obfuscateAddress(device), connectionState, metricId);
706         BluetoothClass deviceClass = device.getBluetoothClass();
707         int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice();
708         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
709                 sAdapterService.obfuscateAddress(device), classOfDevice, metricId);
710 
711         if (intent != null) {
712             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
713             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
714                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
715             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
716             sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT,
717                     Utils.getTempAllowlistBroadcastOptions());
718 
719             synchronized (sAdapterService.getBluetoothConnectionCallbacks()) {
720                 Set<IBluetoothConnectionCallback> bluetoothConnectionCallbacks =
721                         sAdapterService.getBluetoothConnectionCallbacks();
722                 for (IBluetoothConnectionCallback callback : bluetoothConnectionCallbacks) {
723                     try {
724                         if (connectionState == BluetoothAdapter.STATE_CONNECTED) {
725                             callback.onDeviceConnected(device);
726                         } else {
727                             callback.onDeviceDisconnected(device,
728                                     AdapterService.hciToAndroidDisconnectReason(hciReason));
729                         }
730                     } catch (RemoteException ex) {
731                         Log.e(TAG, "RemoteException in calling IBluetoothConnectionCallback");
732                     }
733                 }
734             }
735         } else {
736             Log.e(TAG, "aclStateChangeCallback intent is null. deviceBondState: "
737                     + device.getBondState());
738         }
739     }
740 
741 
fetchUuids(BluetoothDevice device)742     void fetchUuids(BluetoothDevice device) {
743         if (sSdpTracker.contains(device)) {
744             return;
745         }
746 
747         // If no UUIDs are cached and the device is bonding, wait for SDP after the device is bonded
748         DeviceProperties deviceProperties = getDeviceProperties(device);
749         if (deviceProperties != null && deviceProperties.isBonding()
750                 && getDeviceProperties(device).getUuids() == null) {
751             return;
752         }
753 
754         sSdpTracker.add(device);
755 
756         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
757         message.obj = device;
758         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
759 
760         // Uses cached UUIDs if we are bonding. If not, we fetch the UUIDs with SDP.
761         if (deviceProperties == null || !deviceProperties.isBonding()) {
762             sAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
763         }
764     }
765 
updateUuids(BluetoothDevice device)766     void updateUuids(BluetoothDevice device) {
767         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
768         message.obj = device;
769         mHandler.sendMessage(message);
770     }
771 
772     /**
773      * Handles headset connection state change event
774      * @param intent must be {@link BluetoothHeadset#ACTION_CONNECTION_STATE_CHANGED} intent
775      */
776     @VisibleForTesting
onHeadsetConnectionStateChanged(Intent intent)777     void onHeadsetConnectionStateChanged(Intent intent) {
778         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
779         if (device == null) {
780             Log.e(TAG, "onHeadsetConnectionStateChanged() remote device is null");
781             return;
782         }
783         if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED)
784                 == BluetoothProfile.STATE_DISCONNECTED) {
785             // TODO: Rework this when non-HFP sources of battery level indication is added
786             resetBatteryLevel(device);
787         }
788     }
789 
790     @VisibleForTesting
onHfIndicatorValueChanged(Intent intent)791     void onHfIndicatorValueChanged(Intent intent) {
792         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
793         if (device == null) {
794             Log.e(TAG, "onHfIndicatorValueChanged() remote device is null");
795             return;
796         }
797         int indicatorId = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1);
798         int indicatorValue = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -1);
799         if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) {
800             updateBatteryLevel(device, indicatorValue);
801         }
802     }
803 
804     /**
805      * Handle {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
806      * @param intent must be {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
807      */
808     @VisibleForTesting
onVendorSpecificHeadsetEvent(Intent intent)809     void onVendorSpecificHeadsetEvent(Intent intent) {
810         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
811         if (device == null) {
812             Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null");
813             return;
814         }
815         String cmd =
816                 intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
817         if (cmd == null) {
818             Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null");
819             return;
820         }
821         int cmdType =
822                 intent.getIntExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
823                         -1);
824         // Only process set command
825         if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) {
826             debugLog("onVendorSpecificHeadsetEvent() only SET command is processed");
827             return;
828         }
829         Object[] args = (Object[]) intent.getExtras()
830                 .get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
831         if (args == null) {
832             Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null");
833             return;
834         }
835         int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
836         switch (cmd) {
837             case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT:
838                 batteryPercent = getBatteryLevelFromXEventVsc(args);
839                 break;
840             case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV:
841                 batteryPercent = getBatteryLevelFromAppleBatteryVsc(args);
842                 break;
843         }
844         if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
845             updateBatteryLevel(device, batteryPercent);
846             infoLog("Updated device " + device + " battery level to " + String.valueOf(
847                     batteryPercent) + "%");
848         }
849     }
850 
851     /**
852      * Parse
853      *      AT+IPHONEACCEV=[NumberOfIndicators],[IndicatorType],[IndicatorValue]
854      * vendor specific event
855      * @param args Array of arguments on the right side of assignment
856      * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
857      *         when there is an error parsing the arguments
858      */
859     @VisibleForTesting
getBatteryLevelFromAppleBatteryVsc(Object[] args)860     static int getBatteryLevelFromAppleBatteryVsc(Object[] args) {
861         if (args.length == 0) {
862             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() empty arguments");
863             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
864         }
865         int numKvPair;
866         if (args[0] instanceof Integer) {
867             numKvPair = (Integer) args[0];
868         } else {
869             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing number of arguments");
870             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
871         }
872         if (args.length != (numKvPair * 2 + 1)) {
873             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() number of arguments does not match");
874             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
875         }
876         int indicatorType;
877         int indicatorValue = -1;
878         for (int i = 0; i < numKvPair; ++i) {
879             Object indicatorTypeObj = args[2 * i + 1];
880             if (indicatorTypeObj instanceof Integer) {
881                 indicatorType = (Integer) indicatorTypeObj;
882             } else {
883                 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator type");
884                 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
885             }
886             if (indicatorType
887                     != BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL) {
888                 continue;
889             }
890             Object indicatorValueObj = args[2 * i + 2];
891             if (indicatorValueObj instanceof Integer) {
892                 indicatorValue = (Integer) indicatorValueObj;
893             } else {
894                 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator value");
895                 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
896             }
897             break;
898         }
899         return (indicatorValue < 0 || indicatorValue > 9) ? BluetoothDevice.BATTERY_LEVEL_UNKNOWN
900                 : (indicatorValue + 1) * 10;
901     }
902 
903     /**
904      * Parse
905      *      AT+XEVENT=BATTERY,[Level],[NumberOfLevel],[MinutesOfTalk],[IsCharging]
906      * vendor specific event
907      * @param args Array of arguments on the right side of SET command
908      * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
909      *         when there is an error parsing the arguments
910      */
911     @VisibleForTesting
getBatteryLevelFromXEventVsc(Object[] args)912     static int getBatteryLevelFromXEventVsc(Object[] args) {
913         if (args.length == 0) {
914             Log.w(TAG, "getBatteryLevelFromXEventVsc() empty arguments");
915             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
916         }
917         Object eventNameObj = args[0];
918         if (!(eventNameObj instanceof String)) {
919             Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event name");
920             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
921         }
922         String eventName = (String) eventNameObj;
923         if (!eventName.equals(
924                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL)) {
925             infoLog("getBatteryLevelFromXEventVsc() skip none BATTERY event: " + eventName);
926             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
927         }
928         if (args.length != 5) {
929             Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong battery level event length: "
930                     + String.valueOf(args.length));
931             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
932         }
933         if (!(args[1] instanceof Integer) || !(args[2] instanceof Integer)) {
934             Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event values");
935             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
936         }
937         int batteryLevel = (Integer) args[1];
938         int numberOfLevels = (Integer) args[2];
939         if (batteryLevel < 0 || numberOfLevels <= 1 || batteryLevel > numberOfLevels) {
940             Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong event value, batteryLevel="
941                     + String.valueOf(batteryLevel) + ", numberOfLevels=" + String.valueOf(
942                     numberOfLevels));
943             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
944         }
945         return batteryLevel * 100 / (numberOfLevels - 1);
946     }
947 
errorLog(String msg)948     private static void errorLog(String msg) {
949         Log.e(TAG, msg);
950     }
951 
debugLog(String msg)952     private static void debugLog(String msg) {
953         if (DBG) {
954             Log.d(TAG, msg);
955         }
956     }
957 
infoLog(String msg)958     private static void infoLog(String msg) {
959         if (DBG) {
960             Log.i(TAG, msg);
961         }
962     }
963 
warnLog(String msg)964     private static void warnLog(String msg) {
965         Log.w(TAG, msg);
966     }
967 
968 }
969