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