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