Make QS use SettingsLib's BT code

A couple of changes needed to be made to SettingsLib to support this.
 - SettingsLib needed to track ACTION_CONNECTION_STATE_CHANGED
 - The summary code needed to move from Settings up into SettingsLib
 - Added a getMaxConnectionState to CachedBluetoothDevice
    - This simplifies the states of all of the profiles into
      one.

Change-Id: I7f828f0038ad0cf39274986ece6d486d545f0286
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 81e1e45..8d4f302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -16,138 +16,57 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static android.bluetooth.BluetoothAdapter.ERROR;
-import static com.android.systemui.statusbar.policy.BluetoothUtil.connectionStateToString;
-import static com.android.systemui.statusbar.policy.BluetoothUtil.deviceToString;
-import static com.android.systemui.statusbar.policy.BluetoothUtil.profileToString;
-import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToProfile;
-import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidToString;
-import static com.android.systemui.statusbar.policy.BluetoothUtil.uuidsToString;
-
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothA2dpSink;
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothHeadsetClient;
-import android.bluetooth.BluetoothInputDevice;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothMap;
-import android.bluetooth.BluetoothPan;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothProfile.ServiceListener;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
-import android.os.ParcelUuid;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Log;
-import android.util.SparseArray;
 
-import com.android.systemui.statusbar.policy.BluetoothUtil.Profile;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Set;
+import java.util.Collection;
 
