AdapterProperties.java revision 7011cac606d235dd88e3684088db1b3f8b720054
1/*
2 * Copyright (C) 2012 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
17package com.android.bluetooth.btservice;
18
19import android.bluetooth.BluetoothA2dp;
20import android.bluetooth.BluetoothA2dpSink;
21import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothAvrcpController;
23import android.bluetooth.BluetoothClass;
24import android.bluetooth.BluetoothDevice;
25import android.bluetooth.BluetoothHeadset;
26import android.bluetooth.BluetoothHeadsetClient;
27import android.bluetooth.BluetoothHidDevice;
28import android.bluetooth.BluetoothHidHost;
29import android.bluetooth.BluetoothMap;
30import android.bluetooth.BluetoothMapClient;
31import android.bluetooth.BluetoothPan;
32import android.bluetooth.BluetoothPbap;
33import android.bluetooth.BluetoothPbapClient;
34import android.bluetooth.BluetoothProfile;
35import android.bluetooth.BluetoothSap;
36import android.content.BroadcastReceiver;
37import android.content.Context;
38import android.content.Intent;
39import android.content.IntentFilter;
40import android.os.ParcelUuid;
41import android.os.UserHandle;
42import android.util.Log;
43import android.util.Pair;
44
45import com.android.bluetooth.Utils;
46import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
47
48import java.util.HashMap;
49import java.util.concurrent.CopyOnWriteArrayList;
50
51class AdapterProperties {
52    private static final boolean DBG = true;
53    private static final boolean VDBG = false;
54    private static final String TAG = "BluetoothAdapterProperties";
55
56    private static final long DEFAULT_DISCOVERY_TIMEOUT_MS = 12800;
57    private static final int BD_ADDR_LEN = 6; // in bytes
58
59    private volatile String mName;
60    private volatile byte[] mAddress;
61    private volatile BluetoothClass mBluetoothClass;
62    private volatile int mScanMode;
63    private volatile int mDiscoverableTimeout;
64    private volatile ParcelUuid[] mUuids;
65    private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices =
66            new CopyOnWriteArrayList<BluetoothDevice>();
67
68    private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
69    private final HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState =
70            new HashMap<>();
71
72    private volatile int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
73    private volatile int mState = BluetoothAdapter.STATE_OFF;
74
75    private AdapterService mService;
76    private boolean mDiscovering;
77    private long mDiscoveryEndMs; //< Time (ms since epoch) that discovery ended or will end.
78    private RemoteDevices mRemoteDevices;
79    private BluetoothAdapter mAdapter;
80    //TODO - all hw capabilities to be exposed as a class
81    private int mNumOfAdvertisementInstancesSupported;
82    private boolean mRpaOffloadSupported;
83    private int mNumOfOffloadedIrkSupported;
84    private int mNumOfOffloadedScanFilterSupported;
85    private int mOffloadedScanResultStorageBytes;
86    private int mVersSupported;
87    private int mTotNumOfTrackableAdv;
88    private boolean mIsExtendedScanSupported;
89    private boolean mIsDebugLogSupported;
90    private boolean mIsActivityAndEnergyReporting;
91    private boolean mIsLe2MPhySupported;
92    private boolean mIsLeCodedPhySupported;
93    private boolean mIsLeExtendedAdvertisingSupported;
94    private boolean mIsLePeriodicAdvertisingSupported;
95    private int mLeMaximumAdvertisingDataLength;
96
97    private boolean mReceiverRegistered;
98    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
99        @Override
100        public void onReceive(Context context, Intent intent) {
101            String action = intent.getAction();
102            if (action == null) {
103                Log.w(TAG, "Received intent with null action");
104                return;
105            }
106            switch (action) {
107                case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
108                    sendConnectionStateChange(BluetoothProfile.HEADSET, intent);
109                    break;
110                case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
111                    sendConnectionStateChange(BluetoothProfile.A2DP, intent);
112                    break;
113                case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED:
114                    sendConnectionStateChange(BluetoothProfile.HEADSET_CLIENT, intent);
115                    break;
116                case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED:
117                    sendConnectionStateChange(BluetoothProfile.A2DP_SINK, intent);
118                    break;
119                case BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED:
120                    sendConnectionStateChange(BluetoothProfile.HID_DEVICE, intent);
121                    break;
122                case BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED:
123                    sendConnectionStateChange(BluetoothProfile.HID_HOST, intent);
124                    break;
125                case BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED:
126                    sendConnectionStateChange(BluetoothProfile.AVRCP_CONTROLLER, intent);
127                    break;
128                case BluetoothPan.ACTION_CONNECTION_STATE_CHANGED:
129                    sendConnectionStateChange(BluetoothProfile.PAN, intent);
130                    break;
131                case BluetoothMap.ACTION_CONNECTION_STATE_CHANGED:
132                    sendConnectionStateChange(BluetoothProfile.MAP, intent);
133                    break;
134                case BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED:
135                    sendConnectionStateChange(BluetoothProfile.MAP_CLIENT, intent);
136                    break;
137                case BluetoothSap.ACTION_CONNECTION_STATE_CHANGED:
138                    sendConnectionStateChange(BluetoothProfile.SAP, intent);
139                    break;
140                case BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED:
141                    sendConnectionStateChange(BluetoothProfile.PBAP_CLIENT, intent);
142                    break;
143                case BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED:
144                    sendConnectionStateChange(BluetoothProfile.PBAP, intent);
145                    break;
146                default:
147                    Log.w(TAG, "Received unknown intent " + intent);
148                    break;
149            }
150        }
151    };
152
153    // Lock for all getters and setters.
154    // If finer grained locking is needer, more locks
155    // can be added here.
156    private final Object mObject = new Object();
157
158    AdapterProperties(AdapterService service) {
159        mService = service;
160        mAdapter = BluetoothAdapter.getDefaultAdapter();
161    }
162
163    public void init(RemoteDevices remoteDevices) {
164        mProfileConnectionState.clear();
165        mRemoteDevices = remoteDevices;
166
167        IntentFilter filter = new IntentFilter();
168        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
169        filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
170        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
171        filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
172        filter.addAction(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
173        filter.addAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
174        filter.addAction(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);
175        filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
176        filter.addAction(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
177        filter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
178        filter.addAction(BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
179        filter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
180        mService.registerReceiver(mReceiver, filter);
181        mReceiverRegistered = true;
182    }
183
184    public void cleanup() {
185        mRemoteDevices = null;
186        mProfileConnectionState.clear();
187        if (mReceiverRegistered) {
188            mService.unregisterReceiver(mReceiver);
189            mReceiverRegistered = false;
190        }
191        mService = null;
192        mBondedDevices.clear();
193    }
194
195    @Override
196    public Object clone() throws CloneNotSupportedException {
197        throw new CloneNotSupportedException();
198    }
199
200    /**
201     * @return the mName
202     */
203    String getName() {
204        return mName;
205    }
206
207    /**
208     * Set the local adapter property - name
209     * @param name the name to set
210     */
211    boolean setName(String name) {
212        synchronized (mObject) {
213            return mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME,
214                    name.getBytes());
215        }
216    }
217
218    /**
219     * Set the Bluetooth Class of Device (CoD) of the adapter.
220     *
221     * <p>Bluetooth stack stores some adapter properties in native BT stack storage and some in the
222     * Java Android stack. Bluetooth CoD is stored in the Android layer through
223     * {@link android.provider.Settings.Global#BLUETOOTH_CLASS_OF_DEVICE}.
224     *
225     * <p>Due to this, the getAdapterPropertyNative and adapterPropertyChangedCallback methods don't
226     * actually update mBluetoothClass. Hence, we update the field mBluetoothClass every time we
227     * successfully update BluetoothClass.
228     *
229     * @param bluetoothClass BluetoothClass of the device
230     */
231    boolean setBluetoothClass(BluetoothClass bluetoothClass) {
232        synchronized (mObject) {
233            boolean result =
234                    mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE,
235                            bluetoothClass.getClassOfDeviceBytes());
236
237            if (result) {
238                mBluetoothClass = bluetoothClass;
239            }
240
241            return result;
242        }
243    }
244
245    /**
246     * @return the BluetoothClass of the Bluetooth adapter.
247     */
248    BluetoothClass getBluetoothClass() {
249        synchronized (mObject) {
250            return mBluetoothClass;
251        }
252    }
253
254    /**
255     * @return the mScanMode
256     */
257    int getScanMode() {
258        return mScanMode;
259    }
260
261    /**
262     * Set the local adapter property - scanMode
263     *
264     * @param scanMode the ScanMode to set
265     */
266    boolean setScanMode(int scanMode) {
267        synchronized (mObject) {
268            return mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE,
269                    Utils.intToByteArray(scanMode));
270        }
271    }
272
273    /**
274     * @return the mUuids
275     */
276    ParcelUuid[] getUuids() {
277        return mUuids;
278    }
279
280    /**
281     * @return the mAddress
282     */
283    byte[] getAddress() {
284        return mAddress;
285    }
286
287    /**
288     * @param mConnectionState the mConnectionState to set
289     */
290    void setConnectionState(int mConnectionState) {
291        this.mConnectionState = mConnectionState;
292    }
293
294    /**
295     * @return the mConnectionState
296     */
297    int getConnectionState() {
298        return mConnectionState;
299    }
300
301    /**
302     * @param mState the mState to set
303     */
304    void setState(int mState) {
305        debugLog("Setting state to " + mState);
306        this.mState = mState;
307    }
308
309    /**
310     * @return the mState
311     */
312    int getState() {
313        return mState;
314    }
315
316    /**
317     * @return the mNumOfAdvertisementInstancesSupported
318     */
319    int getNumOfAdvertisementInstancesSupported() {
320        return mNumOfAdvertisementInstancesSupported;
321    }
322
323    /**
324     * @return the mRpaOffloadSupported
325     */
326    boolean isRpaOffloadSupported() {
327        return mRpaOffloadSupported;
328    }
329
330    /**
331     * @return the mNumOfOffloadedIrkSupported
332     */
333    int getNumOfOffloadedIrkSupported() {
334        return mNumOfOffloadedIrkSupported;
335    }
336
337    /**
338     * @return the mNumOfOffloadedScanFilterSupported
339     */
340    int getNumOfOffloadedScanFilterSupported() {
341        return mNumOfOffloadedScanFilterSupported;
342    }
343
344    /**
345     * @return the mOffloadedScanResultStorageBytes
346     */
347    int getOffloadedScanResultStorage() {
348        return mOffloadedScanResultStorageBytes;
349    }
350
351    /**
352     * @return tx/rx/idle activity and energy info
353     */
354    boolean isActivityAndEnergyReportingSupported() {
355        return mIsActivityAndEnergyReporting;
356    }
357
358    /**
359     * @return the mIsLe2MPhySupported
360     */
361    boolean isLe2MPhySupported() {
362        return mIsLe2MPhySupported;
363    }
364
365    /**
366     * @return the mIsLeCodedPhySupported
367     */
368    boolean isLeCodedPhySupported() {
369        return mIsLeCodedPhySupported;
370    }
371
372    /**
373     * @return the mIsLeExtendedAdvertisingSupported
374     */
375    boolean isLeExtendedAdvertisingSupported() {
376        return mIsLeExtendedAdvertisingSupported;
377    }
378
379    /**
380     * @return the mIsLePeriodicAdvertisingSupported
381     */
382    boolean isLePeriodicAdvertisingSupported() {
383        return mIsLePeriodicAdvertisingSupported;
384    }
385
386    /**
387     * @return the getLeMaximumAdvertisingDataLength
388     */
389    int getLeMaximumAdvertisingDataLength() {
390        return mLeMaximumAdvertisingDataLength;
391    }
392
393    /**
394     * @return total number of trackable advertisements
395     */
396    int getTotalNumOfTrackableAdvertisements() {
397        return mTotNumOfTrackableAdv;
398    }
399
400    /**
401     * @return the mBondedDevices
402     */
403    BluetoothDevice[] getBondedDevices() {
404        BluetoothDevice[] bondedDeviceList = new BluetoothDevice[0];
405        try {
406            bondedDeviceList = mBondedDevices.toArray(bondedDeviceList);
407        } catch (ArrayStoreException ee) {
408            errorLog("Error retrieving bonded device array");
409        }
410        infoLog("getBondedDevices: length=" + bondedDeviceList.length);
411        return bondedDeviceList;
412    }
413
414    // This function shall be invoked from BondStateMachine whenever the bond
415    // state changes.
416    void onBondStateChanged(BluetoothDevice device, int state) {
417        if (device == null) {
418            Log.w(TAG, "onBondStateChanged, device is null");
419            return;
420        }
421        try {
422            byte[] addrByte = Utils.getByteAddress(device);
423            DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
424            if (prop == null) {
425                prop = mRemoteDevices.addDeviceProperties(addrByte);
426            }
427            prop.setBondState(state);
428
429            if (state == BluetoothDevice.BOND_BONDED) {
430                // add if not already in list
431                if (!mBondedDevices.contains(device)) {
432                    debugLog("Adding bonded device:" + device);
433                    mBondedDevices.add(device);
434                }
435            } else if (state == BluetoothDevice.BOND_NONE) {
436                // remove device from list
437                if (mBondedDevices.remove(device)) {
438                    debugLog("Removing bonded device:" + device);
439                } else {
440                    debugLog("Failed to remove device: " + device);
441                }
442            }
443        } catch (Exception ee) {
444            Log.w(TAG, "onBondStateChanged: Exception ", ee);
445        }
446    }
447
448    int getDiscoverableTimeout() {
449        return mDiscoverableTimeout;
450    }
451
452    boolean setDiscoverableTimeout(int timeout) {
453        synchronized (mObject) {
454            return mService.setAdapterPropertyNative(
455                    AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
456                    Utils.intToByteArray(timeout));
457        }
458    }
459
460    int getProfileConnectionState(int profile) {
461        synchronized (mObject) {
462            Pair<Integer, Integer> p = mProfileConnectionState.get(profile);
463            if (p != null) {
464                return p.first;
465            }
466            return BluetoothProfile.STATE_DISCONNECTED;
467        }
468    }
469
470    long discoveryEndMillis() {
471        return mDiscoveryEndMs;
472    }
473
474    boolean isDiscovering() {
475        return mDiscovering;
476    }
477
478    private void sendConnectionStateChange(int profile, Intent connIntent) {
479        BluetoothDevice device = connIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
480        int prevState = connIntent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
481        int state = connIntent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
482        Log.d(TAG,
483                "PROFILE_CONNECTION_STATE_CHANGE: profile=" + profile + ", device=" + device + ", "
484                        + prevState + " -> " + state);
485        if (!isNormalStateTransition(prevState, state)) {
486            Log.w(TAG,
487                    "PROFILE_CONNECTION_STATE_CHANGE: unexpected transition for profile=" + profile
488                            + ", device=" + device + ", " + prevState + " -> " + state);
489        }
490        sendConnectionStateChange(device, profile, state, prevState);
491    }
492
493    void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) {
494        if (!validateProfileConnectionState(state) || !validateProfileConnectionState(prevState)) {
495            // Previously, an invalid state was broadcast anyway,
496            // with the invalid state converted to -1 in the intent.
497            // Better to log an error and not send an intent with
498            // invalid contents or set mAdapterConnectionState to -1.
499            errorLog("sendConnectionStateChange: invalid state transition " + prevState + " -> "
500                    + state);
501            return;
502        }
503
504        synchronized (mObject) {
505            updateProfileConnectionState(profile, state, prevState);
506
507            if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
508                int newAdapterState = convertToAdapterState(state);
509                int prevAdapterState = convertToAdapterState(prevState);
510                setConnectionState(newAdapterState);
511
512                Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
513                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
514                intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, newAdapterState);
515                intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, prevAdapterState);
516                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
517                Log.d(TAG, "ADAPTER_CONNECTION_STATE_CHANGE: " + device + ": " + prevAdapterState
518                        + " -> " + newAdapterState);
519                if (!isNormalStateTransition(prevState, state)) {
520                    Log.w(TAG, "ADAPTER_CONNECTION_STATE_CHANGE: unexpected transition for profile="
521                            + profile + ", device=" + device + ", " + prevState + " -> " + state);
522                }
523                mService.sendBroadcastAsUser(intent, UserHandle.ALL, AdapterService.BLUETOOTH_PERM);
524            }
525        }
526    }
527
528    private boolean validateProfileConnectionState(int state) {
529        return (state == BluetoothProfile.STATE_DISCONNECTED
530                || state == BluetoothProfile.STATE_CONNECTING
531                || state == BluetoothProfile.STATE_CONNECTED
532                || state == BluetoothProfile.STATE_DISCONNECTING);
533    }
534
535    private static int convertToAdapterState(int state) {
536        switch (state) {
537            case BluetoothProfile.STATE_DISCONNECTED:
538                return BluetoothAdapter.STATE_DISCONNECTED;
539            case BluetoothProfile.STATE_DISCONNECTING:
540                return BluetoothAdapter.STATE_DISCONNECTING;
541            case BluetoothProfile.STATE_CONNECTED:
542                return BluetoothAdapter.STATE_CONNECTED;
543            case BluetoothProfile.STATE_CONNECTING:
544                return BluetoothAdapter.STATE_CONNECTING;
545        }
546        Log.e(TAG, "convertToAdapterState, unknow state " + state);
547        return -1;
548    }
549
550    private static boolean isNormalStateTransition(int prevState, int nextState) {
551        switch (prevState) {
552            case BluetoothProfile.STATE_DISCONNECTED:
553                return nextState == BluetoothProfile.STATE_CONNECTING;
554            case BluetoothProfile.STATE_CONNECTED:
555                return nextState == BluetoothProfile.STATE_DISCONNECTING;
556            case BluetoothProfile.STATE_DISCONNECTING:
557            case BluetoothProfile.STATE_CONNECTING:
558                return (nextState == BluetoothProfile.STATE_DISCONNECTED) || (nextState
559                        == BluetoothProfile.STATE_CONNECTED);
560            default:
561                return false;
562        }
563    }
564
565    private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
566        switch (prevState) {
567            case BluetoothProfile.STATE_CONNECTING:
568                if (mProfilesConnecting > 0) {
569                    mProfilesConnecting--;
570                } else {
571                    Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting);
572                    throw new IllegalStateException(
573                            "Invalid state transition, " + prevState + " -> " + state);
574                }
575                break;
576
577            case BluetoothProfile.STATE_CONNECTED:
578                if (mProfilesConnected > 0) {
579                    mProfilesConnected--;
580                } else {
581                    Log.e(TAG, "mProfilesConnected " + mProfilesConnected);
582                    throw new IllegalStateException(
583                            "Invalid state transition, " + prevState + " -> " + state);
584                }
585                break;
586
587            case BluetoothProfile.STATE_DISCONNECTING:
588                if (mProfilesDisconnecting > 0) {
589                    mProfilesDisconnecting--;
590                } else {
591                    Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting);
592                    throw new IllegalStateException(
593                            "Invalid state transition, " + prevState + " -> " + state);
594                }
595                break;
596        }
597
598        switch (state) {
599            case BluetoothProfile.STATE_CONNECTING:
600                mProfilesConnecting++;
601                return (mProfilesConnected == 0 && mProfilesConnecting == 1);
602
603            case BluetoothProfile.STATE_CONNECTED:
604                mProfilesConnected++;
605                return (mProfilesConnected == 1);
606
607            case BluetoothProfile.STATE_DISCONNECTING:
608                mProfilesDisconnecting++;
609                return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
610
611            case BluetoothProfile.STATE_DISCONNECTED:
612                return (mProfilesConnected == 0 && mProfilesConnecting == 0);
613
614            default:
615                return true;
616        }
617    }
618
619    private void updateProfileConnectionState(int profile, int newState, int oldState) {
620        // mProfileConnectionState is a hashmap -
621        // <Integer, Pair<Integer, Integer>>
622        // The key is the profile, the value is a pair. first element
623        // is the state and the second element is the number of devices
624        // in that state.
625        int numDev = 1;
626        int newHashState = newState;
627        boolean update = true;
628
629        // The following conditions are considered in this function:
630        // 1. If there is no record of profile and state - update
631        // 2. If a new device's state is current hash state - increment
632        //    number of devices in the state.
633        // 3. If a state change has happened to Connected or Connecting
634        //    (if current state is not connected), update.
635        // 4. If numDevices is 1 and that device state is being updated, update
636        // 5. If numDevices is > 1 and one of the devices is changing state,
637        //    decrement numDevices but maintain oldState if it is Connected or
638        //    Connecting
639        Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
640        if (stateNumDev != null) {
641            int currHashState = stateNumDev.first;
642            numDev = stateNumDev.second;
643
644            if (newState == currHashState) {
645                numDev++;
646            } else if (newState == BluetoothProfile.STATE_CONNECTED || (
647                    newState == BluetoothProfile.STATE_CONNECTING
648                            && currHashState != BluetoothProfile.STATE_CONNECTED)) {
649                numDev = 1;
650            } else if (numDev == 1 && oldState == currHashState) {
651                update = true;
652            } else if (numDev > 1 && oldState == currHashState) {
653                numDev--;
654
655                if (currHashState == BluetoothProfile.STATE_CONNECTED
656                        || currHashState == BluetoothProfile.STATE_CONNECTING) {
657                    newHashState = currHashState;
658                }
659            } else {
660                update = false;
661            }
662        }
663
664        if (update) {
665            mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState, numDev));
666        }
667    }
668
669    void adapterPropertyChangedCallback(int[] types, byte[][] values) {
670        Intent intent;
671        int type;
672        byte[] val;
673        for (int i = 0; i < types.length; i++) {
674            val = values[i];
675            type = types[i];
676            infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length);
677            synchronized (mObject) {
678                switch (type) {
679                    case AbstractionLayer.BT_PROPERTY_BDNAME:
680                        mName = new String(val);
681                        intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
682                        intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName);
683                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
684                        mService.sendBroadcastAsUser(intent, UserHandle.ALL,
685                                AdapterService.BLUETOOTH_PERM);
686                        debugLog("Name is: " + mName);
687                        break;
688                    case AbstractionLayer.BT_PROPERTY_BDADDR:
689                        mAddress = val;
690                        String address = Utils.getAddressStringFromByte(mAddress);
691                        debugLog("Address is:" + address);
692                        intent = new Intent(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED);
693                        intent.putExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS, address);
694                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
695                        mService.sendBroadcastAsUser(intent, UserHandle.ALL,
696                                AdapterService.BLUETOOTH_PERM);
697                        break;
698                    case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
699                        if (val == null || val.length != 3) {
700                            debugLog("Invalid BT CoD value from stack.");
701                            return;
702                        }
703                        int bluetoothClass =
704                                ((int) val[0] << 16) + ((int) val[1] << 8) + (int) val[2];
705                        if (bluetoothClass != 0) {
706                            mBluetoothClass = new BluetoothClass(bluetoothClass);
707                        }
708                        debugLog("BT Class:" + mBluetoothClass);
709                        break;
710                    case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE:
711                        int mode = Utils.byteArrayToInt(val, 0);
712                        mScanMode = AdapterService.convertScanModeFromHal(mode);
713                        intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
714                        intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode);
715                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
716                        mService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
717                        debugLog("Scan Mode:" + mScanMode);
718                        if (mBluetoothDisabling) {
719                            mBluetoothDisabling = false;
720                            mService.startBluetoothDisable();
721                        }
722                        break;
723                    case AbstractionLayer.BT_PROPERTY_UUIDS:
724                        mUuids = Utils.byteArrayToUuid(val);
725                        break;
726                    case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES:
727                        int number = val.length / BD_ADDR_LEN;
728                        byte[] addrByte = new byte[BD_ADDR_LEN];
729                        for (int j = 0; j < number; j++) {
730                            System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN);
731                            onBondStateChanged(mAdapter.getRemoteDevice(
732                                    Utils.getAddressStringFromByte(addrByte)),
733                                    BluetoothDevice.BOND_BONDED);
734                        }
735                        break;
736                    case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
737                        mDiscoverableTimeout = Utils.byteArrayToInt(val, 0);
738                        debugLog("Discoverable Timeout:" + mDiscoverableTimeout);
739                        break;
740
741                    case AbstractionLayer.BT_PROPERTY_LOCAL_LE_FEATURES:
742                        updateFeatureSupport(val);
743                        break;
744
745                    default:
746                        errorLog("Property change not handled in Java land:" + type);
747                }
748            }
749        }
750    }
751
752    private void updateFeatureSupport(byte[] val) {
753        mVersSupported = ((0xFF & ((int) val[1])) << 8) + (0xFF & ((int) val[0]));
754        mNumOfAdvertisementInstancesSupported = (0xFF & ((int) val[3]));
755        mRpaOffloadSupported = ((0xFF & ((int) val[4])) != 0);
756        mNumOfOffloadedIrkSupported = (0xFF & ((int) val[5]));
757        mNumOfOffloadedScanFilterSupported = (0xFF & ((int) val[6]));
758        mIsActivityAndEnergyReporting = ((0xFF & ((int) val[7])) != 0);
759        mOffloadedScanResultStorageBytes = ((0xFF & ((int) val[9])) << 8) + (0xFF & ((int) val[8]));
760        mTotNumOfTrackableAdv = ((0xFF & ((int) val[11])) << 8) + (0xFF & ((int) val[10]));
761        mIsExtendedScanSupported = ((0xFF & ((int) val[12])) != 0);
762        mIsDebugLogSupported = ((0xFF & ((int) val[13])) != 0);
763        mIsLe2MPhySupported = ((0xFF & ((int) val[14])) != 0);
764        mIsLeCodedPhySupported = ((0xFF & ((int) val[15])) != 0);
765        mIsLeExtendedAdvertisingSupported = ((0xFF & ((int) val[16])) != 0);
766        mIsLePeriodicAdvertisingSupported = ((0xFF & ((int) val[17])) != 0);
767        mLeMaximumAdvertisingDataLength =
768                (0xFF & ((int) val[18])) + ((0xFF & ((int) val[19])) << 8);
769
770        Log.d(TAG, "BT_PROPERTY_LOCAL_LE_FEATURES: update from BT controller"
771                + " mNumOfAdvertisementInstancesSupported = "
772                + mNumOfAdvertisementInstancesSupported + " mRpaOffloadSupported = "
773                + mRpaOffloadSupported + " mNumOfOffloadedIrkSupported = "
774                + mNumOfOffloadedIrkSupported + " mNumOfOffloadedScanFilterSupported = "
775                + mNumOfOffloadedScanFilterSupported + " mOffloadedScanResultStorageBytes= "
776                + mOffloadedScanResultStorageBytes + " mIsActivityAndEnergyReporting = "
777                + mIsActivityAndEnergyReporting + " mVersSupported = " + mVersSupported
778                + " mTotNumOfTrackableAdv = " + mTotNumOfTrackableAdv
779                + " mIsExtendedScanSupported = " + mIsExtendedScanSupported
780                + " mIsDebugLogSupported = " + mIsDebugLogSupported + " mIsLe2MPhySupported = "
781                + mIsLe2MPhySupported + " mIsLeCodedPhySupported = " + mIsLeCodedPhySupported
782                + " mIsLeExtendedAdvertisingSupported = " + mIsLeExtendedAdvertisingSupported
783                + " mIsLePeriodicAdvertisingSupported = " + mIsLePeriodicAdvertisingSupported
784                + " mLeMaximumAdvertisingDataLength = " + mLeMaximumAdvertisingDataLength);
785    }
786
787    void onBluetoothReady() {
788        debugLog("onBluetoothReady, state=" + getState() + ", ScanMode=" + mScanMode);
789
790        synchronized (mObject) {
791            // Reset adapter and profile connection states
792            setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
793            mProfileConnectionState.clear();
794            mProfilesConnected = 0;
795            mProfilesConnecting = 0;
796            mProfilesDisconnecting = 0;
797            // When BT is being turned on, all adapter properties will be sent in 1
798            // callback. At this stage, set the scan mode.
799            if (getState() == BluetoothAdapter.STATE_TURNING_ON
800                    && mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
801                    /* mDiscoverableTimeout is part of the
802                       adapterPropertyChangedCallback received before
803                       onBluetoothReady */
804                if (mDiscoverableTimeout != 0) {
805                    setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
806                } else /* if timeout == never (0) at startup */ {
807                    setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
808                }
809                    /* though not always required, this keeps NV up-to date on first-boot after
810                    flash */
811                setDiscoverableTimeout(mDiscoverableTimeout);
812            }
813        }
814    }
815
816    private boolean mBluetoothDisabling = false;
817
818    void onBleDisable() {
819        // Sequence BLE_ON to STATE_OFF - that is _complete_ OFF state.
820        // When BT disable is invoked, set the scan_mode to NONE
821        // so no incoming connections are possible
822        debugLog("onBleDisable");
823        if (getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
824            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
825        }
826    }
827
828    void onBluetoothDisable() {
829        // From STATE_ON to BLE_ON
830        // When BT disable is invoked, set the scan_mode to NONE
831        // so no incoming connections are possible
832
833        //Set flag to indicate we are disabling. When property change of scan mode done
834        //continue with disable sequence
835        debugLog("onBluetoothDisable()");
836        mBluetoothDisabling = true;
837        if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
838            // Turn off any Device Search/Inquiry
839            mService.cancelDiscovery();
840            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
841        }
842    }
843
844    void discoveryStateChangeCallback(int state) {
845        infoLog("Callback:discoveryStateChangeCallback with state:" + state);
846        synchronized (mObject) {
847            Intent intent;
848            if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
849                mDiscovering = false;
850                mDiscoveryEndMs = System.currentTimeMillis();
851                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
852                mService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
853            } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
854                mDiscovering = true;
855                mDiscoveryEndMs = System.currentTimeMillis() + DEFAULT_DISCOVERY_TIMEOUT_MS;
856                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
857                mService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
858            }
859        }
860    }
861
862    private static void infoLog(String msg) {
863        if (VDBG) {
864            Log.i(TAG, msg);
865        }
866    }
867
868    private static void debugLog(String msg) {
869        if (DBG) {
870            Log.d(TAG, msg);
871        }
872    }
873
874    private static void errorLog(String msg) {
875        Log.e(TAG, msg);
876    }
877}
878