Merge "Use FastPair battery level in blueooth QS tile." into udc-dev

This commit is contained in:
Vania Januar
2023-03-22 15:37:04 +00:00
committed by Android (Google) Code Review
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 static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.Intent; import android.content.Intent;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper; import android.os.Looper;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.service.quicksettings.Tile; import android.service.quicksettings.Tile;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Switch; import android.widget.Switch;
import androidx.annotation.Nullable;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils; import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background; 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 com.android.systemui.statusbar.policy.BluetoothController;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject; 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 Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
private static final String TAG = BluetoothTile.class.getSimpleName();
private final BluetoothController mController; private final BluetoothController mController;
private CachedBluetoothDevice mMetadataRegisteredDevice = null;
private final Executor mExecutor;
@Inject @Inject
public BluetoothTile( public BluetoothTile(
QSHost host, QSHost host,
@@ -78,6 +87,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
statusBarStateController, activityStarter, qsLogger); statusBarStateController, activityStarter, qsLogger);
mController = bluetoothController; mController = bluetoothController;
mController.observe(getLifecycle(), mCallback); mController.observe(getLifecycle(), mCallback);
mExecutor = new HandlerExecutor(mainHandler);
} }
@Override @Override
@@ -116,6 +126,15 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
return mContext.getString(R.string.quick_settings_bluetooth_label); return mContext.getString(R.string.quick_settings_bluetooth_label);
} }
@Override
protected void handleSetListening(boolean listening) {
super.handleSetListening(listening);
if (!listening) {
stopListeningToStaleDeviceMetadata();
}
}
@Override @Override
protected void handleUpdateState(BooleanState state, Object arg) { protected void handleUpdateState(BooleanState state, Object arg) {
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH);
@@ -125,6 +144,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
final boolean connecting = mController.isBluetoothConnecting(); final boolean connecting = mController.isBluetoothConnecting();
state.isTransient = transientEnabling || connecting || state.isTransient = transientEnabling || connecting ||
mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON; mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
if (!enabled || !connected || state.isTransient) {
stopListeningToStaleDeviceMetadata();
}
state.dualTarget = true; state.dualTarget = true;
state.value = enabled; state.value = enabled;
if (state.slash == null) { if (state.slash == null) {
@@ -187,23 +209,32 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices(); List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices();
if (enabled && connected && !connectedDevices.isEmpty()) { if (enabled && connected && !connectedDevices.isEmpty()) {
if (connectedDevices.size() > 1) { if (connectedDevices.size() > 1) {
stopListeningToStaleDeviceMetadata();
return icuMessageFormat(mContext.getResources(), return icuMessageFormat(mContext.getResources(),
R.string.quick_settings_hotspot_secondary_label_num_devices, R.string.quick_settings_hotspot_secondary_label_num_devices,
connectedDevices.size()); connectedDevices.size());
} }
CachedBluetoothDevice lastDevice = connectedDevices.get(0); CachedBluetoothDevice device = connectedDevices.get(0);
final int batteryLevel = lastDevice.getBatteryLevel();
// 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) { if (batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
return mContext.getString( return mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_battery_level, R.string.quick_settings_bluetooth_secondary_label_battery_level,
Utils.formatPercentage(batteryLevel)); Utils.formatPercentage(batteryLevel));
} else { } else {
final BluetoothClass bluetoothClass = lastDevice.getBtClass(); final BluetoothClass bluetoothClass = device.getBtClass();
if (bluetoothClass != null) { if (bluetoothClass != null) {
if (lastDevice.isHearingAidDevice()) { if (device.isHearingAidDevice()) {
return mContext.getString( return mContext.getString(
R.string.quick_settings_bluetooth_secondary_label_hearing_aids); R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
} else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) { } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
@@ -233,6 +264,36 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
return mController.isBluetoothSupported(); 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() { private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
@Override @Override
public void onBluetoothStateChange(boolean enabled) { public void onBluetoothStateChange(boolean enabled) {
@@ -244,4 +305,9 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
refreshState(); 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; package com.android.systemui.statusbar.policy;
import android.bluetooth.BluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.Dumpable; import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.BluetoothController.Callback; import com.android.systemui.statusbar.policy.BluetoothController.Callback;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor;
public interface BluetoothController extends CallbackController<Callback>, Dumpable { public interface BluetoothController extends CallbackController<Callback>, Dumpable {
boolean isBluetoothSupported(); boolean isBluetoothSupported();
@@ -44,6 +47,11 @@ public interface BluetoothController extends CallbackController<Callback>, Dumpa
int getBondState(CachedBluetoothDevice device); int getBondState(CachedBluetoothDevice device);
List<CachedBluetoothDevice> getConnectedDevices(); List<CachedBluetoothDevice> getConnectedDevices();
void addOnMetadataChangedListener(CachedBluetoothDevice device, Executor executor,
BluetoothAdapter.OnMetadataChangedListener listener);
void removeOnMetadataChangedListener(CachedBluetoothDevice device,
BluetoothAdapter.OnMetadataChangedListener listener);
public interface Callback { public interface Callback {
void onBluetoothStateChange(boolean enabled); void onBluetoothStateChange(boolean enabled);
void onBluetoothDevicesChanged(); void onBluetoothDevicesChanged();

View File

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

View File

@@ -1,6 +1,6 @@
package com.android.systemui.qs.tiles package com.android.systemui.qs.tiles
import android.content.Context import android.bluetooth.BluetoothDevice
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.UserManager import android.os.UserManager
@@ -10,6 +10,8 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake 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.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake 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.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.BluetoothController 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 com.google.common.truth.Truth.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito import org.mockito.Mockito.times
import org.mockito.Mockito.`when` import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@@ -36,21 +42,13 @@ import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
class BluetoothTileTest : SysuiTestCase() { class BluetoothTileTest : SysuiTestCase() {
@Mock @Mock private lateinit var qsLogger: QSLogger
private lateinit var mockContext: Context @Mock private lateinit var qsHost: QSHost
@Mock @Mock private lateinit var metricsLogger: MetricsLogger
private lateinit var qsLogger: QSLogger
@Mock
private lateinit var qsHost: QSHost
@Mock
private lateinit var metricsLogger: MetricsLogger
private val falsingManager = FalsingManagerFake() private val falsingManager = FalsingManagerFake()
@Mock @Mock private lateinit var statusBarStateController: StatusBarStateController
private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var activityStarter: ActivityStarter
@Mock @Mock private lateinit var bluetoothController: BluetoothController
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var bluetoothController: BluetoothController
private val uiEventLogger = UiEventLoggerFake() private val uiEventLogger = UiEventLoggerFake()
private lateinit var testableLooper: TestableLooper private lateinit var testableLooper: TestableLooper
@@ -61,10 +59,11 @@ class BluetoothTileTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this) testableLooper = TestableLooper.get(this)
Mockito.`when`(qsHost.context).thenReturn(mockContext) whenever(qsHost.context).thenReturn(mContext)
Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger)
tile = FakeBluetoothTile( tile =
FakeBluetoothTile(
qsHost, qsHost,
testableLooper.looper, testableLooper.looper,
Handler(testableLooper.looper), Handler(testableLooper.looper),
@@ -73,7 +72,7 @@ class BluetoothTileTest : SysuiTestCase() {
statusBarStateController, statusBarStateController,
activityStarter, activityStarter,
qsLogger, qsLogger,
bluetoothController bluetoothController,
) )
tile.initialize() tile.initialize()
@@ -141,6 +140,75 @@ class BluetoothTileTest : SysuiTestCase() {
.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( private class FakeBluetoothTile(
qsHost: QSHost, qsHost: QSHost,
backgroundLooper: Looper, backgroundLooper: Looper,
@@ -150,8 +218,9 @@ class BluetoothTileTest : SysuiTestCase() {
statusBarStateController: StatusBarStateController, statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter, activityStarter: ActivityStarter,
qsLogger: QSLogger, qsLogger: QSLogger,
bluetoothController: BluetoothController bluetoothController: BluetoothController,
) : BluetoothTile( ) :
BluetoothTile(
qsHost, qsHost,
backgroundLooper, backgroundLooper,
mainHandler, mainHandler,
@@ -160,7 +229,7 @@ class BluetoothTileTest : SysuiTestCase() {
statusBarStateController, statusBarStateController,
activityStarter, activityStarter,
qsLogger, qsLogger,
bluetoothController bluetoothController,
) { ) {
var restrictionChecked: String? = null var restrictionChecked: String? = null
@@ -173,25 +242,44 @@ class BluetoothTileTest : SysuiTestCase() {
} }
fun enableBluetooth() { fun enableBluetooth() {
`when`(bluetoothController.isBluetoothEnabled).thenReturn(true) whenever(bluetoothController.isBluetoothEnabled).thenReturn(true)
} }
fun disableBluetooth() { fun disableBluetooth() {
`when`(bluetoothController.isBluetoothEnabled).thenReturn(false) whenever(bluetoothController.isBluetoothEnabled).thenReturn(false)
} }
fun setBluetoothDisconnected() { fun setBluetoothDisconnected() {
`when`(bluetoothController.isBluetoothConnecting).thenReturn(false) whenever(bluetoothController.isBluetoothConnecting).thenReturn(false)
`when`(bluetoothController.isBluetoothConnected).thenReturn(false) whenever(bluetoothController.isBluetoothConnected).thenReturn(false)
} }
fun setBluetoothConnected() { fun setBluetoothConnected() {
`when`(bluetoothController.isBluetoothConnecting).thenReturn(false) whenever(bluetoothController.isBluetoothConnecting).thenReturn(false)
`when`(bluetoothController.isBluetoothConnected).thenReturn(true) whenever(bluetoothController.isBluetoothConnected).thenReturn(true)
} }
fun setBluetoothConnecting() { fun setBluetoothConnecting() {
`when`(bluetoothController.isBluetoothConnected).thenReturn(false) whenever(bluetoothController.isBluetoothConnected).thenReturn(false)
`when`(bluetoothController.isBluetoothConnecting).thenReturn(true) 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.atLeastOnce;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; 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.bluetooth.BluetoothLogger;
import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker; 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.Before;
import org.junit.Test; import org.junit.Test;
@@ -51,6 +54,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@RunWithLooper @RunWithLooper
@@ -60,10 +64,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
private UserTracker mUserTracker; private UserTracker mUserTracker;
private LocalBluetoothManager mMockBluetoothManager; private LocalBluetoothManager mMockBluetoothManager;
private CachedBluetoothDeviceManager mMockDeviceManager; private CachedBluetoothDeviceManager mMockDeviceManager;
private LocalBluetoothAdapter mMockAdapter; private LocalBluetoothAdapter mMockLocalAdapter;
private TestableLooper mTestableLooper; private TestableLooper mTestableLooper;
private DumpManager mMockDumpManager; private DumpManager mMockDumpManager;
private BluetoothControllerImpl mBluetoothControllerImpl; private BluetoothControllerImpl mBluetoothControllerImpl;
private BluetoothAdapter mMockAdapter;
private List<CachedBluetoothDevice> mDevices; private List<CachedBluetoothDevice> mDevices;
@@ -74,10 +79,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mDevices = new ArrayList<>(); mDevices = new ArrayList<>();
mUserTracker = mock(UserTracker.class); mUserTracker = mock(UserTracker.class);
mMockDeviceManager = mock(CachedBluetoothDeviceManager.class); mMockDeviceManager = mock(CachedBluetoothDeviceManager.class);
mMockAdapter = mock(BluetoothAdapter.class);
when(mMockDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices); when(mMockDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
when(mMockBluetoothManager.getCachedDeviceManager()).thenReturn(mMockDeviceManager); when(mMockBluetoothManager.getCachedDeviceManager()).thenReturn(mMockDeviceManager);
mMockAdapter = mock(LocalBluetoothAdapter.class); mMockLocalAdapter = mock(LocalBluetoothAdapter.class);
when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockAdapter); when(mMockBluetoothManager.getBluetoothAdapter()).thenReturn(mMockLocalAdapter);
when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class)); when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class));
when(mMockBluetoothManager.getProfileManager()) when(mMockBluetoothManager.getProfileManager())
.thenReturn(mock(LocalBluetoothProfileManager.class)); .thenReturn(mock(LocalBluetoothProfileManager.class));
@@ -89,7 +95,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mock(BluetoothLogger.class), mock(BluetoothLogger.class),
mTestableLooper.getLooper(), mTestableLooper.getLooper(),
mTestableLooper.getLooper(), mTestableLooper.getLooper(),
mMockBluetoothManager); mMockBluetoothManager,
mMockAdapter);
} }
@Test @Test
@@ -98,7 +105,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
when(device.isConnected()).thenReturn(true); when(device.isConnected()).thenReturn(true);
when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED); when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED);
mDevices.add(device); mDevices.add(device);
when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED); when(mMockLocalAdapter.getConnectionState())
.thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
mBluetoothControllerImpl.onConnectionStateChanged(null, mBluetoothControllerImpl.onConnectionStateChanged(null,
BluetoothAdapter.STATE_DISCONNECTED); BluetoothAdapter.STATE_DISCONNECTED);
@@ -163,7 +171,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
@Test @Test
public void testOnServiceConnected_updatesConnectionState() { public void testOnServiceConnected_updatesConnectionState() {
when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING); when(mMockLocalAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
mBluetoothControllerImpl.onServiceConnected(); mBluetoothControllerImpl.onServiceConnected();
@@ -184,7 +192,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
@Test @Test
public void testOnBluetoothStateChange_updatesConnectionState() { public void testOnBluetoothStateChange_updatesConnectionState() {
when(mMockAdapter.getConnectionState()).thenReturn( when(mMockLocalAdapter.getConnectionState()).thenReturn(
BluetoothAdapter.STATE_CONNECTING, BluetoothAdapter.STATE_CONNECTING,
BluetoothAdapter.STATE_DISCONNECTED); BluetoothAdapter.STATE_DISCONNECTED);
@@ -240,6 +248,33 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()); 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. */ /** Regression test for b/246876230. */
@Test @Test
public void testOnActiveDeviceChanged_null_noCrash() { public void testOnActiveDeviceChanged_null_noCrash() {

View File

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