-public class BluetoothControllerImpl implements BluetoothController {
+public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
+        CachedBluetoothDevice.Callback {
     private static final String TAG = "BluetoothController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    // This controls the order in which we check the states.  Since a device can only have
-    // one state on screen, but can have multiple profiles, the later states override the
-    // value of earlier states.  So if a device has a profile in CONNECTING and one in
-    // CONNECTED, it will show as CONNECTED, theoretically this shouldn't really happen often,
-    // but seemed worth noting.
-    private static final int[] CONNECTION_STATES = {
-        BluetoothProfile.STATE_DISCONNECTED,
-        BluetoothProfile.STATE_DISCONNECTING,
-        BluetoothProfile.STATE_CONNECTING,
-        BluetoothProfile.STATE_CONNECTED,
-    };
-    // Update all the BT device states.
-    private static final int MSG_UPDATE_CONNECTION_STATES = 1;
-    // Update just one BT device.
-    private static final int MSG_UPDATE_SINGLE_CONNECTION_STATE = 2;
-    // Update whether devices are bonded or not.
-    private static final int MSG_UPDATE_BONDED_DEVICES = 3;
 
-    private static final int MSG_ADD_PROFILE = 4;
-    private static final int MSG_REM_PROFILE = 5;
-
-    private final Context mContext;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
-    private final BluetoothAdapter mAdapter;
-    private final Receiver mReceiver = new Receiver();
-    private final ArrayMap<BluetoothDevice, DeviceInfo> mDeviceInfo = new ArrayMap<>();
-    private final SparseArray<BluetoothProfile> mProfiles = new SparseArray<>();
-
-    private final H mHandler;
+    private final LocalBluetoothManager mLocalBluetoothManager;
 
     private boolean mEnabled;
     private boolean mConnecting;
-    private BluetoothDevice mLastDevice;
+    private CachedBluetoothDevice mLastDevice;
 
     public BluetoothControllerImpl(Context context, Looper bgLooper) {
-        mContext = context;
-        mHandler = new H(bgLooper);
-
-        final BluetoothManager bluetoothManager =
-                (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
-        mAdapter = bluetoothManager.getAdapter();
-        if (mAdapter == null) {
-            Log.w(TAG, "Default BT adapter not found");
-            return;
+        mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, null);
+        if (mLocalBluetoothManager != null) {
+            mLocalBluetoothManager.getEventManager().registerCallback(this);
+            onBluetoothStateChanged(
+                    mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
         }
-
-        mReceiver.register();
-        setAdapterState(mAdapter.getState());
-        updateBondedDevices();
-        bindAllProfiles();
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("BluetoothController state:");
-        pw.print("  mAdapter="); pw.println(mAdapter);
+        pw.print("  mLocalBluetoothManager="); pw.println(mLocalBluetoothManager);
         pw.print("  mEnabled="); pw.println(mEnabled);
         pw.print("  mConnecting="); pw.println(mConnecting);
         pw.print("  mLastDevice="); pw.println(mLastDevice);
         pw.print("  mCallbacks.size="); pw.println(mCallbacks.size());
-        pw.print("  mProfiles="); pw.println(profilesToString(mProfiles));
-        pw.print("  mDeviceInfo.size="); pw.println(mDeviceInfo.size());
-        for (int i = 0; i < mDeviceInfo.size(); i++) {
-            final BluetoothDevice device = mDeviceInfo.keyAt(i);
-            final DeviceInfo info = mDeviceInfo.valueAt(i);
-            pw.print("    "); pw.print(deviceToString(device));
-            pw.print('('); pw.print(uuidsToString(device)); pw.print(')');
-            pw.print("    "); pw.println(infoToString(info));
+        pw.println("  Bluetooth Devices:");
+        for (CachedBluetoothDevice device :
+                mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
+            pw.println("    " + getDeviceString(device));
         }
     }
 
-    private static String infoToString(DeviceInfo info) {
-        return info == null ? null : ("connectionState=" +
-                connectionStateToString(CONNECTION_STATES[info.connectionStateIndex])
-                + ",bonded=" + info.bonded + ",profiles="
-                + profilesToString(info.connectedProfiles));
-    }
-
-    private static String profilesToString(SparseArray<?> profiles) {
-        final int N = profiles.size();
-        final StringBuffer buffer = new StringBuffer();
-        buffer.append('[');
-        for (int i = 0; i < N; i++) {
-            if (i != 0) {
-                buffer.append(',');
-            }
-            buffer.append(BluetoothUtil.profileToString(profiles.keyAt(i)));
-        }
-        buffer.append(']');
-        return buffer.toString();
+    private String getDeviceString(CachedBluetoothDevice device) {
+        return device.getName() + " " + device.getBondState() + " " + device.isConnected();
     }
 
     public void addStateChangedCallback(Callback cb) {
@@ -162,266 +81,63 @@
 
     @Override
     public boolean isBluetoothEnabled() {
-        return mAdapter != null && mAdapter.isEnabled();
+        return mEnabled;
     }
 
     @Override
     public boolean isBluetoothConnected() {
-        return mAdapter != null
-                && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED;
+        return mLocalBluetoothManager != null
+                && mLocalBluetoothManager.getBluetoothAdapter().getConnectionState()
+                == BluetoothAdapter.STATE_CONNECTED;
     }
 
     @Override
     public boolean isBluetoothConnecting() {
-        return mAdapter != null
-                && mAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTING;
+        return mConnecting;
     }
 
     @Override
     public void setBluetoothEnabled(boolean enabled) {
-        if (mAdapter != null) {
-            if (enabled) {
-                mAdapter.enable();
-            } else {
-                mAdapter.disable();
-            }
+        if (mLocalBluetoothManager != null) {
+            mLocalBluetoothManager.getBluetoothAdapter().setBluetoothEnabled(enabled);
         }
     }
 
     @Override
     public boolean isBluetoothSupported() {
-        return mAdapter != null;
+        return mLocalBluetoothManager != null;
     }
 
     @Override
-    public ArraySet<PairedDevice> getPairedDevices() {
-        final ArraySet<PairedDevice> rt = new ArraySet<>();
-        for (int i = 0; i < mDeviceInfo.size(); i++) {
-            final BluetoothDevice device = mDeviceInfo.keyAt(i);
-            final DeviceInfo info = mDeviceInfo.valueAt(i);
-            if (!info.bonded) continue;
-            final PairedDevice paired = new PairedDevice();
-            paired.id = device.getAddress();
-            paired.tag = device;
-            paired.name = device.getAliasName();
-            paired.state = connectionStateToPairedDeviceState(info.connectionStateIndex);
-            rt.add(paired);
-        }
-        return rt;
-    }
-
-    private static int connectionStateToPairedDeviceState(int index) {
-        int state = CONNECTION_STATES[index];
-        if (state == BluetoothAdapter.STATE_CONNECTED) return PairedDevice.STATE_CONNECTED;
-        if (state == BluetoothAdapter.STATE_CONNECTING) return PairedDevice.STATE_CONNECTING;
-        if (state == BluetoothAdapter.STATE_DISCONNECTING) return PairedDevice.STATE_DISCONNECTING;
-        return PairedDevice.STATE_DISCONNECTED;
+    public void connect(final CachedBluetoothDevice device) {
+        if (mLocalBluetoothManager == null || device == null) return;
+        device.connect(true);
     }
 
     @Override
-    public void connect(final PairedDevice pd) {
-        connect(pd, true);
-    }
-
-    @Override
-    public void disconnect(PairedDevice pd) {
-        connect(pd, false);
-    }
-
-    private void connect(PairedDevice pd, final boolean connect) {
-        if (mAdapter == null || pd == null || pd.tag == null) return;
-        final BluetoothDevice device = (BluetoothDevice) pd.tag;
-        final DeviceInfo info = mDeviceInfo.get(device);
-        final String action = connect ? "connect" : "disconnect";
-        if (DEBUG) Log.d(TAG, action + " " + deviceToString(device));
-        final ParcelUuid[] uuids = device.getUuids();
-        if (uuids == null) {
-            Log.w(TAG, "No uuids returned, aborting " + action + " for " + deviceToString(device));
-            return;
-        }
-        SparseArray<Boolean> profiles = new SparseArray<>();
-        if (connect) {
-            // When connecting add every profile we can recognize by uuid.
-            for (ParcelUuid uuid : uuids) {
-                final int profile = uuidToProfile(uuid);
-                if (profile == 0) {
-                    Log.w(TAG, "Device " + deviceToString(device) + " has an unsupported uuid: "
-                            + uuidToString(uuid));
-                    continue;
-                }
-                final boolean connected = info.connectedProfiles.get(profile, false);
-                if (!connected) {
-                    profiles.put(profile, true);
-                }
-            }
-        } else {
-            // When disconnecting, just add every profile we know they are connected to.
-            profiles = info.connectedProfiles;
-        }
-        for (int i = 0; i < profiles.size(); i++) {
-            final int profile = profiles.keyAt(i);
-            if (mProfiles.indexOfKey(profile) >= 0) {
-                final Profile p = BluetoothUtil.getProfile(mProfiles.get(profile));
-                final boolean ok = connect ? p.connect(device) : p.disconnect(device);
-                if (DEBUG) Log.d(TAG, action + " " + profileToString(profile) + " "
-                        + (ok ? "succeeded" : "failed"));
-            } else {
-                Log.w(TAG, "Unable get get Profile for " + profileToString(profile));
-            }
-        }
+    public void disconnect(CachedBluetoothDevice device) {
+        if (mLocalBluetoothManager == null || device == null) return;
+        device.disconnect();
     }
 
     @Override
     public String getLastDeviceName() {
-        return mLastDevice != null ? mLastDevice.getAliasName() : null;
+        return mLastDevice != null ? mLastDevice.getName() : null;
     }
 
-    private void updateBondedDevices() {
-        mHandler.removeMessages(MSG_UPDATE_BONDED_DEVICES);
-        mHandler.sendEmptyMessage(MSG_UPDATE_BONDED_DEVICES);
-    }
-
-    private void updateConnectionStates() {
-        mHandler.removeMessages(MSG_UPDATE_CONNECTION_STATES);
-        mHandler.removeMessages(MSG_UPDATE_SINGLE_CONNECTION_STATE);
-        mHandler.sendEmptyMessage(MSG_UPDATE_CONNECTION_STATES);
-    }
-
-    private void updateConnectionState(BluetoothDevice device, int profile, int state) {
-        if (mHandler.hasMessages(MSG_UPDATE_CONNECTION_STATES)) {
-            // If we are about to update all the devices, then we don't need to update this one.
-            return;
-        }
-        mHandler.obtainMessage(MSG_UPDATE_SINGLE_CONNECTION_STATE, profile, state, device)
-                .sendToTarget();
-    }
-
-    private void handleUpdateBondedDevices() {
-        if (mAdapter == null) return;
-        final Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
-        for (DeviceInfo info : mDeviceInfo.values()) {
-            info.bonded = false;
-        }
-        int bondedCount = 0;
-        BluetoothDevice lastBonded = null;
-        if (bondedDevices != null) {
-            for (BluetoothDevice bondedDevice : bondedDevices) {
-                final boolean bonded = bondedDevice.getBondState() != BluetoothDevice.BOND_NONE;
-                updateInfo(bondedDevice).bonded = bonded;
-                if (bonded) {
-                    bondedCount++;
-                    lastBonded = bondedDevice;
-                }
-            }
-        }
-        if (mLastDevice == null && bondedCount == 1) {
-            mLastDevice = lastBonded;
-        }
-        updateConnectionStates();
-        firePairedDevicesChanged();
-    }
-
-    private void handleUpdateConnectionStates() {
-        final int N = mDeviceInfo.size();
-        for (int i = 0; i < N; i++) {
-            BluetoothDevice device = mDeviceInfo.keyAt(i);
-            DeviceInfo info = updateInfo(device);
-            info.connectionStateIndex = 0;
-            info.connectedProfiles.clear();
-            for (int j = 0; j < mProfiles.size(); j++) {
-                int state = mProfiles.valueAt(j).getConnectionState(device);
-                handleUpdateConnectionState(device, mProfiles.keyAt(j), state);
-            }
-        }
-        handleConnectionChange();
-        firePairedDevicesChanged();
-    }
-
-    private void handleUpdateConnectionState(BluetoothDevice device, int profile, int state) {
-        if (DEBUG) Log.d(TAG, "updateConnectionState " + BluetoothUtil.deviceToString(device)
-                + " " + BluetoothUtil.profileToString(profile)
-                + " " + BluetoothUtil.connectionStateToString(state));
-        DeviceInfo info = updateInfo(device);
-        int stateIndex = 0;
-        for (int i = 0; i < CONNECTION_STATES.length; i++) {
-            if (CONNECTION_STATES[i] == state) {
-                stateIndex = i;
-                break;
-            }
-        }
-        info.profileStates.put(profile, stateIndex);
-
-        info.connectionStateIndex = 0;
-        final int N = info.profileStates.size();
-        for (int i = 0; i < N; i++) {
-            if (info.profileStates.valueAt(i) > info.connectionStateIndex) {
-                info.connectionStateIndex = info.profileStates.valueAt(i);
-            }
-        }
-        if (state == BluetoothProfile.STATE_CONNECTED) {
-            info.connectedProfiles.put(profile, true);
-        } else {
-            info.connectedProfiles.remove(profile);
-        }
-    }
-
-    private void handleConnectionChange() {
-        // If we are no longer connected to the current device, see if we are connected to
-        // something else, so we don't display a name we aren't connected to.
-        if (mLastDevice != null &&
-                CONNECTION_STATES[mDeviceInfo.get(mLastDevice).connectionStateIndex]
-                        != BluetoothProfile.STATE_CONNECTED) {
-            // Make sure we don't keep this device while it isn't connected.
-            mLastDevice = null;
-            // Look for anything else connected.
-            final int size = mDeviceInfo.size();
-            for (int i = 0; i < size; i++) {
-                BluetoothDevice device = mDeviceInfo.keyAt(i);
-                DeviceInfo info = mDeviceInfo.valueAt(i);
-                if (CONNECTION_STATES[info.connectionStateIndex]
-                        == BluetoothProfile.STATE_CONNECTED) {
-                    mLastDevice = device;
-                    break;
-                }
-            }
-        }
-    }
-
-    private void bindAllProfiles() {
-        // Note: This needs to contain all of the types that can be returned by BluetoothUtil
-        // otherwise we can't find the profiles we need when we connect/disconnect.
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP_SINK);
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.AVRCP_CONTROLLER);
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET);
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEADSET_CLIENT);
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.INPUT_DEVICE);
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.MAP);
-        mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.PAN);
-        // Note Health is not in this list because health devices aren't 'connected'.
-        // If profiles are expanded to use more than just connection state and connect/disconnect
-        // then it should be added.
+    @Override
+    public Collection<CachedBluetoothDevice> getDevices() {
+        return mLocalBluetoothManager != null
+                ? mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()
+                : null;
     }
 
     private void firePairedDevicesChanged() {
         for (Callback cb : mCallbacks) {
-            cb.onBluetoothPairedDevicesChanged();
+            cb.onBluetoothDevicesChanged();
         }
     }
 
-    private void setAdapterState(int adapterState) {
-        final boolean enabled = adapterState == BluetoothAdapter.STATE_ON;
-        if (mEnabled == enabled) return;
-        mEnabled = enabled;
-        fireStateChange();
-    }
-
-    private void setConnecting(boolean connecting) {
-        if (mConnecting == connecting) return;
-        mConnecting = connecting;
-        fireStateChange();
-    }
-
     private void fireStateChange() {
         for (Callback cb : mCallbacks) {
             fireStateChange(cb);
@@ -432,141 +148,59 @@
         cb.onBluetoothStateChange(mEnabled, mConnecting);
     }
 
-    private static int getProfileFromAction(String action) {
-        if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-            return BluetoothProfile.A2DP;
-        } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-            return BluetoothProfile.HEADSET;
-        } else if (BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-            return BluetoothProfile.A2DP_SINK;
-        } else if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-            return BluetoothProfile.HEADSET_CLIENT;
-        } else if (BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-            return BluetoothProfile.INPUT_DEVICE;
-        } else if (BluetoothMap.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-            return BluetoothProfile.MAP;
-        } else if (BluetoothPan.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
-            return BluetoothProfile.PAN;
+    private void updateConnected() {
+        if (mLastDevice != null && mLastDevice.isConnected()) {
+            // Our current device is still valid.
+            return;
         }
-        if (DEBUG) Log.d(TAG, "Unknown action " + action);
-        return -1;
-    }
-
-    private final ServiceListener mProfileListener = new ServiceListener() {
-        @Override
-        public void onServiceDisconnected(int profile) {
-            if (DEBUG) Log.d(TAG, "Disconnected from " + BluetoothUtil.profileToString(profile));
-            // We lost a profile, don't do any updates until it gets removed.
-            mHandler.removeMessages(MSG_UPDATE_CONNECTION_STATES);
-            mHandler.removeMessages(MSG_UPDATE_SINGLE_CONNECTION_STATE);
-            mHandler.obtainMessage(MSG_REM_PROFILE, profile, 0).sendToTarget();
-        }
-
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (DEBUG) Log.d(TAG, "Connected to " + BluetoothUtil.profileToString(profile));
-            mHandler.obtainMessage(MSG_ADD_PROFILE, profile, 0, proxy).sendToTarget();
-        }
-    };
-
-    private final class Receiver extends BroadcastReceiver {
-        public void register() {
-            final IntentFilter filter = new IntentFilter();
-            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-            filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
-            filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
-            filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED);
-            filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-            filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-            filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
-            filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
-            filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
-            filter.addAction(BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
-            filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
-            mContext.registerReceiver(this, filter);
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
-            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
-                setAdapterState(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, ERROR));
-                updateBondedDevices();
-                if (DEBUG) Log.d(TAG, "ACTION_STATE_CHANGED " + mEnabled);
-            } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
-                updateInfo(device);
-                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
-                        ERROR);
+        for (CachedBluetoothDevice device : getDevices()) {
+            if (device.isConnected()) {
                 mLastDevice = device;
-                if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED "
-                        + connectionStateToString(state) + " " + deviceToString(device));
-                setConnecting(state == BluetoothAdapter.STATE_CONNECTING);
-            } else if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) {
-                updateInfo(device);
-                mLastDevice = device;
-            } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
-                if (DEBUG) Log.d(TAG, "ACTION_BOND_STATE_CHANGED " + device);
-                updateBondedDevices();
-            } else {
-                int profile = getProfileFromAction(intent.getAction());
-                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-                if (DEBUG) Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGE "
-                        + BluetoothUtil.profileToString(profile)
-                        + " " + BluetoothUtil.connectionStateToString(state));
-                if ((profile != -1) && (state != -1)) {
-                    updateConnectionState(device, profile, state);
-                }
             }
         }
     }
 
