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