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