• 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.annotation.RequiresPermission;
23 import android.app.admin.SecurityLog;
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothAssignedNumbers;
26 import android.bluetooth.BluetoothClass;
27 import android.bluetooth.BluetoothDevice;
28 import android.bluetooth.BluetoothHeadset;
29 import android.bluetooth.BluetoothHeadsetClient;
30 import android.bluetooth.BluetoothManager;
31 import android.bluetooth.BluetoothProfile;
32 import android.bluetooth.BluetoothProtoEnums;
33 import android.bluetooth.BluetoothSinkAudioPolicy;
34 import android.bluetooth.IBluetoothConnectionCallback;
35 import android.content.BroadcastReceiver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.net.MacAddress;
40 import android.os.Handler;
41 import android.os.Looper;
42 import android.os.Message;
43 import android.os.ParcelUuid;
44 import android.os.RemoteException;
45 import android.os.SystemProperties;
46 import android.util.Log;
47 
48 import com.android.bluetooth.BluetoothStatsLog;
49 import com.android.bluetooth.R;
50 import com.android.bluetooth.Utils;
51 import com.android.bluetooth.bas.BatteryService;
52 import com.android.bluetooth.hfp.HeadsetHalConstants;
53 import com.android.internal.annotations.VisibleForTesting;
54 
55 import java.util.ArrayDeque;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.Objects;
60 import java.util.Set;
61 import java.util.function.Predicate;
62 
63 final class RemoteDevices {
64     private static final boolean DBG = false;
65     private static final String TAG = "BluetoothRemoteDevices";
66 
67     // Maximum number of device properties to remember
68     private static final int MAX_DEVICE_QUEUE_SIZE = 200;
69 
70     private BluetoothAdapter mAdapter;
71     private AdapterService mAdapterService;
72     private ArrayList<BluetoothDevice> mSdpTracker;
73     private final Object mObject = new Object();
74 
75     private static final int UUID_INTENT_DELAY = 6000;
76     private static final int MESSAGE_UUID_INTENT = 1;
77     private static final String LOG_SOURCE_DIS = "DIS";
78 
79     private final HashMap<String, DeviceProperties> mDevices;
80     private final HashMap<String, String> mDualDevicesMap;
81     private ArrayDeque<String> mDeviceQueue;
82 
83     /**
84      * Bluetooth HFP v1.8 specifies the Battery Charge indicator of AG can take values from
85      * {@code 0} to {@code 5}, but it does not specify how to map the values back to percentages.
86      * The following mapping is used:
87      *   - Level 0:                 0%
88      *   - Level 1: midpoint of  1-25%
89      *   - Level 2: midpoint of 26-50%
90      *   - Level 3: midpoint of 51-75%
91      *   - Level 4: midpoint of 76-99%
92      *   - Level 5:               100%
93      */
94     private static final int HFP_BATTERY_CHARGE_INDICATOR_0 = 0;
95     private static final int HFP_BATTERY_CHARGE_INDICATOR_1 = 13;
96     private static final int HFP_BATTERY_CHARGE_INDICATOR_2 = 38;
97     private static final int HFP_BATTERY_CHARGE_INDICATOR_3 = 63;
98     private static final int HFP_BATTERY_CHARGE_INDICATOR_4 = 88;
99     private static final int HFP_BATTERY_CHARGE_INDICATOR_5 = 100;
100 
101     private final Handler mHandler;
102     private class RemoteDevicesHandler extends Handler {
103 
104         /**
105          * Handler must be created from an explicit looper to avoid threading ambiguity
106          * @param looper The looper that this handler should be executed on
107          */
RemoteDevicesHandler(Looper looper)108         RemoteDevicesHandler(Looper looper) {
109             super(looper);
110         }
111 
112         @Override
handleMessage(Message msg)113         public void handleMessage(Message msg) {
114             switch (msg.what) {
115                 case MESSAGE_UUID_INTENT:
116                     BluetoothDevice device = (BluetoothDevice) msg.obj;
117                     if (device != null) {
118                         // SDP Sending delayed SDP UUID intent
119                         MetricsLogger.getInstance().cacheCount(
120                                 BluetoothProtoEnums.SDP_SENDING_DELAYED_UUID, 1);
121                         DeviceProperties prop = getDeviceProperties(device);
122                         sendUuidIntent(device, prop);
123                     } else {
124                         // SDP Not sending delayed SDP UUID intent b/c device is not there
125                         MetricsLogger.getInstance().cacheCount(
126                                 BluetoothProtoEnums.SDP_NOT_SENDING_DELAYED_UUID, 1);
127                     }
128                     break;
129             }
130         }
131     }
132 
133     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
134         @Override
135         public void onReceive(Context context, Intent intent) {
136             String action = intent.getAction();
137             switch (action) {
138                 case BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED:
139                     onHfIndicatorValueChanged(intent);
140                     break;
141                 case BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT:
142                     onVendorSpecificHeadsetEvent(intent);
143                     break;
144                 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
145                     onHeadsetConnectionStateChanged(intent);
146                     break;
147                 case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED:
148                     onHeadsetClientConnectionStateChanged(intent);
149                     break;
150                 case BluetoothHeadsetClient.ACTION_AG_EVENT:
151                     onAgIndicatorValueChanged(intent);
152                     break;
153                 default:
154                     Log.w(TAG, "Unhandled intent: " + intent);
155                     break;
156             }
157         }
158     };
159 
160     /**
161      * Predicate that tests if the given {@link BluetoothDevice} is well-known
162      * to be used for physical location.
163      */
164     private final Predicate<BluetoothDevice> mLocationDenylistPredicate = (device) -> {
165         final MacAddress parsedAddress = MacAddress.fromString(device.getAddress());
166         if (mAdapterService.getLocationDenylistMac().test(parsedAddress.toByteArray())) {
167             Log.v(TAG, "Skipping device matching denylist: " + device);
168             return true;
169         }
170         final String name = Utils.getName(device);
171         if (mAdapterService.getLocationDenylistName().test(name)) {
172             Log.v(TAG, "Skipping name matching denylist: " + name);
173             return true;
174         }
175         return false;
176     };
177 
RemoteDevices(AdapterService service, Looper looper)178     RemoteDevices(AdapterService service, Looper looper) {
179         mAdapter = ((Context) service).getSystemService(BluetoothManager.class).getAdapter();
180         mAdapterService = service;
181         mSdpTracker = new ArrayList<BluetoothDevice>();
182         mDevices = new HashMap<String, DeviceProperties>();
183         mDualDevicesMap = new HashMap<String, String>();
184         mDeviceQueue = new ArrayDeque<>();
185         mHandler = new RemoteDevicesHandler(looper);
186     }
187 
188     /**
189      * Init should be called before using this RemoteDevices object
190      */
init()191     void init() {
192         IntentFilter filter = new IntentFilter();
193         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
194         filter.addAction(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
195         filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
196         filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
197                 + BluetoothAssignedNumbers.PLANTRONICS);
198         filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
199                 + BluetoothAssignedNumbers.APPLE);
200         filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
201         filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
202         filter.addAction(BluetoothHeadsetClient.ACTION_AG_EVENT);
203         mAdapterService.registerReceiver(mReceiver, filter);
204     }
205 
206     /**
207      * Clean up should be called when this object is no longer needed, must be called after init()
208      */
209     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
cleanup()210     void cleanup() {
211         // Unregister receiver first, mAdapterService is never null
212         mAdapterService.unregisterReceiver(mReceiver);
213         reset();
214     }
215 
216     /**
217      * Reset should be called when the state of this object needs to be cleared
218      * RemoteDevices is still usable after reset
219      */
220     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
reset()221     void reset() {
222         if (mSdpTracker != null) {
223             mSdpTracker.clear();
224         }
225 
226         synchronized (mDevices) {
227             if (mDevices != null) {
228                 debugLog("reset(): Broadcasting ACL_DISCONNECTED");
229 
230                 mDevices.forEach((address, deviceProperties) -> {
231                     BluetoothDevice bluetoothDevice = deviceProperties.getDevice();
232 
233                     debugLog("reset(): address=" + address + ", connected="
234                             + bluetoothDevice.isConnected());
235 
236                     if (bluetoothDevice.isConnected()) {
237                         Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
238                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bluetoothDevice);
239                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
240                                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
241                         mAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT);
242                     }
243                 });
244                 mDevices.clear();
245             }
246         }
247 
248         if (mDualDevicesMap != null) {
249             mDualDevicesMap.clear();
250         }
251 
252         if (mDeviceQueue != null) {
253             mDeviceQueue.clear();
254         }
255     }
256 
257     @Override
clone()258     public Object clone() throws CloneNotSupportedException {
259         throw new CloneNotSupportedException();
260     }
261 
getDeviceProperties(BluetoothDevice device)262     DeviceProperties getDeviceProperties(BluetoothDevice device) {
263         synchronized (mDevices) {
264             String address = mDualDevicesMap.get(device.getAddress());
265             // If the device is not in the dual map, use its original address
266             if (address == null || mDevices.get(address) == null) {
267                 address = device.getAddress();
268             }
269             return mDevices.get(address);
270         }
271     }
272 
getDevice(byte[] address)273     BluetoothDevice getDevice(byte[] address) {
274         String addressString = Utils.getAddressStringFromByte(address);
275         String deviceAddress = mDualDevicesMap.get(addressString);
276         // If the device is not in the dual map, use its original address
277         if (deviceAddress == null || mDevices.get(deviceAddress) == null) {
278             deviceAddress = addressString;
279         }
280 
281         DeviceProperties prop = mDevices.get(deviceAddress);
282         if (prop != null) {
283             return prop.getDevice();
284         }
285         return null;
286     }
287 
288     @VisibleForTesting
addDeviceProperties(byte[] address)289     DeviceProperties addDeviceProperties(byte[] address) {
290         synchronized (mDevices) {
291             DeviceProperties prop = new DeviceProperties();
292             prop.setDevice(mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)));
293             prop.setAddress(address);
294             String key = Utils.getAddressStringFromByte(address);
295             DeviceProperties pv = mDevices.put(key, prop);
296 
297             if (pv == null) {
298                 mDeviceQueue.offer(key);
299                 if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) {
300                     String deleteKey = mDeviceQueue.poll();
301                     for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
302                         if (device.getAddress().equals(deleteKey)) {
303                             return prop;
304                         }
305                     }
306                     debugLog("Removing device " + deleteKey + " from property map");
307                     mDevices.remove(deleteKey);
308                 }
309             }
310             return prop;
311         }
312     }
313 
314     class DeviceProperties {
315         private String mName;
316         private byte[] mAddress;
317         private String mIdentityAddress;
318         private boolean mIsConsolidated = false;
319         private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED;
320         private int mBredrConnectionHandle = BluetoothDevice.ERROR;
321         private int mLeConnectionHandle = BluetoothDevice.ERROR;
322         private short mRssi;
323         private String mAlias;
324         private BluetoothDevice mDevice;
325         private boolean mIsBondingInitiatedLocally;
326         private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
327         private boolean mIsCoordinatedSetMember;
328         private int mAshaCapability;
329         private int mAshaTruncatedHiSyncId;
330         private String mModelName;
331         @VisibleForTesting int mBondState;
332         @VisibleForTesting int mDeviceType;
333         @VisibleForTesting ParcelUuid[] mUuids;
334         private BluetoothSinkAudioPolicy mAudioPolicy;
335 
DeviceProperties()336         DeviceProperties() {
337             mBondState = BluetoothDevice.BOND_NONE;
338         }
339 
340         /**
341          * @return the mName
342          */
getName()343         String getName() {
344             synchronized (mObject) {
345                 return mName;
346             }
347         }
348 
349         /**
350          * @param name the mName to set
351          */
setName(String name)352         void setName(String name) {
353             synchronized (mObject) {
354                 this.mName = name;
355             }
356         }
357 
358         /**
359          * @return the mIdentityAddress
360          */
getIdentityAddress()361         String getIdentityAddress() {
362             synchronized (mObject) {
363                 return mIdentityAddress;
364             }
365         }
366 
367         /**
368          * @param identityAddress the mIdentityAddress to set
369          */
setIdentityAddress(String identityAddress)370         void setIdentityAddress(String identityAddress) {
371             synchronized (mObject) {
372                 this.mIdentityAddress = identityAddress;
373             }
374         }
375 
376         /**
377          * @return mIsConsolidated
378          */
isConsolidated()379         boolean isConsolidated() {
380             synchronized (mObject) {
381                 return mIsConsolidated;
382             }
383         }
384 
385         /**
386          * @param isConsolidated the mIsConsolidated to set
387          */
setIsConsolidated(boolean isConsolidated)388         void setIsConsolidated(boolean isConsolidated) {
389             synchronized (mObject) {
390                 this.mIsConsolidated = isConsolidated;
391             }
392         }
393 
394         /**
395          * @return the mClass
396          */
getBluetoothClass()397         int getBluetoothClass() {
398             synchronized (mObject) {
399                 return mBluetoothClass;
400             }
401         }
402 
403         /**
404          * @param bluetoothClass the mBluetoothClass to set
405          */
setBluetoothClass(int bluetoothClass)406         void setBluetoothClass(int bluetoothClass) {
407             synchronized (mObject) {
408                 this.mBluetoothClass = bluetoothClass;
409             }
410         }
411 
412         /**
413          * @param transport the transport on which the connection exists
414          * @return the mConnectionHandle
415          */
getConnectionHandle(int transport)416         int getConnectionHandle(int transport) {
417             synchronized (mObject) {
418                 if (transport == BluetoothDevice.TRANSPORT_BREDR) {
419                     return mBredrConnectionHandle;
420                 } else if (transport == BluetoothDevice.TRANSPORT_LE) {
421                     return mLeConnectionHandle;
422                 } else {
423                     return BluetoothDevice.ERROR;
424                 }
425             }
426         }
427 
428         /**
429          * @param connectionHandle the connectionHandle to set
430          * @param transport the transport on which to set the handle
431          */
setConnectionHandle(int connectionHandle, int transport)432         void setConnectionHandle(int connectionHandle, int transport) {
433             synchronized (mObject) {
434                 if (transport == BluetoothDevice.TRANSPORT_BREDR) {
435                     mBredrConnectionHandle = connectionHandle;
436                 } else if (transport == BluetoothDevice.TRANSPORT_LE) {
437                     mLeConnectionHandle = connectionHandle;
438                 } else {
439                     errorLog("setConnectionHandle() unexpected transport value " + transport);
440                 }
441             }
442         }
443 
444         /**
445          * @return the mUuids
446          */
getUuids()447         ParcelUuid[] getUuids() {
448             synchronized (mObject) {
449                 return mUuids;
450             }
451         }
452 
453         /**
454          * @param uuids the mUuids to set
455          */
setUuids(ParcelUuid[] uuids)456         void setUuids(ParcelUuid[] uuids) {
457             synchronized (mObject) {
458                 this.mUuids = uuids;
459             }
460         }
461 
462         /**
463          * @return the mAddress
464          */
getAddress()465         byte[] getAddress() {
466             synchronized (mObject) {
467                 return mAddress;
468             }
469         }
470 
471         /**
472          * @param address the mAddress to set
473          */
setAddress(byte[] address)474         void setAddress(byte[] address) {
475             synchronized (mObject) {
476                 this.mAddress = address;
477             }
478         }
479 
480         /**
481          * @return the mDevice
482          */
getDevice()483         BluetoothDevice getDevice() {
484             synchronized (mObject) {
485                 return mDevice;
486             }
487         }
488 
489         /**
490          * @param device the mDevice to set
491          */
setDevice(BluetoothDevice device)492         void setDevice(BluetoothDevice device) {
493             synchronized (mObject) {
494                 this.mDevice = device;
495             }
496         }
497 
498         /**
499          * @return mRssi
500          */
getRssi()501         short getRssi() {
502             synchronized (mObject) {
503                 return mRssi;
504             }
505         }
506 
507         /**
508          * @param rssi the mRssi to set
509          */
setRssi(short rssi)510         void setRssi(short rssi) {
511             synchronized (mObject) {
512                 this.mRssi = rssi;
513             }
514         }
515 
516         /**
517          * @return mDeviceType
518          */
getDeviceType()519         int getDeviceType() {
520             synchronized (mObject) {
521                 return mDeviceType;
522             }
523         }
524 
525         /**
526          * @param deviceType the mDeviceType to set
527          */
setDeviceType(int deviceType)528         void setDeviceType(int deviceType) {
529             synchronized (mObject) {
530                 this.mDeviceType = deviceType;
531             }
532         }
533 
534         /**
535          * @return the mAlias
536          */
getAlias()537         String getAlias() {
538             synchronized (mObject) {
539                 return mAlias;
540             }
541         }
542 
543         /**
544          * @param mAlias the mAlias to set
545          */
setAlias(BluetoothDevice device, String mAlias)546         void setAlias(BluetoothDevice device, String mAlias) {
547             synchronized (mObject) {
548                 this.mAlias = mAlias;
549                 mAdapterService.setDevicePropertyNative(mAddress,
550                         AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
551                 Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
552                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
553                 intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias);
554                 Utils.sendBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
555                         Utils.getTempAllowlistBroadcastOptions());
556             }
557         }
558 
559         /**
560          * @param newBondState the mBondState to set
561          */
setBondState(int newBondState)562         void setBondState(int newBondState) {
563             synchronized (mObject) {
564                 this.mBondState = newBondState;
565                 if (newBondState == BluetoothDevice.BOND_NONE) {
566                     /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
567                     cachedBluetoothDevice issued a connect using the local cached copy of uuids,
568                     without waiting for the ACTION_UUID intent.
569                     This was resulting in multiple calls to connect().*/
570                     mUuids = null;
571                     mAlias = null;
572                 }
573             }
574         }
575 
576         /**
577          * @return the mBondState
578          */
getBondState()579         int getBondState() {
580             synchronized (mObject) {
581                 return mBondState;
582             }
583         }
584 
isBonding()585         boolean isBonding() {
586             return getBondState() == BluetoothDevice.BOND_BONDING;
587         }
588 
isBondingOrBonded()589         boolean isBondingOrBonded() {
590             return isBonding() || getBondState() == BluetoothDevice.BOND_BONDED;
591         }
592 
593         /**
594          * @param isBondingInitiatedLocally wether bonding is initiated locally
595          */
setBondingInitiatedLocally(boolean isBondingInitiatedLocally)596         void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) {
597             synchronized (mObject) {
598                 this.mIsBondingInitiatedLocally = isBondingInitiatedLocally;
599             }
600         }
601 
602         /**
603          * @return the isBondingInitiatedLocally
604          */
isBondingInitiatedLocally()605         boolean isBondingInitiatedLocally() {
606             synchronized (mObject) {
607                 return mIsBondingInitiatedLocally;
608             }
609         }
610 
611         /**
612          * @return mBatteryLevel
613          */
getBatteryLevel()614         int getBatteryLevel() {
615             synchronized (mObject) {
616                 return mBatteryLevel;
617             }
618         }
619 
620         /**
621          * @param batteryLevel the mBatteryLevel to set
622          */
setBatteryLevel(int batteryLevel)623         void setBatteryLevel(int batteryLevel) {
624             synchronized (mObject) {
625                 this.mBatteryLevel = batteryLevel;
626             }
627         }
628 
629         /**
630          * @return the mIsCoordinatedSetMember
631         */
isCoordinatedSetMember()632         boolean isCoordinatedSetMember() {
633             synchronized (mObject) {
634                 return mIsCoordinatedSetMember;
635             }
636         }
637 
638         /**
639          * @param isCoordinatedSetMember the mIsCoordinatedSetMember to set
640         */
setIsCoordinatedSetMember(boolean isCoordinatedSetMember)641         void setIsCoordinatedSetMember(boolean isCoordinatedSetMember) {
642             synchronized (mObject) {
643                 this.mIsCoordinatedSetMember = isCoordinatedSetMember;
644             }
645         }
646 
647         /**
648          * @return the mAshaCapability
649          */
getAshaCapability()650         int getAshaCapability() {
651             synchronized (mObject) {
652                 return mAshaCapability;
653             }
654         }
655 
setAshaCapability(int ashaCapability)656         void setAshaCapability(int ashaCapability) {
657             synchronized (mObject) {
658                 this.mAshaCapability = ashaCapability;
659             }
660         }
661 
662         /**
663          * @return the mAshaTruncatedHiSyncId
664          */
getAshaTruncatedHiSyncId()665         int getAshaTruncatedHiSyncId() {
666             synchronized (mObject) {
667                 return mAshaTruncatedHiSyncId;
668             }
669         }
670 
setAshaTruncatedHiSyncId(int ashaTruncatedHiSyncId)671         void setAshaTruncatedHiSyncId(int ashaTruncatedHiSyncId) {
672             synchronized (mObject) {
673                 this.mAshaTruncatedHiSyncId = ashaTruncatedHiSyncId;
674             }
675         }
676 
setHfAudioPolicyForRemoteAg(BluetoothSinkAudioPolicy policies)677         public void setHfAudioPolicyForRemoteAg(BluetoothSinkAudioPolicy policies) {
678             mAudioPolicy = policies;
679         }
680 
getHfAudioPolicyForRemoteAg()681         public BluetoothSinkAudioPolicy getHfAudioPolicyForRemoteAg() {
682             return mAudioPolicy;
683         }
684 
setModelName(String modelName)685         public void setModelName(String modelName) {
686             mModelName = modelName;
687         }
688 
689         /**
690          * @return the mModelName
691          */
getModelName()692         String getModelName() {
693             synchronized (mObject) {
694                 return mModelName;
695             }
696         }
697     }
698 
sendUuidIntent(BluetoothDevice device, DeviceProperties prop)699     private void sendUuidIntent(BluetoothDevice device, DeviceProperties prop) {
700         // Send uuids within the stack before the broadcast is sent out
701         ParcelUuid[] uuids = prop == null ? null : prop.getUuids();
702         mAdapterService.sendUuidsInternal(device, uuids);
703 
704         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
705         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
706         intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids);
707         Utils.sendBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
708                 Utils.getTempAllowlistBroadcastOptions());
709 
710         // SDP Sent UUID Intent here
711         MetricsLogger.getInstance().cacheCount(
712                 BluetoothProtoEnums.SDP_SENT_UUID, 1);
713         //Remove the outstanding UUID request
714         mSdpTracker.remove(device);
715     }
716 
717     /**
718      * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing,
719      * we must add device first before setting it's properties. This is a helper method for doing
720      * that.
721      */
setBondingInitiatedLocally(byte[] address)722     void setBondingInitiatedLocally(byte[] address) {
723         DeviceProperties properties;
724 
725         BluetoothDevice device = getDevice(address);
726         if (device == null) {
727             properties = addDeviceProperties(address);
728         } else {
729             properties = getDeviceProperties(device);
730         }
731 
732         properties.setBondingInitiatedLocally(true);
733     }
734 
735     /**
736      * Update battery level in device properties
737      * @param device The remote device to be updated
738      * @param batteryLevel Battery level Indicator between 0-100,
739      *                    {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} is error
740      */
741     @VisibleForTesting
updateBatteryLevel(BluetoothDevice device, int batteryLevel)742     void updateBatteryLevel(BluetoothDevice device, int batteryLevel) {
743         if (device == null || batteryLevel < 0 || batteryLevel > 100) {
744             warnLog("Invalid parameters device=" + String.valueOf(device == null)
745                     + ", batteryLevel=" + String.valueOf(batteryLevel));
746             return;
747         }
748         DeviceProperties deviceProperties = getDeviceProperties(device);
749         if (deviceProperties == null) {
750             deviceProperties = addDeviceProperties(Utils.getByteAddress(device));
751         }
752         synchronized (mObject) {
753             int currentBatteryLevel = deviceProperties.getBatteryLevel();
754             if (batteryLevel == currentBatteryLevel) {
755                 debugLog("Same battery level for device " + device + " received " + String.valueOf(
756                         batteryLevel) + "%");
757                 return;
758             }
759             deviceProperties.setBatteryLevel(batteryLevel);
760         }
761         sendBatteryLevelChangedBroadcast(device, batteryLevel);
762         Log.d(TAG, "Updated device " + device + " battery level to " + batteryLevel + "%");
763     }
764 
765     /**
766      * Reset battery level property to {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} for a device
767      * @param device device whose battery level property needs to be reset
768      */
769     @VisibleForTesting
resetBatteryLevel(BluetoothDevice device)770     void resetBatteryLevel(BluetoothDevice device) {
771         if (device == null) {
772             warnLog("Device is null");
773             return;
774         }
775         DeviceProperties deviceProperties = getDeviceProperties(device);
776         if (deviceProperties == null) {
777             return;
778         }
779         synchronized (mObject) {
780             if (deviceProperties.getBatteryLevel() == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
781                 debugLog("Battery level was never set or is already reset, device=" + device);
782                 return;
783             }
784             deviceProperties.setBatteryLevel(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
785         }
786         sendBatteryLevelChangedBroadcast(device, BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
787         Log.d(TAG, "Reset battery level, device=" + device);
788     }
789 
sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel)790     private void sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel) {
791         Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED);
792         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
793         intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel);
794         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
795         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
796         Utils.sendBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
797                 Utils.getTempAllowlistBroadcastOptions());
798     }
799 
800     /**
801      * Converts HFP's Battery Charge indicator values of {@code 0 -- 5} to an integer percentage.
802      */
803     @VisibleForTesting
batteryChargeIndicatorToPercentge(int indicator)804     static int batteryChargeIndicatorToPercentge(int indicator) {
805         int percent;
806         switch (indicator) {
807             case 5:
808                 percent = HFP_BATTERY_CHARGE_INDICATOR_5;
809                 break;
810             case 4:
811                 percent = HFP_BATTERY_CHARGE_INDICATOR_4;
812                 break;
813             case 3:
814                 percent = HFP_BATTERY_CHARGE_INDICATOR_3;
815                 break;
816             case 2:
817                 percent = HFP_BATTERY_CHARGE_INDICATOR_2;
818                 break;
819             case 1:
820                 percent = HFP_BATTERY_CHARGE_INDICATOR_1;
821                 break;
822             case 0:
823                 percent = HFP_BATTERY_CHARGE_INDICATOR_0;
824                 break;
825             default:
826                 percent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
827         }
828         Log.d(TAG, "Battery charge indicator: " + indicator + "; converted to: " + percent + "%");
829         return percent;
830     }
831 
areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2)832     private static boolean areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2) {
833         final int length1 = uuids1 == null ? 0 : uuids1.length;
834         final int length2 = uuids2 == null ? 0 : uuids2.length;
835         if (length1 != length2) {
836             return false;
837         }
838         Set<ParcelUuid> set = new HashSet<>();
839         for (int i = 0; i < length1; ++i) {
840             set.add(uuids1[i]);
841         }
842         for (int i = 0; i < length2; ++i) {
843             set.remove(uuids2[i]);
844         }
845         return set.isEmpty();
846     }
847 
devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values)848     void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
849         Intent intent;
850         byte[] val;
851         int type;
852         BluetoothDevice bdDevice = getDevice(address);
853         DeviceProperties deviceProperties;
854         if (bdDevice == null) {
855             debugLog("Added new device property");
856             deviceProperties = addDeviceProperties(address);
857             bdDevice = getDevice(address);
858         } else {
859             deviceProperties = getDeviceProperties(bdDevice);
860         }
861 
862         if (types.length <= 0) {
863             errorLog("No properties to update");
864             return;
865         }
866 
867         for (int j = 0; j < types.length; j++) {
868             type = types[j];
869             val = values[j];
870             if (val.length > 0) {
871                 synchronized (mObject) {
872                     debugLog("Property type: " + type);
873                     switch (type) {
874                         case AbstractionLayer.BT_PROPERTY_BDNAME:
875                             final String newName = new String(val);
876                             if (newName.equals(deviceProperties.getName())) {
877                                 debugLog("Skip name update for " + bdDevice);
878                                 break;
879                             }
880                             deviceProperties.setName(newName);
881                             intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
882                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
883                             intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName());
884                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
885                             Utils.sendBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
886                                     Utils.getTempAllowlistBroadcastOptions());
887                             debugLog("Remote device name is: " + deviceProperties.getName());
888                             break;
889                         case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
890                             deviceProperties.setAlias(bdDevice, new String(val));
891                             debugLog("Remote device alias is: " + deviceProperties.getAlias());
892                             break;
893                         case AbstractionLayer.BT_PROPERTY_BDADDR:
894                             deviceProperties.setAddress(val);
895                             debugLog(
896                                     "Remote Address is:"
897                                             + Utils.getRedactedAddressStringFromByte(val));
898                             break;
899                         case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
900                             final int newBluetoothClass = Utils.byteArrayToInt(val);
901                             if (newBluetoothClass == deviceProperties.getBluetoothClass()) {
902                                 debugLog("Skip class update for " + bdDevice);
903                                 break;
904                             }
905                             deviceProperties.setBluetoothClass(newBluetoothClass);
906                             intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
907                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
908                             intent.putExtra(BluetoothDevice.EXTRA_CLASS,
909                                     new BluetoothClass(deviceProperties.getBluetoothClass()));
910                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
911                             Utils.sendBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
912                                     Utils.getTempAllowlistBroadcastOptions());
913                             debugLog("Remote class is:" + newBluetoothClass);
914                             break;
915                         case AbstractionLayer.BT_PROPERTY_UUIDS:
916                             final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val);
917                             if (areUuidsEqual(newUuids, deviceProperties.getUuids())) {
918                                 // SDP Skip adding UUIDs to property cache if equal
919                                 debugLog("Skip uuids update for " + bdDevice.getAddress());
920                                 MetricsLogger.getInstance().cacheCount(
921                                         BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1);
922                                 break;
923                             }
924                             deviceProperties.setUuids(newUuids);
925                             if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) {
926                                 // SDP Adding UUIDs to property cache and sending intent
927                                 MetricsLogger.getInstance().cacheCount(
928                                         BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1);
929                                 mAdapterService.deviceUuidUpdated(bdDevice);
930                                 sendUuidIntent(bdDevice, deviceProperties);
931                             } else if (mAdapterService.getState()
932                                     == BluetoothAdapter.STATE_BLE_ON) {
933                                 // SDP Adding UUIDs to property cache but with no intent
934                                 MetricsLogger.getInstance().cacheCount(
935                                         BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1);
936                                 mAdapterService.deviceUuidUpdated(bdDevice);
937                             } else {
938                                 // SDP Silently dropping UUIDs and with no intent
939                                 MetricsLogger.getInstance().cacheCount(
940                                         BluetoothProtoEnums.SDP_DROP_UUID, 1);
941                             }
942                             break;
943                         case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
944                             if (deviceProperties.isConsolidated()) {
945                                 break;
946                             }
947                             // The device type from hal layer, defined in bluetooth.h,
948                             // matches the type defined in BluetoothDevice.java
949                             deviceProperties.setDeviceType(Utils.byteArrayToInt(val));
950                             break;
951                         case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
952                             // RSSI from hal is in one byte
953                             deviceProperties.setRssi(val[0]);
954                             break;
955                         case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER:
956                             deviceProperties.setIsCoordinatedSetMember(val[0] != 0);
957                             break;
958                         case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY:
959                             deviceProperties.setAshaCapability(val[0]);
960                             break;
961                         case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID:
962                             deviceProperties.setAshaTruncatedHiSyncId(val[0]);
963                             break;
964                         case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM:
965                             final String modelName = new String(val);
966                             debugLog("Remote device model name: " + modelName);
967                             deviceProperties.setModelName(modelName);
968                             BluetoothStatsLog.write(
969                                     BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
970                                     mAdapterService.obfuscateAddress(bdDevice),
971                                     BluetoothProtoEnums.DEVICE_INFO_INTERNAL, LOG_SOURCE_DIS, null,
972                                     modelName, null, null, mAdapterService.getMetricId(bdDevice),
973                                     bdDevice.getAddressType(), 0, 0, 0);
974                             break;
975                     }
976                 }
977             }
978         }
979     }
980 
deviceFoundCallback(byte[] address)981     void deviceFoundCallback(byte[] address) {
982         // The device properties are already registered - we can send the intent
983         // now
984         BluetoothDevice device = getDevice(address);
985         debugLog("deviceFoundCallback: Remote Address is:" + device);
986         DeviceProperties deviceProp = getDeviceProperties(device);
987         if (deviceProp == null) {
988             errorLog("Device Properties is null for Device:" + device);
989             return;
990         }
991         boolean restrict_device_found =
992                 SystemProperties.getBoolean("bluetooth.restrict_discovered_device.enabled", false);
993         if (restrict_device_found && (deviceProp.mName == null || deviceProp.mName.isEmpty())) {
994             debugLog("Device name is null or empty: " + device);
995             return;
996         }
997 
998         Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
999         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1000         intent.putExtra(BluetoothDevice.EXTRA_CLASS,
1001                 new BluetoothClass(deviceProp.getBluetoothClass()));
1002         intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.getRssi());
1003         intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.getName());
1004         intent.putExtra(BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER,
1005                 deviceProp.isCoordinatedSetMember());
1006 
1007         final ArrayList<DiscoveringPackage> packages = mAdapterService.getDiscoveringPackages();
1008         synchronized (packages) {
1009             for (DiscoveringPackage pkg : packages) {
1010                 if (pkg.hasDisavowedLocation()) {
1011                     if (mLocationDenylistPredicate.test(device)) {
1012                         continue;
1013                     }
1014                 }
1015 
1016                 intent.setPackage(pkg.getPackageName());
1017 
1018                 if (pkg.getPermission() != null) {
1019                     mAdapterService.sendBroadcastMultiplePermissions(intent,
1020                             new String[] { BLUETOOTH_SCAN, pkg.getPermission() },
1021                             Utils.getTempBroadcastOptions());
1022                 } else {
1023                     mAdapterService.sendBroadcastMultiplePermissions(intent,
1024                             new String[] { BLUETOOTH_SCAN },
1025                             Utils.getTempBroadcastOptions());
1026                 }
1027             }
1028         }
1029     }
1030 
addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress)1031     void addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress) {
1032         BluetoothDevice device = getDevice(mainAddress);
1033         if (device == null) {
1034             errorLog("addressConsolidateCallback: device is NULL, address="
1035                     + Utils.getRedactedAddressStringFromByte(mainAddress)
1036                     + ", secondaryAddress="
1037                     + Utils.getRedactedAddressStringFromByte(secondaryAddress));
1038             return;
1039         }
1040         Log.d(TAG, "addressConsolidateCallback device: " + device + ", secondaryAddress:"
1041                 + Utils.getRedactedAddressStringFromByte(secondaryAddress));
1042 
1043         DeviceProperties deviceProperties = getDeviceProperties(device);
1044         deviceProperties.setIsConsolidated(true);
1045         deviceProperties.setDeviceType(BluetoothDevice.DEVICE_TYPE_DUAL);
1046         deviceProperties.setIdentityAddress(Utils.getAddressStringFromByte(secondaryAddress));
1047         mDualDevicesMap.put(deviceProperties.getIdentityAddress(), Utils.getAddressStringFromByte(mainAddress));
1048     }
1049 
1050     /**
1051      * Callback to associate an LE-only device's RPA with its identity address
1052      *
1053      * @param mainAddress the device's RPA
1054      * @param secondaryAddress the device's identity address
1055      */
leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress)1056     void leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress) {
1057         BluetoothDevice device = getDevice(mainAddress);
1058         if (device == null) {
1059             errorLog("leAddressAssociateCallback: device is NULL, address="
1060                     + Utils.getRedactedAddressStringFromByte(mainAddress)
1061                     + ", secondaryAddress="
1062                     + Utils.getRedactedAddressStringFromByte(secondaryAddress));
1063             return;
1064         }
1065         Log.d(TAG, "leAddressAssociateCallback device: " + device + ", secondaryAddress:"
1066                 + Utils.getRedactedAddressStringFromByte(secondaryAddress));
1067 
1068         DeviceProperties deviceProperties = getDeviceProperties(device);
1069         deviceProperties.mIdentityAddress = Utils.getAddressStringFromByte(secondaryAddress);
1070     }
1071 
1072     @RequiresPermission(allOf = {
1073             android.Manifest.permission.BLUETOOTH_CONNECT,
1074             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
1075     })
aclStateChangeCallback(int status, byte[] address, int newState, int transportLinkType, int hciReason, int handle)1076     void aclStateChangeCallback(int status, byte[] address, int newState,
1077                                 int transportLinkType, int hciReason, int handle) {
1078         if (status != AbstractionLayer.BT_STATUS_SUCCESS) {
1079             debugLog("aclStateChangeCallback status is " + status + ", skipping");
1080             return;
1081         }
1082 
1083         BluetoothDevice device = getDevice(address);
1084 
1085         if (device == null) {
1086             warnLog("aclStateChangeCallback: device is NULL, address="
1087                     + Utils.getRedactedAddressStringFromByte(address)
1088                     + ", newState=" + newState);
1089             addDeviceProperties(address);
1090             device = Objects.requireNonNull(getDevice(address));
1091         }
1092 
1093         DeviceProperties deviceProperties = getDeviceProperties(device);
1094 
1095         int state = mAdapterService.getState();
1096 
1097         Intent intent = null;
1098         if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
1099             deviceProperties.setConnectionHandle(handle, transportLinkType);
1100             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) {
1101                 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
1102                 intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transportLinkType);
1103             } else if (state == BluetoothAdapter.STATE_BLE_ON
1104                     || state == BluetoothAdapter.STATE_BLE_TURNING_ON) {
1105                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED);
1106             }
1107             BatteryService batteryService = BatteryService.getBatteryService();
1108             if (batteryService != null && transportLinkType == BluetoothDevice.TRANSPORT_LE) {
1109                 batteryService.connectIfPossible(device);
1110             }
1111             SecurityLog.writeEvent(SecurityLog.TAG_BLUETOOTH_CONNECTION,
1112                     Utils.getLoggableAddress(device), /* success */ 1, /* reason */ "");
1113             debugLog(
1114                     "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state)
1115                             + " Connected: " + device);
1116         } else {
1117             deviceProperties.setConnectionHandle(BluetoothDevice.ERROR, transportLinkType);
1118             if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
1119                 // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding.
1120                 intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
1121                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1122                 intent.setPackage(SystemProperties.get(
1123                         Utils.PAIRING_UI_PROPERTY,
1124                         mAdapterService.getString(R.string.pairing_ui_package)));
1125 
1126                 Utils.sendBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
1127                         Utils.getTempAllowlistBroadcastOptions());
1128             } else if (device.getBondState() == BluetoothDevice.BOND_NONE) {
1129                 String key = Utils.getAddressStringFromByte(address);
1130                 mDevices.remove(key);
1131             }
1132             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) {
1133                 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
1134                 intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transportLinkType);
1135             } else if (state == BluetoothAdapter.STATE_BLE_ON
1136                     || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
1137                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED);
1138             }
1139             // Reset battery level on complete disconnection
1140             if (mAdapterService.getConnectionState(device) == 0) {
1141                 BatteryService batteryService = BatteryService.getBatteryService();
1142                 if (batteryService != null
1143                         && batteryService.getConnectionState(device)
1144                                 != BluetoothProfile.STATE_DISCONNECTED
1145                         && transportLinkType == BluetoothDevice.TRANSPORT_LE) {
1146                     batteryService.disconnect(device);
1147                 }
1148                 resetBatteryLevel(device);
1149             }
1150             if (mAdapterService.isAllProfilesUnknown(device)) {
1151                 DeviceProperties deviceProp = getDeviceProperties(device);
1152                 if (deviceProp != null) {
1153                     deviceProp.setBondingInitiatedLocally(false);
1154                 }
1155             }
1156             SecurityLog.writeEvent(SecurityLog.TAG_BLUETOOTH_DISCONNECTION,
1157                     Utils.getLoggableAddress(device),
1158                     BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonToString(
1159                             AdapterService.hciToAndroidDisconnectReason(hciReason)));
1160             debugLog(
1161                     "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state)
1162                             + " Disconnected: " + device
1163                             + " transportLinkType: " + transportLinkType
1164                             + " hciReason: " + hciReason);
1165         }
1166 
1167         int connectionState = newState == AbstractionLayer.BT_ACL_STATE_CONNECTED
1168                 ? BluetoothAdapter.STATE_CONNECTED : BluetoothAdapter.STATE_DISCONNECTED;
1169         int metricId = mAdapterService.getMetricId(device);
1170         BluetoothStatsLog.write(
1171                 BluetoothStatsLog.BLUETOOTH_ACL_CONNECTION_STATE_CHANGED,
1172                 mAdapterService.obfuscateAddress(device),
1173                 connectionState,
1174                 metricId,
1175                 transportLinkType);
1176 
1177         BluetoothClass deviceClass = device.getBluetoothClass();
1178         int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice();
1179         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
1180                 mAdapterService.obfuscateAddress(device), classOfDevice, metricId);
1181 
1182         if (intent != null) {
1183             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device)
1184                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
1185                 .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1186             Utils.sendBroadcast(mAdapterService, intent, BLUETOOTH_CONNECT,
1187                     Utils.getTempAllowlistBroadcastOptions());
1188 
1189             synchronized (mAdapterService.getBluetoothConnectionCallbacks()) {
1190                 Set<IBluetoothConnectionCallback> bluetoothConnectionCallbacks =
1191                         mAdapterService.getBluetoothConnectionCallbacks();
1192                 for (IBluetoothConnectionCallback callback : bluetoothConnectionCallbacks) {
1193                     try {
1194                         if (connectionState == BluetoothAdapter.STATE_CONNECTED) {
1195                             callback.onDeviceConnected(device);
1196                         } else {
1197                             callback.onDeviceDisconnected(device,
1198                                     AdapterService.hciToAndroidDisconnectReason(hciReason));
1199                         }
1200                     } catch (RemoteException ex) {
1201                         Log.e(TAG, "RemoteException in calling IBluetoothConnectionCallback");
1202                     }
1203                 }
1204             }
1205         } else {
1206             Log.e(TAG, "aclStateChangeCallback intent is null. deviceBondState: "
1207                     + device.getBondState());
1208         }
1209     }
1210 
1211 
fetchUuids(BluetoothDevice device, int transport)1212     void fetchUuids(BluetoothDevice device, int transport) {
1213         if (mSdpTracker.contains(device)) {
1214             // SDP Skip fetch UUIDs if cached
1215             MetricsLogger.getInstance().cacheCount(
1216                     BluetoothProtoEnums.SDP_FETCH_UUID_SKIP_ALREADY_CACHED, 1);
1217             return;
1218         }
1219 
1220         // If no UUIDs are cached and the device is bonding, wait for SDP after the device is bonded
1221         DeviceProperties deviceProperties = getDeviceProperties(device);
1222         if (deviceProperties != null && deviceProperties.isBonding()
1223                 && getDeviceProperties(device).getUuids() == null) {
1224             // SDP Skip fetch UUIDs due to bonding
1225             MetricsLogger.getInstance().cacheCount(
1226                     BluetoothProtoEnums.SDP_FETCH_UUID_SKIP_ALREADY_BONDED, 1);
1227             return;
1228         }
1229 
1230         mSdpTracker.add(device);
1231 
1232         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1233         message.obj = device;
1234         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1235 
1236         // Uses cached UUIDs if we are bonding. If not, we fetch the UUIDs with SDP.
1237         if (deviceProperties == null || !deviceProperties.isBonding()) {
1238             // SDP Invoked native code to spin up SDP cycle
1239             mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()),
1240                     transport);
1241             MetricsLogger.getInstance().cacheCount(
1242                     BluetoothProtoEnums.SDP_INVOKE_SDP_CYCLE, 1);
1243         }
1244     }
1245 
updateUuids(BluetoothDevice device)1246     void updateUuids(BluetoothDevice device) {
1247         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1248         message.obj = device;
1249         mHandler.sendMessage(message);
1250     }
1251 
1252     /**
1253      * Handles headset connection state change event
1254      * @param intent must be {@link BluetoothHeadset#ACTION_CONNECTION_STATE_CHANGED} intent
1255      */
1256     @VisibleForTesting
onHeadsetConnectionStateChanged(Intent intent)1257     void onHeadsetConnectionStateChanged(Intent intent) {
1258         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1259         if (device == null) {
1260             Log.e(TAG, "onHeadsetConnectionStateChanged() remote device is null");
1261             return;
1262         }
1263         if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED)
1264                 == BluetoothProfile.STATE_DISCONNECTED
1265                 && !hasBatteryService(device)) {
1266             resetBatteryLevel(device);
1267         }
1268     }
1269 
1270     @VisibleForTesting
onHfIndicatorValueChanged(Intent intent)1271     void onHfIndicatorValueChanged(Intent intent) {
1272         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1273         if (device == null) {
1274             Log.e(TAG, "onHfIndicatorValueChanged() remote device is null");
1275             return;
1276         }
1277         int indicatorId = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1);
1278         int indicatorValue = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -1);
1279         if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) {
1280             updateBatteryLevel(device, indicatorValue);
1281         }
1282     }
1283 
1284     /**
1285      * Handle {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
1286      * @param intent must be {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
1287      */
1288     @VisibleForTesting
onVendorSpecificHeadsetEvent(Intent intent)1289     void onVendorSpecificHeadsetEvent(Intent intent) {
1290         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1291         if (device == null) {
1292             Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null");
1293             return;
1294         }
1295         String cmd =
1296                 intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
1297         if (cmd == null) {
1298             Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null");
1299             return;
1300         }
1301         int cmdType =
1302                 intent.getIntExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
1303                         -1);
1304         // Only process set command
1305         if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) {
1306             debugLog("onVendorSpecificHeadsetEvent() only SET command is processed");
1307             return;
1308         }
1309         Object[] args = (Object[]) intent.getExtras()
1310                 .get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
1311         if (args == null) {
1312             Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null");
1313             return;
1314         }
1315         int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1316         switch (cmd) {
1317             case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT:
1318                 batteryPercent = getBatteryLevelFromXEventVsc(args);
1319                 break;
1320             case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV:
1321                 batteryPercent = getBatteryLevelFromAppleBatteryVsc(args);
1322                 break;
1323         }
1324         if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
1325             updateBatteryLevel(device, batteryPercent);
1326             infoLog("Updated device " + device + " battery level to " + String.valueOf(
1327                     batteryPercent) + "%");
1328         }
1329     }
1330 
1331     /**
1332      * Parse
1333      *      AT+IPHONEACCEV=[NumberOfIndicators],[IndicatorType],[IndicatorValue]
1334      * vendor specific event
1335      * @param args Array of arguments on the right side of assignment
1336      * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
1337      *         when there is an error parsing the arguments
1338      */
1339     @VisibleForTesting
getBatteryLevelFromAppleBatteryVsc(Object[] args)1340     static int getBatteryLevelFromAppleBatteryVsc(Object[] args) {
1341         if (args.length == 0) {
1342             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() empty arguments");
1343             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1344         }
1345         int numKvPair;
1346         if (args[0] instanceof Integer) {
1347             numKvPair = (Integer) args[0];
1348         } else {
1349             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing number of arguments");
1350             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1351         }
1352         if (args.length != (numKvPair * 2 + 1)) {
1353             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() number of arguments does not match");
1354             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1355         }
1356         int indicatorType;
1357         int indicatorValue = -1;
1358         for (int i = 0; i < numKvPair; ++i) {
1359             Object indicatorTypeObj = args[2 * i + 1];
1360             if (indicatorTypeObj instanceof Integer) {
1361                 indicatorType = (Integer) indicatorTypeObj;
1362             } else {
1363                 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator type");
1364                 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1365             }
1366             if (indicatorType
1367                     != BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL) {
1368                 continue;
1369             }
1370             Object indicatorValueObj = args[2 * i + 2];
1371             if (indicatorValueObj instanceof Integer) {
1372                 indicatorValue = (Integer) indicatorValueObj;
1373             } else {
1374                 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator value");
1375                 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1376             }
1377             break;
1378         }
1379         return (indicatorValue < 0 || indicatorValue > 9) ? BluetoothDevice.BATTERY_LEVEL_UNKNOWN
1380                 : (indicatorValue + 1) * 10;
1381     }
1382 
1383     /**
1384      * Parse
1385      *      AT+XEVENT=BATTERY,[Level],[NumberOfLevel],[MinutesOfTalk],[IsCharging]
1386      * vendor specific event
1387      * @param args Array of arguments on the right side of SET command
1388      * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
1389      *         when there is an error parsing the arguments
1390      */
1391     @VisibleForTesting
getBatteryLevelFromXEventVsc(Object[] args)1392     static int getBatteryLevelFromXEventVsc(Object[] args) {
1393         if (args.length == 0) {
1394             Log.w(TAG, "getBatteryLevelFromXEventVsc() empty arguments");
1395             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1396         }
1397         Object eventNameObj = args[0];
1398         if (!(eventNameObj instanceof String)) {
1399             Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event name");
1400             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1401         }
1402         String eventName = (String) eventNameObj;
1403         if (!eventName.equals(
1404                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL)) {
1405             infoLog("getBatteryLevelFromXEventVsc() skip none BATTERY event: " + eventName);
1406             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1407         }
1408         if (args.length != 5) {
1409             Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong battery level event length: "
1410                     + String.valueOf(args.length));
1411             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1412         }
1413         if (!(args[1] instanceof Integer) || !(args[2] instanceof Integer)) {
1414             Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event values");
1415             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1416         }
1417         int batteryLevel = (Integer) args[1];
1418         int numberOfLevels = (Integer) args[2];
1419         if (batteryLevel < 0 || numberOfLevels <= 1 || batteryLevel > numberOfLevels) {
1420             Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong event value, batteryLevel="
1421                     + String.valueOf(batteryLevel) + ", numberOfLevels=" + String.valueOf(
1422                     numberOfLevels));
1423             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1424         }
1425         return batteryLevel * 100 / (numberOfLevels - 1);
1426     }
1427 
1428     @VisibleForTesting
hasBatteryService(BluetoothDevice device)1429     boolean hasBatteryService(BluetoothDevice device) {
1430         BatteryService batteryService = BatteryService.getBatteryService();
1431         return batteryService != null
1432                 && batteryService.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED;
1433     }
1434 
1435     /**
1436      * Handles headset client connection state change event
1437      * @param intent must be {@link BluetoothHeadsetClient#ACTION_CONNECTION_STATE_CHANGED} intent
1438      */
1439     @VisibleForTesting
onHeadsetClientConnectionStateChanged(Intent intent)1440     void onHeadsetClientConnectionStateChanged(Intent intent) {
1441         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1442         if (device == null) {
1443             Log.e(TAG, "onHeadsetClientConnectionStateChanged() remote device is null");
1444             return;
1445         }
1446         if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED)
1447                 == BluetoothProfile.STATE_DISCONNECTED
1448                 && !hasBatteryService(device)) {
1449             resetBatteryLevel(device);
1450         }
1451     }
1452 
1453     @VisibleForTesting
onAgIndicatorValueChanged(Intent intent)1454     void onAgIndicatorValueChanged(Intent intent) {
1455         BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1456         if (device == null) {
1457             Log.e(TAG, "onAgIndicatorValueChanged() remote device is null");
1458             return;
1459         }
1460 
1461         if (intent.hasExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL)) {
1462             int batteryLevel = intent.getIntExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, -1);
1463             updateBatteryLevel(device, batteryChargeIndicatorToPercentge(batteryLevel));
1464         }
1465     }
1466 
errorLog(String msg)1467     private static void errorLog(String msg) {
1468         Log.e(TAG, msg);
1469     }
1470 
debugLog(String msg)1471     private static void debugLog(String msg) {
1472         if (DBG) {
1473             Log.d(TAG, msg);
1474         }
1475     }
1476 
infoLog(String msg)1477     private static void infoLog(String msg) {
1478         if (DBG) {
1479             Log.i(TAG, msg);
1480         }
1481     }
1482 
warnLog(String msg)1483     private static void warnLog(String msg) {
1484         Log.w(TAG, msg);
1485     }
1486 
1487 }
1488