Merge "DO NOT MERGE Extract Bluetooth device scanning logic into abstract superclass." into pi-car-dev
diff --git a/src/com/android/car/settings/bluetooth/BluetoothScanningDevicesGroupPreferenceController.java b/src/com/android/car/settings/bluetooth/BluetoothScanningDevicesGroupPreferenceController.java
new file mode 100644
index 0000000..f39598a
--- /dev/null
+++ b/src/com/android/car/settings/bluetooth/BluetoothScanningDevicesGroupPreferenceController.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.settings.bluetooth;
+
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.car.drivingstate.CarUxRestrictions;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.preference.PreferenceGroup;
+
+import com.android.car.settings.common.FragmentController;
+import com.android.car.settings.common.Logger;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * Controller which sets the Bluetooth adapter to discovery mode and begins scanning for
+ * discoverable devices for as long as the preference group is shown. Discovery
+ * and scanning are halted while any device is pairing. Users with the {@link
+ * DISALLOW_CONFIG_BLUETOOTH} restriction cannot scan for devices, so only cached devices will be
+ * shown.
+ */
+public abstract class BluetoothScanningDevicesGroupPreferenceController extends
+ BluetoothDevicesGroupPreferenceController {
+
+ private static final Logger LOG = new Logger(
+ BluetoothScanningDevicesGroupPreferenceController.class);
+
+ private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ private final AlwaysDiscoverable mAlwaysDiscoverable;
+ private boolean mIsScanningEnabled;
+
+ public BluetoothScanningDevicesGroupPreferenceController(Context context, String preferenceKey,
+ FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
+ super(context, preferenceKey, fragmentController, uxRestrictions);
+ mAlwaysDiscoverable = new AlwaysDiscoverable(context, mBluetoothAdapter);
+ }
+
+ @Override
+ protected final void onDeviceClicked(CachedBluetoothDevice cachedDevice) {
+ LOG.d("onDeviceClicked: " + cachedDevice);
+ disableScanning();
+ onDeviceClickedInternal(cachedDevice);
+ }
+
+ /**
+ * Called when the user selects a device in the group.
+ *
+ * @param cachedDevice the device represented by the selected preference.
+ */
+ protected abstract void onDeviceClickedInternal(CachedBluetoothDevice cachedDevice);
+
+ @Override
+ protected void onStopInternal() {
+ super.onStopInternal();
+ disableScanning();
+ getBluetoothManager().getCachedDeviceManager().clearNonBondedDevices();
+ getPreferenceMap().clear();
+ getPreference().removeAll();
+ }
+
+ @Override
+ protected void updateState(PreferenceGroup preferenceGroup) {
+ super.updateState(preferenceGroup);
+ if (shouldEnableScanning()) {
+ enableScanning();
+ } else {
+ disableScanning();
+ }
+ }
+
+ private boolean shouldEnableScanning() {
+ for (CachedBluetoothDevice device : getPreferenceMap().keySet()) {
+ if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
+ return false;
+ }
+ }
+ // Users who cannot configure Bluetooth cannot scan.
+ return !getCarUserManagerHelper().isCurrentProcessUserHasRestriction(
+ DISALLOW_CONFIG_BLUETOOTH);
+ }
+
+ /**
+ * Starts scanning for devices which will be displayed in the group for a user to select.
+ * Calls are idempotent.
+ */
+ private void enableScanning() {
+ mIsScanningEnabled = true;
+ if (!mBluetoothAdapter.isDiscovering()) {
+ mBluetoothAdapter.startDiscovery();
+ }
+ mAlwaysDiscoverable.start();
+ getPreference().setEnabled(true);
+ }
+
+ /** Stops scanning for devices and disables interaction. Calls are idempotent. */
+ private void disableScanning() {
+ mIsScanningEnabled = false;
+ getPreference().setEnabled(false);
+ mAlwaysDiscoverable.stop();
+ if (mBluetoothAdapter.isDiscovering()) {
+ mBluetoothAdapter.cancelDiscovery();
+ }
+ }
+
+ @Override
+ public void onScanningStateChanged(boolean started) {
+ LOG.d("onScanningStateChanged started: " + started + " mIsScanningEnabled: "
+ + mIsScanningEnabled);
+ if (!started && mIsScanningEnabled) {
+ enableScanning();
+ }
+ }
+
+ @Override
+ public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+ LOG.d("onDeviceBondStateChanged device: " + cachedDevice + " state: " + bondState);
+ refreshUi();
+ }
+
+ /**
+ * Helper class to keep the {@link BluetoothAdapter} in discoverable mode indefinitely. By
+ * default, setting the scan mode to BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE will
+ * timeout, but for pairing, we want to keep the device discoverable as long as the page is
+ * scanning.
+ */
+ private static final class AlwaysDiscoverable extends BroadcastReceiver {
+
+ private final Context mContext;
+ private final BluetoothAdapter mAdapter;
+ private final IntentFilter mIntentFilter = new IntentFilter(
+ BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+
+ private boolean mStarted;
+
+ AlwaysDiscoverable(Context context, BluetoothAdapter adapter) {
+ mContext = context;
+ mAdapter = adapter;
+ }
+
+ /**
+ * Sets the adapter scan mode to
+ * {@link BluetoothAdapter#SCAN_MODE_CONNECTABLE_DISCOVERABLE}. {@link #start()} calls
+ * should have a matching calls to {@link #stop()} when discover mode is no longer needed.
+ */
+ void start() {
+ if (mStarted) {
+ return;
+ }
+ mContext.registerReceiver(this, mIntentFilter);
+ mStarted = true;
+ setDiscoverable();
+ }
+
+ void stop() {
+ if (!mStarted) {
+ return;
+ }
+ mContext.unregisterReceiver(this);
+ mStarted = false;
+ mAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ setDiscoverable();
+ }
+
+ private void setDiscoverable() {
+ if (mAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ mAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ }
+ }
+ }
+}
diff --git a/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceController.java b/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceController.java
index c98a274..b460baa 100644
--- a/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceController.java
+++ b/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceController.java
@@ -18,15 +18,8 @@
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
import android.car.drivingstate.CarUxRestrictions;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-
-import androidx.preference.PreferenceGroup;
import com.android.car.settings.R;
import com.android.car.settings.common.FragmentController;
@@ -42,19 +35,14 @@
* DISALLOW_CONFIG_BLUETOOTH} restriction cannot pair devices.
*/
public class BluetoothUnbondedDevicesPreferenceController extends
- BluetoothDevicesGroupPreferenceController {
+ BluetoothScanningDevicesGroupPreferenceController {
private static final Logger LOG = new Logger(
BluetoothUnbondedDevicesPreferenceController.class);
- private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- private final AlwaysDiscoverable mAlwaysDiscoverable;
- private boolean mIsScanningEnabled;
-
public BluetoothUnbondedDevicesPreferenceController(Context context, String preferenceKey,
FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
super(context, preferenceKey, fragmentController, uxRestrictions);
- mAlwaysDiscoverable = new AlwaysDiscoverable(context, mBluetoothAdapter);
}
@Override
@@ -63,9 +51,7 @@
}
@Override
- protected void onDeviceClicked(CachedBluetoothDevice cachedDevice) {
- LOG.d("onDeviceClicked: " + cachedDevice);
- disableScanning();
+ protected void onDeviceClickedInternal(CachedBluetoothDevice cachedDevice) {
if (cachedDevice.startPairing()) {
LOG.d("startPairing");
// Indicate that this client (vehicle) would like access to contacts (PBAP) and messages
@@ -89,125 +75,4 @@
}
return availabilityStatus;
}
-
- @Override
- protected void onStopInternal() {
- super.onStopInternal();
- disableScanning();
- getBluetoothManager().getCachedDeviceManager().clearNonBondedDevices();
- getPreferenceMap().clear();
- getPreference().removeAll();
- }
-
- @Override
- protected void updateState(PreferenceGroup preferenceGroup) {
- super.updateState(preferenceGroup);
- if (shouldEnableScanning()) {
- enableScanning();
- } else {
- disableScanning();
- }
- }
-
- private boolean shouldEnableScanning() {
- for (CachedBluetoothDevice device : getPreferenceMap().keySet()) {
- if (device.getBondState() == BluetoothDevice.BOND_BONDING) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Starts scanning for devices which will be displayed in the group for a user to select.
- * Calls are idempotent.
- */
- private void enableScanning() {
- mIsScanningEnabled = true;
- if (!mBluetoothAdapter.isDiscovering()) {
- mBluetoothAdapter.startDiscovery();
- }
- mAlwaysDiscoverable.start();
- getPreference().setEnabled(true);
- }
-
- /** Stops scanning for devices and disables interaction. Calls are idempotent. */
- private void disableScanning() {
- mIsScanningEnabled = false;
- getPreference().setEnabled(false);
- mAlwaysDiscoverable.stop();
- if (mBluetoothAdapter.isDiscovering()) {
- mBluetoothAdapter.cancelDiscovery();
- }
- }
-
- @Override
- public void onScanningStateChanged(boolean started) {
- LOG.d("onScanningStateChanged started: " + started + " mIsScanningEnabled: "
- + mIsScanningEnabled);
- if (!started && mIsScanningEnabled) {
- enableScanning();
- }
- }
-
- @Override
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
- LOG.d("onDeviceBondStateChanged device: " + cachedDevice + " state: " + bondState);
- refreshUi();
- }
-
- /**
- * Helper class to keep the {@link BluetoothAdapter} in discoverable mode indefinitely. By
- * default, setting the scan mode to BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE will
- * timeout, but for pairing, we want to keep the device discoverable as long as the page is
- * scanning.
- */
- private static final class AlwaysDiscoverable extends BroadcastReceiver {
-
- private final Context mContext;
- private final BluetoothAdapter mAdapter;
- private final IntentFilter mIntentFilter = new IntentFilter(
- BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
-
- private boolean mStarted;
-
- AlwaysDiscoverable(Context context, BluetoothAdapter adapter) {
- mContext = context;
- mAdapter = adapter;
- }
-
- /**
- * Sets the adapter scan mode to
- * {@link BluetoothAdapter#SCAN_MODE_CONNECTABLE_DISCOVERABLE}. {@link #start()} calls
- * should have a matching calls to {@link #stop()} when discover mode is no longer needed.
- */
- void start() {
- if (mStarted) {
- return;
- }
- mContext.registerReceiver(this, mIntentFilter);
- mStarted = true;
- setDiscoverable();
- }
-
- void stop() {
- if (!mStarted) {
- return;
- }
- mContext.unregisterReceiver(this);
- mStarted = false;
- mAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- setDiscoverable();
- }
-
- private void setDiscoverable() {
- if (mAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
- mAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
- }
- }
- }
}
diff --git a/tests/robotests/src/com/android/car/settings/bluetooth/BluetoothScanningDevicesGroupPreferenceControllerTest.java b/tests/robotests/src/com/android/car/settings/bluetooth/BluetoothScanningDevicesGroupPreferenceControllerTest.java
new file mode 100644
index 0000000..3f62452
--- /dev/null
+++ b/tests/robotests/src/com/android/car/settings/bluetooth/BluetoothScanningDevicesGroupPreferenceControllerTest.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.settings.bluetooth;
+
+import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
+import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.car.drivingstate.CarUxRestrictions;
+import android.car.userlib.CarUserManagerHelper;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.preference.PreferenceGroup;
+
+import com.android.car.settings.CarSettingsRobolectricTestRunner;
+import com.android.car.settings.common.FragmentController;
+import com.android.car.settings.common.LogicalPreferenceGroup;
+import com.android.car.settings.common.PreferenceControllerTestHelper;
+import com.android.car.settings.testutils.ShadowBluetoothAdapter;
+import com.android.car.settings.testutils.ShadowBluetoothPan;
+import com.android.car.settings.testutils.ShadowCarUserManagerHelper;
+import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.Collections;
+
+/** Unit test for {@link BluetoothScanningDevicesGroupPreferenceController}. */
+@RunWith(CarSettingsRobolectricTestRunner.class)
+@Config(shadows = {ShadowCarUserManagerHelper.class, ShadowBluetoothAdapter.class,
+ ShadowBluetoothPan.class})
+public class BluetoothScanningDevicesGroupPreferenceControllerTest {
+
+ @Mock
+ private CarUserManagerHelper mCarUserManagerHelper;
+ @Mock
+ private CachedBluetoothDevice mCachedDevice;
+ @Mock
+ private BluetoothDevice mDevice;
+ @Mock
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ private CachedBluetoothDeviceManager mSaveRealCachedDeviceManager;
+ private LocalBluetoothManager mLocalBluetoothManager;
+ private Context mContext;
+ private PreferenceGroup mPreferenceGroup;
+ private PreferenceControllerTestHelper<TestBluetoothScanningDevicesGroupPreferenceController>
+ mControllerHelper;
+ private TestBluetoothScanningDevicesGroupPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowCarUserManagerHelper.setMockInstance(mCarUserManagerHelper);
+ mContext = RuntimeEnvironment.application;
+
+ mLocalBluetoothManager = LocalBluetoothManager.getInstance(mContext, /* onInitCallback= */
+ null);
+ mSaveRealCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager();
+ ReflectionHelpers.setField(mLocalBluetoothManager, "mCachedDeviceManager",
+ mCachedDeviceManager);
+
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ when(mCachedDevice.getDevice()).thenReturn(mDevice);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ Collections.singletonList(mCachedDevice));
+
+ // Make sure controller is available.
+ Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
+ FEATURE_BLUETOOTH, /* supported= */ true);
+ BluetoothAdapter.getDefaultAdapter().enable();
+ getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
+
+ mPreferenceGroup = new LogicalPreferenceGroup(mContext);
+ mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
+ TestBluetoothScanningDevicesGroupPreferenceController.class, mPreferenceGroup);
+ mController = mControllerHelper.getController();
+ }
+
+ @After
+ public void tearDown() {
+ ShadowCarUserManagerHelper.reset();
+ ShadowBluetoothAdapter.reset();
+ ReflectionHelpers.setField(mLocalBluetoothManager, "mCachedDeviceManager",
+ mSaveRealCachedDeviceManager);
+ }
+
+ @Test
+ public void disallowConfigBluetooth_doesNotStartScanning() {
+ when(mCarUserManagerHelper.isCurrentProcessUserHasRestriction(
+ DISALLOW_CONFIG_BLUETOOTH)).thenReturn(true);
+
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
+ // User can't scan, but they can still see known devices.
+ assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void onScanningStateChanged_scanningEnabled_receiveStopped_restartsScanning() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
+
+ BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
+ mController.onScanningStateChanged(/* started= */ false);
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
+ }
+
+ @Test
+ public void onScanningStateChanged_scanningDisabled_receiveStopped_doesNothing() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ // Set a device bonding to disable scanning.
+ when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+ mController.refreshUi();
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
+
+ mController.onScanningStateChanged(/* started= */ false);
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
+ }
+
+ @Test
+ public void onDeviceBondStateChanged_refreshesUi() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
+
+ // Change state to bonding to cancel scanning on refresh.
+ when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+ mController.onDeviceBondStateChanged(mCachedDevice, BluetoothDevice.BOND_BONDING);
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
+ }
+
+ @Test
+ public void onDeviceClicked_callsInternal() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ BluetoothDevicePreference devicePreference =
+ (BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
+
+ devicePreference.performClick();
+
+ assertThat(mController.getLastClickedDevice()).isEquivalentAccordingToCompareTo(
+ devicePreference.getCachedDevice());
+ }
+
+ @Test
+ public void onDeviceClicked_cancelsScanning() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ BluetoothDevicePreference devicePreference =
+ (BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
+
+ devicePreference.performClick();
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
+ }
+
+ @Test
+ public void refreshUi_noDeviceBonding_startsScanning() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+
+ mController.refreshUi();
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
+ }
+
+ @Test
+ public void refreshUi_noDeviceBonding_enablesGroup() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+
+ mController.refreshUi();
+
+ assertThat(mPreferenceGroup.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void refreshUi_noDeviceBonding_setsScanModeConnectableDiscoverable() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+
+ mController.refreshUi();
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ }
+
+ @Test
+ public void refreshUi_deviceBonding_stopsScanning() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ mController.refreshUi();
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
+ }
+
+ @Test
+ public void refreshUi_deviceBonding_disablesGroup() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ mController.refreshUi();
+
+ assertThat(mPreferenceGroup.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void refreshUi_deviceBonding_setsScanModeConnectable() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ mController.refreshUi();
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ }
+
+ @Test
+ public void onStop_stopsScanning() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
+
+ mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
+ }
+
+ @Test
+ public void onStop_clearsNonBondedDevices() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+ verify(mCachedDeviceManager).clearNonBondedDevices();
+ }
+
+ @Test
+ public void onStop_clearsGroup() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ assertThat(mPreferenceGroup.getPreferenceCount()).isGreaterThan(0);
+
+ mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+ assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void onStop_setsScanModeConnectable() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ }
+
+ @Test
+ public void discoverableScanModeTimeout_controllerStarted_resetsDiscoverableScanMode() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+
+ BluetoothAdapter.getDefaultAdapter().setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ mContext.sendBroadcast(new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ }
+
+ @Test
+ public void discoverableScanModeTimeout_controllerStopped_doesNotResetDiscoverableScanMode() {
+ mControllerHelper.markState(Lifecycle.State.STARTED);
+ mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+ BluetoothAdapter.getDefaultAdapter().setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ mContext.sendBroadcast(new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
+
+ assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ }
+
+ private ShadowBluetoothAdapter getShadowBluetoothAdapter() {
+ return (ShadowBluetoothAdapter) Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ }
+
+ private static final class TestBluetoothScanningDevicesGroupPreferenceController extends
+ BluetoothScanningDevicesGroupPreferenceController {
+
+ private CachedBluetoothDevice mLastClickedDevice;
+
+ TestBluetoothScanningDevicesGroupPreferenceController(Context context,
+ String preferenceKey,
+ FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
+ super(context, preferenceKey, fragmentController, uxRestrictions);
+ }
+
+ @Override
+ protected void onDeviceClickedInternal(CachedBluetoothDevice cachedDevice) {
+ mLastClickedDevice = cachedDevice;
+ }
+
+ CachedBluetoothDevice getLastClickedDevice() {
+ return mLastClickedDevice;
+ }
+
+ @Override
+ protected BluetoothDeviceFilter.Filter getDeviceFilter() {
+ return BluetoothDeviceFilter.ALL_FILTER;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceControllerTest.java
index 28e2b0d..8065703 100644
--- a/tests/robotests/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/car/settings/bluetooth/BluetoothUnbondedDevicesPreferenceControllerTest.java
@@ -31,7 +31,6 @@
import android.bluetooth.BluetoothDevice;
import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
-import android.content.Intent;
import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceGroup;
@@ -76,19 +75,17 @@
private CachedBluetoothDeviceManager mCachedDeviceManager;
private CachedBluetoothDeviceManager mSaveRealCachedDeviceManager;
private LocalBluetoothManager mLocalBluetoothManager;
- private Context mContext;
private PreferenceGroup mPreferenceGroup;
private PreferenceControllerTestHelper<BluetoothUnbondedDevicesPreferenceController>
mControllerHelper;
- private BluetoothUnbondedDevicesPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowCarUserManagerHelper.setMockInstance(mCarUserManagerHelper);
- mContext = RuntimeEnvironment.application;
+ Context context = RuntimeEnvironment.application;
- mLocalBluetoothManager = LocalBluetoothManager.getInstance(mContext, /* onInitCallback= */
+ mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, /* onInitCallback= */
null);
mSaveRealCachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager();
ReflectionHelpers.setField(mLocalBluetoothManager, "mCachedDeviceManager",
@@ -105,15 +102,14 @@
Arrays.asList(mUnbondedCachedDevice, bondedCachedDevice));
// Make sure controller is available.
- Shadows.shadowOf(mContext.getPackageManager()).setSystemFeature(
+ Shadows.shadowOf(context.getPackageManager()).setSystemFeature(
FEATURE_BLUETOOTH, /* supported= */ true);
BluetoothAdapter.getDefaultAdapter().enable();
getShadowBluetoothAdapter().setState(BluetoothAdapter.STATE_ON);
- mPreferenceGroup = new LogicalPreferenceGroup(mContext);
- mControllerHelper = new PreferenceControllerTestHelper<>(mContext,
+ mPreferenceGroup = new LogicalPreferenceGroup(context);
+ mControllerHelper = new PreferenceControllerTestHelper<>(context,
BluetoothUnbondedDevicesPreferenceController.class, mPreferenceGroup);
- mController = mControllerHelper.getController();
}
@After
@@ -134,43 +130,6 @@
}
@Test
- public void onScanningStateChanged_scanningEnabled_receiveStopped_restartsScanning() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
-
- BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
- mController.onScanningStateChanged(/* started= */ false);
-
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
- }
-
- @Test
- public void onScanningStateChanged_scanningDisabled_receiveStopped_doesNothing() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- // Set a device bonding to disable scanning.
- when(mUnbondedCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
- mController.refreshUi();
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
-
- mController.onScanningStateChanged(/* started= */ false);
-
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
- }
-
- @Test
- public void onDeviceBondStateChanged_refreshesUi() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(1);
-
- // Bond the only unbonded device.
- when(mUnbondedCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
- when(mUnbondedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
- mController.onDeviceBondStateChanged(mUnbondedCachedDevice, BluetoothDevice.BOND_BONDED);
-
- assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
- }
-
- @Test
public void onDeviceClicked_startsPairing() {
mControllerHelper.markState(Lifecycle.State.STARTED);
BluetoothDevicePreference devicePreference =
@@ -182,20 +141,7 @@
}
@Test
- public void onDeviceClicked_pairingStarted_cancelsScanning() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- BluetoothDevicePreference devicePreference =
- (BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
- when(mUnbondedCachedDevice.startPairing()).thenReturn(true);
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
-
- devicePreference.performClick();
-
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
- }
-
- @Test
- public void onDeviceClicked_pairingStartFails_resumeScanning() {
+ public void onDeviceClicked_pairingStartFails_resumesScanning() {
mControllerHelper.markState(Lifecycle.State.STARTED);
BluetoothDevicePreference devicePreference =
(BluetoothDevicePreference) mPreferenceGroup.getPreference(0);
@@ -221,7 +167,7 @@
}
@Test
- public void onDeviceClicked_requests_messageAccess() {
+ public void onDeviceClicked_requestsMessageAccess() {
mControllerHelper.markState(Lifecycle.State.STARTED);
when(mUnbondedCachedDevice.startPairing()).thenReturn(true);
BluetoothDevicePreference devicePreference =
@@ -242,125 +188,6 @@
DISABLED_FOR_USER);
}
- @Test
- public void refreshUi_noDeviceBonding_startsScanning() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
-
- mController.refreshUi();
-
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
- }
-
- @Test
- public void refreshUi_noDeviceBonding_enablesGroup() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
-
- mController.refreshUi();
-
- assertThat(mPreferenceGroup.isEnabled()).isTrue();
- }
-
- @Test
- public void refreshUi_noDeviceBonding_setsScanModeConnectableDiscoverable() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
-
- mController.refreshUi();
-
- assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
- BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
- }
-
- @Test
- public void refreshUi_deviceBonding_stopsScanning() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- when(mUnbondedCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
-
- mController.refreshUi();
-
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
- }
-
- @Test
- public void refreshUi_deviceBonding_disablesGroup() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- when(mUnbondedCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
-
- mController.refreshUi();
-
- assertThat(mPreferenceGroup.isEnabled()).isFalse();
- }
-
- @Test
- public void refreshUi_deviceBonding_setsScanModeConnectable() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- when(mUnbondedCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
-
- mController.refreshUi();
-
- assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
- BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- }
-
- @Test
- public void onStop_stopsScanning() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isTrue();
-
- mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
-
- assertThat(BluetoothAdapter.getDefaultAdapter().isDiscovering()).isFalse();
- }
-
- @Test
- public void onStop_clearsNonBondedDevices() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
-
- verify(mCachedDeviceManager).clearNonBondedDevices();
- }
-
- @Test
- public void onStop_clearsGroup() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- assertThat(mPreferenceGroup.getPreferenceCount()).isGreaterThan(0);
-
- mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
-
- assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0);
- }
-
- @Test
- public void onStop_setsScanModeConnectable() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
-
- assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
- BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- }
-
- @Test
- public void discoverableScanModeTimeout_controllerStarted_resetsDiscoverableScanMode() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
-
- BluetoothAdapter.getDefaultAdapter().setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- mContext.sendBroadcast(new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
-
- assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
- BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
- }
-
- @Test
- public void discoverableScanModeTimeout_controllerStopped_doesNotResetDiscoverableScanMode() {
- mControllerHelper.markState(Lifecycle.State.STARTED);
- mControllerHelper.sendLifecycleEvent(Lifecycle.Event.ON_STOP);
-
- BluetoothAdapter.getDefaultAdapter().setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- mContext.sendBroadcast(new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED));
-
- assertThat(BluetoothAdapter.getDefaultAdapter().getScanMode()).isEqualTo(
- BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- }
-
private ShadowBluetoothAdapter getShadowBluetoothAdapter() {
return (ShadowBluetoothAdapter) Shadow.extract(BluetoothAdapter.getDefaultAdapter());
}