Home | History | Annotate | Download | only in btservice
      1 /*
      2  * Copyright (C) 2012-2014 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.BluetoothClass;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.content.Intent;
     23 import android.os.Handler;
     24 import android.os.Message;
     25 import android.os.ParcelUuid;
     26 import android.util.Log;
     27 import com.android.bluetooth.R;
     28 import com.android.bluetooth.Utils;
     29 import java.util.ArrayList;
     30 import java.util.HashMap;
     31 import java.util.LinkedList;
     32 import java.util.Queue;
     33 
     34 final class RemoteDevices {
     35     private static final boolean DBG = false;
     36     private static final String TAG = "BluetoothRemoteDevices";
     37 
     38     // Maximum number of device properties to remember
     39     private static final int MAX_DEVICE_QUEUE_SIZE = 200;
     40 
     41     private static BluetoothAdapter mAdapter;
     42     private static AdapterService mAdapterService;
     43     private static ArrayList<BluetoothDevice> mSdpTracker;
     44     private Object mObject = new Object();
     45 
     46     private static final int UUID_INTENT_DELAY = 6000;
     47     private static final int MESSAGE_UUID_INTENT = 1;
     48 
     49     private HashMap<String, DeviceProperties> mDevices;
     50     private Queue<String> mDeviceQueue;
     51 
     52     RemoteDevices(AdapterService service) {
     53         mAdapter = BluetoothAdapter.getDefaultAdapter();
     54         mAdapterService = service;
     55         mSdpTracker = new ArrayList<BluetoothDevice>();
     56         mDevices = new HashMap<String, DeviceProperties>();
     57         mDeviceQueue = new LinkedList<String>();
     58     }
     59 
     60 
     61     void cleanup() {
     62         if (mSdpTracker !=null)
     63             mSdpTracker.clear();
     64 
     65         if (mDevices != null)
     66             mDevices.clear();
     67 
     68         if (mDeviceQueue != null)
     69             mDeviceQueue.clear();
     70     }
     71 
     72     @Override
     73     public Object clone() throws CloneNotSupportedException {
     74         throw new CloneNotSupportedException();
     75     }
     76 
     77     DeviceProperties getDeviceProperties(BluetoothDevice device) {
     78         synchronized (mDevices) {
     79             return mDevices.get(device.getAddress());
     80         }
     81     }
     82 
     83     BluetoothDevice getDevice(byte[] address) {
     84         DeviceProperties prop = mDevices.get(Utils.getAddressStringFromByte(address));
     85         if (prop == null)
     86            return null;
     87         return prop.getDevice();
     88     }
     89 
     90     DeviceProperties addDeviceProperties(byte[] address) {
     91         synchronized (mDevices) {
     92             DeviceProperties prop = new DeviceProperties();
     93             prop.mDevice = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
     94             prop.mAddress = address;
     95             String key = Utils.getAddressStringFromByte(address);
     96             DeviceProperties pv = mDevices.put(key, prop);
     97 
     98             if (pv == null) {
     99                 mDeviceQueue.offer(key);
    100                 if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) {
    101                     String deleteKey = mDeviceQueue.poll();
    102                     for (BluetoothDevice device : mAdapterService.getBondedDevices()) {
    103                         if (device.getAddress().equals(deleteKey)) return prop;
    104                     }
    105                     debugLog("Removing device " + deleteKey + " from property map");
    106                     mDevices.remove(deleteKey);
    107                 }
    108             }
    109             return prop;
    110         }
    111     }
    112 
    113     class DeviceProperties {
    114         private String mName;
    115         private byte[] mAddress;
    116         private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED;
    117         private short mRssi;
    118         private ParcelUuid[] mUuids;
    119         private int mDeviceType;
    120         private String mAlias;
    121         private int mBondState;
    122         private BluetoothDevice mDevice;
    123         private boolean isBondingInitiatedLocally;
    124 
    125         DeviceProperties() {
    126             mBondState = BluetoothDevice.BOND_NONE;
    127         }
    128 
    129         /**
    130          * @return the mName
    131          */
    132         String getName() {
    133             synchronized (mObject) {
    134                 return mName;
    135             }
    136         }
    137 
    138         /**
    139          * @return the mClass
    140          */
    141         int getBluetoothClass() {
    142             synchronized (mObject) {
    143                 return mBluetoothClass;
    144             }
    145         }
    146 
    147         /**
    148          * @return the mUuids
    149          */
    150         ParcelUuid[] getUuids() {
    151             synchronized (mObject) {
    152                 return mUuids;
    153             }
    154         }
    155 
    156         /**
    157          * @return the mAddress
    158          */
    159         byte[] getAddress() {
    160             synchronized (mObject) {
    161                 return mAddress;
    162             }
    163         }
    164 
    165         /**
    166          * @return the mDevice
    167          */
    168         BluetoothDevice getDevice() {
    169             synchronized (mObject) {
    170                 return mDevice;
    171             }
    172         }
    173 
    174         /**
    175          * @return mRssi
    176          */
    177         short getRssi() {
    178             synchronized (mObject) {
    179                 return mRssi;
    180             }
    181         }
    182 
    183         /**
    184          * @return mDeviceType
    185          */
    186         int getDeviceType() {
    187             synchronized (mObject) {
    188                 return mDeviceType;
    189             }
    190         }
    191 
    192         /**
    193          * @return the mAlias
    194          */
    195         String getAlias() {
    196             synchronized (mObject) {
    197                 return mAlias;
    198             }
    199         }
    200 
    201         /**
    202          * @param mAlias the mAlias to set
    203          */
    204         void setAlias(BluetoothDevice device, String mAlias) {
    205             synchronized (mObject) {
    206                 this.mAlias = mAlias;
    207                 mAdapterService.setDevicePropertyNative(mAddress,
    208                     AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
    209                 Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
    210                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    211                 intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias);
    212                 mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM);
    213             }
    214         }
    215 
    216         /**
    217          * @param mBondState the mBondState to set
    218          */
    219         void setBondState(int mBondState) {
    220             synchronized (mObject) {
    221                 this.mBondState = mBondState;
    222                 if (mBondState == BluetoothDevice.BOND_NONE)
    223                 {
    224                     /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
    225                     cachedBluetoothDevice issued a connect using the local cached copy of uuids,
    226                     without waiting for the ACTION_UUID intent.
    227                     This was resulting in multiple calls to connect().*/
    228                     mUuids = null;
    229                 }
    230             }
    231         }
    232 
    233         /**
    234          * @return the mBondState
    235          */
    236         int getBondState() {
    237             synchronized (mObject) {
    238                 return mBondState;
    239             }
    240         }
    241 
    242         /**
    243          * @param isBondingInitiatedLocally wether bonding is initiated locally
    244          */
    245         void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) {
    246             synchronized (mObject) {
    247                 this.isBondingInitiatedLocally = isBondingInitiatedLocally;
    248             }
    249         }
    250 
    251         /**
    252          * @return the isBondingInitiatedLocally
    253          */
    254         boolean isBondingInitiatedLocally() {
    255             synchronized (mObject) {
    256                 return isBondingInitiatedLocally;
    257             }
    258         }
    259     }
    260 
    261     private void sendUuidIntent(BluetoothDevice device) {
    262         DeviceProperties prop = getDeviceProperties(device);
    263         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
    264         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    265         intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null ? null : prop.mUuids);
    266         mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);
    267 
    268         //Remove the outstanding UUID request
    269         mSdpTracker.remove(device);
    270     }
    271 
    272   /**
    273    * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, we
    274    * must add device first before setting it's properties. This is a helper method for doing that.
    275    */
    276   void setBondingInitiatedLocally(byte[] address) {
    277         DeviceProperties properties;
    278 
    279         BluetoothDevice device = getDevice(address);
    280         if (device == null) {
    281             properties = addDeviceProperties(address);
    282         } else {
    283             properties = getDeviceProperties(device);
    284         }
    285 
    286         properties.setBondingInitiatedLocally(true);
    287     }
    288 
    289 
    290     void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
    291         Intent intent;
    292         byte[] val;
    293         int type;
    294         BluetoothDevice bdDevice = getDevice(address);
    295         DeviceProperties device;
    296         if (bdDevice == null) {
    297             debugLog("Added new device property");
    298             device = addDeviceProperties(address);
    299             bdDevice = getDevice(address);
    300         } else {
    301             device = getDeviceProperties(bdDevice);
    302         }
    303 
    304         if (types.length <= 0) {
    305             errorLog("No properties to update");
    306             return;
    307         }
    308 
    309         for (int j = 0; j < types.length; j++) {
    310             type = types[j];
    311             val = values[j];
    312             if (val.length > 0) {
    313                 synchronized(mObject) {
    314                     debugLog("Property type: " + type);
    315                     switch (type) {
    316                         case AbstractionLayer.BT_PROPERTY_BDNAME:
    317                             device.mName = new String(val);
    318                             intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
    319                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
    320                             intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
    321                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    322                             mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    323                             debugLog("Remote Device name is: " + device.mName);
    324                             break;
    325                         case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
    326                             if (device.mAlias != null) {
    327                                 System.arraycopy(val, 0, device.mAlias, 0, val.length);
    328                             }
    329                             else {
    330                                 device.mAlias = new String(val);
    331                             }
    332                             break;
    333                         case AbstractionLayer.BT_PROPERTY_BDADDR:
    334                             device.mAddress = val;
    335                             debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
    336                             break;
    337                         case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
    338                             device.mBluetoothClass =  Utils.byteArrayToInt(val);
    339                             intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
    340                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
    341                             intent.putExtra(BluetoothDevice.EXTRA_CLASS,
    342                                     new BluetoothClass(device.mBluetoothClass));
    343                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    344                             mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    345                             debugLog("Remote class is:" + device.mBluetoothClass);
    346                             break;
    347                         case AbstractionLayer.BT_PROPERTY_UUIDS:
    348                             int numUuids = val.length/AbstractionLayer.BT_UUID_SIZE;
    349                             device.mUuids = Utils.byteArrayToUuid(val);
    350                             if (mAdapterService.getState() == BluetoothAdapter.STATE_ON)
    351                                 sendUuidIntent(bdDevice);
    352                             break;
    353                         case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
    354                             // The device type from hal layer, defined in bluetooth.h,
    355                             // matches the type defined in BluetoothDevice.java
    356                             device.mDeviceType = Utils.byteArrayToInt(val);
    357                             break;
    358                         case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
    359                             // RSSI from hal is in one byte
    360                             device.mRssi = val[0];
    361                             break;
    362                     }
    363                 }
    364             }
    365         }
    366     }
    367 
    368     void deviceFoundCallback(byte[] address) {
    369         // The device properties are already registered - we can send the intent
    370         // now
    371         BluetoothDevice device = getDevice(address);
    372         debugLog("deviceFoundCallback: Remote Address is:" + device);
    373         DeviceProperties deviceProp = getDeviceProperties(device);
    374         if (deviceProp == null) {
    375             errorLog("Device Properties is null for Device:" + device);
    376             return;
    377         }
    378 
    379         Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
    380         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    381         intent.putExtra(BluetoothDevice.EXTRA_CLASS,
    382                 new BluetoothClass(deviceProp.mBluetoothClass));
    383         intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
    384         intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
    385 
    386         mAdapterService.sendBroadcastMultiplePermissions(intent,
    387                 new String[] {AdapterService.BLUETOOTH_PERM,
    388                         android.Manifest.permission.ACCESS_COARSE_LOCATION});
    389     }
    390 
    391     void aclStateChangeCallback(int status, byte[] address, int newState) {
    392         BluetoothDevice device = getDevice(address);
    393 
    394         if (device == null) {
    395             errorLog("aclStateChangeCallback: Device is NULL");
    396             return;
    397         }
    398         int state = mAdapterService.getState();
    399 
    400         Intent intent = null;
    401         if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
    402             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) {
    403                 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
    404             } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON) {
    405                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED);
    406             }
    407             debugLog("aclStateChangeCallback: Adapter State: "
    408                     + BluetoothAdapter.nameForState(state) + " Connected: " + device);
    409         } else {
    410             if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
    411                 // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding.
    412                 intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
    413                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    414                 intent.setPackage(mAdapterService.getString(R.string.pairing_ui_package));
    415                 mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    416             }
    417             if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) {
    418                 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    419             } else if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
    420                 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED);
    421             }
    422             debugLog("aclStateChangeCallback: Adapter State: "
    423                     + BluetoothAdapter.nameForState(state) + " Disconnected: " + device);
    424         }
    425 
    426         if (intent != null) {
    427             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    428             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
    429                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
    430             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    431             mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    432         } else {
    433             Log.e(TAG, "aclStateChangeCallback intent is null. deviceBondState: "
    434                     + device.getBondState());
    435         }
    436     }
    437 
    438 
    439     void fetchUuids(BluetoothDevice device) {
    440         if (mSdpTracker.contains(device)) return;
    441         mSdpTracker.add(device);
    442 
    443         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
    444         message.obj = device;
    445         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
    446 
    447         mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
    448     }
    449 
    450     void updateUuids(BluetoothDevice device) {
    451         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
    452         message.obj = device;
    453         mHandler.sendMessage(message);
    454     }
    455 
    456     private final Handler mHandler = new Handler() {
    457         @Override
    458         public void handleMessage(Message msg) {
    459             switch (msg.what) {
    460             case MESSAGE_UUID_INTENT:
    461                 BluetoothDevice device = (BluetoothDevice)msg.obj;
    462                 if (device != null) {
    463                     sendUuidIntent(device);
    464                 }
    465                 break;
    466             }
    467         }
    468     };
    469 
    470     private void errorLog(String msg) {
    471         Log.e(TAG, msg);
    472     }
    473 
    474     private void debugLog(String msg) {
    475         if (DBG) Log.d(TAG, msg);
    476     }
    477 
    478     private void infoLog(String msg) {
    479         if (DBG) Log.i(TAG, msg);
    480     }
    481 
    482     private void warnLog(String msg) {
    483         Log.w(TAG, msg);
    484     }
    485 
    486 }
    487