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