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