AdapterProperties.java revision 106b714a2e69fa9c0734d87a02ab7277c30f8960
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.BluetoothAdapter;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothProfile;
22import android.content.Context;
23import android.content.Intent;
24import android.os.ParcelUuid;
25import android.os.UserHandle;
26import android.util.Log;
27import android.util.Pair;
28
29import com.android.bluetooth.Utils;
30import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
31
32import java.util.HashMap;
33import java.util.ArrayList;
34import java.util.concurrent.CopyOnWriteArrayList;
35
36class AdapterProperties {
37    private static final boolean DBG = true;
38    private static final boolean VDBG = false;
39    private static final String TAG = "BluetoothAdapterProperties";
40
41    private static final int BD_ADDR_LEN = 6; // 6 bytes
42    private String mName;
43    private byte[] mAddress;
44    private int mBluetoothClass;
45    private int mScanMode;
46    private int mDiscoverableTimeout;
47    private ParcelUuid[] mUuids;
48    private CopyOnWriteArrayList<BluetoothDevice> mBondedDevices = new CopyOnWriteArrayList<BluetoothDevice>();
49
50    private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
51    private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState;
52
53
54    private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
55    private int mState = BluetoothAdapter.STATE_OFF;
56
57    private AdapterService mService;
58    private boolean mDiscovering;
59    private RemoteDevices mRemoteDevices;
60    private BluetoothAdapter mAdapter;
61    //TODO - all hw capabilities to be exposed as a class
62    private int mNumOfAdvertisementInstancesSupported;
63    private boolean mRpaOffloadSupported;
64    private int mNumOfOffloadedIrkSupported;
65    private int mNumOfOffloadedScanFilterSupported;
66    private int mOffloadedScanResultStorageBytes;
67
68    // Lock for all getters and setters.
69    // If finer grained locking is needer, more locks
70    // can be added here.
71    private Object mObject = new Object();
72
73    public AdapterProperties(AdapterService service) {
74        mService = service;
75        mAdapter = BluetoothAdapter.getDefaultAdapter();
76    }
77    public void init(RemoteDevices remoteDevices) {
78        if (mProfileConnectionState ==null) {
79            mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
80        } else {
81            mProfileConnectionState.clear();
82        }
83        mRemoteDevices = remoteDevices;
84    }
85
86    public void cleanup() {
87        mRemoteDevices = null;
88        if (mProfileConnectionState != null) {
89            mProfileConnectionState.clear();
90            mProfileConnectionState = null;
91        }
92        mService = null;
93        if (!mBondedDevices.isEmpty())
94            mBondedDevices.clear();
95    }
96
97    public Object Clone() throws CloneNotSupportedException {
98        throw new CloneNotSupportedException();
99    }
100
101    /**
102     * @return the mName
103     */
104    String getName() {
105        synchronized (mObject) {
106            return mName;
107        }
108    }
109
110    /**
111     * Set the local adapter property - name
112     * @param name the name to set
113     */
114    boolean setName(String name) {
115        synchronized (mObject) {
116            return mService.setAdapterPropertyNative(
117                    AbstractionLayer.BT_PROPERTY_BDNAME, name.getBytes());
118        }
119    }
120
121    /**
122     * @return the mClass
123     */
124    int getBluetoothClass() {
125        synchronized (mObject) {
126            return mBluetoothClass;
127        }
128    }
129
130    /**
131     * @return the mScanMode
132     */
133    int getScanMode() {
134        synchronized (mObject) {
135            return mScanMode;
136        }
137    }
138
139    /**
140     * Set the local adapter property - scanMode
141     *
142     * @param scanMode the ScanMode to set
143     */
144    boolean setScanMode(int scanMode) {
145        synchronized (mObject) {
146            return mService.setAdapterPropertyNative(
147                    AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE, Utils.intToByteArray(scanMode));
148        }
149    }
150
151    /**
152     * @return the mUuids
153     */
154    ParcelUuid[] getUuids() {
155        synchronized (mObject) {
156            return mUuids;
157        }
158    }
159
160    /**
161     * Set local adapter UUIDs.
162     *
163     * @param uuids the uuids to be set.
164     */
165    boolean setUuids(ParcelUuid[] uuids) {
166        synchronized (mObject) {
167            return mService.setAdapterPropertyNative(
168                    AbstractionLayer.BT_PROPERTY_UUIDS, Utils.uuidsToByteArray(uuids));
169        }
170    }
171
172    /**
173     * @return the mAddress
174     */
175    byte[] getAddress() {
176        synchronized (mObject) {
177            return mAddress;
178        }
179    }
180
181    /**
182     * @param mConnectionState the mConnectionState to set
183     */
184    void setConnectionState(int mConnectionState) {
185        synchronized (mObject) {
186            this.mConnectionState = mConnectionState;
187        }
188    }
189
190    /**
191     * @return the mConnectionState
192     */
193    int getConnectionState() {
194        synchronized (mObject) {
195            return mConnectionState;
196        }
197    }
198
199    /**
200     * @param mState the mState to set
201     */
202    void setState(int mState) {
203        synchronized (mObject) {
204            debugLog("Setting state to " + mState);
205            this.mState = mState;
206        }
207    }
208
209    /**
210     * @return the mState
211     */
212    int getState() {
213        /* remove the lock to work around a platform deadlock problem */
214        /* and also for read access, it is safe to remove the lock to save CPU power */
215        return mState;
216    }
217
218    /**
219     * @return the mNumOfAdvertisementInstancesSupported
220     */
221    int getNumOfAdvertisementInstancesSupported() {
222        return mNumOfAdvertisementInstancesSupported;
223    }
224
225    /**
226     * @return the mRpaOffloadSupported
227     */
228    boolean isRpaOffloadSupported() {
229        return mRpaOffloadSupported;
230    }
231
232    /**
233     * @return the mNumOfOffloadedIrkSupported
234     */
235    int getNumOfOffloadedIrkSupported() {
236        return mNumOfOffloadedIrkSupported;
237    }
238
239    /**
240     * @return the mNumOfOffloadedScanFilterSupported
241     */
242    int getNumOfOffloadedScanFilterSupported() {
243        return mNumOfOffloadedScanFilterSupported;
244    }
245
246    /**
247     * @return the mOffloadedScanResultStorageBytes
248     */
249    int getOffloadedScanResultStorage() {
250        return mOffloadedScanResultStorageBytes;
251    }
252
253    /**
254     * @return the mBondedDevices
255     */
256    BluetoothDevice[] getBondedDevices() {
257        BluetoothDevice[] bondedDeviceList = new BluetoothDevice[0];
258        synchronized (mObject) {
259            if(mBondedDevices.isEmpty())
260                return (new BluetoothDevice[0]);
261
262            try {
263                bondedDeviceList = mBondedDevices.toArray(bondedDeviceList);
264                debugLog("getBondedDevices: length="+bondedDeviceList.length);
265                return bondedDeviceList;
266            } catch(ArrayStoreException ee) {
267                errorLog("Error retrieving bonded device array");
268                return (new BluetoothDevice[0]);
269            }
270        }
271    }
272    // This function shall be invoked from BondStateMachine whenever the bond
273    // state changes.
274    void onBondStateChanged(BluetoothDevice device, int state)
275    {
276        if(device == null)
277            return;
278        try {
279            byte[] addrByte = Utils.getByteAddress(device);
280            DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
281            if (prop == null)
282                prop = mRemoteDevices.addDeviceProperties(addrByte);
283            prop.setBondState(state);
284
285            if (state == BluetoothDevice.BOND_BONDED) {
286                // add if not already in list
287                if(!mBondedDevices.contains(device)) {
288                    debugLog("Adding bonded device:" +  device);
289                    mBondedDevices.add(device);
290                }
291            } else if (state == BluetoothDevice.BOND_NONE) {
292                // remove device from list
293                if (mBondedDevices.remove(device))
294                    debugLog("Removing bonded device:" +  device);
295                else
296                    debugLog("Failed to remove device: " + device);
297            }
298        }
299        catch(Exception ee) {
300            Log.e(TAG, "Exception in onBondStateChanged : ", ee);
301        }
302    }
303
304    int getDiscoverableTimeout() {
305        synchronized (mObject) {
306            return mDiscoverableTimeout;
307        }
308    }
309
310    boolean setDiscoverableTimeout(int timeout) {
311        synchronized (mObject) {
312            return mService.setAdapterPropertyNative(
313                    AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
314                    Utils.intToByteArray(timeout));
315        }
316    }
317
318    int getProfileConnectionState(int profile) {
319        synchronized (mObject) {
320            Pair<Integer, Integer> p = mProfileConnectionState.get(profile);
321            if (p != null) return p.first;
322            return BluetoothProfile.STATE_DISCONNECTED;
323        }
324    }
325
326    boolean isDiscovering() {
327        synchronized (mObject) {
328            return mDiscovering;
329        }
330    }
331
332    void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) {
333        if (!validateProfileConnectionState(state) ||
334                !validateProfileConnectionState(prevState)) {
335            // Previously, an invalid state was broadcast anyway,
336            // with the invalid state converted to -1 in the intent.
337            // Better to log an error and not send an intent with
338            // invalid contents or set mAdapterConnectionState to -1.
339            errorLog("Error in sendConnectionStateChange: "
340                    + "prevState " + prevState + " state " + state);
341            return;
342        }
343
344        synchronized (mObject) {
345            updateProfileConnectionState(profile, state, prevState);
346
347            if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
348                setConnectionState(state);
349
350                Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
351                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
352                intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
353                        convertToAdapterState(state));
354                intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
355                        convertToAdapterState(prevState));
356                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
357                mService.sendBroadcastAsUser(intent, UserHandle.ALL,
358                        mService.BLUETOOTH_PERM);
359                Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
360                        + prevState + " -> " + state);
361            }
362        }
363    }
364
365    private boolean validateProfileConnectionState(int state) {
366        return (state == BluetoothProfile.STATE_DISCONNECTED ||
367                state == BluetoothProfile.STATE_CONNECTING ||
368                state == BluetoothProfile.STATE_CONNECTED ||
369                state == BluetoothProfile.STATE_DISCONNECTING);
370    }
371
372
373    private int convertToAdapterState(int state) {
374        switch (state) {
375            case BluetoothProfile.STATE_DISCONNECTED:
376                return BluetoothAdapter.STATE_DISCONNECTED;
377            case BluetoothProfile.STATE_DISCONNECTING:
378                return BluetoothAdapter.STATE_DISCONNECTING;
379            case BluetoothProfile.STATE_CONNECTED:
380                return BluetoothAdapter.STATE_CONNECTED;
381            case BluetoothProfile.STATE_CONNECTING:
382                return BluetoothAdapter.STATE_CONNECTING;
383        }
384        Log.e(TAG, "Error in convertToAdapterState");
385        return -1;
386    }
387
388    private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
389        switch (prevState) {
390            case BluetoothProfile.STATE_CONNECTING:
391                mProfilesConnecting--;
392                break;
393
394            case BluetoothProfile.STATE_CONNECTED:
395                mProfilesConnected--;
396                break;
397
398            case BluetoothProfile.STATE_DISCONNECTING:
399                mProfilesDisconnecting--;
400                break;
401        }
402
403        switch (state) {
404            case BluetoothProfile.STATE_CONNECTING:
405                mProfilesConnecting++;
406                return (mProfilesConnected == 0 && mProfilesConnecting == 1);
407
408            case BluetoothProfile.STATE_CONNECTED:
409                mProfilesConnected++;
410                return (mProfilesConnected == 1);
411
412            case BluetoothProfile.STATE_DISCONNECTING:
413                mProfilesDisconnecting++;
414                return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
415
416            case BluetoothProfile.STATE_DISCONNECTED:
417                return (mProfilesConnected == 0 && mProfilesConnecting == 0);
418
419            default:
420                return true;
421        }
422    }
423
424    private void updateProfileConnectionState(int profile, int newState, int oldState) {
425        // mProfileConnectionState is a hashmap -
426        // <Integer, Pair<Integer, Integer>>
427        // The key is the profile, the value is a pair. first element
428        // is the state and the second element is the number of devices
429        // in that state.
430        int numDev = 1;
431        int newHashState = newState;
432        boolean update = true;
433
434        // The following conditions are considered in this function:
435        // 1. If there is no record of profile and state - update
436        // 2. If a new device's state is current hash state - increment
437        //    number of devices in the state.
438        // 3. If a state change has happened to Connected or Connecting
439        //    (if current state is not connected), update.
440        // 4. If numDevices is 1 and that device state is being updated, update
441        // 5. If numDevices is > 1 and one of the devices is changing state,
442        //    decrement numDevices but maintain oldState if it is Connected or
443        //    Connecting
444        Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
445        if (stateNumDev != null) {
446            int currHashState = stateNumDev.first;
447            numDev = stateNumDev.second;
448
449            if (newState == currHashState) {
450                numDev ++;
451            } else if (newState == BluetoothProfile.STATE_CONNECTED ||
452                   (newState == BluetoothProfile.STATE_CONNECTING &&
453                    currHashState != BluetoothProfile.STATE_CONNECTED)) {
454                 numDev = 1;
455            } else if (numDev == 1 && oldState == currHashState) {
456                 update = true;
457            } else if (numDev > 1 && oldState == currHashState) {
458                 numDev --;
459
460                 if (currHashState == BluetoothProfile.STATE_CONNECTED ||
461                     currHashState == BluetoothProfile.STATE_CONNECTING) {
462                    newHashState = currHashState;
463                 }
464            } else {
465                 update = false;
466            }
467        }
468
469        if (update) {
470            mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState,
471                    numDev));
472        }
473    }
474
475    void adapterPropertyChangedCallback(int[] types, byte[][] values) {
476        Intent intent;
477        int type;
478        byte[] val;
479        for (int i = 0; i < types.length; i++) {
480            val = values[i];
481            type = types[i];
482            infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length);
483            synchronized (mObject) {
484                switch (type) {
485                    case AbstractionLayer.BT_PROPERTY_BDNAME:
486                        mName = new String(val);
487                        intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
488                        intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName);
489                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
490                        mService.sendBroadcastAsUser(intent, UserHandle.ALL,
491                                 mService.BLUETOOTH_PERM);
492                        debugLog("Name is: " + mName);
493                        break;
494                    case AbstractionLayer.BT_PROPERTY_BDADDR:
495                        mAddress = val;
496                        debugLog("Address is:" + Utils.getAddressStringFromByte(mAddress));
497                        break;
498                    case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
499                        mBluetoothClass = Utils.byteArrayToInt(val, 0);
500                        debugLog("BT Class:" + mBluetoothClass);
501                        break;
502                    case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE:
503                        int mode = Utils.byteArrayToInt(val, 0);
504                        mScanMode = mService.convertScanModeFromHal(mode);
505                        intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
506                        intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode);
507                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
508                        mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
509                        debugLog("Scan Mode:" + mScanMode);
510                        if (mBluetoothDisabling) {
511                            mBluetoothDisabling=false;
512                            mService.startBluetoothDisable();
513                        }
514                        break;
515                    case AbstractionLayer.BT_PROPERTY_UUIDS:
516                        mUuids = Utils.byteArrayToUuid(val);
517                        break;
518                    case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES:
519                        int number = val.length/BD_ADDR_LEN;
520                        byte[] addrByte = new byte[BD_ADDR_LEN];
521                        for (int j = 0; j < number; j++) {
522                            System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN);
523                            onBondStateChanged(mAdapter.getRemoteDevice(
524                                               Utils.getAddressStringFromByte(addrByte)),
525                                               BluetoothDevice.BOND_BONDED);
526                        }
527                        break;
528                    case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
529                        mDiscoverableTimeout = Utils.byteArrayToInt(val, 0);
530                        debugLog("Discoverable Timeout:" + mDiscoverableTimeout);
531                        break;
532
533                    case AbstractionLayer.BT_PROPERTY_LOCAL_LE_FEATURES:
534                        mNumOfAdvertisementInstancesSupported = (0x000000FF & ((int)val[1]));
535                        mRpaOffloadSupported = ((0x000000FF & ((int)val[2]))!= 0);
536                        mNumOfOffloadedIrkSupported =  (0x000000FF & ((int)val[3]));
537                        mNumOfOffloadedScanFilterSupported = (0x000000FF & ((int)val[4]));
538                        mOffloadedScanResultStorageBytes = (0x000000FF & ((int)val[5]));
539
540                        Log.d(TAG, "BT_PROPERTY_LOCAL_LE_FEATURES: update from BT controller"
541                                      + " mNumOfAdvertisementInstancesSupported = " + mNumOfAdvertisementInstancesSupported
542                                      + " mRpaOffloadSupported = " + mRpaOffloadSupported
543                                      + " mNumOfOffloadedIrkSupported = " + mNumOfOffloadedIrkSupported
544                                      + " mNumOfOffloadedScanFilterSupported = "
545                                      + mNumOfOffloadedScanFilterSupported
546                                      + " mOffloadedScanResultStorageBytes = " + mOffloadedScanResultStorageBytes);
547
548                        break;
549
550                    default:
551                        errorLog("Property change not handled in Java land:" + type);
552                }
553            }
554        }
555    }
556
557    void onBluetoothReady() {
558        Log.d(TAG, "ScanMode =  " + mScanMode );
559        Log.d(TAG, "State =  " + getState() );
560
561        // When BT is being turned on, all adapter properties will be sent in 1
562        // callback. At this stage, set the scan mode.
563        synchronized (mObject) {
564            if (getState() == BluetoothAdapter.STATE_TURNING_ON &&
565                    mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
566                    /* mDiscoverableTimeout is part of the
567                       adapterPropertyChangedCallback received before
568                       onBluetoothReady */
569                    if (mDiscoverableTimeout != 0)
570                      setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
571                    else /* if timeout == never (0) at startup */
572                      setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
573                    /* though not always required, this keeps NV up-to date on first-boot after flash */
574                    setDiscoverableTimeout(mDiscoverableTimeout);
575            }
576        }
577    }
578
579    private boolean mBluetoothDisabling=false;
580
581    void onBluetoothDisable() {
582        // When BT disable is invoked, set the scan_mode to NONE
583        // so no incoming connections are possible
584
585        //Set flag to indicate we are disabling. When property change of scan mode done
586        //continue with disable sequence
587        debugLog("onBluetoothDisable()");
588        mBluetoothDisabling = true;
589        if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
590            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
591        }
592    }
593    void discoveryStateChangeCallback(int state) {
594        infoLog("Callback:discoveryStateChangeCallback with state:" + state);
595        synchronized (mObject) {
596            Intent intent;
597            if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
598                mDiscovering = false;
599                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
600                mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
601            } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
602                mDiscovering = true;
603                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
604                mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
605            }
606        }
607    }
608
609    private void infoLog(String msg) {
610        if (DBG) Log.i(TAG, msg);
611    }
612
613    private void debugLog(String msg) {
614        if (DBG) Log.d(TAG, msg);
615    }
616
617    private void errorLog(String msg) {
618        Log.e(TAG, msg);
619    }
620}
621