Fix sysui depending on slow bt calls

Move calls to a background thread and cache the results to avoid
slowing down sysui when there are too many bluetooth devices.

Test: runtest systemui
Change-Id: Ica10e86ca6b2783c30ed9de68f1a91dc5cfdf068
Fixes: 38118549
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 36d24b3..dc4b115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -18,6 +18,8 @@
 
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -33,8 +35,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.WeakHashMap;
 
 public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
         CachedBluetoothDevice.Callback {
@@ -44,18 +48,22 @@
     private final LocalBluetoothManager mLocalBluetoothManager;
     private final UserManager mUserManager;
     private final int mCurrentUser;
+    private final WeakHashMap<CachedBluetoothDevice, ActuallyCachedState> mCachedState =
+            new WeakHashMap<>();
+    private final Handler mBgHandler;
 
     private boolean mEnabled;
     private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
     private CachedBluetoothDevice mLastDevice;
 
-    private final H mHandler = new H();
+    private final H mHandler = new H(Looper.getMainLooper());
     private int mState;
 
     public BluetoothControllerImpl(Context context, Looper bgLooper) {
         mLocalBluetoothManager = Dependency.get(LocalBluetoothManager.class);
+        mBgHandler = new Handler(bgLooper);
         if (mLocalBluetoothManager != null) {
-            mLocalBluetoothManager.getEventManager().setReceiverHandler(new Handler(bgLooper));
+            mLocalBluetoothManager.getEventManager().setReceiverHandler(mBgHandler);
             mLocalBluetoothManager.getEventManager().registerCallback(this);
             onBluetoothStateChanged(
                     mLocalBluetoothManager.getBluetoothAdapter().getBluetoothState());
@@ -106,6 +114,16 @@
     }
 
     @Override
+    public int getBondState(CachedBluetoothDevice device) {
+        return getCachedState(device).mBondState;
+    }
+
+    @Override
+    public int getMaxConnectionState(CachedBluetoothDevice device) {
+        return getCachedState(device).mMaxConnectionState;
+    }
+
+    @Override
     public void addCallback(Callback cb) {
         mHandler.obtainMessage(H.MSG_ADD_CALLBACK, cb).sendToTarget();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
@@ -225,12 +243,14 @@
 
     @Override
     public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
+        mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
     }
 
     @Override
     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+        mCachedState.remove(cachedDevice);
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
     }
@@ -243,11 +263,44 @@
 
     @Override
     public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+        mCachedState.remove(cachedDevice);
         mLastDevice = cachedDevice;
         updateConnected();
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
+    private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
+        ActuallyCachedState state = mCachedState.get(device);
+        if (state == null) {
+            state = new ActuallyCachedState(device, mHandler);
+            mBgHandler.post(state);
+            mCachedState.put(device, state);
+            return state;
+        }
+        return state;
+    }
+
+    private static class ActuallyCachedState implements Runnable {
+
+        private final WeakReference<CachedBluetoothDevice> mDevice;
+        private final Handler mUiHandler;
+        private int mBondState = BluetoothDevice.BOND_NONE;
+        private int mMaxConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+
+        private ActuallyCachedState(CachedBluetoothDevice device, Handler uiHandler) {
+            mDevice = new WeakReference<>(device);
+            mUiHandler = uiHandler;
+        }
+
+        @Override
+        public void run() {
+            mBondState = mDevice.get().getBondState();
+            mMaxConnectionState = mDevice.get().getMaxConnectionState();
+            mUiHandler.removeMessages(H.MSG_PAIRED_DEVICES_CHANGED);
+            mUiHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
+        }
+    }
+
     private final class H extends Handler {
         private final ArrayList<BluetoothController.Callback> mCallbacks = new ArrayList<>();
 
@@ -256,6 +309,10 @@
         private static final int MSG_ADD_CALLBACK = 3;
         private static final int MSG_REMOVE_CALLBACK = 4;
 
+        public H(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {