AdapterProperties.java revision 2f9a79b2d545e1a8b50a0018456892a9934db99a
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
218    void addBondedDevice(BluetoothDevice device) {
219        if(device == null)
220            return;
221
222        try {
223            byte[] addrByte = Utils.getByteAddress(device);
224            DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
225            if (prop == null)
226                prop = mRemoteDevices.addDeviceProperties(addrByte);
227            prop.setBondState(BluetoothDevice.BOND_BONDED);
228
229            // add if not already in list
230            if(!mBondedDevices.contains(device)) {
231                debugLog("Adding bonded device:" +  device);
232                mBondedDevices.add(device);
233            }
234        }
235        catch(Exception ee) {
236            Log.e(TAG, "Exception in addBondedDevice : ", ee);
237        }
238    }
239
240    void removeBondedDevice(BluetoothDevice device) {
241        if(device == null)
242            return;
243
244        try {
245            byte[] addrByte = device.getAddress().getBytes();
246            DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
247            if (prop == null)
248                prop = mRemoteDevices.addDeviceProperties(addrByte);
249            prop.setBondState(BluetoothDevice.BOND_NONE);
250
251            // remove device from list
252            if (mBondedDevices.remove(device))
253                debugLog("Removing bonded device:" +  device);
254            else
255                debugLog("Failed to remove device: " + device);
256        }
257        catch(Exception ee) {
258            Log.e(TAG, "Exception in removeBondedDevice : ", ee);
259        }
260    }
261
262    int getDiscoverableTimeout() {
263        synchronized (mObject) {
264            return mDiscoverableTimeout;
265        }
266    }
267
268    boolean setDiscoverableTimeout(int timeout) {
269        synchronized (mObject) {
270            return mService.setAdapterPropertyNative(
271                    AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
272                    Utils.intToByteArray(timeout));
273        }
274    }
275
276    int getProfileConnectionState(int profile) {
277        synchronized (mObject) {
278            Pair<Integer, Integer> p = mProfileConnectionState.get(profile);
279            if (p != null) return p.first;
280            return BluetoothProfile.STATE_DISCONNECTED;
281        }
282    }
283
284    boolean isDiscovering() {
285        synchronized (mObject) {
286            return mDiscovering;
287        }
288    }
289
290    void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) {
291        if (!validateProfileConnectionState(state) ||
292                !validateProfileConnectionState(prevState)) {
293            // Previously, an invalid state was broadcast anyway,
294            // with the invalid state converted to -1 in the intent.
295            // Better to log an error and not send an intent with
296            // invalid contents or set mAdapterConnectionState to -1.
297            errorLog("Error in sendConnectionStateChange: "
298                    + "prevState " + prevState + " state " + state);
299            return;
300        }
301
302        synchronized (mObject) {
303            updateProfileConnectionState(profile, state, prevState);
304
305            if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
306                setConnectionState(state);
307
308                Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
309                intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
310                intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
311                        convertToAdapterState(state));
312                intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
313                        convertToAdapterState(prevState));
314                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
315                mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
316                debugLog("CONNECTION_STATE_CHANGE: " + device + ": "
317                        + prevState + " -> " + state);
318            }
319        }
320    }
321
322    private boolean validateProfileConnectionState(int state) {
323        return (state == BluetoothProfile.STATE_DISCONNECTED ||
324                state == BluetoothProfile.STATE_CONNECTING ||
325                state == BluetoothProfile.STATE_CONNECTED ||
326                state == BluetoothProfile.STATE_DISCONNECTING);
327    }
328
329
330    private int convertToAdapterState(int state) {
331        switch (state) {
332            case BluetoothProfile.STATE_DISCONNECTED:
333                return BluetoothAdapter.STATE_DISCONNECTED;
334            case BluetoothProfile.STATE_DISCONNECTING:
335                return BluetoothAdapter.STATE_DISCONNECTING;
336            case BluetoothProfile.STATE_CONNECTED:
337                return BluetoothAdapter.STATE_CONNECTED;
338            case BluetoothProfile.STATE_CONNECTING:
339                return BluetoothAdapter.STATE_CONNECTING;
340        }
341        Log.e(TAG, "Error in convertToAdapterState");
342        return -1;
343    }
344
345    private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
346        switch (prevState) {
347            case BluetoothProfile.STATE_CONNECTING:
348                mProfilesConnecting--;
349                break;
350
351            case BluetoothProfile.STATE_CONNECTED:
352                mProfilesConnected--;
353                break;
354
355            case BluetoothProfile.STATE_DISCONNECTING:
356                mProfilesDisconnecting--;
357                break;
358        }
359
360        switch (state) {
361            case BluetoothProfile.STATE_CONNECTING:
362                mProfilesConnecting++;
363                return (mProfilesConnected == 0 && mProfilesConnecting == 1);
364
365            case BluetoothProfile.STATE_CONNECTED:
366                mProfilesConnected++;
367                return (mProfilesConnected == 1);
368
369            case BluetoothProfile.STATE_DISCONNECTING:
370                mProfilesDisconnecting++;
371                return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
372
373            case BluetoothProfile.STATE_DISCONNECTED:
374                return (mProfilesConnected == 0 && mProfilesConnecting == 0);
375
376            default:
377                return true;
378        }
379    }
380
381    private void updateProfileConnectionState(int profile, int newState, int oldState) {
382        // mProfileConnectionState is a hashmap -
383        // <Integer, Pair<Integer, Integer>>
384        // The key is the profile, the value is a pair. first element
385        // is the state and the second element is the number of devices
386        // in that state.
387        int numDev = 1;
388        int newHashState = newState;
389        boolean update = true;
390
391        // The following conditions are considered in this function:
392        // 1. If there is no record of profile and state - update
393        // 2. If a new device's state is current hash state - increment
394        //    number of devices in the state.
395        // 3. If a state change has happened to Connected or Connecting
396        //    (if current state is not connected), update.
397        // 4. If numDevices is 1 and that device state is being updated, update
398        // 5. If numDevices is > 1 and one of the devices is changing state,
399        //    decrement numDevices but maintain oldState if it is Connected or
400        //    Connecting
401        Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
402        if (stateNumDev != null) {
403            int currHashState = stateNumDev.first;
404            numDev = stateNumDev.second;
405
406            if (newState == currHashState) {
407                numDev ++;
408            } else if (newState == BluetoothProfile.STATE_CONNECTED ||
409                   (newState == BluetoothProfile.STATE_CONNECTING &&
410                    currHashState != BluetoothProfile.STATE_CONNECTED)) {
411                 numDev = 1;
412            } else if (numDev == 1 && oldState == currHashState) {
413                 update = true;
414            } else if (numDev > 1 && oldState == currHashState) {
415                 numDev --;
416
417                 if (currHashState == BluetoothProfile.STATE_CONNECTED ||
418                     currHashState == BluetoothProfile.STATE_CONNECTING) {
419                    newHashState = currHashState;
420                 }
421            } else {
422                 update = false;
423            }
424        }
425
426        if (update) {
427            mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState,
428                    numDev));
429        }
430    }
431
432    void adapterPropertyChangedCallback(int[] types, byte[][] values) {
433        Intent intent;
434        int type;
435        byte[] val;
436        for (int i = 0; i < types.length; i++) {
437            val = values[i];
438            type = types[i];
439            infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length);
440            synchronized (mObject) {
441                switch (type) {
442                    case AbstractionLayer.BT_PROPERTY_BDNAME:
443                        mName = new String(val);
444                        intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
445                        intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName);
446                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
447                        mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
448                        debugLog("Name is: " + mName);
449                        break;
450                    case AbstractionLayer.BT_PROPERTY_BDADDR:
451                        mAddress = val;
452                        debugLog("Address is:" + Utils.getAddressStringFromByte(mAddress));
453                        break;
454                    case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
455                        mBluetoothClass = Utils.byteArrayToInt(val, 0);
456                        debugLog("BT Class:" + mBluetoothClass);
457                        break;
458                    case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE:
459                        int mode = Utils.byteArrayToInt(val, 0);
460                        mScanMode = mService.convertScanModeFromHal(mode);
461                        intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
462                        intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode);
463                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
464                        mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
465                        debugLog("Scan Mode:" + mScanMode);
466                        if (mBluetoothDisabling) {
467                            mBluetoothDisabling=false;
468                            mService.startBluetoothDisable();
469                        }
470                        break;
471                    case AbstractionLayer.BT_PROPERTY_UUIDS:
472                        mUuids = Utils.byteArrayToUuid(val);
473                        break;
474                    case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES:
475                        int number = val.length/BD_ADDR_LEN;
476                        byte[] addrByte = new byte[BD_ADDR_LEN];
477                        for (int j = 0; j < number; j++) {
478                            System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN);
479                            addBondedDevice(mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(addrByte)));
480                        }
481                        break;
482                    case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
483                        mDiscoverableTimeout = Utils.byteArrayToInt(val, 0);
484                        debugLog("Discoverable Timeout:" + mDiscoverableTimeout);
485                        break;
486                    default:
487                        errorLog("Property change not handled in Java land:" + type);
488                }
489            }
490        }
491    }
492
493    void onBluetoothReady() {
494        Log.d(TAG, "ScanMode =  " + mScanMode );
495        Log.d(TAG, "State =  " + getState() );
496
497        // When BT is being turned on, all adapter properties will be sent in 1
498        // callback. At this stage, set the scan mode.
499        synchronized (mObject) {
500            if (getState() == BluetoothAdapter.STATE_TURNING_ON &&
501                    mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
502                    /* mDiscoverableTimeout is part of the
503                       adapterPropertyChangedCallback received before
504                       onBluetoothReady */
505                    if (mDiscoverableTimeout != 0)
506                      setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
507                    else /* if timeout == never (0) at startup */
508                      setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
509                    /* though not always required, this keeps NV up-to date on first-boot after flash */
510                    setDiscoverableTimeout(mDiscoverableTimeout);
511            }
512        }
513    }
514
515    private boolean mBluetoothDisabling=false;
516
517    void onBluetoothDisable() {
518        // When BT disable is invoked, set the scan_mode to NONE
519        // so no incoming connections are possible
520
521        //Set flag to indicate we are disabling. When property change of scan mode done
522        //continue with disable sequence
523        debugLog("onBluetoothDisable()");
524        mBluetoothDisabling = true;
525        if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
526            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
527        }
528    }
529    void discoveryStateChangeCallback(int state) {
530        infoLog("Callback:discoveryStateChangeCallback with state:" + state);
531        synchronized (mObject) {
532            Intent intent;
533            if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
534                mDiscovering = false;
535                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
536                mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
537            } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
538                mDiscovering = true;
539                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
540                mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
541            }
542        }
543    }
544
545    private void infoLog(String msg) {
546        Log.i(TAG, msg);
547    }
548
549    private void debugLog(String msg) {
550        if (DBG) Log.d(TAG, msg);
551    }
552
553    private void errorLog(String msg) {
554        Log.e(TAG, msg);
555    }
556}
557