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