• 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_PRIVILEGED;
21 import static android.Manifest.permission.BLUETOOTH_SCAN;
22 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
23 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
24 import static android.bluetooth.BluetoothUtils.RemoteExceptionIgnoringConsumer;
25 
26 import static com.android.modules.utils.build.SdkLevel.isAtLeastV;
27 
28 import static java.util.Objects.requireNonNullElseGet;
29 
30 import android.annotation.NonNull;
31 import android.annotation.RequiresPermission;
32 import android.annotation.SuppressLint;
33 import android.app.Activity;
34 import android.app.admin.SecurityLog;
35 import android.bluetooth.BluetoothAdapter;
36 import android.bluetooth.BluetoothAssignedNumbers;
37 import android.bluetooth.BluetoothClass;
38 import android.bluetooth.BluetoothDevice;
39 import android.bluetooth.BluetoothDevice.AddressType;
40 import android.bluetooth.BluetoothHeadset;
41 import android.bluetooth.BluetoothManager;
42 import android.bluetooth.BluetoothProfile;
43 import android.bluetooth.BluetoothProtoEnums;
44 import android.bluetooth.BluetoothSinkAudioPolicy;
45 import android.bluetooth.BluetoothUtils;
46 import android.bluetooth.IBluetoothConnectionCallback;
47 import android.content.AttributionSource;
48 import android.content.Intent;
49 import android.content.pm.Attribution;
50 import android.net.MacAddress;
51 import android.os.Build;
52 import android.os.Handler;
53 import android.os.Looper;
54 import android.os.Message;
55 import android.os.ParcelUuid;
56 import android.os.SystemProperties;
57 import android.util.Log;
58 
59 import com.android.bluetooth.BluetoothStatsLog;
60 import com.android.bluetooth.R;
61 import com.android.bluetooth.Utils;
62 import com.android.bluetooth.bas.BatteryService;
63 import com.android.bluetooth.flags.Flags;
64 import com.android.bluetooth.hfp.HeadsetHalConstants;
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.modules.utils.build.SdkLevel;
67 
68 import java.nio.charset.StandardCharsets;
69 import java.util.ArrayDeque;
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.Collections;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.LinkedHashMap;
76 import java.util.List;
77 import java.util.Map;
78 import java.util.Set;
79 import java.util.function.Predicate;
80 
81 /** Remote device manager. This class is currently mostly used for HF and AG remote devices. */
82 public class RemoteDevices {
83     private static final String TAG =
84             Utils.TAG_PREFIX_BLUETOOTH + RemoteDevices.class.getSimpleName();
85 
86     // Maximum number of device properties to remember
87     private static final int MAX_DEVICE_QUEUE_SIZE = 200;
88 
89     private final BluetoothAdapter mAdapter;
90     private AdapterService mAdapterService;
91     private final ArrayList<BluetoothDevice> mSdpTracker;
92     private final Object mObject = new Object();
93 
94     private static final int UUID_INTENT_DELAY = 6000;
95     private static final int MESSAGE_UUID_INTENT = 1;
96     private static final int MESSAGE_UUID_STATUS_SUCCESS = 0;
97     private static final int MESSAGE_UUID_STATUS_TIMEOUT = 1;
98     private static final String LOG_SOURCE_DIS = "DIS";
99 
100     private final HashMap<String, DeviceProperties> mDevices;
101     private final HashMap<String, String> mDualDevicesMap;
102     private final ArrayDeque<String> mDeviceQueue;
103 
104     /**
105      * Bluetooth HFP v1.8 specifies the Battery Charge indicator of AG can take values from {@code
106      * 0} to {@code 5}, but it does not specify how to map the values back to percentages. The
107      * following mapping is used: - Level 0: 0% - Level 1: midpoint of 1-25% - Level 2: midpoint of
108      * 26-50% - Level 3: midpoint of 51-75% - Level 4: midpoint of 76-99% - Level 5: 100%
109      */
110     private static final int HFP_BATTERY_CHARGE_INDICATOR_0 = 0;
111 
112     private static final int HFP_BATTERY_CHARGE_INDICATOR_1 = 13;
113     private static final int HFP_BATTERY_CHARGE_INDICATOR_2 = 38;
114     private static final int HFP_BATTERY_CHARGE_INDICATOR_3 = 63;
115     private static final int HFP_BATTERY_CHARGE_INDICATOR_4 = 88;
116     private static final int HFP_BATTERY_CHARGE_INDICATOR_5 = 100;
117 
118     private final Handler mHandler;
119     private final Handler mMainHandler;
120 
121     private class RemoteDevicesHandler extends Handler {
122 
123         /**
124          * Handler must be created from an explicit looper to avoid threading ambiguity
125          *
126          * @param looper The looper that this handler should be executed on
127          */
RemoteDevicesHandler(Looper looper)128         RemoteDevicesHandler(Looper looper) {
129             super(looper);
130         }
131 
132         @Override
handleMessage(Message msg)133         public void handleMessage(Message msg) {
134             switch (msg.what) {
135                 case MESSAGE_UUID_INTENT:
136                     BluetoothDevice device = (BluetoothDevice) msg.obj;
137                     if (device != null) {
138                         boolean success = (msg.arg1 == MESSAGE_UUID_STATUS_SUCCESS);
139                         debugLog("MESSAGE_UUID_INTENT: " + device);
140                         // SDP Sending delayed SDP UUID intent
141                         MetricsLogger.getInstance()
142                                 .cacheCount(BluetoothProtoEnums.SDP_SENDING_DELAYED_UUID, 1);
143                         DeviceProperties prop = getDeviceProperties(device);
144                         sendUuidIntent(device, prop, success);
145                     } else {
146                         // SDP Not sending delayed SDP UUID intent b/c device is not there
147                         MetricsLogger.getInstance()
148                                 .cacheCount(BluetoothProtoEnums.SDP_NOT_SENDING_DELAYED_UUID, 1);
149                     }
150                     break;
151             }
152         }
153     }
154 
155     /**
156      * Predicate that tests if the given {@link BluetoothDevice} is well-known to be used for
157      * physical location.
158      */
159     private final Predicate<BluetoothDevice> mLocationDenylistPredicate =
160             (device) -> {
161                 final MacAddress parsedAddress = MacAddress.fromString(device.getAddress());
162                 if (mAdapterService.getLocationDenylistMac().test(parsedAddress.toByteArray())) {
163                     Log.v(TAG, "Skipping device matching denylist: " + device);
164                     return true;
165                 }
166                 final String name = Utils.getName(device);
167                 if (mAdapterService.getLocationDenylistName().test(name)) {
168                     Log.v(TAG, "Skipping name matching denylist: " + name);
169                     return true;
170                 }
171                 return false;
172             };
173 
RemoteDevices(AdapterService service, Looper looper)174     RemoteDevices(AdapterService service, Looper looper) {
175         mAdapter = service.getSystemService(BluetoothManager.class).getAdapter();
176         mAdapterService = service;
177         mSdpTracker = new ArrayList<>();
178         mDevices = new HashMap<>();
179         mDualDevicesMap = new HashMap<>();
180         mDeviceQueue = new ArrayDeque<>();
181         mHandler = new RemoteDevicesHandler(looper);
182         mMainHandler = new Handler(Looper.getMainLooper());
183     }
184 
185     /**
186      * Reset should be called when the state of this object needs to be cleared RemoteDevices is
187      * still usable after reset
188      */
189     @RequiresPermission(BLUETOOTH_CONNECT)
reset()190     void reset() {
191         mSdpTracker.clear();
192 
193         // Unregister Handler and stop all queued messages.
194         mMainHandler.removeCallbacksAndMessages(null);
195 
196         synchronized (mDevices) {
197             debugLog("reset(): Broadcasting ACL_DISCONNECTED");
198 
199             mDevices.forEach(
200                     (address, deviceProperties) -> {
201                         BluetoothDevice bluetoothDevice = deviceProperties.getDevice();
202 
203                         debugLog(
204                                 "reset(): address="
205                                         + BluetoothUtils.toAnonymizedAddress(address)
206                                         + ", connected="
207                                         + bluetoothDevice.isConnected());
208 
209                         if (bluetoothDevice.isConnected()) {
210                             int transport =
211                                     deviceProperties.getConnectionHandle(
212                                                             BluetoothDevice.TRANSPORT_BREDR)
213                                                     != BluetoothDevice.ERROR
214                                             ? BluetoothDevice.TRANSPORT_BREDR
215                                             : BluetoothDevice.TRANSPORT_LE;
216                             mAdapterService.notifyAclDisconnected(bluetoothDevice, transport);
217                             Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
218                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bluetoothDevice);
219                             intent.addFlags(
220                                     Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
221                                             | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
222                             mAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT);
223                         }
224                     });
225             mDevices.clear();
226         }
227 
228         mDualDevicesMap.clear();
229         mDeviceQueue.clear();
230     }
231 
232     @Override
clone()233     public Object clone() throws CloneNotSupportedException {
234         throw new CloneNotSupportedException();
235     }
236 
getDeviceProperties(BluetoothDevice device)237     DeviceProperties getDeviceProperties(BluetoothDevice device) {
238         if (device == null) {
239             return null;
240         }
241 
242         synchronized (mDevices) {
243             String address = mDualDevicesMap.get(device.getAddress());
244             // If the device is not in the dual map, use its original address
245             if (address == null || mDevices.get(address) == null) {
246                 address = device.getAddress();
247             }
248             return mDevices.get(address);
249         }
250     }
251 
getBondState(BluetoothDevice device)252     int getBondState(BluetoothDevice device) {
253         DeviceProperties deviceProp = getDeviceProperties(device);
254         if (deviceProp == null) {
255             return BluetoothDevice.BOND_NONE;
256         }
257         return deviceProp.getBondState();
258     }
259 
getName(BluetoothDevice device)260     String getName(BluetoothDevice device) {
261         DeviceProperties deviceProp = getDeviceProperties(device);
262         if (deviceProp == null) {
263             return null;
264         }
265         return deviceProp.getName();
266     }
267 
getType(BluetoothDevice device)268     int getType(BluetoothDevice device) {
269         DeviceProperties deviceProp = getDeviceProperties(device);
270         if (deviceProp == null) {
271             return BluetoothDevice.DEVICE_TYPE_UNKNOWN;
272         }
273         return deviceProp.getDeviceType();
274     }
275 
getUuids(BluetoothDevice device)276     public ParcelUuid[] getUuids(BluetoothDevice device) {
277         DeviceProperties deviceProp = getDeviceProperties(device);
278         if (deviceProp == null) {
279             return null;
280         }
281         return deviceProp.getUuids();
282     }
283 
getBluetoothClass(BluetoothDevice device)284     public int getBluetoothClass(BluetoothDevice device) {
285         DeviceProperties deviceProp = getDeviceProperties(device);
286         if (deviceProp == null) {
287             return 0;
288         }
289         return deviceProp.getBluetoothClass();
290     }
291 
getDevice(byte[] address)292     BluetoothDevice getDevice(byte[] address) {
293         String addressString = Utils.getAddressStringFromByte(address);
294         String deviceAddress = mDualDevicesMap.get(addressString);
295         // If the device is not in the dual map, use its original address
296         if (deviceAddress == null || mDevices.get(deviceAddress) == null) {
297             deviceAddress = addressString;
298         }
299 
300         DeviceProperties prop = mDevices.get(deviceAddress);
301         if (prop != null) {
302             return prop.getDevice();
303         }
304         return null;
305     }
306 
307     @VisibleForTesting
addDeviceProperties(byte[] address)308     DeviceProperties addDeviceProperties(byte[] address) {
309         synchronized (mDevices) {
310             String key = Utils.getAddressStringFromByte(address);
311             if (Flags.fixAddDeviceProperties() && mDevices.containsKey(key)) {
312                 debugLog("Properties for device " + key + " are already added");
313                 return mDevices.get(key);
314             }
315 
316             DeviceProperties prop = new DeviceProperties();
317             prop.setDevice(mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)));
318             prop.setAddress(address);
319 
320             DeviceProperties pv = mDevices.put(key, prop);
321 
322             if (pv == null) {
323                 mDeviceQueue.offer(key);
324                 if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) {
325                     String deleteKey = mDeviceQueue.poll();
326                     for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
327                         if (device.getAddress().equals(deleteKey)) {
328                             return prop;
329                         }
330                     }
331                     debugLog("Removing device " + deleteKey + " from property map");
332                     mDevices.remove(deleteKey);
333                 }
334             }
335             return prop;
336         }
337     }
338 
339     class DeviceProperties {
340         private static final int MAX_PACKAGE_NAMES = 4;
341         private String mName;
342         private byte[] mAddress;
343         private String mIdentityAddress;
344         @AddressType private int mIdentityAddressType = BluetoothDevice.ADDRESS_TYPE_UNKNOWN;
345         private boolean mIsConsolidated = false;
346         private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED;
347         private int mBredrConnectionHandle = BluetoothDevice.ERROR;
348         private int mLeConnectionHandle = BluetoothDevice.ERROR;
349         private short mRssi;
350         private String mAlias;
351         private BluetoothDevice mDevice;
352         private boolean mIsBondingInitiatedLocally;
353         private int mBatteryLevelFromHfp = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
354         private int mBatteryLevelFromBatteryService = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
355         private boolean mIsCoordinatedSetMember;
356         private int mAshaCapability;
357         private int mAshaTruncatedHiSyncId;
358         private String mModelName;
359         @VisibleForTesting int mBondState;
360         @VisibleForTesting int mDeviceType;
361         @VisibleForTesting ParcelUuid[] mUuidsBrEdr;
362         @VisibleForTesting ParcelUuid[] mUuidsLe;
363         @VisibleForTesting boolean mHfpBatteryIndicator = false;
364         private BluetoothSinkAudioPolicy mAudioPolicy;
365 
366         // LRU cache of package names associated to this device
367         private final Set<String> mPackages =
368                 Collections.newSetFromMap(
369                         new LinkedHashMap<String, Boolean>() {
370                             // This is called on every add. Returning true removes the eldest entry.
371                             protected boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) {
372                                 return size() >= MAX_PACKAGE_NAMES;
373                             }
374                         });
375 
DeviceProperties()376         DeviceProperties() {
377             mBondState = BluetoothDevice.BOND_NONE;
378         }
379 
380         /**
381          * @return the mName
382          */
getName()383         String getName() {
384             synchronized (mObject) {
385                 return mName;
386             }
387         }
388 
389         /**
390          * @param name the mName to set
391          */
setName(String name)392         void setName(String name) {
393             synchronized (mObject) {
394                 this.mName = name;
395             }
396         }
397 
398         /**
399          * @return the mIdentityAddress
400          */
getIdentityAddress()401         String getIdentityAddress() {
402             synchronized (mObject) {
403                 return mIdentityAddress;
404             }
405         }
406 
407         /**
408          * @param identityAddress the mIdentityAddress to set
409          */
setIdentityAddress(String identityAddress)410         void setIdentityAddress(String identityAddress) {
411             synchronized (mObject) {
412                 this.mIdentityAddress = identityAddress;
413             }
414         }
415 
416         /**
417          * @return the mIdentityAddressType
418          */
419         @AddressType
getIdentityAddressType()420         int getIdentityAddressType() {
421             synchronized (mObject) {
422                 return mIdentityAddressType;
423             }
424         }
425 
426         /**
427          * @param identityAddressType the mIdentityAddressType to set
428          */
setIdentityAddressType(int identityAddressType)429         void setIdentityAddressType(int identityAddressType) {
430             synchronized (mObject) {
431                 this.mIdentityAddressType = identityAddressType;
432             }
433         }
434 
435         /**
436          * @param identityAddressTypeFromNative the mIdentityAddressType to set after mapping to
437          *     Java layer.
438          */
setIdentityAddressTypeFromNative(int identityAddressTypeFromNative)439         void setIdentityAddressTypeFromNative(int identityAddressTypeFromNative) {
440             /*
441              * from system/types/ble_address_with_type.h
442              *
443              * #define BLE_ADDR_PUBLIC 0x00
444              * #define BLE_ADDR_RANDOM 0x01
445              */
446             int identityAddressType = BluetoothDevice.ADDRESS_TYPE_UNKNOWN;
447             switch (identityAddressTypeFromNative) {
448                 case 0x00:
449                     identityAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC;
450                     break;
451                 case 0x01:
452                     identityAddressType = BluetoothDevice.ADDRESS_TYPE_RANDOM;
453                     break;
454                 default:
455                     errorLog(
456                             "Unexpected identity address type received from native: "
457                                     + identityAddressTypeFromNative);
458                     break;
459             }
460             synchronized (mObject) {
461                 this.mIdentityAddressType = identityAddressType;
462             }
463         }
464 
465         /**
466          * @return mIsConsolidated
467          */
isConsolidated()468         boolean isConsolidated() {
469             synchronized (mObject) {
470                 return mIsConsolidated;
471             }
472         }
473 
474         /**
475          * @param isConsolidated the mIsConsolidated to set
476          */
setIsConsolidated(boolean isConsolidated)477         void setIsConsolidated(boolean isConsolidated) {
478             synchronized (mObject) {
479                 this.mIsConsolidated = isConsolidated;
480             }
481         }
482 
483         /**
484          * @return the mClass
485          */
getBluetoothClass()486         int getBluetoothClass() {
487             synchronized (mObject) {
488                 return mBluetoothClass;
489             }
490         }
491 
492         /**
493          * @param bluetoothClass the mBluetoothClass to set
494          */
setBluetoothClass(int bluetoothClass)495         void setBluetoothClass(int bluetoothClass) {
496             synchronized (mObject) {
497                 this.mBluetoothClass = bluetoothClass;
498             }
499         }
500 
501         /**
502          * @param transport the transport on which the connection exists
503          * @return the mConnectionHandle
504          */
getConnectionHandle(int transport)505         int getConnectionHandle(int transport) {
506             synchronized (mObject) {
507                 if (transport == BluetoothDevice.TRANSPORT_BREDR) {
508                     return mBredrConnectionHandle;
509                 } else if (transport == BluetoothDevice.TRANSPORT_LE) {
510                     return mLeConnectionHandle;
511                 } else {
512                     return BluetoothDevice.ERROR;
513                 }
514             }
515         }
516 
517         /**
518          * @param connectionHandle the connectionHandle to set
519          * @param transport the transport on which to set the handle
520          */
setConnectionHandle(int connectionHandle, int transport)521         void setConnectionHandle(int connectionHandle, int transport) {
522             synchronized (mObject) {
523                 if (transport == BluetoothDevice.TRANSPORT_BREDR) {
524                     mBredrConnectionHandle = connectionHandle;
525                 } else if (transport == BluetoothDevice.TRANSPORT_LE) {
526                     mLeConnectionHandle = connectionHandle;
527                 } else {
528                     errorLog("setConnectionHandle() unexpected transport value " + transport);
529                 }
530             }
531         }
532 
533         /**
534          * @return the UUIDs on LE and Classic transport
535          */
getUuids()536         ParcelUuid[] getUuids() {
537             synchronized (mObject) {
538                 /* When we bond dual mode device, and discover LE and Classic services, stack would
539                  * return LE and Classic UUIDs separately, but Java apps expect them merged.
540                  */
541                 int combinedUuidsLength =
542                         (mUuidsBrEdr != null ? mUuidsBrEdr.length : 0)
543                                 + (mUuidsLe != null ? mUuidsLe.length : 0);
544                 if (!Flags.separateServiceStorage() || combinedUuidsLength == 0) {
545                     return mUuidsBrEdr;
546                 }
547 
548                 java.util.LinkedHashSet<ParcelUuid> result =
549                         new java.util.LinkedHashSet<ParcelUuid>();
550                 if (mUuidsBrEdr != null) {
551                     for (ParcelUuid uuid : mUuidsBrEdr) {
552                         result.add(uuid);
553                     }
554                 }
555 
556                 if (mUuidsLe != null) {
557                     for (ParcelUuid uuid : mUuidsLe) {
558                         result.add(uuid);
559                     }
560                 }
561 
562                 return result.toArray(new ParcelUuid[result.size()]);
563             }
564         }
565 
566         /**
567          * @return just classic transport UUIDS
568          */
getUuidsBrEdr()569         ParcelUuid[] getUuidsBrEdr() {
570             synchronized (mObject) {
571                 return mUuidsBrEdr;
572             }
573         }
574 
575         /**
576          * @param uuids the mUuidsBrEdr to set
577          */
setUuidsBrEdr(ParcelUuid[] uuids)578         void setUuidsBrEdr(ParcelUuid[] uuids) {
579             synchronized (mObject) {
580                 this.mUuidsBrEdr = uuids;
581             }
582         }
583 
584         /**
585          * @return the mUuidsLe
586          */
getUuidsLe()587         ParcelUuid[] getUuidsLe() {
588             synchronized (mObject) {
589                 return mUuidsLe;
590             }
591         }
592 
593         /**
594          * @param uuids the mUuidsLe to set
595          */
setUuidsLe(ParcelUuid[] uuids)596         void setUuidsLe(ParcelUuid[] uuids) {
597             synchronized (mObject) {
598                 this.mUuidsLe = uuids;
599             }
600         }
601 
602         /**
603          * @return the mAddress
604          */
getAddress()605         byte[] getAddress() {
606             synchronized (mObject) {
607                 return mAddress;
608             }
609         }
610 
611         /**
612          * @param address the mAddress to set
613          */
setAddress(byte[] address)614         void setAddress(byte[] address) {
615             synchronized (mObject) {
616                 this.mAddress = address;
617             }
618         }
619 
620         /**
621          * @return the mDevice
622          */
getDevice()623         BluetoothDevice getDevice() {
624             synchronized (mObject) {
625                 return mDevice;
626             }
627         }
628 
629         /**
630          * @param device the mDevice to set
631          */
setDevice(BluetoothDevice device)632         void setDevice(BluetoothDevice device) {
633             synchronized (mObject) {
634                 this.mDevice = device;
635             }
636         }
637 
638         /**
639          * @return mRssi
640          */
getRssi()641         short getRssi() {
642             synchronized (mObject) {
643                 return mRssi;
644             }
645         }
646 
647         /**
648          * @param rssi the mRssi to set
649          */
setRssi(short rssi)650         void setRssi(short rssi) {
651             synchronized (mObject) {
652                 this.mRssi = rssi;
653             }
654         }
655 
656         /**
657          * @return mDeviceType
658          */
getDeviceType()659         int getDeviceType() {
660             synchronized (mObject) {
661                 return mDeviceType;
662             }
663         }
664 
665         /**
666          * @param deviceType the mDeviceType to set
667          */
668         @VisibleForTesting
setDeviceType(int deviceType)669         void setDeviceType(int deviceType) {
670             synchronized (mObject) {
671                 this.mDeviceType = deviceType;
672             }
673         }
674 
675         /**
676          * @return the mAlias
677          */
getAlias()678         String getAlias() {
679             synchronized (mObject) {
680                 return mAlias;
681             }
682         }
683 
684         /**
685          * @param mAlias the mAlias to set
686          */
setAlias(BluetoothDevice device, String mAlias)687         void setAlias(BluetoothDevice device, String mAlias) {
688             synchronized (mObject) {
689                 this.mAlias = mAlias;
690                 mAdapterService
691                         .getNative()
692                         .setDeviceProperty(
693                                 mAddress,
694                                 AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME,
695                                 mAlias.getBytes());
696                 Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
697                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
698                 intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias);
699                 mAdapterService.sendBroadcast(
700                         intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
701             }
702         }
703 
704         /**
705          * @param newBondState the mBondState to set
706          */
setBondState(int newBondState)707         void setBondState(int newBondState) {
708             synchronized (mObject) {
709                 this.mBondState = newBondState;
710                 if (newBondState == BluetoothDevice.BOND_NONE) {
711                     /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
712                     cachedBluetoothDevice issued a connect using the local cached copy of uuids,
713                     without waiting for the ACTION_UUID intent.
714                     This was resulting in multiple calls to connect().*/
715                     mUuidsBrEdr = null;
716                     mUuidsLe = null;
717                     mAlias = null;
718                 }
719             }
720         }
721 
722         /**
723          * @return the mBondState
724          */
getBondState()725         int getBondState() {
726             synchronized (mObject) {
727                 return mBondState;
728             }
729         }
730 
isBonding()731         boolean isBonding() {
732             return getBondState() == BluetoothDevice.BOND_BONDING;
733         }
734 
isBondingOrBonded()735         boolean isBondingOrBonded() {
736             return isBonding() || getBondState() == BluetoothDevice.BOND_BONDED;
737         }
738 
739         /**
740          * @param isBondingInitiatedLocally whether bonding is initiated locally
741          */
setBondingInitiatedLocally(boolean isBondingInitiatedLocally)742         void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) {
743             synchronized (mObject) {
744                 this.mIsBondingInitiatedLocally = isBondingInitiatedLocally;
745             }
746         }
747 
748         /**
749          * @return the isBondingInitiatedLocally
750          */
isBondingInitiatedLocally()751         boolean isBondingInitiatedLocally() {
752             synchronized (mObject) {
753                 return mIsBondingInitiatedLocally;
754             }
755         }
756 
757         /**
758          * @return mBatteryLevel
759          */
getBatteryLevel()760         int getBatteryLevel() {
761             synchronized (mObject) {
762                 if (mBatteryLevelFromBatteryService != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
763                     return mBatteryLevelFromBatteryService;
764                 }
765                 return mBatteryLevelFromHfp;
766             }
767         }
768 
769         /**
770          * @param hfpBatteryIndicator is set to true based on the HF battery indicator support
771          *     received from AT+BIND command and set to false in disconnect path.
772          */
setHfpBatteryIndicatorStatus(boolean hfpBatteryIndicator)773         void setHfpBatteryIndicatorStatus(boolean hfpBatteryIndicator) {
774             this.mHfpBatteryIndicator = hfpBatteryIndicator;
775         }
776 
777         /**
778          * @return mHfpBatteryIndicator
779          */
isHfpBatteryIndicatorEnabled()780         boolean isHfpBatteryIndicatorEnabled() {
781             return mHfpBatteryIndicator;
782         }
783 
setBatteryLevelFromHfp(int batteryLevel)784         void setBatteryLevelFromHfp(int batteryLevel) {
785             synchronized (mObject) {
786                 if (mBatteryLevelFromHfp == batteryLevel) {
787                     return;
788                 }
789                 mBatteryLevelFromHfp = batteryLevel;
790             }
791         }
792 
setBatteryLevelFromBatteryService(int batteryLevel)793         void setBatteryLevelFromBatteryService(int batteryLevel) {
794             synchronized (mObject) {
795                 if (mBatteryLevelFromBatteryService == batteryLevel) {
796                     return;
797                 }
798                 mBatteryLevelFromBatteryService = batteryLevel;
799             }
800         }
801 
802         /**
803          * @return the mIsCoordinatedSetMember
804          */
isCoordinatedSetMember()805         boolean isCoordinatedSetMember() {
806             synchronized (mObject) {
807                 return mIsCoordinatedSetMember;
808             }
809         }
810 
811         /**
812          * @param isCoordinatedSetMember the mIsCoordinatedSetMember to set
813          */
setIsCoordinatedSetMember(boolean isCoordinatedSetMember)814         void setIsCoordinatedSetMember(boolean isCoordinatedSetMember) {
815             if ((mAdapterService.getSupportedProfilesBitMask()
816                             & (1 << BluetoothProfile.CSIP_SET_COORDINATOR))
817                     == 0) {
818                 debugLog("CSIP is not supported");
819                 return;
820             }
821             synchronized (mObject) {
822                 this.mIsCoordinatedSetMember = isCoordinatedSetMember;
823             }
824         }
825 
826         /**
827          * @return the mAshaCapability
828          */
getAshaCapability()829         int getAshaCapability() {
830             synchronized (mObject) {
831                 return mAshaCapability;
832             }
833         }
834 
setAshaCapability(int ashaCapability)835         void setAshaCapability(int ashaCapability) {
836             synchronized (mObject) {
837                 this.mAshaCapability = ashaCapability;
838             }
839         }
840 
841         /**
842          * @return the mAshaTruncatedHiSyncId
843          */
getAshaTruncatedHiSyncId()844         int getAshaTruncatedHiSyncId() {
845             synchronized (mObject) {
846                 return mAshaTruncatedHiSyncId;
847             }
848         }
849 
setAshaTruncatedHiSyncId(int ashaTruncatedHiSyncId)850         void setAshaTruncatedHiSyncId(int ashaTruncatedHiSyncId) {
851             synchronized (mObject) {
852                 this.mAshaTruncatedHiSyncId = ashaTruncatedHiSyncId;
853             }
854         }
855 
setHfAudioPolicyForRemoteAg(BluetoothSinkAudioPolicy policies)856         public void setHfAudioPolicyForRemoteAg(BluetoothSinkAudioPolicy policies) {
857             mAudioPolicy = policies;
858         }
859 
getHfAudioPolicyForRemoteAg()860         public BluetoothSinkAudioPolicy getHfAudioPolicyForRemoteAg() {
861             return mAudioPolicy;
862         }
863 
setModelName(String modelName)864         public void setModelName(String modelName) {
865             mModelName = modelName;
866             mAdapterService.setMetadata(
867                     this.mDevice,
868                     BluetoothDevice.METADATA_MODEL_NAME,
869                     mModelName.getBytes(StandardCharsets.UTF_8));
870         }
871 
872         /**
873          * @return the mModelName
874          */
getModelName()875         String getModelName() {
876             synchronized (mObject) {
877                 return mModelName;
878             }
879         }
880 
881         @NonNull
getPackages()882         public String[] getPackages() {
883             synchronized (mObject) {
884                 return mPackages.toArray(new String[0]);
885             }
886         }
887 
addPackage(String packageName)888         public void addPackage(String packageName) {
889             synchronized (mObject) {
890                 // Removing the package ensures that the LRU cache order is updated. Adding it back
891                 // will make it the newest.
892                 mPackages.remove(packageName);
893                 mPackages.add(packageName);
894             }
895         }
896     }
897 
sendUuidIntent(BluetoothDevice device, DeviceProperties prop, boolean success)898     private void sendUuidIntent(BluetoothDevice device, DeviceProperties prop, boolean success) {
899         // Send uuids within the stack before the broadcast is sent out
900         ParcelUuid[] uuids = prop == null ? null : prop.getUuids();
901 
902         if (!Flags.preventDuplicateUuidIntent() || success) {
903             mAdapterService.sendUuidsInternal(device, uuids);
904         }
905 
906         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
907         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
908         intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids);
909         mAdapterService.sendBroadcast(
910                 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
911 
912         // SDP Sent UUID Intent here
913         MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_SENT_UUID, 1);
914 
915         // Remove the outstanding UUID request
916         if (Flags.preventDuplicateUuidIntent()) {
917             // Handler.removeMessages() compares the object pointer so we cannot use the device
918             // directly. So we have to extract original BluetoothDevice object from mSdpTracker.
919             int index = mSdpTracker.indexOf(device);
920             if (index >= 0) {
921                 BluetoothDevice originalDevice = mSdpTracker.get(index);
922                 if (originalDevice != null) {
923                     mHandler.removeMessages(MESSAGE_UUID_INTENT, originalDevice);
924                 }
925             }
926         }
927         mSdpTracker.remove(device);
928     }
929 
930     /**
931      * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing,
932      * we must add device first before setting it's properties. This is a helper method for doing
933      * that.
934      */
setBondingInitiatedLocally(byte[] address)935     void setBondingInitiatedLocally(byte[] address) {
936         DeviceProperties properties;
937 
938         BluetoothDevice device = getDevice(address);
939         if (device == null) {
940             properties = addDeviceProperties(address);
941         } else {
942             properties = getDeviceProperties(device);
943         }
944 
945         properties.setBondingInitiatedLocally(true);
946     }
947 
updateBatteryLevel(BluetoothDevice device, int batteryLevel, boolean isBas)948     void updateBatteryLevel(BluetoothDevice device, int batteryLevel, boolean isBas) {
949         if (device == null || batteryLevel < 0 || batteryLevel > 100) {
950             warnLog(
951                     "Invalid parameters device="
952                             + String.valueOf(device == null)
953                             + ", batteryLevel="
954                             + String.valueOf(batteryLevel));
955             return;
956         }
957         DeviceProperties deviceProperties = getDeviceProperties(device);
958         if (deviceProperties == null) {
959             deviceProperties = addDeviceProperties(Utils.getByteAddress(device));
960         }
961         int prevBatteryLevel = deviceProperties.getBatteryLevel();
962         if (isBas) {
963             deviceProperties.setBatteryLevelFromBatteryService(batteryLevel);
964         } else {
965             deviceProperties.setBatteryLevelFromHfp(batteryLevel);
966         }
967         int newBatteryLevel = deviceProperties.getBatteryLevel();
968         if (prevBatteryLevel == newBatteryLevel) {
969             debugLog(
970                     "Same battery level for device "
971                             + device
972                             + " received "
973                             + String.valueOf(batteryLevel)
974                             + "%");
975             return;
976         }
977         sendBatteryLevelChangedBroadcast(device, newBatteryLevel);
978         Log.d(TAG, "Updated device " + device + " battery level to " + newBatteryLevel + "%");
979     }
980 
resetBatteryLevel(BluetoothDevice device, boolean isBas)981     void resetBatteryLevel(BluetoothDevice device, boolean isBas) {
982         if (device == null) {
983             warnLog("Device is null");
984             return;
985         }
986         DeviceProperties deviceProperties = getDeviceProperties(device);
987         if (deviceProperties == null) {
988             return;
989         }
990         int prevBatteryLevel = deviceProperties.getBatteryLevel();
991         if (isBas) {
992             deviceProperties.setBatteryLevelFromBatteryService(
993                     BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
994         } else {
995             deviceProperties.setBatteryLevelFromHfp(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
996         }
997 
998         if (Flags.enableBatteryLevelUpdateOnlyThroughHfIndicator()) {
999             deviceProperties.setHfpBatteryIndicatorStatus(false);
1000         }
1001         int newBatteryLevel = deviceProperties.getBatteryLevel();
1002         if (prevBatteryLevel == newBatteryLevel) {
1003             debugLog("Battery level was not changed due to reset, device=" + device);
1004             return;
1005         }
1006         sendBatteryLevelChangedBroadcast(device, newBatteryLevel);
1007         Log.d(TAG, "Updated device " + device + " battery level to " + newBatteryLevel + "%");
1008     }
1009 
sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel)1010     private void sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel) {
1011         Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED);
1012         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1013         intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel);
1014         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1015         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1016         mAdapterService.sendBroadcast(
1017                 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1018     }
1019 
1020     /**
1021      * Converts HFP's Battery Charge indicator values of {@code 0 -- 5} to an integer percentage.
1022      */
1023     @VisibleForTesting
batteryChargeIndicatorToPercentage(int indicator)1024     static int batteryChargeIndicatorToPercentage(int indicator) {
1025         int percent;
1026         switch (indicator) {
1027             case 5:
1028                 percent = HFP_BATTERY_CHARGE_INDICATOR_5;
1029                 break;
1030             case 4:
1031                 percent = HFP_BATTERY_CHARGE_INDICATOR_4;
1032                 break;
1033             case 3:
1034                 percent = HFP_BATTERY_CHARGE_INDICATOR_3;
1035                 break;
1036             case 2:
1037                 percent = HFP_BATTERY_CHARGE_INDICATOR_2;
1038                 break;
1039             case 1:
1040                 percent = HFP_BATTERY_CHARGE_INDICATOR_1;
1041                 break;
1042             case 0:
1043                 percent = HFP_BATTERY_CHARGE_INDICATOR_0;
1044                 break;
1045             default:
1046                 percent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1047         }
1048         Log.d(TAG, "Battery charge indicator: " + indicator + "; converted to: " + percent + "%");
1049         return percent;
1050     }
1051 
areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2)1052     private static boolean areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2) {
1053         final int length1 = uuids1 == null ? 0 : uuids1.length;
1054         final int length2 = uuids2 == null ? 0 : uuids2.length;
1055         if (length1 != length2) {
1056             return false;
1057         }
1058         Set<ParcelUuid> set = new HashSet<>();
1059         for (int i = 0; i < length1; ++i) {
1060             set.add(uuids1[i]);
1061         }
1062         for (int i = 0; i < length2; ++i) {
1063             set.remove(uuids2[i]);
1064         }
1065         return set.isEmpty();
1066     }
1067 
devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values)1068     void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
1069         Intent intent;
1070         byte[] val;
1071         int type;
1072         BluetoothDevice bdDevice = getDevice(address);
1073         DeviceProperties deviceProperties;
1074         if (bdDevice == null) {
1075             debugLog("Added new device property, device=" + bdDevice);
1076             deviceProperties = addDeviceProperties(address);
1077             bdDevice = getDevice(address);
1078         } else {
1079             deviceProperties = getDeviceProperties(bdDevice);
1080         }
1081 
1082         if (types.length <= 0) {
1083             errorLog("No properties to update");
1084             return;
1085         }
1086 
1087         boolean uuids_updated = false;
1088 
1089         for (int j = 0; j < types.length; j++) {
1090             type = types[j];
1091             val = values[j];
1092             if (val.length == 0) {
1093                 continue;
1094             }
1095 
1096             synchronized (mObject) {
1097                 debugLog("Update property, device=" + bdDevice + ", type: " + type);
1098                 switch (type) {
1099                     case AbstractionLayer.BT_PROPERTY_BDNAME:
1100                         final String newName = new String(val);
1101                         if (newName.equals(deviceProperties.getName())) {
1102                             debugLog("Skip name update for " + bdDevice);
1103                             break;
1104                         }
1105                         deviceProperties.setName(newName);
1106                         List<String> wordBreakdownList =
1107                                 MetricsLogger.getInstance().getWordBreakdownList(newName);
1108                         if (SdkLevel.isAtLeastU()) {
1109                             MetricsLogger.getInstance()
1110                                     .uploadRestrictedBluetoothDeviceName(wordBreakdownList);
1111                         }
1112                         intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
1113                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
1114                         intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName());
1115                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1116                         mAdapterService.sendBroadcast(
1117                                 intent,
1118                                 BLUETOOTH_CONNECT,
1119                                 Utils.getTempBroadcastOptions().toBundle());
1120                         debugLog("Remote device name is: " + deviceProperties.getName());
1121                         break;
1122                     case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
1123                         deviceProperties.setAlias(bdDevice, new String(val));
1124                         debugLog("Remote device alias is: " + deviceProperties.getAlias());
1125                         break;
1126                     case AbstractionLayer.BT_PROPERTY_BDADDR:
1127                         deviceProperties.setAddress(val);
1128                         debugLog(
1129                                 "Remote Address is:" + Utils.getRedactedAddressStringFromByte(val));
1130                         break;
1131                     case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
1132                         final int newBluetoothClass = Utils.byteArrayToInt(val);
1133                         if (newBluetoothClass == deviceProperties.getBluetoothClass()) {
1134                             debugLog(
1135                                     "Skip class update, device="
1136                                             + bdDevice
1137                                             + ", cod=0x"
1138                                             + Integer.toHexString(newBluetoothClass));
1139                             break;
1140                         }
1141                         deviceProperties.setBluetoothClass(newBluetoothClass);
1142                         intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
1143                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
1144                         intent.putExtra(
1145                                 BluetoothDevice.EXTRA_CLASS,
1146                                 new BluetoothClass(deviceProperties.getBluetoothClass()));
1147                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
1148                         mAdapterService.sendBroadcast(
1149                                 intent,
1150                                 BLUETOOTH_CONNECT,
1151                                 Utils.getTempBroadcastOptions().toBundle());
1152                         debugLog(
1153                                 "Remote class update, device="
1154                                         + bdDevice
1155                                         + ", cod=0x"
1156                                         + Integer.toHexString(newBluetoothClass));
1157                         break;
1158                     case AbstractionLayer.BT_PROPERTY_UUIDS:
1159                     case AbstractionLayer.BT_PROPERTY_UUIDS_LE:
1160                         if (type == AbstractionLayer.BT_PROPERTY_UUIDS) {
1161                             final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val);
1162                             if (areUuidsEqual(newUuids, deviceProperties.getUuidsBrEdr())) {
1163                                 // SDP Skip adding UUIDs to property cache if equal
1164                                 debugLog("Skip uuids update for " + bdDevice.getAddress());
1165                                 MetricsLogger.getInstance()
1166                                         .cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1);
1167                                 break;
1168                             }
1169                             deviceProperties.setUuidsBrEdr(newUuids);
1170                         } else if (type == AbstractionLayer.BT_PROPERTY_UUIDS_LE) {
1171                             final ParcelUuid[] newUuidsLe = Utils.byteArrayToUuid(val);
1172                             if (areUuidsEqual(newUuidsLe, deviceProperties.getUuidsLe())) {
1173                                 // SDP Skip adding UUIDs to property cache if equal
1174                                 debugLog("Skip LE uuids update for " + bdDevice.getAddress());
1175                                 MetricsLogger.getInstance()
1176                                         .cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1);
1177                                 break;
1178                             }
1179                             deviceProperties.setUuidsLe(newUuidsLe);
1180                         }
1181                         uuids_updated = true;
1182                         break;
1183                     case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
1184                         if (deviceProperties.isConsolidated()) {
1185                             break;
1186                         }
1187                         // The device type from hal layer, defined in bluetooth.h,
1188                         // matches the type defined in BluetoothDevice.java
1189                         deviceProperties.setDeviceType(Utils.byteArrayToInt(val));
1190                         break;
1191                     case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
1192                         // RSSI from hal is in one byte
1193                         deviceProperties.setRssi(val[0]);
1194                         break;
1195                     case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER:
1196                         deviceProperties.setIsCoordinatedSetMember(val[0] != 0);
1197                         break;
1198                     case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY:
1199                         deviceProperties.setAshaCapability(val[0]);
1200                         break;
1201                     case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID:
1202                         deviceProperties.setAshaTruncatedHiSyncId(val[0]);
1203                         break;
1204                     case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM:
1205                         final String modelName = new String(val);
1206                         debugLog("Remote device model name: " + modelName);
1207                         deviceProperties.setModelName(modelName);
1208                         BluetoothStatsLog.write(
1209                                 BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED,
1210                                 mAdapterService.obfuscateAddress(bdDevice),
1211                                 BluetoothProtoEnums.DEVICE_INFO_INTERNAL,
1212                                 LOG_SOURCE_DIS,
1213                                 null,
1214                                 modelName,
1215                                 null,
1216                                 null,
1217                                 mAdapterService.getMetricId(bdDevice),
1218                                 bdDevice.getAddressType(),
1219                                 0,
1220                                 0,
1221                                 0);
1222                         break;
1223                 }
1224             }
1225         }
1226 
1227         if (!uuids_updated) {
1228             return;
1229         }
1230 
1231         /* uuids_updated == true
1232          * We might have received LE and BREDR UUIDS separately, ensure that UUID intent is sent
1233          * just once */
1234 
1235         if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) {
1236             // SDP Adding UUIDs to property cache and sending intent
1237             MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1);
1238             mAdapterService.deviceUuidUpdated(bdDevice);
1239             sendUuidIntent(bdDevice, deviceProperties, true);
1240         } else if (mAdapterService.getState() == BluetoothAdapter.STATE_BLE_ON) {
1241             // SDP Adding UUIDs to property cache but with no intent
1242             MetricsLogger.getInstance()
1243                     .cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1);
1244             mAdapterService.deviceUuidUpdated(bdDevice);
1245         } else {
1246             // SDP Silently dropping UUIDs and with no intent
1247             MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1);
1248         }
1249     }
1250 
deviceFoundCallback(byte[] address)1251     void deviceFoundCallback(byte[] address) {
1252         // The device properties are already registered - we can send the intent
1253         // now
1254         BluetoothDevice device = getDevice(address);
1255         DeviceProperties deviceProp = getDeviceProperties(device);
1256         if (deviceProp == null) {
1257             errorLog("deviceFoundCallback: Device Properties is null for Device:" + device);
1258             return;
1259         }
1260         boolean restrict_device_found =
1261                 SystemProperties.getBoolean("bluetooth.restrict_discovered_device.enabled", false);
1262         if (restrict_device_found && (deviceProp.mName == null || deviceProp.mName.isEmpty())) {
1263             warnLog("deviceFoundCallback: Device name is null or empty: " + device);
1264             return;
1265         }
1266 
1267         infoLog("deviceFoundCallback: Remote Address is:" + device);
1268         Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
1269         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1270         intent.putExtra(
1271                 BluetoothDevice.EXTRA_CLASS, new BluetoothClass(deviceProp.getBluetoothClass()));
1272         intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.getRssi());
1273         intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.getName());
1274         intent.putExtra(
1275                 BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER,
1276                 deviceProp.isCoordinatedSetMember());
1277 
1278         final List<DiscoveringPackage> packages = mAdapterService.getDiscoveringPackages();
1279         synchronized (packages) {
1280             for (DiscoveringPackage pkg : packages) {
1281                 if (pkg.hasDisavowedLocation()) {
1282                     if (mLocationDenylistPredicate.test(device)) {
1283                         continue;
1284                     }
1285                 }
1286 
1287                 intent.setPackage(pkg.packageName());
1288 
1289                 if (pkg.permission() != null) {
1290                     mAdapterService.sendBroadcastMultiplePermissions(
1291                             intent,
1292                             new String[] {BLUETOOTH_SCAN, pkg.permission()},
1293                             Utils.getTempBroadcastOptions());
1294                 } else {
1295                     mAdapterService.sendBroadcastMultiplePermissions(
1296                             intent, new String[] {BLUETOOTH_SCAN}, Utils.getTempBroadcastOptions());
1297                 }
1298             }
1299         }
1300     }
1301 
addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress)1302     void addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress) {
1303         DeviceProperties deviceProperties;
1304         BluetoothDevice device = getDevice(mainAddress);
1305         if (device == null) {
1306             deviceProperties = addDeviceProperties(mainAddress);
1307             device = deviceProperties.getDevice();
1308         } else {
1309             deviceProperties = getDeviceProperties(device);
1310         }
1311         Log.d(
1312                 TAG,
1313                 "addressConsolidateCallback device: "
1314                         + device
1315                         + ", secondaryAddress:"
1316                         + Utils.getRedactedAddressStringFromByte(secondaryAddress));
1317 
1318         deviceProperties.setIsConsolidated(true);
1319         deviceProperties.setDeviceType(BluetoothDevice.DEVICE_TYPE_DUAL);
1320         deviceProperties.setIdentityAddress(Utils.getAddressStringFromByte(secondaryAddress));
1321         mDualDevicesMap.put(
1322                 deviceProperties.getIdentityAddress(), Utils.getAddressStringFromByte(mainAddress));
1323     }
1324 
1325     /**
1326      * Callback to associate an LE-only device's RPA with its identity address and identity address
1327      * type
1328      *
1329      * @param mainAddress the device's RPA
1330      * @param secondaryAddress the device's identity address
1331      * @param identityAddressTypeFromNative the device's identity address type from native
1332      */
leAddressAssociateCallback( byte[] mainAddress, byte[] secondaryAddress, int identityAddressTypeFromNative)1333     void leAddressAssociateCallback(
1334             byte[] mainAddress, byte[] secondaryAddress, int identityAddressTypeFromNative) {
1335         DeviceProperties deviceProperties;
1336         BluetoothDevice device = getDevice(mainAddress);
1337         if (device == null) {
1338             deviceProperties = addDeviceProperties(mainAddress);
1339             device = deviceProperties.getDevice();
1340         } else {
1341             deviceProperties = getDeviceProperties(device);
1342         }
1343         Log.d(
1344                 TAG,
1345                 "leAddressAssociateCallback device: "
1346                         + device
1347                         + ", secondaryAddress:"
1348                         + Utils.getRedactedAddressStringFromByte(secondaryAddress)
1349                         + ", identityAddressTypeFromNative="
1350                         + identityAddressTypeFromNative);
1351 
1352         deviceProperties.setIdentityAddress(Utils.getAddressStringFromByte(secondaryAddress));
1353         deviceProperties.setIdentityAddressTypeFromNative(identityAddressTypeFromNative);
1354     }
1355 
aclStateChangeCallback( int status, byte[] address, int newState, int transportLinkType, int hciReason, int handle)1356     void aclStateChangeCallback(
1357             int status,
1358             byte[] address,
1359             int newState,
1360             int transportLinkType,
1361             int hciReason,
1362             int handle) {
1363         if (status != AbstractionLayer.BT_STATUS_SUCCESS) {
1364             debugLog("aclStateChangeCallback status is " + status + ", skipping");
1365             return;
1366         }
1367 
1368         final BluetoothDevice device =
1369                 requireNonNullElseGet(
1370                         getDevice(address),
1371                         () -> {
1372                             Log.w(
1373                                     TAG,
1374                                     "aclStateChangeCallback: device is NULL"
1375                                             + ("address="
1376                                                     + Utils.getRedactedAddressStringFromByte(
1377                                                             address))
1378                                             + (" newState=" + newState));
1379                             return addDeviceProperties(address).getDevice();
1380                         });
1381 
1382         DeviceProperties deviceProperties = getDeviceProperties(device);
1383 
1384         int state = mAdapterService.getState();
1385 
1386         Intent intent = null;
1387         if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
1388             deviceProperties.setConnectionHandle(handle, transportLinkType);
1389             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) {
1390                 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
1391                 intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transportLinkType);
1392             } else if (state == BluetoothAdapter.STATE_BLE_ON
1393                     || state == BluetoothAdapter.STATE_BLE_TURNING_ON) {
1394                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED);
1395             }
1396             BatteryService batteryService = BatteryService.getBatteryService();
1397             if (batteryService != null && transportLinkType == BluetoothDevice.TRANSPORT_LE) {
1398                 batteryService.connectIfPossible(device);
1399             }
1400             mAdapterService.updatePhonePolicyOnAclConnect(device);
1401             SecurityLog.writeEvent(
1402                     SecurityLog.TAG_BLUETOOTH_CONNECTION,
1403                     Utils.getLoggableAddress(device), /* success */
1404                     1, /* reason */
1405                     "");
1406             debugLog(
1407                     "aclStateChangeCallback: Adapter State: "
1408                             + BluetoothAdapter.nameForState(state)
1409                             + " Connected: "
1410                             + device);
1411         } else {
1412             deviceProperties.setConnectionHandle(BluetoothDevice.ERROR, transportLinkType);
1413             if (getBondState(device) == BluetoothDevice.BOND_BONDING) {
1414                 // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding.
1415                 sendPairingCancelIntent(device);
1416             } else if (getBondState(device) == BluetoothDevice.BOND_NONE) {
1417                 removeAddressMapping(Utils.getAddressStringFromByte(address));
1418             }
1419             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) {
1420                 mAdapterService.notifyAclDisconnected(device, transportLinkType);
1421                 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
1422                 intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transportLinkType);
1423             } else if (state == BluetoothAdapter.STATE_BLE_ON
1424                     || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
1425                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED);
1426             }
1427             // Reset battery level on complete disconnection
1428             if (mAdapterService.getConnectionState(device) == 0) {
1429                 BatteryService batteryService = BatteryService.getBatteryService();
1430                 if (batteryService != null
1431                         && batteryService.getConnectionState(device) != STATE_DISCONNECTED
1432                         && transportLinkType == BluetoothDevice.TRANSPORT_LE) {
1433                     batteryService.disconnect(device);
1434                 }
1435                 resetBatteryLevel(device, /* isBas= */ true);
1436             }
1437 
1438             if (mAdapterService.isAllProfilesUnknown(device)) {
1439                 DeviceProperties deviceProp = getDeviceProperties(device);
1440                 if (deviceProp != null) {
1441                     deviceProp.setBondingInitiatedLocally(false);
1442                 }
1443             }
1444             SecurityLog.writeEvent(
1445                     SecurityLog.TAG_BLUETOOTH_DISCONNECTION,
1446                     Utils.getLoggableAddress(device),
1447                     BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonToString(
1448                             AdapterService.hciToAndroidDisconnectReason(hciReason)));
1449             debugLog(
1450                     "aclStateChangeCallback: Adapter State: "
1451                             + BluetoothAdapter.nameForState(state)
1452                             + " Disconnected: "
1453                             + device
1454                             + " transportLinkType: "
1455                             + transportLinkType
1456                             + " hciReason: "
1457                             + hciReason);
1458         }
1459 
1460         int connectionState =
1461                 newState == AbstractionLayer.BT_ACL_STATE_CONNECTED
1462                         ? BluetoothAdapter.STATE_CONNECTED
1463                         : BluetoothAdapter.STATE_DISCONNECTED;
1464         int metricId = mAdapterService.getMetricId(device);
1465         BluetoothStatsLog.write(
1466                 BluetoothStatsLog.BLUETOOTH_ACL_CONNECTION_STATE_CHANGED,
1467                 mAdapterService.obfuscateAddress(device),
1468                 connectionState,
1469                 metricId,
1470                 transportLinkType);
1471 
1472         BluetoothStatsLog.write(
1473                 BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED,
1474                 mAdapterService.obfuscateAddress(device),
1475                 getBluetoothClass(device),
1476                 metricId);
1477 
1478         byte[] remoteDeviceInfoBytes = MetricsLogger.getInstance().getRemoteDeviceInfoProto(device);
1479 
1480         BluetoothStatsLog.write(
1481                 BluetoothStatsLog.REMOTE_DEVICE_INFORMATION_WITH_METRIC_ID,
1482                 metricId,
1483                 remoteDeviceInfoBytes);
1484 
1485         if (intent == null) {
1486             Log.e(TAG, "aclStateChangeCallback intent is null. BondState: " + getBondState(device));
1487             return;
1488         }
1489 
1490         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device)
1491                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)
1492                 .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1493         mAdapterService.sendBroadcast(
1494                 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1495 
1496         RemoteExceptionIgnoringConsumer<IBluetoothConnectionCallback> connectionChangeConsumer;
1497         if (connectionState == BluetoothAdapter.STATE_CONNECTED) {
1498             connectionChangeConsumer = cb -> cb.onDeviceConnected(device);
1499         } else {
1500             final int disconnectReason;
1501             if (hciReason == 0x16 /* HCI_ERR_CONN_CAUSE_LOCAL_HOST */
1502                     && mAdapterService.getDatabase().getKeyMissingCount(device) > 0) {
1503                 // Native stack disconnects the link on detecting the bond loss. Native GATT would
1504                 // return HCI_ERR_CONN_CAUSE_LOCAL_HOST in such case, but the apps should see
1505                 // HCI_ERR_AUTH_FAILURE.
1506                 Log.d(
1507                         TAG,
1508                         "aclStateChangeCallback() - disconnected due to bond loss for device="
1509                                 + device);
1510                 disconnectReason = 0x05; /* HCI_ERR_AUTH_FAILURE */
1511             } else {
1512                 disconnectReason = hciReason;
1513             }
1514             connectionChangeConsumer =
1515                     cb ->
1516                             cb.onDeviceDisconnected(
1517                                     device,
1518                                     AdapterService.hciToAndroidDisconnectReason(disconnectReason));
1519         }
1520 
1521         mAdapterService.aclStateChangeBroadcastCallback(connectionChangeConsumer);
1522     }
1523 
sendPairingCancelIntent(BluetoothDevice device)1524     private void sendPairingCancelIntent(BluetoothDevice device) {
1525         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
1526         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1527         intent.setPackage(
1528                 SystemProperties.get(
1529                         Utils.PAIRING_UI_PROPERTY,
1530                         mAdapterService.getString(R.string.pairing_ui_package)));
1531 
1532         Log.i(TAG, "sendPairingCancelIntent: device=" + device);
1533         mAdapterService.sendBroadcast(
1534                 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle());
1535     }
1536 
removeAddressMapping(String address)1537     private void removeAddressMapping(String address) {
1538         DeviceProperties deviceProperties = mDevices.get(address);
1539         if (deviceProperties != null) {
1540             String pseudoAddress = mDualDevicesMap.get(address);
1541             if (pseudoAddress != null) {
1542                 deviceProperties = mDevices.get(pseudoAddress);
1543             }
1544         }
1545 
1546         if (deviceProperties != null) {
1547             int leConnectionHandle =
1548                     deviceProperties.getConnectionHandle(BluetoothDevice.TRANSPORT_LE);
1549             int bredrConnectionHandle =
1550                     deviceProperties.getConnectionHandle(BluetoothDevice.TRANSPORT_BREDR);
1551             if (leConnectionHandle != BluetoothDevice.ERROR
1552                     || bredrConnectionHandle != BluetoothDevice.ERROR) {
1553                 // Device still connected, wait for disconnection to remove the properties
1554                 return;
1555             }
1556         }
1557 
1558         synchronized (mDevices) {
1559             mDevices.remove(address);
1560             mDeviceQueue.remove(address); // Remove from LRU cache
1561 
1562             // Remove from dual mode device mappings
1563             mDualDevicesMap.values().remove(address);
1564             mDualDevicesMap.remove(address);
1565         }
1566     }
1567 
onBondStateChange(BluetoothDevice device, int newState)1568     void onBondStateChange(BluetoothDevice device, int newState) {
1569         String address = device.getAddress();
1570 
1571         if (newState == BluetoothDevice.BOND_NONE) {
1572             removeAddressMapping(address);
1573         }
1574     }
1575 
1576     // TODO: remove when key_missing_public flag is deleted
1577     @SuppressLint("AndroidFrameworkRequiresPermission")
keyMissingCallback(byte[] address)1578     void keyMissingCallback(byte[] address) {
1579         BluetoothDevice bluetoothDevice = getDevice(address);
1580         if (bluetoothDevice == null) {
1581             errorLog(
1582                     "keyMissingCallback: device is NULL, address="
1583                             + Utils.getRedactedAddressStringFromByte(address));
1584             return;
1585         }
1586         Log.i(TAG, "keyMissingCallback device: " + bluetoothDevice);
1587 
1588         if (getBondState(bluetoothDevice) == BluetoothDevice.BOND_BONDED) {
1589             Intent intent =
1590                     new Intent(BluetoothDevice.ACTION_KEY_MISSING)
1591                             .putExtra(BluetoothDevice.EXTRA_DEVICE, bluetoothDevice)
1592                             .addFlags(
1593                                     Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1594                                             | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1595 
1596             // Log transition to key missing state, if the key missing count is 0 which indicates
1597             //  that the device is bonded until now.
1598             if (mAdapterService.getDatabase().getKeyMissingCount(bluetoothDevice) == 0) {
1599                 MetricsLogger.getInstance()
1600                         .logBluetoothEvent(
1601                                 bluetoothDevice,
1602                                 BluetoothStatsLog
1603                                         .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__TRANSITION,
1604                                 BluetoothStatsLog
1605                                         .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__BOND_BONDED_TO_ACTION_KEY_MISSING,
1606                                 0);
1607             }
1608 
1609             // Bond loss detected, add to the count.
1610             mAdapterService.getDatabase().updateKeyMissingCount(bluetoothDevice, true);
1611 
1612             // Some apps are not able to handle the key missing broadcast, so we need to remove
1613             // the bond to prevent them from misbehaving.
1614             // TODO (b/402854328): Remove when the misbehaving apps are updated
1615             if (bondLossIopFixNeeded(bluetoothDevice)) {
1616                 DeviceProperties deviceProperties = getDeviceProperties(bluetoothDevice);
1617                 if (deviceProperties == null) {
1618                     return;
1619                 }
1620                 String[] packages = deviceProperties.getPackages();
1621                 if (packages.length == 0) {
1622                     return;
1623                 }
1624 
1625                 Log.w(
1626                         TAG,
1627                         "Removing "
1628                                 + bluetoothDevice
1629                                 + " on behalf of: "
1630                                 + Arrays.toString(packages));
1631                 bluetoothDevice.removeBond();
1632             }
1633 
1634             if (Flags.keyMissingPublic()) {
1635                 mAdapterService.sendOrderedBroadcast(
1636                         intent,
1637                         BLUETOOTH_CONNECT,
1638                         Utils.getTempBroadcastOptions().toBundle(),
1639                         null /* resultReceiver */,
1640                         null /* scheduler */,
1641                         Activity.RESULT_OK /* initialCode */,
1642                         null /* initialData */,
1643                         null /* initialExtras */);
1644                 return;
1645             }
1646 
1647             if (isAtLeastV()
1648                     && Flags.keyMissingAsOrderedBroadcast()
1649                     && android.os.Flags.orderedBroadcastMultiplePermissions()) {
1650                 mAdapterService.sendOrderedBroadcastMultiplePermissions(
1651                         intent,
1652                         new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
1653                         null /* receiverAppOp */,
1654                         null /* resultReceiver */,
1655                         null /* scheduler */,
1656                         Activity.RESULT_OK /* initialCode */,
1657                         null /* initialData */,
1658                         null /* initialExtras */,
1659                         Utils.getTempBroadcastOptions().toBundle());
1660             } else {
1661                 mAdapterService.sendBroadcastMultiplePermissions(
1662                         intent,
1663                         new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
1664                         Utils.getTempBroadcastOptions());
1665             }
1666         }
1667     }
1668 
encryptionChangeCallback( byte[] address, int status, boolean encryptionEnable, int transport, boolean secureConnection, int keySize)1669     void encryptionChangeCallback(
1670             byte[] address,
1671             int status,
1672             boolean encryptionEnable,
1673             int transport,
1674             boolean secureConnection,
1675             int keySize) {
1676         BluetoothDevice bluetoothDevice = getDevice(address);
1677         if (bluetoothDevice == null) {
1678             errorLog(
1679                     "encryptionChangeCallback: device is NULL, address="
1680                             + Utils.getRedactedAddressStringFromByte(address));
1681             return;
1682         }
1683         Log.d(
1684                 TAG,
1685                 "encryptionChangeCallback device: "
1686                         + bluetoothDevice
1687                         + ", status: "
1688                         + status
1689                         + ", enabled: "
1690                         + encryptionEnable
1691                         + ", transport: "
1692                         + transport
1693                         + ", secureConnection: "
1694                         + secureConnection
1695                         + ", keySize: "
1696                         + keySize);
1697 
1698         int algorithm = BluetoothDevice.ENCRYPTION_ALGORITHM_NONE;
1699         if (encryptionEnable) {
1700             if (secureConnection || transport == BluetoothDevice.TRANSPORT_LE) {
1701                 /* LE link or Classic Secure Connections */
1702                 algorithm = BluetoothDevice.ENCRYPTION_ALGORITHM_AES;
1703             } else {
1704                 /* Classic link using non-secure connections mode */
1705                 algorithm = BluetoothDevice.ENCRYPTION_ALGORITHM_E0;
1706             }
1707 
1708             // Log transition to encryption change state (bonded), if the key missing count is > 0
1709             //  which indicates that the device is in key missing state.
1710             if (mAdapterService.getDatabase().getKeyMissingCount(bluetoothDevice) > 0) {
1711                 MetricsLogger.getInstance()
1712                         .logBluetoothEvent(
1713                                 bluetoothDevice,
1714                                 BluetoothStatsLog
1715                                         .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__TRANSITION,
1716                                 BluetoothStatsLog
1717                                         .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__ACTION_KEY_MISSING_TO_ENCRYPTION_CHANGE,
1718                                 0);
1719 
1720                 // Successful bond detected, reset the count.
1721                 mAdapterService.getDatabase().updateKeyMissingCount(bluetoothDevice, false);
1722             }
1723         }
1724 
1725         Intent intent =
1726                 new Intent(BluetoothDevice.ACTION_ENCRYPTION_CHANGE)
1727                         .putExtra(BluetoothDevice.EXTRA_DEVICE, bluetoothDevice)
1728                         .putExtra(BluetoothDevice.EXTRA_TRANSPORT, transport)
1729                         .putExtra(BluetoothDevice.EXTRA_ENCRYPTION_STATUS, status)
1730                         .putExtra(BluetoothDevice.EXTRA_ENCRYPTION_ENABLED, encryptionEnable)
1731                         .putExtra(BluetoothDevice.EXTRA_KEY_SIZE, keySize)
1732                         .putExtra(BluetoothDevice.EXTRA_ENCRYPTION_ALGORITHM, algorithm);
1733 
1734         if (com.android.bluetooth.flags.Flags.encryptionChangeBroadcast()) {
1735             mAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT);
1736         }
1737     }
1738 
fetchUuids(BluetoothDevice device, int transport)1739     void fetchUuids(BluetoothDevice device, int transport) {
1740         if (mSdpTracker.contains(device)) {
1741             debugLog(
1742                     "Skip fetch UUIDs are they are already cached peer:"
1743                             + device
1744                             + " transport:"
1745                             + transport);
1746             MetricsLogger.getInstance()
1747                     .cacheCount(BluetoothProtoEnums.SDP_FETCH_UUID_SKIP_ALREADY_CACHED, 1);
1748             return;
1749         }
1750 
1751         // If no UUIDs are cached and the device is bonding, wait for SDP after the device is bonded
1752         DeviceProperties deviceProperties = getDeviceProperties(device);
1753         if (deviceProperties != null
1754                 && deviceProperties.isBonding()
1755                 && getDeviceProperties(device).getUuids() == null) {
1756             debugLog("Skip fetch UUIDs due to bonding peer:" + device + " transport:" + transport);
1757             MetricsLogger.getInstance()
1758                     .cacheCount(BluetoothProtoEnums.SDP_FETCH_UUID_SKIP_ALREADY_BONDED, 1);
1759             return;
1760         }
1761 
1762         mSdpTracker.add(device);
1763 
1764         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1765         message.obj = device;
1766         message.arg1 = MESSAGE_UUID_STATUS_TIMEOUT;
1767         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1768 
1769         // Uses cached UUIDs if we are bonding. If not, we fetch the UUIDs with SDP.
1770         if (deviceProperties == null || !deviceProperties.isBonding()) {
1771             debugLog(
1772                     "Invoking core stack to spin up SDP cycle peer:"
1773                             + device
1774                             + " transport:"
1775                             + transport);
1776             mAdapterService
1777                     .getNative()
1778                     .getRemoteServices(Utils.getBytesFromAddress(device.getAddress()), transport);
1779             MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_INVOKE_SDP_CYCLE, 1);
1780         }
1781     }
1782 
updateUuids(BluetoothDevice device)1783     void updateUuids(BluetoothDevice device) {
1784         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1785         message.obj = device;
1786         message.arg1 = MESSAGE_UUID_STATUS_SUCCESS;
1787         mHandler.sendMessage(message);
1788     }
1789 
1790     /** Handles headset connection state change event */
handleHeadsetConnectionStateChanged( BluetoothDevice device, int fromState, int toState)1791     public void handleHeadsetConnectionStateChanged(
1792             BluetoothDevice device, int fromState, int toState) {
1793         mMainHandler.post(() -> onHeadsetConnectionStateChanged(device, fromState, toState));
1794     }
1795 
1796     @VisibleForTesting
onHeadsetConnectionStateChanged(BluetoothDevice device, int fromState, int toState)1797     void onHeadsetConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
1798         if (device == null) {
1799             Log.e(TAG, "onHeadsetConnectionStateChanged() remote device is null");
1800             return;
1801         }
1802         if (toState == STATE_DISCONNECTED && !hasBatteryService(device)) {
1803             resetBatteryLevel(device, /* isBas= */ false);
1804         }
1805     }
1806 
1807     /** Handle Indicator status events from Hands-free. */
handleHfIndicatorStatus( BluetoothDevice device, int indicatorId, boolean indicatorStatus)1808     public void handleHfIndicatorStatus(
1809             BluetoothDevice device, int indicatorId, boolean indicatorStatus) {
1810         mMainHandler.post(() -> onHfIndicatorStatus(device, indicatorId, indicatorStatus));
1811     }
1812 
1813     @VisibleForTesting
onHfIndicatorStatus(BluetoothDevice device, int indicatorId, boolean indicatorStatus)1814     void onHfIndicatorStatus(BluetoothDevice device, int indicatorId, boolean indicatorStatus) {
1815         if (device == null) {
1816             Log.e(TAG, "onHfIndicatorStatus() remote device is null");
1817             return;
1818         }
1819         if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) {
1820             getDeviceProperties(device).setHfpBatteryIndicatorStatus(indicatorStatus);
1821         }
1822     }
1823 
1824     /** Handle indication events from Hands-free. */
handleHfIndicatorValueChanged( BluetoothDevice device, int indicatorId, int indicatorValue)1825     public void handleHfIndicatorValueChanged(
1826             BluetoothDevice device, int indicatorId, int indicatorValue) {
1827         mMainHandler.post(() -> onHfIndicatorValueChanged(device, indicatorId, indicatorValue));
1828     }
1829 
1830     @VisibleForTesting
onHfIndicatorValueChanged(BluetoothDevice device, int indicatorId, int indicatorValue)1831     void onHfIndicatorValueChanged(BluetoothDevice device, int indicatorId, int indicatorValue) {
1832         if (device == null) {
1833             Log.e(TAG, "onHfIndicatorValueChanged() remote device is null");
1834             return;
1835         }
1836         if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) {
1837             updateBatteryLevel(device, indicatorValue, /* isBas= */ false);
1838         }
1839     }
1840 
1841     /** Handles Headset specific Bluetooth events */
handleVendorSpecificHeadsetEvent( BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args)1842     public void handleVendorSpecificHeadsetEvent(
1843             BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args) {
1844         mMainHandler.post(
1845                 () -> onVendorSpecificHeadsetEvent(device, cmd, companyId, cmdType, args));
1846     }
1847 
1848     @VisibleForTesting
onVendorSpecificHeadsetEvent( BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args)1849     void onVendorSpecificHeadsetEvent(
1850             BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args) {
1851         if (device == null) {
1852             Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null");
1853             return;
1854         }
1855         if (companyId != BluetoothAssignedNumbers.PLANTRONICS
1856                 && companyId != BluetoothAssignedNumbers.APPLE) {
1857             Log.i(
1858                     TAG,
1859                     "onVendorSpecificHeadsetEvent() filtered out non-PLANTRONICS and non-APPLE "
1860                             + "vendor commands");
1861             return;
1862         }
1863         if (cmd == null) {
1864             Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null");
1865             return;
1866         }
1867         // Only process set command
1868         if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) {
1869             debugLog("onVendorSpecificHeadsetEvent() only SET command is processed");
1870             return;
1871         }
1872         if (args == null) {
1873             Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null");
1874             return;
1875         }
1876 
1877         if (Flags.enableBatteryLevelUpdateOnlyThroughHfIndicator()) {
1878             DeviceProperties deviceProperties = getDeviceProperties(device);
1879             if ((deviceProperties.isHfpBatteryIndicatorEnabled())
1880                     && ((BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT.equals(cmd))
1881                             || (BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV.equals(
1882                                     cmd)))) {
1883                 infoLog(
1884                         "Ignoring Battery Level update through vendor specific command as"
1885                                 + "HfpBatteryIndicator support is enabled.");
1886                 return;
1887             }
1888         }
1889 
1890         int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1891         switch (cmd) {
1892             case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT:
1893                 batteryPercent = getBatteryLevelFromXEventVsc(args);
1894                 break;
1895             case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV:
1896                 batteryPercent = getBatteryLevelFromAppleBatteryVsc(args);
1897                 break;
1898         }
1899         if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
1900             updateBatteryLevel(device, batteryPercent, /* isBas= */ false);
1901             infoLog(
1902                     "Updated device "
1903                             + device
1904                             + " battery level to "
1905                             + String.valueOf(batteryPercent)
1906                             + "%");
1907         }
1908     }
1909 
1910     /**
1911      * Parse AT+IPHONEACCEV=[NumberOfIndicators],[IndicatorType],[IndicatorValue] vendor specific
1912      * event
1913      *
1914      * @param args Array of arguments on the right side of assignment
1915      * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
1916      *     when there is an error parsing the arguments
1917      */
1918     @VisibleForTesting
getBatteryLevelFromAppleBatteryVsc(Object[] args)1919     static int getBatteryLevelFromAppleBatteryVsc(Object[] args) {
1920         if (args.length == 0) {
1921             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() empty arguments");
1922             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1923         }
1924         int numKvPair;
1925         if (args[0] instanceof Integer) {
1926             numKvPair = (Integer) args[0];
1927         } else {
1928             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing number of arguments");
1929             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1930         }
1931         if (args.length != (numKvPair * 2 + 1)) {
1932             Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() number of arguments does not match");
1933             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1934         }
1935         int indicatorType;
1936         int indicatorValue = -1;
1937         for (int i = 0; i < numKvPair; ++i) {
1938             Object indicatorTypeObj = args[2 * i + 1];
1939             if (indicatorTypeObj instanceof Integer) {
1940                 indicatorType = (Integer) indicatorTypeObj;
1941             } else {
1942                 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator type");
1943                 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1944             }
1945             if (indicatorType
1946                     != BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL) {
1947                 continue;
1948             }
1949             Object indicatorValueObj = args[2 * i + 2];
1950             if (indicatorValueObj instanceof Integer) {
1951                 indicatorValue = (Integer) indicatorValueObj;
1952             } else {
1953                 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator value");
1954                 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1955             }
1956             break;
1957         }
1958         return (indicatorValue < 0 || indicatorValue > 9)
1959                 ? BluetoothDevice.BATTERY_LEVEL_UNKNOWN
1960                 : (indicatorValue + 1) * 10;
1961     }
1962 
1963     /**
1964      * Parse AT+XEVENT=BATTERY,[Level],[NumberOfLevel],[MinutesOfTalk],[IsCharging] vendor specific
1965      * event
1966      *
1967      * @param args Array of arguments on the right side of SET command
1968      * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
1969      *     when there is an error parsing the arguments
1970      */
1971     @VisibleForTesting
getBatteryLevelFromXEventVsc(Object[] args)1972     static int getBatteryLevelFromXEventVsc(Object[] args) {
1973         if (args.length == 0) {
1974             Log.w(TAG, "getBatteryLevelFromXEventVsc() empty arguments");
1975             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1976         }
1977         Object eventNameObj = args[0];
1978         if (!(eventNameObj instanceof String)) {
1979             Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event name");
1980             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1981         }
1982         String eventName = (String) eventNameObj;
1983         if (!eventName.equals(
1984                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL)) {
1985             infoLog("getBatteryLevelFromXEventVsc() skip none BATTERY event: " + eventName);
1986             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1987         }
1988         if (args.length != 5) {
1989             Log.w(
1990                     TAG,
1991                     "getBatteryLevelFromXEventVsc() wrong battery level event length: "
1992                             + String.valueOf(args.length));
1993             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1994         }
1995         if (!(args[1] instanceof Integer) || !(args[2] instanceof Integer)) {
1996             Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event values");
1997             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
1998         }
1999         int batteryLevel = (Integer) args[1];
2000         int numberOfLevels = (Integer) args[2];
2001         if (batteryLevel < 0 || numberOfLevels <= 1 || batteryLevel > numberOfLevels) {
2002             Log.w(
2003                     TAG,
2004                     "getBatteryLevelFromXEventVsc() wrong event value, batteryLevel="
2005                             + String.valueOf(batteryLevel)
2006                             + ", numberOfLevels="
2007                             + String.valueOf(numberOfLevels));
2008             return BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
2009         }
2010         return batteryLevel * 100 / (numberOfLevels - 1);
2011     }
2012 
2013     @VisibleForTesting
hasBatteryService(BluetoothDevice device)2014     boolean hasBatteryService(BluetoothDevice device) {
2015         BatteryService batteryService = BatteryService.getBatteryService();
2016         return batteryService != null
2017                 && batteryService.getConnectionState(device) == STATE_CONNECTED;
2018     }
2019 
2020     /** Handles headset client connection state change event. */
handleHeadsetClientConnectionStateChanged( BluetoothDevice device, int fromState, int toState)2021     public void handleHeadsetClientConnectionStateChanged(
2022             BluetoothDevice device, int fromState, int toState) {
2023         mMainHandler.post(() -> onHeadsetClientConnectionStateChanged(device, fromState, toState));
2024     }
2025 
2026     @VisibleForTesting
onHeadsetClientConnectionStateChanged(BluetoothDevice device, int fromState, int toState)2027     void onHeadsetClientConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
2028         if (device == null) {
2029             Log.e(TAG, "onHeadsetClientConnectionStateChanged() remote device is null");
2030             return;
2031         }
2032         if (toState == STATE_DISCONNECTED && !hasBatteryService(device)) {
2033             resetBatteryLevel(device, /* isBas= */ false);
2034         }
2035     }
2036 
2037     /** Handle battery level changes indication events from Audio Gateway. */
handleAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel)2038     public void handleAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel) {
2039         mMainHandler.post(() -> onAgBatteryLevelChanged(device, batteryLevel));
2040     }
2041 
2042     @VisibleForTesting
onAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel)2043     void onAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel) {
2044         if (device == null) {
2045             Log.e(TAG, "onAgBatteryLevelChanged() remote device is null");
2046             return;
2047         }
2048         updateBatteryLevel(
2049                 device, batteryChargeIndicatorToPercentage(batteryLevel), /* isBas= */ false);
2050     }
2051 
2052     private static final String[] BOND_LOSS_IOP_PACKAGES = {
2053         "com.sjm.crmd.patientApp_Android", "com.abbott.crm.ngq.patient",
2054     };
2055 
2056     private static final Set<String> BOND_LOSS_IOP_DEVICE_NAMES = Set.of("CM", "DM");
2057 
2058     // TODO (b/402854328): Remove when the misbehaving apps are updated
bondLossIopFixNeeded(BluetoothDevice device)2059     public boolean bondLossIopFixNeeded(BluetoothDevice device) {
2060         DeviceProperties deviceProperties = getDeviceProperties(device);
2061         if (deviceProperties == null) {
2062             return false;
2063         }
2064 
2065         String deviceName = deviceProperties.getName();
2066         if (deviceName == null) {
2067             return false;
2068         }
2069 
2070         String[] packages = deviceProperties.getPackages();
2071         if (packages.length == 0) {
2072             return false;
2073         }
2074 
2075         if (!BOND_LOSS_IOP_DEVICE_NAMES.contains(deviceName)) {
2076             return false;
2077         }
2078 
2079         for (String iopFixPackage : BOND_LOSS_IOP_PACKAGES) {
2080             for (String packageName : packages) {
2081                 if (packageName.contains(iopFixPackage)
2082                         && !Utils.checkCallerTargetSdk(
2083                                 mAdapterService, packageName, Build.VERSION_CODES.BAKLAVA)) {
2084                     Log.w(
2085                             TAG,
2086                             "bondLossIopFixNeeded(): "
2087                                     + " IOP fix needed for "
2088                                     + device
2089                                     + " name: "
2090                                     + deviceName
2091                                     + " package: "
2092                                     + packageName);
2093                     return true;
2094                 }
2095             }
2096         }
2097 
2098         return false;
2099     }
2100 
errorLog(String msg)2101     private static void errorLog(String msg) {
2102         Log.e(TAG, msg);
2103     }
2104 
debugLog(String msg)2105     private static void debugLog(String msg) {
2106         Log.d(TAG, msg);
2107     }
2108 
infoLog(String msg)2109     private static void infoLog(String msg) {
2110         Log.i(TAG, msg);
2111     }
2112 
warnLog(String msg)2113     private static void warnLog(String msg) {
2114         Log.w(TAG, msg);
2115     }
2116 }
2117