From 2948963f31a9113ad8f325b06f81ad85520fc67a Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Tue, 30 Oct 2018 11:27:30 -0700 Subject: [PATCH] Add ACL broadcast for bt devices When we receive profile state change, the device may still be physically connected(e.g. BluetoothDevice.isConnected() return true). In this case UI still show a device is connected. Add ACL broadcast listener which will be triggered when device is physically disconnected. In this callback we can refresh UI to make it show correct information. Bug: 79947085 Test: RunSettingsLibRoboTests Change-Id: I8370cc568fcb0d5995f164e7e62391a9525cbe19 --- .../bluetooth/BluetoothCallback.java | 13 +++++++ .../bluetooth/BluetoothEventManager.java | 39 +++++++++++++++++++ .../bluetooth/BluetoothEventManagerTest.java | 30 ++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java index a842229090725..3152e65d5a366 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java @@ -127,4 +127,17 @@ public interface BluetoothCallback { default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state, int bluetoothProfile) { } + + /** + * Called when ACL connection state is changed. It listens to + * {@link android.bluetooth.BluetoothDevice#ACTION_ACL_CONNECTED} and {@link + * android.bluetooth.BluetoothDevice#ACTION_ACL_DISCONNECTED} + * + * @param cachedDevice Bluetooth device that changed + * @param state the Bluetooth device connection state, the possible values are: + * {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTED}, + * {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED} + */ + default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 022bf6915886a..2b7babd06b47f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -119,6 +119,10 @@ public class BluetoothEventManager { addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED, new AudioModeChangedHandler()); + // ACL connection changed broadcasts + addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler()); + addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler()); + registerAdapterIntentReceiver(); } @@ -236,6 +240,15 @@ public class BluetoothEventManager { } } + private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice, + int state) { + synchronized (mCallbacks) { + for (BluetoothCallback callback : mCallbacks) { + callback.onAclConnectionStateChanged(activeDevice, state); + } + } + } + @VisibleForTesting void addHandler(String action, Handler handler) { mHandlerMap.put(action, handler); @@ -447,6 +460,32 @@ public class BluetoothEventManager { } } + private class AclStateChangedHandler implements Handler { + @Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + final String action = intent.getAction(); + if (action == null) { + Log.w(TAG, "AclStateChangedHandler: action is null"); + return; + } + final CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device); + final int state; + switch (action) { + case BluetoothDevice.ACTION_ACL_CONNECTED: + state = BluetoothAdapter.STATE_CONNECTED; + break; + case BluetoothDevice.ACTION_ACL_DISCONNECTED: + state = BluetoothAdapter.STATE_DISCONNECTED; + break; + default: + Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action); + return; + + } + dispatchAclStateChanged(activeDevice, state); + } + } + private class AudioModeChangedHandler implements Handler { @Override diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java index 020234c6e12ff..c147d5e306c2d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java @@ -19,7 +19,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; @@ -50,6 +53,8 @@ public class BluetoothEventManagerTest { private BluetoothCallback mBluetoothCallback; @Mock private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; private Context mContext; private Intent mIntent; @@ -62,6 +67,7 @@ public class BluetoothEventManagerTest { mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); } @Test @@ -126,4 +132,28 @@ public class BluetoothEventManagerTest { verify(mBluetoothCallback).onProfileConnectionStateChanged(mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP); } + + @Test + public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() { + mBluetoothEventManager.registerCallback(mBluetoothCallback); + mIntent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + + mContext.sendBroadcast(mIntent); + + verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice, + BluetoothAdapter.STATE_DISCONNECTED); + } + + @Test + public void dispatchAclConnectionStateChanged_aclConnected_shouldDispatchCallback() { + mBluetoothEventManager.registerCallback(mBluetoothCallback); + mIntent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + + mContext.sendBroadcast(mIntent); + + verify(mBluetoothCallback).onAclConnectionStateChanged(mCachedBluetoothDevice, + BluetoothAdapter.STATE_CONNECTED); + } }