Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2011 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.settingslib.bluetooth;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothClass;
     21 import android.bluetooth.BluetoothDevice;
     22 import android.content.BroadcastReceiver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.util.Log;
     27 
     28 import com.android.settingslib.R;
     29 
     30 import java.util.ArrayList;
     31 import java.util.Collection;
     32 import java.util.HashMap;
     33 import java.util.Map;
     34 import java.util.Set;
     35 
     36 /**
     37  * BluetoothEventManager receives broadcasts and callbacks from the Bluetooth
     38  * API and dispatches the event on the UI thread to the right class in the
     39  * Settings.
     40  */
     41 public class BluetoothEventManager {
     42     private static final String TAG = "BluetoothEventManager";
     43 
     44     private final LocalBluetoothAdapter mLocalAdapter;
     45     private final CachedBluetoothDeviceManager mDeviceManager;
     46     private LocalBluetoothProfileManager mProfileManager;
     47     private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
     48     private final Map<String, Handler> mHandlerMap;
     49     private Context mContext;
     50 
     51     private final Collection<BluetoothCallback> mCallbacks =
     52             new ArrayList<BluetoothCallback>();
     53 
     54     private android.os.Handler mReceiverHandler;
     55 
     56     interface Handler {
     57         void onReceive(Context context, Intent intent, BluetoothDevice device);
     58     }
     59 
     60     private void addHandler(String action, Handler handler) {
     61         mHandlerMap.put(action, handler);
     62         mAdapterIntentFilter.addAction(action);
     63     }
     64 
     65     void addProfileHandler(String action, Handler handler) {
     66         mHandlerMap.put(action, handler);
     67         mProfileIntentFilter.addAction(action);
     68     }
     69 
     70     // Set profile manager after construction due to circular dependency
     71     void setProfileManager(LocalBluetoothProfileManager manager) {
     72         mProfileManager = manager;
     73     }
     74 
     75     BluetoothEventManager(LocalBluetoothAdapter adapter,
     76             CachedBluetoothDeviceManager deviceManager, Context context) {
     77         mLocalAdapter = adapter;
     78         mDeviceManager = deviceManager;
     79         mAdapterIntentFilter = new IntentFilter();
     80         mProfileIntentFilter = new IntentFilter();
     81         mHandlerMap = new HashMap<String, Handler>();
     82         mContext = context;
     83 
     84         // Bluetooth on/off broadcasts
     85         addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
     86         // Generic connected/not broadcast
     87         addHandler(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED,
     88                 new ConnectionStateChangedHandler());
     89 
     90         // Discovery broadcasts
     91         addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
     92         addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
     93         addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
     94         addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
     95         addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
     96         addHandler(BluetoothDevice.ACTION_ALIAS_CHANGED, new NameChangedHandler());
     97 
     98         // Pairing broadcasts
     99         addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
    100         addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
    101 
    102         // Fine-grained state broadcasts
    103         addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
    104         addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
    105 
    106         // Dock event broadcasts
    107         addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
    108 
    109         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
    110     }
    111 
    112     void registerProfileIntentReceiver() {
    113         mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
    114     }
    115 
    116     public void setReceiverHandler(android.os.Handler handler) {
    117         mContext.unregisterReceiver(mBroadcastReceiver);
    118         mReceiverHandler = handler;
    119         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
    120         registerProfileIntentReceiver();
    121     }
    122 
    123     /** Register to start receiving callbacks for Bluetooth events. */
    124     public void registerCallback(BluetoothCallback callback) {
    125         synchronized (mCallbacks) {
    126             mCallbacks.add(callback);
    127         }
    128     }
    129 
    130     /** Unregister to stop receiving callbacks for Bluetooth events. */
    131     public void unregisterCallback(BluetoothCallback callback) {
    132         synchronized (mCallbacks) {
    133             mCallbacks.remove(callback);
    134         }
    135     }
    136 
    137     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    138         @Override
    139         public void onReceive(Context context, Intent intent) {
    140             String action = intent.getAction();
    141             BluetoothDevice device = intent
    142                     .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    143 
    144             Handler handler = mHandlerMap.get(action);
    145             if (handler != null) {
    146                 handler.onReceive(context, intent, device);
    147             }
    148         }
    149     };
    150 
    151     private class AdapterStateChangedHandler implements Handler {
    152         public void onReceive(Context context, Intent intent,
    153                 BluetoothDevice device) {
    154             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
    155                                     BluetoothAdapter.ERROR);
    156             // update local profiles and get paired devices
    157             mLocalAdapter.setBluetoothStateInt(state);
    158             // send callback to update UI and possibly start scanning
    159             synchronized (mCallbacks) {
    160                 for (BluetoothCallback callback : mCallbacks) {
    161                     callback.onBluetoothStateChanged(state);
    162                 }
    163             }
    164             // Inform CachedDeviceManager that the adapter state has changed
    165             mDeviceManager.onBluetoothStateChanged(state);
    166         }
    167     }
    168 
    169     private class ScanningStateChangedHandler implements Handler {
    170         private final boolean mStarted;
    171 
    172         ScanningStateChangedHandler(boolean started) {
    173             mStarted = started;
    174         }
    175         public void onReceive(Context context, Intent intent,
    176                 BluetoothDevice device) {
    177             synchronized (mCallbacks) {
    178                 for (BluetoothCallback callback : mCallbacks) {
    179                     callback.onScanningStateChanged(mStarted);
    180                 }
    181             }
    182             mDeviceManager.onScanningStateChanged(mStarted);
    183         }
    184     }
    185 
    186     private class DeviceFoundHandler implements Handler {
    187         public void onReceive(Context context, Intent intent,
    188                 BluetoothDevice device) {
    189             short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
    190             BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
    191             String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
    192             // TODO Pick up UUID. They should be available for 2.1 devices.
    193             // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
    194             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
    195             if (cachedDevice == null) {
    196                 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
    197                 Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
    198                         + cachedDevice);
    199             }
    200             cachedDevice.setRssi(rssi);
    201             cachedDevice.setBtClass(btClass);
    202             cachedDevice.setNewName(name);
    203             cachedDevice.setVisible(true);
    204         }
    205     }
    206 
    207     private class ConnectionStateChangedHandler implements Handler {
    208         @Override
    209         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
    210             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
    211             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
    212                     BluetoothAdapter.ERROR);
    213             dispatchConnectionStateChanged(cachedDevice, state);
    214         }
    215     }
    216 
    217     private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
    218         synchronized (mCallbacks) {
    219             for (BluetoothCallback callback : mCallbacks) {
    220                 callback.onConnectionStateChanged(cachedDevice, state);
    221             }
    222         }
    223     }
    224 
    225     void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
    226         synchronized (mCallbacks) {
    227             for (BluetoothCallback callback : mCallbacks) {
    228                 callback.onDeviceAdded(cachedDevice);
    229             }
    230         }
    231     }
    232 
    233     private class DeviceDisappearedHandler implements Handler {
    234         public void onReceive(Context context, Intent intent,
    235                 BluetoothDevice device) {
    236             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
    237             if (cachedDevice == null) {
    238                 Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
    239                 return;
    240             }
    241             if (CachedBluetoothDeviceManager.onDeviceDisappeared(cachedDevice)) {
    242                 synchronized (mCallbacks) {
    243                     for (BluetoothCallback callback : mCallbacks) {
    244                         callback.onDeviceDeleted(cachedDevice);
    245                     }
    246                 }
    247             }
    248         }
    249     }
    250 
    251     private class NameChangedHandler implements Handler {
    252         public void onReceive(Context context, Intent intent,
    253                 BluetoothDevice device) {
    254             mDeviceManager.onDeviceNameUpdated(device);
    255         }
    256     }
    257 
    258     private class BondStateChangedHandler implements Handler {
    259         public void onReceive(Context context, Intent intent,
    260                 BluetoothDevice device) {
    261             if (device == null) {
    262                 Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
    263                 return;
    264             }
    265             int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
    266                                                BluetoothDevice.ERROR);
    267             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
    268             if (cachedDevice == null) {
    269                 Log.w(TAG, "CachedBluetoothDevice for device " + device +
    270                         " not found, calling readPairedDevices().");
    271                 if (readPairedDevices()) {
    272                     cachedDevice = mDeviceManager.findDevice(device);
    273                 }
    274 
    275                 if (cachedDevice == null) {
    276                     Log.w(TAG, "Got bonding state changed for " + device +
    277                             ", but we have no record of that device.");
    278 
    279                     cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
    280                     dispatchDeviceAdded(cachedDevice);
    281                 }
    282             }
    283 
    284             synchronized (mCallbacks) {
    285                 for (BluetoothCallback callback : mCallbacks) {
    286                     callback.onDeviceBondStateChanged(cachedDevice, bondState);
    287                 }
    288             }
    289             cachedDevice.onBondingStateChanged(bondState);
    290 
    291             if (bondState == BluetoothDevice.BOND_NONE) {
    292                 int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
    293                         BluetoothDevice.ERROR);
    294 
    295                 showUnbondMessage(context, cachedDevice.getName(), reason);
    296             }
    297         }
    298 
    299         /**
    300          * Called when we have reached the unbonded state.
    301          *
    302          * @param reason one of the error reasons from
    303          *            BluetoothDevice.UNBOND_REASON_*
    304          */
    305         private void showUnbondMessage(Context context, String name, int reason) {
    306             int errorMsg;
    307 
    308             switch(reason) {
    309             case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
    310                 errorMsg = R.string.bluetooth_pairing_pin_error_message;
    311                 break;
    312             case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
    313                 errorMsg = R.string.bluetooth_pairing_rejected_error_message;
    314                 break;
    315             case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
    316                 errorMsg = R.string.bluetooth_pairing_device_down_error_message;
    317                 break;
    318             case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
    319             case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
    320             case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
    321             case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
    322                 errorMsg = R.string.bluetooth_pairing_error_message;
    323                 break;
    324             default:
    325                 Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
    326                 return;
    327             }
    328             Utils.showError(context, name, errorMsg);
    329         }
    330     }
    331 
    332     private class ClassChangedHandler implements Handler {
    333         public void onReceive(Context context, Intent intent,
    334                 BluetoothDevice device) {
    335             mDeviceManager.onBtClassChanged(device);
    336         }
    337     }
    338 
    339     private class UuidChangedHandler implements Handler {
    340         public void onReceive(Context context, Intent intent,
    341                 BluetoothDevice device) {
    342             mDeviceManager.onUuidChanged(device);
    343         }
    344     }
    345 
    346     private class PairingCancelHandler implements Handler {
    347         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
    348             if (device == null) {
    349                 Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
    350                 return;
    351             }
    352             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
    353             if (cachedDevice == null) {
    354                 Log.e(TAG, "ACTION_PAIRING_CANCEL with no cached device");
    355                 return;
    356             }
    357             int errorMsg = R.string.bluetooth_pairing_error_message;
    358             if (context != null && cachedDevice != null) {
    359                 Utils.showError(context, cachedDevice.getName(), errorMsg);
    360             }
    361         }
    362     }
    363 
    364     private class DockEventHandler implements Handler {
    365         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
    366             // Remove if unpair device upon undocking
    367             int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
    368             int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
    369             if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
    370                 if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
    371                     CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
    372                     if (cachedDevice != null) {
    373                         cachedDevice.setVisible(false);
    374                     }
    375                 }
    376             }
    377         }
    378     }
    379     boolean readPairedDevices() {
    380         Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
    381         if (bondedDevices == null) {
    382             return false;
    383         }
    384 
    385         boolean deviceAdded = false;
    386         for (BluetoothDevice device : bondedDevices) {
    387             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
    388             if (cachedDevice == null) {
    389                 cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
    390                 dispatchDeviceAdded(cachedDevice);
    391                 deviceAdded = true;
    392             }
    393         }
    394 
    395         return deviceAdded;
    396     }
    397 }
    398