Merge "Bluetooth: Always scan while on pairing or DevicePicker page"

This commit is contained in:
TreeHugger Robot
2017-06-05 22:11:40 +00:00
committed by Android (Google) Code Review
5 changed files with 210 additions and 51 deletions

View File

@@ -82,7 +82,7 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
// Make the device only visible to connected devices. // Make the device only visible to connected devices.
mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE); mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
mLocalAdapter.stopScanning(); disableScanning();
} }
@Override @Override
@@ -98,25 +98,29 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
return MetricsEvent.BLUETOOTH; return MetricsEvent.BLUETOOTH;
} }
@VisibleForTesting @Override
void startScanning() { void enableScanning() {
if (mAvailableDevicesCategory != null) { // Clear all device states before first scan
removeAllDevices(); if (!mInitialScanStarted) {
if (mAvailableDevicesCategory != null) {
removeAllDevices();
}
mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
mInitialScanStarted = true;
} }
super.enableScanning();
mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
mInitialScanStarted = true;
mLocalAdapter.startScanning(true);
} }
@Override @Override
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
mLocalAdapter.stopScanning(); disableScanning();
super.onDevicePreferenceClick(btPreference); super.onDevicePreferenceClick(btPreference);
} }
@Override @Override
public void onScanningStateChanged(boolean started) { public void onScanningStateChanged(boolean started) {
super.onScanningStateChanged(started);
started |= mScanEnabled;
mAvailableDevicesCategory.setProgress(started); mAvailableDevicesCategory.setProgress(started);
} }
@@ -131,14 +135,10 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
R.string.bluetooth_preference_found_devices, R.string.bluetooth_preference_found_devices,
BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER, mInitialScanStarted); BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER, mInitialScanStarted);
updateFooterPreference(mFooterPreference); updateFooterPreference(mFooterPreference);
if (!mInitialScanStarted) {
startScanning();
}
// mLocalAdapter.setScanMode is internally synchronized so it is okay for multiple // mLocalAdapter.setScanMode is internally synchronized so it is okay for multiple
// threads to execute. // threads to execute.
mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
enableScanning();
break; break;
case BluetoothAdapter.STATE_OFF: case BluetoothAdapter.STATE_OFF:
@@ -158,6 +158,15 @@ public class BluetoothPairingDetail extends DeviceListPreferenceFragment impleme
if (bondState == BluetoothDevice.BOND_BONDED) { if (bondState == BluetoothDevice.BOND_BONDED) {
// If one device is connected(bonded), then close this fragment. // If one device is connected(bonded), then close this fragment.
finish(); finish();
return;
}
if (mSelectedDevice != null && cachedDevice != null) {
BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedDevice.equals(device)
&& bondState == BluetoothDevice.BOND_NONE) {
// If currently selected device failed to bond, restart scanning
enableScanning();
}
} }
} }

View File

@@ -54,6 +54,9 @@ public abstract class DeviceListPreferenceFragment extends
private BluetoothDeviceFilter.Filter mFilter; private BluetoothDeviceFilter.Filter mFilter;
@VisibleForTesting
boolean mScanEnabled;
BluetoothDevice mSelectedDevice; BluetoothDevice mSelectedDevice;
LocalBluetoothAdapter mLocalAdapter; LocalBluetoothAdapter mLocalAdapter;
@@ -216,8 +219,25 @@ public abstract class DeviceListPreferenceFragment extends
} }
} }
@VisibleForTesting
void enableScanning() {
// LocalBluetoothAdapter already handles repeated scan requests
mLocalAdapter.startScanning(true);
mScanEnabled = true;
}
@VisibleForTesting
void disableScanning() {
mLocalAdapter.stopScanning();
mScanEnabled = false;
}
@Override @Override
public void onScanningStateChanged(boolean started) {} public void onScanningStateChanged(boolean started) {
if (!started && mScanEnabled) {
mLocalAdapter.startScanning(true);
}
}
@Override @Override
public void onBluetoothStateChanged(int bluetoothState) {} public void onBluetoothStateChanged(int bluetoothState) {}

View File

