summaryrefslogtreecommitdiffstats
path: root/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java')
-rwxr-xr-xpackages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java362
1 files changed, 362 insertions, 0 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
new file mode 100755
index 0000000..7c92368
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * BluetoothEventManager receives broadcasts and callbacks from the Bluetooth
+ * API and dispatches the event on the UI thread to the right class in the
+ * Settings.
+ */
+public final class BluetoothEventManager {
+ private static final String TAG = "BluetoothEventManager";
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private LocalBluetoothProfileManager mProfileManager;
+ private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
+ private final Map<String, Handler> mHandlerMap;
+ private Context mContext;
+
+ private final Collection<BluetoothCallback> mCallbacks =
+ new ArrayList<BluetoothCallback>();
+
+ interface Handler {
+ void onReceive(Context context, Intent intent, BluetoothDevice device);
+ }
+
+ void addHandler(String action, Handler handler) {
+ mHandlerMap.put(action, handler);
+ mAdapterIntentFilter.addAction(action);
+ }
+
+ void addProfileHandler(String action, Handler handler) {
+ mHandlerMap.put(action, handler);
+ mProfileIntentFilter.addAction(action);
+ }
+
+ // Set profile manager after construction due to circular dependency
+ void setProfileManager(LocalBluetoothProfileManager manager) {
+ mProfileManager = manager;
+ }
+
+ BluetoothEventManager(LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager, Context context) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mAdapterIntentFilter = new IntentFilter();
+ mProfileIntentFilter = new IntentFilter();
+ mHandlerMap = new HashMap<String, Handler>();
+ mContext = context;
+
+ // Bluetooth on/off broadcasts
+ addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
+
+ // Discovery broadcasts
+ addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
+ addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
+ addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
+ addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
+ addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
+
+ // Pairing broadcasts
+ addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
+ addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
+
+ // Fine-grained state broadcasts
+ addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
+ addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
+
+ // Dock event broadcasts
+ addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
+
+ mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);
+ }
+
+ void registerProfileIntentReceiver() {
+ mContext.registerReceiver(mBroadcastReceiver, mProfileIntentFilter);
+ }
+
+ /** Register to start receiving callbacks for Bluetooth events. */
+ public void registerCallback(BluetoothCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ /** Unregister to stop receiving callbacks for Bluetooth events. */
+ public void unregisterCallback(BluetoothCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ BluetoothDevice device = intent
+ .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ Handler handler = mHandlerMap.get(action);
+ if (handler != null) {
+ handler.onReceive(context, intent, device);
+ }
+ }
+ };
+
+ private class AdapterStateChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ // update local profiles and get paired devices
+ mLocalAdapter.setBluetoothStateInt(state);
+ // send callback to update UI and possibly start scanning
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onBluetoothStateChanged(state);
+ }
+ }
+ // Inform CachedDeviceManager that the adapter state has changed
+ mDeviceManager.onBluetoothStateChanged(state);
+ }
+ }
+
+ private class ScanningStateChangedHandler implements Handler {
+ private final boolean mStarted;
+
+ ScanningStateChangedHandler(boolean started) {
+ mStarted = started;
+ }
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onScanningStateChanged(mStarted);
+ }
+ }
+ mDeviceManager.onScanningStateChanged(mStarted);
+ }
+ }
+
+ private class DeviceFoundHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
+ BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
+ String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
+ // TODO Pick up UUID. They should be available for 2.1 devices.
+ // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
+ Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
+ + cachedDevice);
+ // callback to UI to create Preference for new device
+ dispatchDeviceAdded(cachedDevice);
+ }
+ cachedDevice.setRssi(rssi);
+ cachedDevice.setBtClass(btClass);
+ cachedDevice.setNewName(name);
+ cachedDevice.setVisible(true);
+ }
+ }
+
+ private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceAdded(cachedDevice);
+ }
+ }
+ }
+
+ private class DeviceDisappearedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
+ return;
+ }
+ if (CachedBluetoothDeviceManager.onDeviceDisappeared(cachedDevice)) {
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceDeleted(cachedDevice);
+ }
+ }
+ }
+ }
+ }
+
+ private class NameChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ mDeviceManager.onDeviceNameUpdated(device);
+ }
+ }
+
+ private class BondStateChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ if (device == null) {
+ Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+ return;
+ }
+ int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ BluetoothDevice.ERROR);
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.w(TAG, "CachedBluetoothDevice for device " + device +
+ " not found, calling readPairedDevices().");
+ if (!readPairedDevices()) {
+ Log.e(TAG, "Got bonding state changed for " + device +
+ ", but we have no record of that device.");
+ return;
+ }
+ cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.e(TAG, "Got bonding state changed for " + device +
+ ", but device not added in cache.");
+ return;
+ }
+ }
+
+ synchronized (mCallbacks) {
+ for (BluetoothCallback callback : mCallbacks) {
+ callback.onDeviceBondStateChanged(cachedDevice, bondState);
+ }
+ }
+ cachedDevice.onBondingStateChanged(bondState);
+
+ if (bondState == BluetoothDevice.BOND_NONE) {
+ int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
+ BluetoothDevice.ERROR);
+
+ showUnbondMessage(context, cachedDevice.getName(), reason);
+ }
+ }
+
+ /**
+ * Called when we have reached the unbonded state.
+ *
+ * @param reason one of the error reasons from
+ * BluetoothDevice.UNBOND_REASON_*
+ */
+ private void showUnbondMessage(Context context, String name, int reason) {
+ int errorMsg;
+
+ switch(reason) {
+ case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
+ errorMsg = R.string.bluetooth_pairing_pin_error_message;
+ break;
+ case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
+ errorMsg = R.string.bluetooth_pairing_rejected_error_message;
+ break;
+ case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
+ errorMsg = R.string.bluetooth_pairing_device_down_error_message;
+ break;
+ case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
+ case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
+ case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
+ case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
+ errorMsg = R.string.bluetooth_pairing_error_message;
+ break;
+ default:
+ Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
+ return;
+ }
+ Utils.showError(context, name, errorMsg);
+ }
+ }
+
+ private class ClassChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ mDeviceManager.onBtClassChanged(device);
+ }
+ }
+
+ private class UuidChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ mDeviceManager.onUuidChanged(device);
+ }
+ }
+
+ private class PairingCancelHandler implements Handler {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ if (device == null) {
+ Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
+ return;
+ }
+ int errorMsg = R.string.bluetooth_pairing_error_message;
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ Utils.showError(context, cachedDevice.getName(), errorMsg);
+ }
+ }
+
+ private class DockEventHandler implements Handler {
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ // Remove if unpair device upon undocking
+ int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
+ int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
+ if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice != null) {
+ cachedDevice.setVisible(false);
+ }
+ }
+ }
+ }
+ }
+ boolean readPairedDevices() {
+ Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
+ if (bondedDevices == null) {
+ return false;
+ }
+
+ boolean deviceAdded = false;
+ for (BluetoothDevice device : bondedDevices) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
+ dispatchDeviceAdded(cachedDevice);
+ deviceAdded = true;
+ }
+ }
+
+ return deviceAdded;
+ }
+}