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         /* 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                     default:
    491                         errorLog("Property change not handled in Java land:" + type);
    492                 }
    493             }
    494         }
    495     }
    496 
    497     void onBluetoothReady() {
    498         Log.d(TAG, "ScanMode =  " + mScanMode );
    499         Log.d(TAG, "State =  " + getState() );
    500 
    501         // When BT is being turned on, all adapter properties will be sent in 1
    502         // callback. At this stage, set the scan mode.
    503         synchronized (mObject) {
    504             if (getState() == BluetoothAdapter.STATE_TURNING_ON &&
    505                     mScanMode == BluetoothAdapter.SCAN_MODE_NONE) {
    506                     /* mDiscoverableTimeout is part of the
    507                        adapterPropertyChangedCallback received before
    508                        onBluetoothReady */
    509                     if (mDiscoverableTimeout != 0)
    510                       setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE);
    511                     else /* if timeout == never (0) at startup */
    512                       setScanMode(AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
    513                     /* though not always required, this keeps NV up-to date on first-boot after flash */
    514                     setDiscoverableTimeout(mDiscoverableTimeout);
    515             }
    516         }
    517     }
    518 
    519     private boolean mBluetoothDisabling=false;
    520 
    521     void onBluetoothDisable() {
    522         // When BT disable is invoked, set the scan_mode to NONE
    523         // so no incoming connections are possible
    524 
    525         //Set flag to indicate we are disabling. When property change of scan mode done
    526         //continue with disable sequence
    527         debugLog("onBluetoothDisable()");
    528         mBluetoothDisabling = true;
    529         if (getState() == BluetoothAdapter.STATE_TURNING_OFF) {
    530             setScanMode(AbstractionLayer.BT_SCAN_MODE_NONE);
    531         }
    532     }
    533     void discoveryStateChangeCallback(int state) {
    534         infoLog("Callback:discoveryStateChangeCallback with state:" + state);
    535         synchronized (mObject) {
    536             Intent intent;
    537             if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {
    538                 mDiscovering = false;
    539                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    540                 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
    541             } else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {
    542                 mDiscovering = true;
    543                 intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    544                 mService.sendBroadcast(intent, mService.BLUETOOTH_PERM);
    545             }
    546         }
    547     }
    548 
    549     private void infoLog(String msg) {
    550         if (DBG) Log.i(TAG, msg);
    551     }
    552 
    553     private void debugLog(String msg) {
    554         if (DBG) Log.d(TAG, msg);
    555     }
    556 
    557     private void errorLog(String msg) {
    558         Log.e(TAG, msg);
    559     }
    560 }
    561