From 061763140833d578d9f1e62d03b9d161ae6cd319 Mon Sep 17 00:00:00 2001 From: tom hsu Date: Thu, 11 May 2023 02:23:06 +0800 Subject: [PATCH] [Settings] Avoid NPE if BT device is changed by framework. - Do not register only one BT device for primary to avoid primary BT devcie change to another. - Register and unregister all BT devices Bug: 280236099 Test: atest passed Change-Id: I610144c7f8f649e40d99cf1dc7f50d1f3b80f109 --- ...ancedBluetoothDetailsHeaderController.java | 51 +++++++++++++---- ...dBluetoothDetailsHeaderControllerTest.java | 57 ++++++++++++++----- 2 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java index 07e9633652f..5f1c42259bc 100644 --- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java @@ -57,7 +57,9 @@ import com.android.settingslib.widget.LayoutPreference; import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; /** @@ -92,6 +94,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont @VisibleForTesting final Map mIconCache; private CachedBluetoothDevice mCachedDevice; + private Set mBluetoothDevices; @VisibleForTesting BluetoothAdapter mBluetoothAdapter; @VisibleForTesting @@ -142,23 +145,13 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont if (!isAvailable()) { return; } - mIsRegisterCallback = true; - mCachedDevice.registerCallback(this); - mBluetoothAdapter.addOnMetadataChangedListener(mCachedDevice.getDevice(), - mContext.getMainExecutor(), mMetadataListener); - + registerBluetoothDevice(); refresh(); } @Override public void onStop() { - if (!mIsRegisterCallback) { - return; - } - mCachedDevice.unregisterCallback(this); - mBluetoothAdapter.removeOnMetadataChangedListener(mCachedDevice.getDevice(), - mMetadataListener); - mIsRegisterCallback = false; + unRegisterBluetoothDevice(); } @Override @@ -176,6 +169,40 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont mCachedDevice = cachedBluetoothDevice; } + private void registerBluetoothDevice() { + if (mBluetoothDevices == null) { + mBluetoothDevices = new HashSet<>(); + } + mBluetoothDevices.clear(); + if (mCachedDevice.getDevice() != null) { + mBluetoothDevices.add(mCachedDevice.getDevice()); + } + mCachedDevice.getMemberDevice().forEach(cbd -> { + if (cbd != null) { + mBluetoothDevices.add(cbd.getDevice()); + } + }); + if (mBluetoothDevices.isEmpty()) { + Log.d(TAG, "No BT devcie to register."); + return; + } + mCachedDevice.registerCallback(this); + mBluetoothDevices.forEach(bd -> + mBluetoothAdapter.addOnMetadataChangedListener(bd, + mContext.getMainExecutor(), mMetadataListener)); + } + + private void unRegisterBluetoothDevice() { + if (mBluetoothDevices == null || mBluetoothDevices.isEmpty()) { + Log.d(TAG, "No BT devcie to unregister."); + return; + } + mCachedDevice.unregisterCallback(this); + mBluetoothDevices.forEach(bd -> mBluetoothAdapter.removeOnMetadataChangedListener(bd, + mMetadataListener)); + mBluetoothDevices.clear(); + } + @VisibleForTesting void refresh() { if (mLayoutPreference != null && mCachedDevice != null) { diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java index e334af545cf..2c9fb9975e4 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java @@ -56,6 +56,9 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import java.util.HashSet; +import java.util.Set; + @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class}) public class AdvancedBluetoothDetailsHeaderControllerTest { @@ -380,40 +383,68 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("true".getBytes()); + Set cacheBluetoothDevices = new HashSet<>(); + when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); mController.onStart(); + verify(mCachedDevice).registerCallback(mController); verify(mBluetoothAdapter).addOnMetadataChangedListener(mBluetoothDevice, mContext.getMainExecutor(), mController.mMetadataListener); } @Test - public void onStop_isRegisterCallback_unregisterCallback() { - mController.mIsRegisterCallback = true; - - mController.onStop(); - - verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice, - mController.mMetadataListener); - } - - @Test - public void onStart_notAvailable_registerCallback() { + public void onStart_notAvailable_notNeedToRegisterCallback() { when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("false".getBytes()); mController.onStart(); + verify(mCachedDevice, never()).registerCallback(mController); verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice, mContext.getMainExecutor(), mController.mMetadataListener); } @Test - public void onStop_notRegisterCallback_unregisterCallback() { - mController.mIsRegisterCallback = false; + public void onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + when(mCachedDevice.getDevice()).thenReturn(null); + Set cacheBluetoothDevices = new HashSet<>(); + when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); + + mController.onStart(); + + verify(mCachedDevice, never()).registerCallback(mController); + verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice, + mContext.getMainExecutor(), mController.mMetadataListener); + } + + @Test + public void onStop_availableAndHasBluetoothDevice_unregisterCallback() { + onStart_isAvailable_registerCallback(); mController.onStop(); + verify(mCachedDevice).unregisterCallback(mController); + verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice, + mController.mMetadataListener); + } + + @Test + public void onStop_noBluetoothDevice_noNeedToUnregisterCallback() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + when(mCachedDevice.getDevice()).thenReturn(null); + + mController.onStart(); + mController.onStop(); + + verify(mCachedDevice, never()).unregisterCallback(mController); verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(mBluetoothDevice, mController.mMetadataListener); }