Home | History | Annotate | Download | only in btservice
      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 
     17 package com.android.bluetooth.btservice;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.bluetooth.BluetoothProfile;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.os.ParcelUuid;
     25 import android.os.UserHandle;
     26 import android.util.Log;
     27 import android.util.Pair;
     28 
     29 import com.android.bluetooth.Utils;
     30 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
     31 
     32 import java.util.HashMap;
     33 import java.util.ArrayList;
     34 
     35 class 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         synchronized (mObject) {
    207             if (VDBG) debugLog("State = " + mState);
    208             return mState;
    209         }
    210     }
    211 
    212     /**
    213      * @return the mBondedDevices
    214      */
    215     BluetoothDevice[] getBondedDevices() {
    216         BluetoothDevice[] bondedDeviceList = new BluetoothDevice[0];
    217         synchronized (mObject) {
    218             if(mBondedDevices.isEmpty())
    219                 return (new BluetoothDevice[0]);
    220 
    221             try {
    222                 bondedDeviceList = mBondedDevices.toArray(bondedDeviceList);
    223                 debugLog("getBondedDevices: length="+bondedDeviceList.length);
    224                 return bondedDeviceList;
    225             } catch(ArrayStoreException ee) {
    226                 errorLog("Error retrieving bonded device array");
    227                 return (new BluetoothDevice[0]);
    228             }
    229         }
    230     }
    231     // This function shall be invoked from BondStateMachine whenever the bond
    232     // state changes.
    233     void onBondStateChanged(BluetoothDevice device, int state)
    234     {
    235         if(device == null)
    236             return;
    237         try {
    238             byte[] addrByte = Utils.getByteAddress(device);
    239             DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
    240             if (prop == null)
    241                 prop = mRemoteDevices.addDeviceProperties(addrByte);
    242             prop.setBondState(state);
    243 
    244             if (state == BluetoothDevice.BOND_BONDED) {
    245                 // add if not already in list
    246                 if(!mBondedDevices.contains(device)) {
    247                     debugLog("Adding bonded device:" +  device);
    248                     mBondedDevices.add(device);
    249                 }
    250             } else if (state == BluetoothDevice.BOND_NONE) {
    251                 // remove device from list
    252                 if (mBondedDevices.remove(device))
    253                     debugLog("Removing bonded device:" +  device);
    254                 else
    255                     debugLog("Failed to remove device: " + device);
    256             }
    257         }
    258         catch(Exception ee) {
    259             Log.e(TAG, "Exception in onBondStateChanged : ", ee);
    260         }
    261     }
    262 
    263     int getDiscoverableTimeout() {
    264         synchronized (mObject) {
    265             return mDiscoverableTimeout;
    266         }
    267     }
    268 
    269     boolean setDiscoverableTimeout(int timeout) {
    270         synchronized (mObject) {
    271             return mService.setAdapterPropertyNative(
    272                     AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
    273                     Utils.intToByteArray(timeout));
    274         }
    275     }
    276 
    277     int getProfileConnectionState(int profile) {
    278         synchronized (mObject) {
    279             Pair<Integer, Integer> p = mProfileConnectionState.get(profile);
    280             if (p != null) return p.first;
    281             return BluetoothProfile.STATE_DISCONNECTED;
    282         }
    283     }
    284 
    285     boolean isDiscovering() {
    286         synchronized (mObject) {
    287             return mDiscovering;
    288         }
    289     }
    290 
    291     void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) {
    292         if (!validateProfileConnectionState(state) ||
    293                 !validateProfileConnectionState(prevState)) {
    294             // Previously, an invalid state was broadcast anyway,
    295             // with the invalid state converted to -1 in the intent.
    296             // Better to log an error and not send an intent with
    297             // invalid contents or set mAdapterConnectionState to -1.
    298             errorLog("Error in sendConnectionStateChange: "
    299                     + "prevState " + prevState + " state " + state);
    300             return;
    301         }
    302 
    303         synchronized (mObject) {
    304             updateProfileConnectionState(profile, state, prevState);
    305 
    306             if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
    307                 setConnectionState(state);
    308 
    309                 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
    310                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    311                 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
    312                         convertToAdapterState(state));
    313                 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
    314                         convertToAdapterState(prevState));
    315                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    316                 mService.sendBroadcastAsUser(intent, UserHandle.ALL,
    317                         mService.BLUETOOTH_PERM);
    318                 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
    319                         + prevState + " -> " + state);
    320             }
    321         }
    322     }
    323 
    324     private boolean validateProfileConnectionState(int state) {
    325         return (state == BluetoothProfile.STATE_DISCONNECTED ||
    326                 state == BluetoothProfile.STATE_CONNECTING ||
    327                 state == BluetoothProfile.STATE_CONNECTED ||
    328                 state == BluetoothProfile.STATE_DISCONNECTING);
    329     }
    330 
    331 
    332     private int convertToAdapterState(int state) {
    333         switch (state) {
    334             case BluetoothProfile.STATE_DISCONNECTED:
    335                 return BluetoothAdapter.STATE_DISCONNECTED;
    336             case BluetoothProfile.STATE_DISCONNECTING:
    337                 return BluetoothAdapter.STATE_DISCONNECTING;
    338             case BluetoothProfile.STATE_CONNECTED:
    339                 return BluetoothAdapter.STATE_CONNECTED;
    340             case BluetoothProfile.STATE_CONNECTING:
    341                 return BluetoothAdapter.STATE_CONNECTING;
    342         }
    343         Log.e(TAG, "Error in convertToAdapterState");
    344         return -1;
    345     }
    346 
    347     private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
    348         switch (prevState) {
    349             case BluetoothProfile.STATE_CONNECTING:
    350                 mProfilesConnecting--;
    351                 break;
    352 
    353             case BluetoothProfile.STATE_CONNECTED:
    354                 mProfilesConnected--;
    355                 break;
    356 
    357             case BluetoothProfile.STATE_DISCONNECTING:
    358                 mProfilesDisconnecting--;
    359                 break;
    360         }
    361 
    362         switch (state) {
    363             case BluetoothProfile.STATE_CONNECTING:
    364                 mProfilesConnecting++;
    365                 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
    366 
    367             case BluetoothProfile.STATE_CONNECTED:
    368                 mProfilesConnected++;
    369                 return (mProfilesConnected == 1);
    370 
    371             case BluetoothProfile.STATE_DISCONNECTING:
    372                 mProfilesDisconnecting++;
    373                 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
    374 
    375             case BluetoothProfile.STATE_DISCONNECTED:
    376                 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
    377 
    378             default:
    379                 return true;
    380         }
    381     }
    382 
    383     private void updateProfileConnectionState(int profile, int newState, int oldState) {
    384         // mProfileConnectionState is a hashmap -
    385         // <Integer, Pair<Integer, Integer>>
    386         // The key is the profile, the value is a pair. first element
    387         // is the state and the second element is the number of devices
    388         // in that state.
    389         int numDev = 1;
    390         int newHashState = newState;
    391         boolean update = true;
    392 
    393         // The following conditions are considered in this function:
    394         // 1. If there is no record of profile and state - update
    395         // 2. If a new device's state is current hash state - increment
    396         //    number of devices in the state.
    397         // 3. If a state change has happened to Connected or Connecting
    398         //    (if current state is not connected), update.
    399         // 4. If numDevices is 1 and that device state is being updated, update
    400         // 5. If numDevices is > 1 and one of the devices is changing state,
    401         //    decrement numDevices but maintain oldState if it is Connected or
    402         //    Connecting
    403         Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
    404         if (stateNumDev != null) {
    405             int currHashState = stateNumDev.first;
    406             numDev = stateNumDev.second;
    407 
    408             if (newState == currHashState) {
    409                 numDev ++;
    410             } else if (newState == BluetoothProfile.STATE_CONNECTED ||
    411                    (newState == BluetoothProfile.STATE_CONNECTING &&
    412                     currHashState != BluetoothProfile.STATE_CONNECTED)) {
    413                  numDev = 1;
    414             } else if (numDev == 1 && oldState == currHashState) {
    415                  update = true;
    416             } else if (numDev > 1 && oldState == currHashState) {
    417                  numDev --;
    418 
    419                  if (currHashState == BluetoothProfile.STATE_CONNECTED ||
    420                      currHashState == BluetoothProfile.STATE_CONNECTING) {
    421                     newHashState = currHashState;
    422                  }
    423             } else {
    424                  update = false;
    425             }
    426         }
    427 
    428         if (update) {
    429             mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState,
    430                     numDev));
    431         }
    432     }
    433 
    434     void adapterPropertyChangedCallback(int[] types, byte[][] values) {
    435         Intent intent;
    436         int type;
    437         byte[] val;
    438         for (int i = 0; i < types.length; i++) {
    439             val = values[i];
    440             type = types[i];
    441             infoLog("adapterPropertyChangedCallback with type:" + type + " len:" + val.length);
    442             synchronized (mObject) {
    443                 switch (type) {
    444                     case AbstractionLayer.BT_PROPERTY_BDNAME:
    445                         mName = new String(val);
    446                         intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
    447                         intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, mName);
    448                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    449                         mService.sendBroadcastAsUser(intent, UserHandle.ALL,
    450                                  mService.BLUETOOTH_PERM);
    451                         debugLog("Name is: " + mName);
    452                         break;
    453                     case AbstractionLayer.BT_PROPERTY_BDADDR:
    454                         mAddress = val;
    455                         debugLog("Address is:" + Utils.getAddressStringFromByte(mAddress));
    456                         break;
    457                     case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
    458                         mBluetoothClass = Utils.byteArrayToInt(val, 0);
    459                         debugLog("BT Class:" + mBluetoothClass);
    460                         break;
    461                     case AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE:
    462                         int mode = Utils.byteArrayToInt(val, 0);
    463                         mScanMode = mService.convertScanModeFromHal(mode);
    464                         intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
    465                         intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mScanMode);
    466                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    467                         mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
    468                         debugLog("Scan Mode:" + mScanMode);
    469                         if (mBluetoothDisabling) {
    470                             mBluetoothDisabling=false;
    471                             mService.startBluetoothDisable();
    472                         }
    473                         break;
    474                     case AbstractionLayer.BT_PROPERTY_UUIDS:
    475                         mUuids = Utils.byteArrayToUuid(val);
    476                         break;
    477                     case AbstractionLayer.BT_PROPERTY_ADAPTER_BONDED_DEVICES:
    478                         int number = val.length/BD_ADDR_LEN;
    479                         byte[] addrByte = new byte[BD_ADDR_LEN];
    480                         for (int j = 0; j < number; j++) {
    481                             System.arraycopy(val, j * BD_ADDR_LEN, addrByte, 0, BD_ADDR_LEN);
    482                             onBondStateChanged(mAdapter.getRemoteDevice(
    483                                                Utils.getAddressStringFromByte(addrByte)),
    484                                                BluetoothDevice.BOND_BONDED);
    485                         }
    486                         break;
    487                     case AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT:
    488                         mDiscoverableTimeout = Utils.byteArrayToInt(val, 0);
    489                         debugLog("Discoverable Timeout:" + mDiscoverableTimeout);
    490                         break;
    491                     default:
    492                         errorLog("Property change not handled in Java land:" + type);
    493                 }
    494             }
    495         }
    496     }
    497 
    498     void onBluetoothReady() {
    499         Log.d(TAG, "ScanMode =  " + mScanMode );
    500         Log.d(TAG, "State =  " + getState() );
    501 
    502         // When BT is being turned on, all adapter properties will be sent in 1
    503         // callback. At this stage, set the scan mode.
    504         synchronized (mObject) {
    505             if (getState() == BluetoothAdapter.STATE_TURNING_ON &&
    506                     mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
    507                     /* mDiscoverableTimeout is part of the
    508                        adapterPropertyChangedCallback received before
    509                        onBluetoothReady */
    510                     if (mDiscoverableTimeout != 0)
    511                       setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
    512                     else /* if timeout == never (0) at startup */
    513                       setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
    514                     /* though not always required, this keeps NV up-to date on first-boot after flash */
    515                     setDiscoverableTimeout(mDiscoverableTimeout);
    516             }
    517         }
    518     }
    519 
    520     private boolean mBluetoothDisabling=false;
    521 
    522     void onBluetoothDisable() {
    523         // When BT disable is invoked, set the scan_mode to NONE
    524         // so no incoming connections are possible
    525 
    526         //Set flag to indicate we are disabling. When property change of scan mode done
    527         //continue with disable sequence
    528         debugLog("onBluetoothDisable()");
    529         mBluetoothDisabling = true;
    530         if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
    531             setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
    532         }
    533     }
    534     void discoveryStateChangeCallback(int state) {
    535         infoLog("Callback:discoveryStateChangeCallback with state:" + state);
    536         synchronized (mObject) {
    537             Intent intent;
    538             if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
    539                 mDiscovering = false;
    540                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    541                 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
    542             } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
    543                 mDiscovering = true;
    544                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    545                 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
    546             }
    547         }
    548     }
    549 
    550     private void infoLog(String msg) {
    551         if (DBG) Log.i(TAG, msg);
    552     }
    553 
    554     private void debugLog(String msg) {
    555         if (DBG) Log.d(TAG, msg);
    556     }
    557 
    558     private void errorLog(String msg) {
    559         Log.e(TAG, msg);
    560     }
    561 }
    562