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.BluetoothClass;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.os.Handler;
     25 import android.os.Message;
     26 import android.os.ParcelUuid;
     27 import android.util.Log;
     28 
     29 import com.android.bluetooth.Utils;
     30 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
     31 
     32 import java.util.ArrayList;
     33 import java.util.HashMap;
     34 import java.util.LinkedList;
     35 
     36 
     37 final class RemoteDevices {
     38     private static final boolean DBG = false;
     39     private static final String TAG = "BluetoothRemoteDevices";
     40 
     41 
     42     private static BluetoothAdapter mAdapter;
     43     private static AdapterService mAdapterService;
     44     private static ArrayList<BluetoothDevice> mSdpTracker;
     45 
     46     private Object mObject = new Object();
     47 
     48     private static final int UUID_INTENT_DELAY = 6000;
     49     private static final int MESSAGE_UUID_INTENT = 1;
     50 
     51     private HashMap<BluetoothDevice, DeviceProperties> mDevices;
     52 
     53     RemoteDevices(AdapterService service) {
     54         mAdapter = BluetoothAdapter.getDefaultAdapter();
     55         mAdapterService = service;
     56         mSdpTracker = new ArrayList<BluetoothDevice>();
     57         mDevices = new HashMap<BluetoothDevice, DeviceProperties>();
     58     }
     59 
     60 
     61     void cleanup() {
     62         if (mSdpTracker !=null)
     63             mSdpTracker.clear();
     64 
     65         if (mDevices != null)
     66             mDevices.clear();
     67     }
     68 
     69     public Object Clone() throws CloneNotSupportedException {
     70         throw new CloneNotSupportedException();
     71     }
     72 
     73     DeviceProperties getDeviceProperties(BluetoothDevice device) {
     74         synchronized (mDevices) {
     75             return mDevices.get(device);
     76         }
     77     }
     78 
     79     BluetoothDevice getDevice(byte[] address) {
     80         for (BluetoothDevice dev : mDevices.keySet()) {
     81             if (dev.getAddress().equals(Utils.getAddressStringFromByte(address))) {
     82                 return dev;
     83             }
     84         }
     85         return null;
     86     }
     87 
     88     DeviceProperties addDeviceProperties(byte[] address) {
     89         synchronized (mDevices) {
     90             DeviceProperties prop = new DeviceProperties();
     91             BluetoothDevice device =
     92                     mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
     93             prop.mAddress = address;
     94             mDevices.put(device, prop);
     95             return prop;
     96         }
     97     }
     98 
     99     class DeviceProperties {
    100         private String mName;
    101         private byte[] mAddress;
    102         private int mBluetoothClass;
    103         private short mRssi;
    104         private ParcelUuid[] mUuids;
    105         private int mDeviceType;
    106         private String mAlias;
    107         private int mBondState;
    108 
    109         DeviceProperties() {
    110             mBondState = BluetoothDevice.BOND_NONE;
    111         }
    112 
    113         /**
    114          * @return the mName
    115          */
    116         String getName() {
    117             synchronized (mObject) {
    118                 return mName;
    119             }
    120         }
    121 
    122         /**
    123          * @return the mClass
    124          */
    125         int getBluetoothClass() {
    126             synchronized (mObject) {
    127                 return mBluetoothClass;
    128             }
    129         }
    130 
    131         /**
    132          * @return the mUuids
    133          */
    134         ParcelUuid[] getUuids() {
    135             synchronized (mObject) {
    136                 return mUuids;
    137             }
    138         }
    139 
    140         /**
    141          * @return the mAddress
    142          */
    143         byte[] getAddress() {
    144             synchronized (mObject) {
    145                 return mAddress;
    146             }
    147         }
    148 
    149         /**
    150          * @return mRssi
    151          */
    152         short getRssi() {
    153             synchronized (mObject) {
    154                 return mRssi;
    155             }
    156         }
    157 
    158         /**
    159          *
    160          * @return mDeviceType
    161          */
    162         int getDeviceType() {
    163             synchronized (mObject) {
    164                 return mDeviceType;
    165             }
    166         }
    167 
    168         /**
    169          * @return the mAlias
    170          */
    171         String getAlias() {
    172             synchronized (mObject) {
    173                 return mAlias;
    174             }
    175         }
    176 
    177         /**
    178          * @param mAlias the mAlias to set
    179          */
    180         void setAlias(String mAlias) {
    181             synchronized (mObject) {
    182                 mAdapterService.setDevicePropertyNative(mAddress,
    183                     AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
    184             }
    185         }
    186 
    187         /**
    188          * @param mBondState the mBondState to set
    189          */
    190         void setBondState(int mBondState) {
    191             synchronized (mObject) {
    192                 this.mBondState = mBondState;
    193                 if (mBondState == BluetoothDevice.BOND_NONE)
    194                 {
    195                     /* Clearing the Uuids local copy when the device is unpaired. If not cleared,
    196                     cachedBluetoothDevice issued a connect using the local cached copy of uuids,
    197                     without waiting for the ACTION_UUID intent.
    198                     This was resulting in multiple calls to connect().*/
    199                     mUuids = null;
    200                 }
    201             }
    202         }
    203 
    204         /**
    205          * @return the mBondState
    206          */
    207         int getBondState() {
    208             synchronized (mObject) {
    209                 return mBondState;
    210             }
    211         }
    212     }
    213 
    214 
    215     private void sendUuidIntent(BluetoothDevice device) {
    216         DeviceProperties prop = getDeviceProperties(device);
    217         Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
    218         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    219         intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null? null: prop.mUuids);
    220         mAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM);
    221 
    222         //Remove the outstanding UUID request
    223         mSdpTracker.remove(device);
    224     }
    225 
    226     private void sendDisplayPinIntent(byte[] address, int pin) {
    227         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
    228         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, getDevice(address));
    229         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, pin);
    230         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
    231                     BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN);
    232         mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
    233     }
    234 
    235     void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) {
    236         Intent intent;
    237         byte[] val;
    238         int type;
    239         BluetoothDevice bdDevice = getDevice(address);
    240         DeviceProperties device;
    241         if (bdDevice == null) {
    242             device = addDeviceProperties(address);
    243             bdDevice = getDevice(address);
    244         } else {
    245             device = getDeviceProperties(bdDevice);
    246         }
    247 
    248         for (int j = 0; j < types.length; j++) {
    249             type = types[j];
    250             val = values[j];
    251             if(val.length <= 0)
    252                 errorLog("devicePropertyChangedCallback: bdDevice: " + bdDevice + ", value is empty for type: " + type);
    253             else {
    254                 synchronized(mObject) {
    255                     switch (type) {
    256                         case AbstractionLayer.BT_PROPERTY_BDNAME:
    257                             device.mName = new String(val);
    258                             intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
    259                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
    260                             intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName);
    261                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    262                             mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    263                             debugLog("Remote Device name is: " + device.mName);
    264                             break;
    265                         case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME:
    266                             if (device.mAlias != null) {
    267                                 System.arraycopy(val, 0, device.mAlias, 0, val.length);
    268                             }
    269                             else {
    270                                 device.mAlias = new String(val);
    271                             }
    272                             break;
    273                         case AbstractionLayer.BT_PROPERTY_BDADDR:
    274                             device.mAddress = val;
    275                             debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val));
    276                             break;
    277                         case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE:
    278                             device.mBluetoothClass =  Utils.byteArrayToInt(val);
    279                             intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
    280                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice);
    281                             intent.putExtra(BluetoothDevice.EXTRA_CLASS,
    282                                     new BluetoothClass(device.mBluetoothClass));
    283                             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    284                             mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    285                             debugLog("Remote class is:" + device.mBluetoothClass);
    286                             break;
    287                         case AbstractionLayer.BT_PROPERTY_UUIDS:
    288                             int numUuids = val.length/AbstractionLayer.BT_UUID_SIZE;
    289                             device.mUuids = Utils.byteArrayToUuid(val);
    290                             sendUuidIntent(bdDevice);
    291                             break;
    292                         case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE:
    293                             device.mDeviceType = Utils.byteArrayToInt(val);
    294                             break;
    295                         case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI:
    296                             device.mRssi = Utils.byteArrayToShort(val);
    297                             break;
    298                     }
    299                 }
    300             }
    301         }
    302     }
    303 
    304     void deviceFoundCallback(byte[] address) {
    305         // The device properties are already registered - we can send the intent
    306         // now
    307         BluetoothDevice device = getDevice(address);
    308         debugLog("deviceFoundCallback: Remote Address is:" + device);
    309         DeviceProperties deviceProp = getDeviceProperties(device);
    310         if (deviceProp == null) {
    311             errorLog("Device Properties is null for Device:" + device);
    312             return;
    313         }
    314 
    315         Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
    316         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    317         intent.putExtra(BluetoothDevice.EXTRA_CLASS,
    318                 new BluetoothClass(Integer.valueOf(deviceProp.mBluetoothClass)));
    319         intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi);
    320         intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName);
    321 
    322         mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    323     }
    324 
    325     void pinRequestCallback(byte[] address, byte[] name, int cod) {
    326         //TODO(BT): Get wakelock and update name and cod
    327         BluetoothDevice bdDevice = getDevice(address);
    328         if (bdDevice == null) {
    329             addDeviceProperties(address);
    330         }
    331         BluetoothClass btClass = bdDevice.getBluetoothClass();
    332         int btDeviceClass = btClass.getDeviceClass();
    333         if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD ||
    334             btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
    335             // Its a keyboard. Follow the HID spec recommendation of creating the
    336             // passkey and displaying it to the user. If the keyboard doesn't follow
    337             // the spec recommendation, check if the keyboard has a fixed PIN zero
    338             // and pair.
    339             //TODO: Add sFixedPinZerosAutoPairKeyboard() and maintain list of devices that have fixed pin
    340             /*if (mAdapterService.isFixedPinZerosAutoPairKeyboard(address)) {
    341                                mAdapterService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
    342                                return;
    343                      }*/
    344             // Generate a variable PIN. This is not truly random but good enough.
    345             int pin = (int) Math.floor(Math.random() * 1000000);
    346             sendDisplayPinIntent(address, pin);
    347             return;
    348         }
    349         infoLog("pinRequestCallback: " + address + " name:" + name + " cod:" +
    350                 cod);
    351         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
    352         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, getDevice(address));
    353         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
    354                 BluetoothDevice.PAIRING_VARIANT_PIN);
    355         mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
    356         return;
    357     }
    358 
    359     void sspRequestCallback(byte[] address, byte[] name, int cod, int pairingVariant,
    360             int passkey) {
    361         //TODO(BT): Get wakelock and update name and cod
    362         BluetoothDevice bdDevice = getDevice(address);
    363         if (bdDevice == null) {
    364             addDeviceProperties(address);
    365         }
    366 
    367         infoLog("sspRequestCallback: " + address + " name: " + name + " cod: " +
    368                 cod + " pairingVariant " + pairingVariant + " passkey: " + passkey);
    369         int variant;
    370         boolean displayPasskey = false;
    371         if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION) {
    372             variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION;
    373             displayPasskey = true;
    374         } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_CONSENT) {
    375             variant = BluetoothDevice.PAIRING_VARIANT_CONSENT;
    376         } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY) {
    377             variant = BluetoothDevice.PAIRING_VARIANT_PASSKEY;
    378         } else if (pairingVariant == AbstractionLayer.BT_SSP_VARIANT_PASSKEY_NOTIFICATION) {
    379             variant = BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY;
    380 	    displayPasskey = true;
    381         } else {
    382             errorLog("SSP Pairing variant not present");
    383             return;
    384         }
    385         BluetoothDevice device = getDevice(address);
    386         if (device == null) {
    387            warnLog("Device is not known for:" + Utils.getAddressStringFromByte(address));
    388            addDeviceProperties(address);
    389            device = getDevice(address);
    390         }
    391         Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
    392         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    393         if (displayPasskey) {
    394             intent.putExtra(BluetoothDevice.EXTRA_PAIRING_KEY, passkey);
    395         }
    396         intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, variant);
    397         mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_ADMIN_PERM);
    398     }
    399 
    400     void aclStateChangeCallback(int status, byte[] address, int newState) {
    401         BluetoothDevice device = getDevice(address);
    402 
    403         if (device == null) {
    404             errorLog("aclStateChangeCallback: Device is NULL");
    405             return;
    406         }
    407 
    408         Intent intent = null;
    409         if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) {
    410             intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
    411             debugLog("aclStateChangeCallback: State:Connected to Device:" + device);
    412         } else {
    413             intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    414             debugLog("aclStateChangeCallback: State:DisConnected to Device:" + device);
    415         }
    416         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    417         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    418         mAdapterService.sendBroadcast(intent, mAdapterService.BLUETOOTH_PERM);
    419     }
    420 
    421     void fetchUuids(BluetoothDevice device) {
    422         if (mSdpTracker.contains(device)) return;
    423         mSdpTracker.add(device);
    424 
    425         Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
    426         message.obj = device;
    427         mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
    428 
    429         //mAdapterService.getDevicePropertyNative(Utils.getBytesFromAddress(device.getAddress()), AbstractionLayer.BT_PROPERTY_UUIDS);
    430         mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()));
    431     }
    432 
    433     private final Handler mHandler = new Handler() {
    434         @Override
    435         public void handleMessage(Message msg) {
    436             switch (msg.what) {
    437             case MESSAGE_UUID_INTENT:
    438                 BluetoothDevice device = (BluetoothDevice)msg.obj;
    439                 if (device != null) {
    440                     sendUuidIntent(device);
    441                 }
    442                 break;
    443             }
    444         }
    445     };
    446 
    447     private void errorLog(String msg) {
    448         Log.e(TAG, msg);
    449     }
    450 
    451     private void debugLog(String msg) {
    452         if (DBG) Log.d(TAG, msg);
    453     }
    454 
    455     private void infoLog(String msg) {
    456         if (DBG) Log.i(TAG, msg);
    457     }
    458 
    459     private void warnLog(String msg) {
    460         Log.w(TAG, msg);
    461     }
    462 
    463 }
    464