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