[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
This commit is contained in:
tom hsu
2023-05-11 02:23:06 +08:00
parent d3897afda6
commit 0617631408
2 changed files with 83 additions and 25 deletions

View File

@@ -57,7 +57,9 @@ import com.android.settingslib.widget.LayoutPreference;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@@ -92,6 +94,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
@VisibleForTesting @VisibleForTesting
final Map<String, Bitmap> mIconCache; final Map<String, Bitmap> mIconCache;
private CachedBluetoothDevice mCachedDevice; private CachedBluetoothDevice mCachedDevice;
private Set<BluetoothDevice> mBluetoothDevices;
@VisibleForTesting @VisibleForTesting
BluetoothAdapter mBluetoothAdapter; BluetoothAdapter mBluetoothAdapter;
@VisibleForTesting @VisibleForTesting
@@ -142,23 +145,13 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
if (!isAvailable()) { if (!isAvailable()) {
return; return;
} }
mIsRegisterCallback = true; registerBluetoothDevice();
mCachedDevice.registerCallback(this);
mBluetoothAdapter.addOnMetadataChangedListener(mCachedDevice.getDevice(),
mContext.getMainExecutor(), mMetadataListener);
refresh(); refresh();
} }
@Override @Override
public void onStop() { public void onStop() {
if (!mIsRegisterCallback) { unRegisterBluetoothDevice();
return;
}
mCachedDevice.unregisterCallback(this);
mBluetoothAdapter.removeOnMetadataChangedListener(mCachedDevice.getDevice(),
mMetadataListener);
mIsRegisterCallback = false;
} }
@Override @Override
@@ -176,6 +169,40 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
mCachedDevice = cachedBluetoothDevice; 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 @VisibleForTesting
void refresh() { void refresh() {
if (mLayoutPreference != null && mCachedDevice != null) { if (mLayoutPreference != null && mCachedDevice != null) {

View File

@@ -56,6 +56,9 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.util.HashSet;
import java.util.Set;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class}) @Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class})
public class AdvancedBluetoothDetailsHeaderControllerTest { public class AdvancedBluetoothDetailsHeaderControllerTest {
@@ -380,40 +383,68 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes()); .thenReturn("true".getBytes());
Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices);
mController.onStart(); mController.onStart();
verify(mCachedDevice).registerCallback(mController);
verify(mBluetoothAdapter).addOnMetadataChangedListener(mBluetoothDevice, verify(mBluetoothAdapter).addOnMetadataChangedListener(mBluetoothDevice,
mContext.getMainExecutor(), mController.mMetadataListener); mContext.getMainExecutor(), mController.mMetadataListener);
} }
@Test @Test
public void onStop_isRegisterCallback_unregisterCallback() { public void onStart_notAvailable_notNeedToRegisterCallback() {
mController.mIsRegisterCallback = true;
mController.onStop();
verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice,
mController.mMetadataListener);
}
@Test
public void onStart_notAvailable_registerCallback() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("false".getBytes()); .thenReturn("false".getBytes());
mController.onStart(); mController.onStart();
verify(mCachedDevice, never()).registerCallback(mController);
verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice, verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice,
mContext.getMainExecutor(), mController.mMetadataListener); mContext.getMainExecutor(), mController.mMetadataListener);
} }
@Test @Test
public void onStop_notRegisterCallback_unregisterCallback() { public void onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback() {
mController.mIsRegisterCallback = false; 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<CachedBluetoothDevice> 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(); 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, verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(mBluetoothDevice,
mController.mMetadataListener); mController.mMetadataListener);
} }