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