diff options
author | Eugene Susla <eugenesusla@google.com> | 2017-02-23 18:24:39 -0800 |
---|---|---|
committer | Eugene Susla <eugenesusla@google.com> | 2017-03-08 16:52:14 -0800 |
commit | e70e6aa62c6f3a9a79624a4f9d97df95edda0364 (patch) | |
tree | c75658771072c342ce6b35d132a2e4363721bcd0 /packages/CompanionDeviceManager | |
parent | 35fe82fd542bd030dfdacae848acbcaa6c781a0b (diff) |
Support multiple filters per association request
By supporting multiple filters per one request we should be able to cover
multiple kinds of use cases such as:
- Letting the user select from a list of devices of more then one medium
type (e.g. Bluetooth and BLE)
- Allowing to provide multiple criteria for any field (e.g. filtering by
more than one service UUID)
Bug: 30932767
Test: Provide multiple filters and ensure that devices matching either are
shown in the list to choose from.
Ensure wifi SSIDs are shown in the list if wifi filter is provided
Change-Id: I6621da388e2bf4ed97c5af2692629a321d0b63c7
Diffstat (limited to 'packages/CompanionDeviceManager')
3 files changed, 162 insertions, 53 deletions
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index 65cac09a09b3..34bc4ebcd0aa 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -26,6 +26,8 @@ <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <application android:allowClearUserData="true" diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java index 12bab18c88c9..14b9de59bf5a 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -16,10 +16,9 @@ package com.android.companiondevicemanager; -import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayName; +import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; import android.app.Activity; -import android.bluetooth.BluetoothDevice; import android.companion.CompanionDeviceManager; import android.content.Intent; import android.content.pm.PackageManager; @@ -34,6 +33,8 @@ import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; +import com.android.companiondevicemanager.DeviceDiscoveryService.DeviceFilterPair; + public class DeviceChooserActivity extends Activity { private static final boolean DEBUG = false; @@ -55,11 +56,11 @@ public class DeviceChooserActivity extends Activity { if (getService().mRequest.isSingleDevice()) { setContentView(R.layout.device_confirmation); - final BluetoothDevice selectedDevice = getService().mDevicesFound.get(0); + final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0); setTitle(Html.fromHtml(getString( R.string.confirmation_title, getCallingAppName(), - getDeviceDisplayName(selectedDevice)), 0)); + selectedDevice.getDisplayName()), 0)); getService().mSelectedDevice = selectedDevice; } else { setContentView(R.layout.device_chooser); @@ -127,10 +128,11 @@ public class DeviceChooserActivity extends Activity { return DeviceDiscoveryService.sInstance; } - protected void onPairTapped(BluetoothDevice selectedDevice) { - getService().onDeviceSelected(getCallingPackage(), selectedDevice.getAddress()); + protected void onPairTapped(DeviceFilterPair selectedDevice) { + getService().onDeviceSelected( + getCallingPackage(), getDeviceMacAddress(selectedDevice.device)); setResult(RESULT_OK, - new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice)); + new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device)); finish(); } }
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java index f0f910848943..e1e60bb99374 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java @@ -16,8 +16,10 @@ package com.android.companiondevicemanager; -import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayName; -import static android.companion.BluetoothLEDeviceFilter.nullsafe; +import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal; +import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; + +import static com.android.internal.util.ArrayUtils.isEmpty; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,28 +34,38 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.companion.AssociationRequest; +import android.companion.BluetoothDeviceFilter; +import android.companion.BluetoothDeviceFilterUtils; import android.companion.BluetoothLEDeviceFilter; import android.companion.CompanionDeviceManager; +import android.companion.DeviceFilter; import android.companion.ICompanionDeviceDiscoveryService; import android.companion.ICompanionDeviceDiscoveryServiceCallback; import android.companion.IFindDeviceCallback; +import android.companion.WifiDeviceFilter; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; import android.graphics.drawable.Drawable; +import android.net.wifi.WifiManager; import android.os.IBinder; +import android.os.Parcelable; import android.os.RemoteException; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; + import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Objects; public class DeviceDiscoveryService extends Service { @@ -63,12 +75,16 @@ public class DeviceDiscoveryService extends Service { static DeviceDiscoveryService sInstance; private BluetoothAdapter mBluetoothAdapter; - private BluetoothLEDeviceFilter mFilter; - private ScanFilter mScanFilter; + private WifiManager mWifiManager; private ScanSettings mDefaultScanSettings = new ScanSettings.Builder().build(); - AssociationRequest<?> mRequest; - List<BluetoothDevice> mDevicesFound; - BluetoothDevice mSelectedDevice; + private List<DeviceFilter<?>> mFilters; + private List<BluetoothLEDeviceFilter> mBLEFilters; + private List<BluetoothDeviceFilter> mBluetoothFilters; + private List<WifiDeviceFilter> mWifiFilters; + private List<ScanFilter> mBLEScanFilters; + AssociationRequest mRequest; + List<DeviceFilterPair> mDevicesFound; + DeviceFilterPair mSelectedDevice; DevicesAdapter mDevicesAdapter; IFindDeviceCallback mFindCallback; ICompanionDeviceDiscoveryServiceCallback mServiceCallback; @@ -95,11 +111,13 @@ public class DeviceDiscoveryService extends Service { private final ScanCallback mBLEScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { - final BluetoothDevice device = result.getDevice(); + final DeviceFilterPair<ScanResult> deviceFilterPair + = DeviceFilterPair.findMatch(result, mBLEFilters); + if (deviceFilterPair == null) return; if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) { - onDeviceLost(device); + onDeviceLost(deviceFilterPair); } else { - onDeviceFound(device); + onDeviceFound(deviceFilterPair); } } }; @@ -109,15 +127,35 @@ public class DeviceDiscoveryService extends Service { private BroadcastReceiver mBluetoothDeviceFoundBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - final BluetoothDevice device = intent.getParcelableExtra( - BluetoothDevice.EXTRA_DEVICE); - if (!mFilter.matches(device)) return; // ignore device - + final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + final DeviceFilterPair<BluetoothDevice> deviceFilterPair + = DeviceFilterPair.findMatch(device, mBluetoothFilters); + if (deviceFilterPair == null) return; if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) { - onDeviceFound(device); + onDeviceFound(deviceFilterPair); } else { - onDeviceLost(device); + onDeviceLost(deviceFilterPair); + } + } + }; + + private BroadcastReceiver mWifiDeviceFoundBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { + List<android.net.wifi.ScanResult> scanResults = mWifiManager.getScanResults(); + + if (DEBUG) { + Log.i(LOG_TAG, "Wifi scan results: " + TextUtils.join("\n", scanResults)); + } + + for (int i = 0; i < scanResults.size(); i++) { + DeviceFilterPair<android.net.wifi.ScanResult> deviceFilterPair = + DeviceFilterPair.findMatch(scanResults.get(i), mWifiFilters); + if (deviceFilterPair != null) onDeviceFound(deviceFilterPair); + } } + } }; @@ -135,6 +173,7 @@ public class DeviceDiscoveryService extends Service { mBluetoothAdapter = getSystemService(BluetoothManager.class).getAdapter(); mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); + mWifiManager = getSystemService(WifiManager.class); mDevicesFound = new ArrayList<>(); mDevicesAdapter = new DevicesAdapter(); @@ -142,23 +181,39 @@ public class DeviceDiscoveryService extends Service { sInstance = this; } - private void startDiscovery(AssociationRequest<?> request) { - //TODO support other protocols as well + private void startDiscovery(AssociationRequest request) { mRequest = request; - mFilter = nullsafe((BluetoothLEDeviceFilter) request.getDeviceFilter()); - mScanFilter = mFilter.getScanFilter(); + + mFilters = request.getDeviceFilters(); + mWifiFilters = ArrayUtils.filter(mFilters, WifiDeviceFilter.class); + mBluetoothFilters = ArrayUtils.filter(mFilters, BluetoothDeviceFilter.class); + mBLEFilters = ArrayUtils.filter(mFilters, BluetoothLEDeviceFilter.class); + mBLEScanFilters = ArrayUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter); reset(); - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BluetoothDevice.ACTION_FOUND); - intentFilter.addAction(BluetoothDevice.ACTION_DISAPPEARED); + if (shouldScan(mBluetoothFilters)) { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothDevice.ACTION_FOUND); + intentFilter.addAction(BluetoothDevice.ACTION_DISAPPEARED); - registerReceiver(mBluetoothDeviceFoundBroadcastReceiver, intentFilter); - mBluetoothAdapter.startDiscovery(); + registerReceiver(mBluetoothDeviceFoundBroadcastReceiver, intentFilter); + mBluetoothAdapter.startDiscovery(); + } - mBLEScanner.startScan( - Collections.singletonList(mScanFilter), mDefaultScanSettings, mBLEScanCallback); + if (shouldScan(mBLEFilters)) { + mBLEScanner.startScan(mBLEScanFilters, mDefaultScanSettings, mBLEScanCallback); + } + + if (shouldScan(mWifiFilters)) { + registerReceiver(mWifiDeviceFoundBroadcastReceiver, + new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + mWifiManager.startScan(); + } + } + + private boolean shouldScan(List<? extends DeviceFilter> mediumSpecificFilters) { + return !isEmpty(mediumSpecificFilters) || isEmpty(mFilters); } private void reset() { @@ -178,25 +233,18 @@ public class DeviceDiscoveryService extends Service { mBluetoothAdapter.cancelDiscovery(); mBLEScanner.stopScan(mBLEScanCallback); unregisterReceiver(mBluetoothDeviceFoundBroadcastReceiver); + unregisterReceiver(mWifiDeviceFoundBroadcastReceiver); stopSelf(); } - private void onDeviceFound(BluetoothDevice device) { + private void onDeviceFound(@Nullable DeviceFilterPair device) { if (mDevicesFound.contains(device)) { return; } - if (DEBUG) { - Log.i(LOG_TAG, "Considering device " + getDeviceDisplayName(device)); - } + if (DEBUG) Log.i(LOG_TAG, "Found device " + device.getDisplayName() + " " + + getDeviceMacAddress(device.device)); - if (!mFilter.matches(device)) { - return; - } - - if (DEBUG) { - Log.i(LOG_TAG, "Found device " + getDeviceDisplayName(device)); - } if (mDevicesFound.isEmpty()) { onReadyToShowUI(); } @@ -217,12 +265,10 @@ public class DeviceDiscoveryService extends Service { } } - private void onDeviceLost(BluetoothDevice device) { + private void onDeviceLost(@Nullable DeviceFilterPair device) { mDevicesFound.remove(device); mDevicesAdapter.notifyDataSetChanged(); - if (DEBUG) { - Log.i(LOG_TAG, "Lost device " + getDeviceDisplayName(device)); - } + if (DEBUG) Log.i(LOG_TAG, "Lost device " + device.getDisplayName()); } void onDeviceSelected(String callingPackage, String deviceAddress) { @@ -236,7 +282,8 @@ public class DeviceDiscoveryService extends Service { } } - class DevicesAdapter extends ArrayAdapter<BluetoothDevice> { + class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> { + //TODO wifi icon private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); private Drawable icon(int drawableRes) { @@ -261,8 +308,8 @@ public class DeviceDiscoveryService extends Service { return view; } - private void bind(TextView textView, BluetoothDevice device) { - textView.setText(getDeviceDisplayName(device)); + private void bind(TextView textView, DeviceFilterPair device) { + textView.setText(device.getDisplayName()); textView.setBackgroundColor( device.equals(mSelectedDevice) ? Color.GRAY @@ -285,4 +332,62 @@ public class DeviceDiscoveryService extends Service { return textView; } } + + /** + * A pair of device and a filter that matched this device if any. + * + * @param <T> device type + */ + static class DeviceFilterPair<T extends Parcelable> { + public final T device; + @Nullable + public final DeviceFilter<T> filter; + + private DeviceFilterPair(T device, @Nullable DeviceFilter<T> filter) { + this.device = device; + this.filter = filter; + } + + /** + * {@code (device, null)} if the filters list is empty or null + * {@code null} if none of the provided filters match the device + * {@code (device, filter)} where filter is among the list of filters and matches the device + */ + @Nullable + public static <T extends Parcelable> DeviceFilterPair<T> findMatch( + T dev, @Nullable List<? extends DeviceFilter<T>> filters) { + if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null); + final DeviceFilter<T> matchingFilter = ArrayUtils.find(filters, (f) -> f.matches(dev)); + return matchingFilter != null ? new DeviceFilterPair<>(dev, matchingFilter) : null; + } + + public String getDisplayName() { + if (filter == null) { + Preconditions.checkNotNull(device); + if (device instanceof BluetoothDevice) { + return getDeviceDisplayNameInternal((BluetoothDevice) device); + } else if (device instanceof android.net.wifi.ScanResult) { + return getDeviceDisplayNameInternal((android.net.wifi.ScanResult) device); + } else if (device instanceof ScanResult) { + return getDeviceDisplayNameInternal(((ScanResult) device).getDevice()); + } else { + throw new IllegalArgumentException("Unknown device type: " + device.getClass()); + } + } + return filter.getDeviceDisplayName(device); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DeviceFilterPair<?> that = (DeviceFilterPair<?>) o; + return Objects.equals(getDeviceMacAddress(device), getDeviceMacAddress(that.device)); + } + + @Override + public int hashCode() { + return Objects.hash(getDeviceMacAddress(device)); + } + } } |