Use FastPair battery level in blueooth QS tile.

If FastPair battery level is not available, this will fallback
to the regular battery level as provided by the bluetooth stack.

Bug: 266467369
Test: BluetoothTileTest
Change-Id: I97e2b80b2c39f5c622fe15a93b5dd4071952f4e5
This commit is contained in:
Vania Januar
2023-01-26 11:17:08 +00:00
parent f2c1506ea0
commit 50bbe7cd1e
6 changed files with 308 additions and 69 deletions

View File

@@ -18,24 +18,26 @@ package com.android.systemui.qs.tiles;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Switch;
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
@@ -50,6 +52,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -60,8 +63,14 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
private static final String TAG = BluetoothTile.class.getSimpleName();
private final BluetoothController mController;
private CachedBluetoothDevice mMetadataRegisteredDevice = null;
private final Executor mExecutor;
@Inject
public BluetoothTile(
QSHost host,
@@ -78,6 +87,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
statusBarStateController, activityStarter, qsLogger);
mController = bluetoothController;
mController.observe(getLifecycle(), mCallback);
mExecutor = new HandlerExecutor(mainHandler);
}
@Override
@@ -116,6 +126,15 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
return mContext.getString(R.string.quick_settings_bluetooth_label);
}
@Override
protected void handleSetListening(boolean listening) {
super.handleSetListening(listening);
if (!listening) {
stopListeningToStaleDeviceMetadata();
}
}
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH);
@@ -125,6 +144,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
final boolean connecting = mController.isBluetoothConnecting();
state.isTransient = transientEnabling || connecting ||
mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
if (!enabled || !connected || state.isTransient) {
stopListeningToStaleDeviceMetadata();
}
state.dualTarget = true;
state.value = enabled;
if (state.slash == null) {
@@ -187,23 +209,32 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices();
if (enabled && connected && !connectedDevices.isEmpty()) {
if (connectedDevices.size() > 1) {
stopListeningToStaleDeviceMetadata();
return icuMessageFormat(mContext.getResources(),
R.string.quick_settings_hotspot_secondary_label_num_devices,
connectedDevices.size());
}
CachedBluetoothDevice lastDevice = connectedDevices.get(0);
final int batteryLevel = lastDevice.getBatteryLevel();
CachedBluetoothDevice device = connectedDevices.get(0);
// Use battery level provided by FastPair metadata if available.
// If not, fallback to the default battery level from bluetooth.
int batteryLevel = getMetadataBatteryLevel(device);
if (batteryLevel > BluetoothUtils.META_INT_ERROR) {
listenToMetadata(device);
} else {
stopListeningToStaleDeviceMetadata();
batteryLevel = device.getBatteryLevel();
}
if (batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
return mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level,
Utils.formatPercentage(batteryLevel));
} else {
final BluetoothClass bluetoothClass = lastDevice.getBtClass();
final BluetoothClass bluetoothClass = device.getBtClass();
if (bluetoothClass != null) {
if (lastDevice.isHearingAidDevice()) {
if (device.isHearingAidDevice()) {
return mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
} else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
@@ -233,6 +264,36 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
return mController.isBluetoothSupported();
}
private int getMetadataBatteryLevel(CachedBluetoothDevice device) {
return BluetoothUtils.getIntMetaData(device.getDevice(),
BluetoothDevice.METADATA_MAIN_BATTERY);
}
private void listenToMetadata(CachedBluetoothDevice cachedDevice) {
if (cachedDevice == mMetadataRegisteredDevice) return;
stopListeningToStaleDeviceMetadata();
try {
mController.addOnMetadataChangedListener(cachedDevice,
mExecutor,
mMetadataChangedListener);
mMetadataRegisteredDevice = cachedDevice;
} catch (IllegalArgumentException e) {
Log.e(TAG, "Battery metadata listener already registered for device.");
}
}
private void stopListeningToStaleDeviceMetadata() {
if (mMetadataRegisteredDevice == null) return;
try {
mController.removeOnMetadataChangedListener(
mMetadataRegisteredDevice,
mMetadataChangedListener);
mMetadataRegisteredDevice = null;
} catch (IllegalArgumentException e) {
Log.e(TAG, "Battery metadata listener already unregistered for device.");
}
}
private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
@Override
public void onBluetoothStateChange(boolean enabled) {
@@ -244,4 +305,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
refreshState();
}
};
private final BluetoothAdapter.OnMetadataChangedListener mMetadataChangedListener =
(device, key, value) -> {
if (key == BluetoothDevice.METADATA_MAIN_BATTERY) refreshState();
};
}

View File

@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.policy;
import android.bluetooth.BluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.BluetoothController.Callback;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
public interface BluetoothController extends CallbackController<Callback>, Dumpable {
boolean isBluetoothSupported();
@@ -44,6 +47,11 @@ public interface BluetoothController extends CallbackController<Callback>, Dumpa
int getBondState(CachedBluetoothDevice device);
List<CachedBluetoothDevice> getConnectedDevices();
void addOnMetadataChangedListener(CachedBluetoothDevice device, Executor executor,
BluetoothAdapter.OnMetadataChangedListener listener);
void removeOnMetadataChangedListener(CachedBluetoothDevice device,
BluetoothAdapter.OnMetadataChangedListener listener);
public interface Callback {
void onBluetoothStateChange(boolean enabled);
void onBluetoothDevicesChanged();

View File

@@ -48,6 +48,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -78,6 +79,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
private final H mHandler;
private int mState;
private final BluetoothAdapter mAdapter;
/**
*/
@Inject
@@ -88,7 +90,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
BluetoothLogger logger,
@Background Looper bgLooper,
@Main Looper mainLooper,
@Nullable LocalBluetoothManager localBluetoothManager) {
@Nullable LocalBluetoothManager localBluetoothManager,
@Nullable BluetoothAdapter bluetoothAdapter) {
mDumpManager = dumpManager;
mLogger = logger;
mLocalBluetoothManager = localBluetoothManager;
@@ -103,6 +106,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mCurrentUser = userTracker.getUserId();
mDumpManager.registerDumpable(TAG, this);
mAdapter = bluetoothAdapter;
}
@Override
@@ -412,6 +416,30 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
public void addOnMetadataChangedListener(
@NonNull CachedBluetoothDevice cachedDevice,
Executor executor,
BluetoothAdapter.OnMetadataChangedListener listener
) {
if (mAdapter == null) return;
mAdapter.addOnMetadataChangedListener(
cachedDevice.getDevice(),
executor,
listener
);
}
public void removeOnMetadataChangedListener(
@NonNull CachedBluetoothDevice cachedDevice,
BluetoothAdapter.OnMetadataChangedListener listener
) {
if (mAdapter == null) return;
mAdapter.removeOnMetadataChangedListener(
cachedDevice.getDevice(),
listener
);
}
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
ActuallyCachedState state = mCachedState.get(device);
if (state == null) {

View File

@@ -1,6 +1,6 @@
package com.android.systemui.qs.tiles
import android.content.Context
import android.bluetooth.BluetoothDevice
import android.os.Handler
import android.os.Looper
import android.os.UserManager
@@ -10,6 +10,8 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.settingslib.Utils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
@@ -21,14 +23,18 @@ import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -36,21 +42,13 @@ import org.mockito.MockitoAnnotations
@SmallTest
class BluetoothTileTest : SysuiTestCase() {
@Mock
private lateinit var mockContext: Context
@Mock
private lateinit var qsLogger: QSLogger
@Mock
private lateinit var qsHost: QSHost
@Mock
private lateinit var metricsLogger: MetricsLogger
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var qsHost: QSHost
@Mock private lateinit var metricsLogger: MetricsLogger
private val falsingManager = FalsingManagerFake()
@Mock
private lateinit var statusBarStateController: StatusBarStateController
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var bluetoothController: BluetoothController
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var bluetoothController: BluetoothController
private val uiEventLogger = UiEventLoggerFake()
private lateinit var testableLooper: TestableLooper
@@ -61,20 +59,21 @@ class BluetoothTileTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
Mockito.`when`(qsHost.context).thenReturn(mockContext)
Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
whenever(qsHost.context).thenReturn(mContext)
whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
tile = FakeBluetoothTile(
qsHost,
testableLooper.looper,
Handler(testableLooper.looper),
falsingManager,
metricsLogger,
statusBarStateController,
activityStarter,
qsLogger,
bluetoothController
)
tile =
FakeBluetoothTile(
qsHost,
testableLooper.looper,
Handler(testableLooper.looper),
falsingManager,
metricsLogger,
statusBarStateController,
activityStarter,
qsLogger,
bluetoothController,
)
tile.initialize()
testableLooper.processAllMessages()
@@ -102,7 +101,7 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.icon)
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
}
@Test
@@ -114,7 +113,7 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.icon)
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
}
@Test
@@ -126,7 +125,7 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.icon)
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on))
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on))
}
@Test
@@ -138,7 +137,76 @@ class BluetoothTileTest : SysuiTestCase() {
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.icon)
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search))
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search))
}
@Test
fun testSecondaryLabel_whenBatteryMetadataAvailable_isMetadataBatteryLevelState() {
val cachedDevice = mock<CachedBluetoothDevice>()
val state = QSTile.BooleanState()
listenToDeviceMetadata(state, cachedDevice, 50)
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.secondaryLabel)
.isEqualTo(
mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level,
Utils.formatPercentage(50)
)
)
verify(bluetoothController)
.addOnMetadataChangedListener(eq(cachedDevice), any(), any())
}
@Test
fun testSecondaryLabel_whenBatteryMetadataUnavailable_isBluetoothBatteryLevelState() {
val state = QSTile.BooleanState()
val cachedDevice = mock<CachedBluetoothDevice>()
listenToDeviceMetadata(state, cachedDevice, 50)
val cachedDevice2 = mock<CachedBluetoothDevice>()
val btDevice = mock<BluetoothDevice>()
whenever(cachedDevice2.device).thenReturn(btDevice)
whenever(btDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(null)
whenever(cachedDevice2.batteryLevel).thenReturn(25)
addConnectedDevice(cachedDevice2)
tile.handleUpdateState(state, /* arg= */ null)
assertThat(state.secondaryLabel)
.isEqualTo(
mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level,
Utils.formatPercentage(25)
)
)
verify(bluetoothController, times(1))
.removeOnMetadataChangedListener(eq(cachedDevice), any())
}
@Test
fun testMetadataListener_whenDisconnected_isUnregistered() {
val state = QSTile.BooleanState()
val cachedDevice = mock<CachedBluetoothDevice>()
listenToDeviceMetadata(state, cachedDevice, 50)
disableBluetooth()
tile.handleUpdateState(state, null)
verify(bluetoothController, times(1))
.removeOnMetadataChangedListener(eq(cachedDevice), any())
}
@Test
fun testMetadataListener_whenTileNotListening_isUnregistered() {
val state = QSTile.BooleanState()
val cachedDevice = mock<CachedBluetoothDevice>()
listenToDeviceMetadata(state, cachedDevice, 50)
tile.handleSetListening(false)
verify(bluetoothController, times(1))
.removeOnMetadataChangedListener(eq(cachedDevice), any())
}
private class FakeBluetoothTile(
@@ -150,18 +218,19 @@ class BluetoothTileTest : SysuiTestCase() {
statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter,
qsLogger: QSLogger,
bluetoothController: BluetoothController
) : BluetoothTile(
qsHost,
backgroundLooper,
mainHandler,
falsingManager,
metricsLogger,
statusBarStateController,
activityStarter,
qsLogger,
bluetoothController
) {
bluetoothController: BluetoothController,
) :
BluetoothTile(
qsHost,
backgroundLooper,
mainHandler,
falsingManager,
metricsLogger,
statusBarStateController,
activityStarter,
qsLogger,
bluetoothController,
) {
var restrictionChecked: String? = null
override fun checkIfRestrictionEnforcedByAdminOnly(
@@ -173,25 +242,44 @@ class BluetoothTileTest : SysuiTestCase() {
}
fun enableBluetooth() {
`when`(bluetoothController.isBluetoothEnabled).thenReturn(true)
whenever(bluetoothController.isBluetoothEnabled).thenReturn(true)
}
fun disableBluetooth() {
`when`(bluetoothController.isBluetoothEnabled).thenReturn(false)
whenever(bluetoothController.isBluetoothEnabled).thenReturn(false)
}
fun setBluetoothDisconnected() {
`when`(bluetoothController.isBluetoothConnecting).thenReturn(false)
`when`(bluetoothController.isBluetoothConnected).thenReturn(false)
whenever(bluetoothController.isBluetoothConnecting).thenReturn(false)
whenever(bluetoothController.isBluetoothConnected).thenReturn(false)
}
fun setBluetoothConnected() {
`when`(bluetoothController.isBluetoothConnecting).thenReturn(false)
`when`(bluetoothController.isBluetoothConnected).thenReturn(true)
whenever(bluetoothController.isBluetoothConnecting).thenReturn(false)
whenever(bluetoothController.isBluetoothConnected).thenReturn(true)
}
fun setBluetoothConnecting() {
`when`(bluetoothController.isBluetoothConnected).thenReturn(false)
`when`(bluetoothController.isBluetoothConnecting).thenReturn(true)
whenever(bluetoothController.isBluetoothConnected).thenReturn(false)
whenever(bluetoothController.isBluetoothConnecting).thenReturn(true)
}
fun addConnectedDevice(device: CachedBluetoothDevice) {
whenever(bluetoothController.connectedDevices).thenReturn(listOf(device))
}
fun listenToDeviceMetadata(
state: QSTile.BooleanState,
cachedDevice: CachedBluetoothDevice,
batteryLevel: Int
) {
val btDevice = mock<BluetoothDevice>()
whenever(cachedDevice.device).thenReturn(btDevice)
whenever(btDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY))
.thenReturn(batteryLevel.toString().toByteArray())
enableBluetooth()
setBluetoothConnected()
addConnectedDevice(cachedDevice)
tile.handleUpdateState(state, /* arg= */ null)
}
}

View File

@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,6 +45,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.bluetooth.BluetoothLogger;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -51,6 +54,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -60,10 +64,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
private UserTracker mUserTracker;
private LocalBluetoothManager mMockBluetoothManager;
private CachedBluetoothDeviceManager mMockDeviceManager;
private LocalBluetoothAdapter mMockAdapter;
private LocalBluetoothAdapter mMockLocalAdapter;
private TestableLooper mTestableLooper;
private DumpManager mMockDumpManager;
private BluetoothControllerImpl mBluetoothControllerImpl;
private BluetoothAdapter mMockAdapter;
private List<CachedBluetoothDevice> mDevices;
@@ -74,10 +79,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mDevices = new ArrayList<>();
mUserTracker = mock(UserTracker.class);
mMockDeviceManager = mock(CachedBluetoothDeviceManager.class);
mMockAdapter = mock(BluetoothAdapter.class);
when(mMockDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
when(mMockBluetoothManager.getCachedDeviceManager()).thenReturn(mMockDeviceManager);
mMockAdapter = mock(LocalBluetoothAdapter.class);
when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockAdapter);
mMockLocalAdapter = mock(LocalBluetoothAdapter.class);
when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockLocalAdapter);
when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class));
when(mMockBluetoothManager.getProfileManager())
.thenReturn(mock(LocalBluetoothProfileManager.class));
@@ -89,7 +95,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mock(BluetoothLogger.class),
mTestableLooper.getLooper(),
mTestableLooper.getLooper(),
mMockBluetoothManager);
mMockBluetoothManager,
mMockAdapter);
}
@Test
@@ -98,7 +105,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
when(device.isConnected()).thenReturn(true);
when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED);
mDevices.add(device);
when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
when(mMockLocalAdapter.getConnectionState())
.thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
mBluetoothControllerImpl.onConnectionStateChanged(null,
BluetoothAdapter.STATE_DISCONNECTED);
@@ -163,7 +171,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
@Test
public void testOnServiceConnected_updatesConnectionState() {
when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
when(mMockLocalAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
mBluetoothControllerImpl.onServiceConnected();
@@ -184,7 +192,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
@Test
public void testOnBluetoothStateChange_updatesConnectionState() {
when(mMockAdapter.getConnectionState()).thenReturn(
when(mMockLocalAdapter.getConnectionState()).thenReturn(
BluetoothAdapter.STATE_CONNECTING,
BluetoothAdapter.STATE_DISCONNECTED);
@@ -240,6 +248,33 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly());
}
@Test
public void testAddOnMetadataChangedListener_registersListenerOnAdapter() {
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
BluetoothDevice device = mock(BluetoothDevice.class);
when(cachedDevice.getDevice()).thenReturn(device);
Executor executor = new FakeExecutor(new FakeSystemClock());
BluetoothAdapter.OnMetadataChangedListener listener = (bluetoothDevice, i, bytes) -> {
};
mBluetoothControllerImpl.addOnMetadataChangedListener(cachedDevice, executor, listener);
verify(mMockAdapter, times(1)).addOnMetadataChangedListener(device, executor, listener);
}
@Test
public void testRemoveOnMetadataChangedListener_removesListenerFromAdapter() {
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
BluetoothDevice device = mock(BluetoothDevice.class);
when(cachedDevice.getDevice()).thenReturn(device);
BluetoothAdapter.OnMetadataChangedListener listener = (bluetoothDevice, i, bytes) -> {
};
mBluetoothControllerImpl.removeOnMetadataChangedListener(cachedDevice, listener);
verify(mMockAdapter, times(1)).removeOnMetadataChangedListener(device, listener);
}
/** Regression test for b/246876230. */
@Test
public void testOnActiveDeviceChanged_null_noCrash() {

View File

@@ -14,6 +14,7 @@
package com.android.systemui.utils.leaks;
import android.bluetooth.BluetoothAdapter;
import android.testing.LeakCheck;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -23,6 +24,7 @@ import com.android.systemui.statusbar.policy.BluetoothController.Callback;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
BluetoothController {
@@ -110,4 +112,16 @@ public class FakeBluetoothController extends BaseLeakChecker<Callback> implement
public List<CachedBluetoothDevice> getConnectedDevices() {
return Collections.emptyList();
}
@Override
public void addOnMetadataChangedListener(CachedBluetoothDevice device, Executor executor,
BluetoothAdapter.OnMetadataChangedListener listener) {
}
@Override
public void removeOnMetadataChangedListener(CachedBluetoothDevice device,
BluetoothAdapter.OnMetadataChangedListener listener) {
}
}