@@ -25,7 +25,6 @@ import android.os.Bundle;
import android.os.UserManager; import android.os.UserManager;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R; import com.android.settings.R;
@@ -41,7 +40,6 @@ import java.util.List;
* connection management. * connection management.
*/ */
public final class DevicePickerFragment extends DeviceListPreferenceFragment { public final class DevicePickerFragment extends DeviceListPreferenceFragment {
private static final int MENU_ID_REFRESH = Menu.FIRST;
private static final String KEY_BT_DEVICE_LIST = "bt_device_list"; private static final String KEY_BT_DEVICE_LIST = "bt_device_list";
private static final String TAG = "DevicePickerFragment"; private static final String TAG = "DevicePickerFragment";
@@ -52,7 +50,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
private boolean mNeedAuth; private boolean mNeedAuth;
private String mLaunchPackage; private String mLaunchPackage;
private String mLaunchClass; private String mLaunchClass;
private boolean mStartScanOnStart; private boolean mScanAllowed;
@Override @Override
void initPreferencesFromPreferenceScreen() { void initPreferencesFromPreferenceScreen() {
@@ -66,22 +64,9 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(Menu.NONE, MENU_ID_REFRESH, 0, R.string.bluetooth_search_for_devices)
.setEnabled(true)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ID_REFRESH:
mLocalAdapter.startScanning(true);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override @Override
public int getMetricsCategory() { public int getMetricsCategory() {
return MetricsEvent.BLUETOOTH_DEVICE_PICKER; return MetricsEvent.BLUETOOTH_DEVICE_PICKER;
@@ -92,8 +77,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getActivity().setTitle(getString(R.string.device_picker)); getActivity().setTitle(getString(R.string.device_picker));
UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
mStartScanOnStart = !um.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH) mScanAllowed = !um.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH);
&& (savedInstanceState == null); // don't start scan after rotation
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@@ -102,12 +86,18 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
super.onStart(); super.onStart();
addCachedDevices(); addCachedDevices();
mSelectedDevice = null; mSelectedDevice = null;
if (mStartScanOnStart) { if (mScanAllowed) {
mLocalAdapter.startScanning(true); enableScanning();
mStartScanOnStart = false;
} }
} }
@Override
public void onStop() {
// Try disable scanning no matter what, no effect if enableScanning has not been called
disableScanning();
super.onStop();
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
@@ -121,7 +111,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
@Override @Override
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
mLocalAdapter.stopScanning(); disableScanning();
LocalBluetoothPreferences.persistSelectedDeviceInPicker( LocalBluetoothPreferences.persistSelectedDeviceInPicker(
getActivity(), mSelectedDevice.getAddress()); getActivity(), mSelectedDevice.getAddress());
if ((btPreference.getCachedDevice().getBondState() == if ((btPreference.getCachedDevice().getBondState() ==
@@ -135,12 +125,15 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice,
int bondState) { int bondState) {
BluetoothDevice device = cachedDevice.getDevice();
if (!device.equals(mSelectedDevice)) {
return;
}
if (bondState == BluetoothDevice.BOND_BONDED) { if (bondState == BluetoothDevice.BOND_BONDED) {
BluetoothDevice device = cachedDevice.getDevice(); sendDevicePickedIntent(device);
if (device.equals(mSelectedDevice)) { finish();
sendDevicePickedIntent(device); } else if (bondState == BluetoothDevice.BOND_NONE) {
finish(); enableScanning();
}
} }
} }
@@ -149,7 +142,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment {
super.onBluetoothStateChanged(bluetoothState); super.onBluetoothStateChanged(bluetoothState);
if (bluetoothState == BluetoothAdapter.STATE_ON) { if (bluetoothState == BluetoothAdapter.STATE_ON) {
mLocalAdapter.startScanning(false); enableScanning();
} }
} }

View File

@@ -23,7 +23,9 @@ import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
@@ -103,7 +105,7 @@ public class BluetoothPairingDetailTest {
mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory; mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
mFragment.mDeviceListGroup = mAvailableDevicesCategory; mFragment.mDeviceListGroup = mAvailableDevicesCategory;
mFragment.startScanning(); mFragment.enableScanning();
verify(mLocalAdapter).startScanning(true); verify(mLocalAdapter).startScanning(true);
verify(mAvailableDevicesCategory).removeAll(); verify(mAvailableDevicesCategory).removeAll();
@@ -130,4 +132,54 @@ public class BluetoothPairingDetailTest {
verify(mFragment).finish(); verify(mFragment).finish();
} }
@Test
public void testOnScanningStateChanged_restartScanAfterInitialScanning() {
mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
mFragment.mFooterPreference = mFooterPreference;
mFragment.mDeviceListGroup = mAvailableDevicesCategory;
doNothing().when(mFragment).addDeviceCategory(any(), anyInt(), any(), anyBoolean());
// Initial Bluetooth ON will trigger scan enable, list clear and scan start
mFragment.updateContent(BluetoothAdapter.STATE_ON);
verify(mFragment).enableScanning();
assertThat(mAvailableDevicesCategory.getPreferenceCount()).isEqualTo(0);
verify(mLocalAdapter).startScanning(true);
// Subsequent scan started event will not trigger start/stop nor list clear
mFragment.onScanningStateChanged(true);
verify(mLocalAdapter, times(1)).startScanning(anyBoolean());
verify(mAvailableDevicesCategory, times(1)).setProgress(true);
// Subsequent scan finished event will trigger scan start without list clean
mFragment.onScanningStateChanged(false);
verify(mLocalAdapter, times(2)).startScanning(true);
verify(mAvailableDevicesCategory, times(2)).setProgress(true);
// Subsequent scan started event will not trigger any change
mFragment.onScanningStateChanged(true);
verify(mLocalAdapter, times(2)).startScanning(anyBoolean());
verify(mAvailableDevicesCategory, times(3)).setProgress(true);
verify(mLocalAdapter, never()).stopScanning();
// Disable scanning will trigger scan stop
mFragment.disableScanning();
verify(mLocalAdapter, times(1)).stopScanning();
// Subsequent scan start event will not trigger any change besides progress circle
mFragment.onScanningStateChanged(true);
verify(mAvailableDevicesCategory, times(4)).setProgress(true);
// However, subsequent scan finished event won't trigger new scan start and will stop
// progress circle from spinning
mFragment.onScanningStateChanged(false);
verify(mAvailableDevicesCategory, times(1)).setProgress(false);
verify(mLocalAdapter, times(2)).startScanning(anyBoolean());
verify(mLocalAdapter, times(1)).stopScanning();
// Verify that clean up only happen once at initialization
verify(mAvailableDevicesCategory, times(1)).removeAll();
}
} }

View File

@@ -19,9 +19,12 @@ package com.android.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
@@ -83,6 +86,92 @@ public class DeviceListPreferenceFragmentTest {
assertThat(mMyDevicePreference.getTitle()).isEqualTo(FOOTAGE_MAC_STRING); assertThat(mMyDevicePreference.getTitle()).isEqualTo(FOOTAGE_MAC_STRING);
} }
@Test
public void testEnableDisableScanning_testStateAfterEanbleDisable() {
mFragment.enableScanning();
verify(mLocalAdapter).startScanning(true);
assertThat(mFragment.mScanEnabled).isTrue();
mFragment.disableScanning();
verify(mLocalAdapter).stopScanning();
assertThat(mFragment.mScanEnabled).isFalse();
}
@Test
public void testScanningStateChanged_testScanStarted() {
mFragment.enableScanning();
assertThat(mFragment.mScanEnabled).isTrue();
verify(mLocalAdapter).startScanning(true);
mFragment.onScanningStateChanged(true);
verify(mLocalAdapter, times(1)).startScanning(anyBoolean());
}
@Test
public void testScanningStateChanged_testScanFinished() {
// Could happen when last scanning not done while current scan gets enabled
mFragment.enableScanning();
verify(mLocalAdapter).startScanning(true);
assertThat(mFragment.mScanEnabled).isTrue();
mFragment.onScanningStateChanged(false);
verify(mLocalAdapter, times(2)).startScanning(true);
}
@Test
public void testScanningStateChanged_testScanStateMultiple() {
// Could happen when last scanning not done while current scan gets enabled
mFragment.enableScanning();
assertThat(mFragment.mScanEnabled).isTrue();
verify(mLocalAdapter).startScanning(true);
mFragment.onScanningStateChanged(true);
verify(mLocalAdapter, times(1)).startScanning(anyBoolean());
mFragment.onScanningStateChanged(false);
verify(mLocalAdapter, times(2)).startScanning(true);
mFragment.onScanningStateChanged(true);
verify(mLocalAdapter, times(2)).startScanning(anyBoolean());
mFragment.disableScanning();
verify(mLocalAdapter).stopScanning();
mFragment.onScanningStateChanged(false);
verify(mLocalAdapter, times(2)).startScanning(anyBoolean());
mFragment.onScanningStateChanged(true);
verify(mLocalAdapter, times(2)).startScanning(anyBoolean());
}
@Test
public void testScanningStateChanged_testScanFinishedAfterDisable() {
mFragment.enableScanning();
verify(mLocalAdapter).startScanning(true);
assertThat(mFragment.mScanEnabled).isTrue();
mFragment.disableScanning();
verify(mLocalAdapter).stopScanning();
assertThat(mFragment.mScanEnabled).isFalse();
mFragment.onScanningStateChanged(false);
verify(mLocalAdapter, times(1)).startScanning(anyBoolean());
}
@Test
public void testScanningStateChanged_testScanStartedAfterDisable() {
mFragment.enableScanning();
verify(mLocalAdapter).startScanning(true);
assertThat(mFragment.mScanEnabled).isTrue();
mFragment.disableScanning();
verify(mLocalAdapter).stopScanning();
assertThat(mFragment.mScanEnabled).isFalse();
mFragment.onScanningStateChanged(true);
verify(mLocalAdapter, times(1)).startScanning(anyBoolean());
}
/** /**
* Fragment to test since {@code DeviceListPreferenceFragment} is abstract * Fragment to test since {@code DeviceListPreferenceFragment} is abstract
*/ */
@@ -98,14 +187,10 @@ public class DeviceListPreferenceFragmentTest {
} }
@Override @Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {}
}
@Override @Override
void initPreferencesFromPreferenceScreen() { void initPreferencesFromPreferenceScreen() {}
}
@Override @Override
public String getDeviceListKey() { public String getDeviceListKey() {