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 = false;
37    private static final String TAG = "BluetoothAdapterProperties";
38
39    private static final int BD_ADDR_LEN = 6; // 6 bytes
40    private String mName;
41    private byte[] mAddress;
42    private int mBluetoothClass;
43    private int mScanMode;
44    private int mDiscoverableTimeout;
45    private ParcelUuid[] mUuids;
46    private ArrayList<BluetoothDevice> mBondedDevices = new ArrayList<BluetoothDevice>();
47
48    private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting;
49    private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState;
50
51
52    private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
53    private int mState = BluetoothAdapter.STATE_OFF;
54
55    private AdapterService mService;
56    private boolean mDiscovering;
57    private RemoteDevices mRemoteDevices;
58    private BluetoothAdapter mAdapter;
59
60    // Lock for all getters and setters.
61    // If finer grained locking is needer, more locks
62    // can be added here.
63    private Object mObject = new Object();
64
65    public AdapterProperties(AdapterService service) {
66        mService = service;
67        mAdapter = BluetoothAdapter.getDefaultAdapter();
68    }
69    public void init(RemoteDevices remoteDevices) {
70        if (mProfileConnectionState ==null) {
71            mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
72        } else {
73            mProfileConnectionState.clear();
74        }
75        mRemoteDevices = remoteDevices;
76    }
77
78    public void cleanup() {
79        mRemoteDevices = null;
80        if (mProfileConnectionState != null) {
81            mProfileConnectionState.clear();
82            mProfileConnectionState = null;
83        }
84        mService = null;
85        if (!mBondedDevices.isEmpty())
86            mBondedDevices.clear();
87    }
88
89    public Object Clone() throws CloneNotSupportedException {
90        throw new CloneNotSupportedException();
91    }
92
93    /**
94     * @return the mName
95     */
96    String getName() {
97        synchronized (mObject) {
98            return mName;
99        }
100    }
101
102    /**
103     * Set the local adapter property - name
104     * @param name the name to set
105     */
106    boolean setName(String name) {
107        synchronized (mObject) {
108            return mService.setAdapterPropertyNative(
109                    AbstractionLayer.BT_PROPERTY_BDNAME, name.getBytes());
110        }
111    }
112
113    /**
114     * @return the mClass
115     */
116    int getBluetoothClass() {
117        synchronized (mObject) {
118            return mBluetoothClass;
119        }
120    }
121
122    /**
123     * @return the mScanMode
124     */
125    int getScanMode() {
126        synchronized (mObject) {
127            return mScanMode;
128        }
129    }
130
131    /**
132     * Set the local adapter property - scanMode
133     *
134     * @param scanMode the ScanMode to set
135     */
136    boolean setScanMode(int scanMode) {
137        synchronized (mObject) {
138            return mService.setAdapterPropertyNative(
139                    AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE, Utils.intToByteArray(scanMode));
140        }
141    }
142
143    /**
144     * @return the mUuids
145     */
146    ParcelUuid[] getUuids() {
147        synchronized (mObject) {
148            return mUuids;
149        }
150    }
151
152    /**
153     * Set local adapter UUIDs.
154     *
155     * @param uuids the uuids to be set.
156     */
157    boolean setUuids(ParcelUuid[] uuids) {
158        synchronized (mObject) {
159            return mService.setAdapterPropertyNative(
160                    AbstractionLayer.BT_PROPERTY_UUIDS, Utils.uuidsToByteArray(uuids));
161        }
162    }
163
164    /**
165     * @return the mAddress
166     */
167    byte[] getAddress() {
168        synchronized (mObject) {
169            return mAddress;
170        }
171    }
172
173    /**
174     * @param mConnectionState the mConnectionState to set
175     */
176    void setConnectionState(int mConnectionState) {
177        synchronized (mObject) {
178            this.mConnectionState = mConnectionState;
179        }
180    }
181
182    /**
183     * @return the mConnectionState
184     */
185    int getConnectionState() {
186        synchronized (mObject) {
187            return mConnectionState;
188        }
189    }
190
191    /**
192     * @param mState the mState to set
193     */
194    void setState(int mState) {
195        synchronized (mObject) {
196            debugLog("Setting state to " + mState);
197            this.mState = mState;
198        }
199    }
200
201    /**
202     * @return the mState
203     */
204    int getState() {
205        synchronized (mObject) {
206            debugLog("State = " + mState);
207            return mState;
208        }
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.sendBroadcast(intent, mService.BLUETOOTH_PERM);
449                        debugLog("Name is: " + mName);
450                        break;
451                    case AbstractionLayer.BT_PROPERTY_BDADDR:
452                        mAddress = val;
453                        debugLog("Address is:" + Utils.getAddressStringFromByte(mAddress));
454                        break;
455                    case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
456                        mBluetoothClass = Utils.byteArrayToInt(val, 0);
457                        debugLog("BT Class:" + mBluetoothClass);
458                        break;
459                    case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE:
460                        int mode = Utils.byteArrayToInt(val, 0);
461                        mScanMode = mService.convertScanModeFromHal(mode);
462                        intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
463                        intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode);
464                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
465                        mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
466                        debugLog("Scan Mode:" + mScanMode);
467                        if (mBluetoothDisabling) {
468                            mBluetoothDisabling=false;
469                            mService.startBluetoothDisable();
470                        }
471                        break;
472                    case AbstractionLayer.BT_PROPERTY_UUIDS:
473                        mUuids = Utils.byteArrayToUuid(val);
474                        break;
475                    case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES:
476                        int number = val.length/BD_ADDR_LEN;
477                        byte[] addrByte = new byte[BD_ADDR_LEN];
478                        for (int j = 0; j < number; j++) {
479                            System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN);
480                            onBondStateChanged(mAdapter.getRemoteDevice(
481                                               Utils.getAddressStringFromByte(addrByte)),
482                                               BluetoothDevice.BOND_BONDED);
483                        }
484                        break;
485                    case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
486                        mDiscoverableTimeout = Utils.byteArrayToInt(val, 0);
487                        debugLog("Discoverable Timeout:" + mDiscoverableTimeout);
488                        break;
489                    default:
490                        errorLog("Property change not handled in Java land:" + type);
491                }
492            }
493        }
494    }
495
496    void onBluetoothReady() {
497        Log.d(TAG, "ScanMode =  " + mScanMode );
498        Log.d(TAG, "State =  " + getState() );
499
500        // When BT is being turned on, all adapter properties will be sent in 1
501        // callback. At this stage, set the scan mode.
502        synchronized (mObject) {
503            if (getState() == BluetoothAdapter.STATE_TURNING_ON &&
504                    mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
505                    /* mDiscoverableTimeout is part of the
506                       adapterPropertyChangedCallback received before
507                       onBluetoothReady */
508                    if (mDiscoverableTimeout != 0)
509                      setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
510                    else /* if timeout == never (0) at startup */
511                      setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
512                    /* though not always required, this keeps NV up-to date on first-boot after flash */
513                    setDiscoverableTimeout(mDiscoverableTimeout);
514            }
515        }
516    }
517
518    private boolean mBluetoothDisabling=false;
519
520    void onBluetoothDisable() {
521        // When BT disable is invoked, set the scan_mode to NONE
522        // so no incoming connections are possible
523
524        //Set flag to indicate we are disabling. When property change of scan mode done
525        //continue with disable sequence
526        debugLog("onBluetoothDisable()");
527        mBluetoothDisabling = true;
528        if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
529            setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
530        }
531    }
532    void discoveryStateChangeCallback(int state) {
533        infoLog("Callback:discoveryStateChangeCallback with state:" + state);
534        synchronized (mObject) {
535            Intent intent;
536            if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
537                mDiscovering = false;
538                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
539                mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
540            } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
541                mDiscovering = true;
542                intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
543                mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
544            }
545        }
546    }
547
548    private void infoLog(String msg) {
549        Log.i(TAG, msg);
550    }
551
552    private void debugLog(String msg) {
553        if (DBG) Log.d(TAG, msg);
554    }
555
556    private void errorLog(String msg) {
557        Log.e(TAG, msg);
558    }
559}
560