-    private DeviceInfo updateInfo(BluetoothDevice device) {
-        DeviceInfo info = mDeviceInfo.get(device);
-        info = info != null ? info : new DeviceInfo();
-        mDeviceInfo.put(device, info);
-        return info;
+    @Override
+    public void onBluetoothStateChanged(int bluetoothState) {
+        mEnabled = bluetoothState == BluetoothAdapter.STATE_ON;
+        fireStateChange();
     }
 
-    private class H extends Handler {
-        public H(Looper l) {
-            super(l);
-        }
+    @Override
+    public void onScanningStateChanged(boolean started) {
+        // Don't care.
+    }
 
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UPDATE_CONNECTION_STATES:
-                    handleUpdateConnectionStates();
-                    firePairedDevicesChanged();
-                    break;
-                case MSG_UPDATE_SINGLE_CONNECTION_STATE:
-                    handleUpdateConnectionState((BluetoothDevice) msg.obj, msg.arg1, msg.arg2);
-                    handleConnectionChange();
-                    firePairedDevicesChanged();
-                    break;
-                case MSG_UPDATE_BONDED_DEVICES:
-                    handleUpdateBondedDevices();
-                    firePairedDevicesChanged();
-                    break;
-                case MSG_ADD_PROFILE:
-                    mProfiles.put(msg.arg1, (BluetoothProfile) msg.obj);
-                    handleUpdateConnectionStates();
-                    firePairedDevicesChanged();
-                    break;
-                case MSG_REM_PROFILE:
-                    mProfiles.remove(msg.arg1);
-                    handleUpdateConnectionStates();
-                    firePairedDevicesChanged();
-                    break;
-            }
-        };
-    };
+    @Override
+    public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
+        cachedDevice.registerCallback(this);
+        updateConnected();
+        firePairedDevicesChanged();
+    }
 
-    private static class DeviceInfo {
-        int connectionStateIndex = 0;
-        boolean bonded;  // per getBondedDevices
-        SparseArray<Boolean> connectedProfiles = new SparseArray<>();
-        SparseArray<Integer> profileStates = new SparseArray<>();
+    @Override
+    public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
+        updateConnected();
+        firePairedDevicesChanged();
+    }
+
+    @Override
+    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+        updateConnected();
+        firePairedDevicesChanged();
+    }
+
+    @Override
+    public void onDeviceAttributesChanged() {
+        updateConnected();
+        firePairedDevicesChanged();
+    }
+
+    @Override
+    public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+        mConnecting = state == BluetoothAdapter.STATE_CONNECTING;
+        mLastDevice = cachedDevice;
+        updateConnected();
+        fireStateChange();
     }
 }