I want to scan and bond with nearby android smartwatches which are running WearOS. I followed the instruction here using CompanionDeviceManager: https://developer.android.com/guide/topics/connectivity/companion-device-pairing
The CompanionDeviceManager always shows a list of all device types including other mobile phones, laptops, bluetooth speakers, and watches (Both bonded and unpaired). Comparing to Google Wear OS app, it also use CompanionDeviceManager (since I saw the same popup UI) and it always show the Android Watch only.
How does it filter the smartwatch among plenty of other bluetooth devices?
Here is my code:
private fun scanLeDevice(enable: Boolean) {
val deviceFilter: BluetoothLeDeviceFilter = BluetoothLeDeviceFilter.Builder()
.build()
// The argument provided in setSingleDevice() determines whether a single
// device name or a list of device names is presented to the user as
// pairing options.
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
.setSingleDevice(false)
.addDeviceFilter(deviceFilter)
.build()
// When the app tries to pair with the Bluetooth device, show the
// appropriate pairing request dialog to the user.
deviceManager.associate(pairingRequest,
object : CompanionDeviceManager.Callback() {
override fun onDeviceFound(chooserLauncher: IntentSender) {
startIntentSenderForResult(chooserLauncher,
Companion.SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
}
override fun onFailure(error: CharSequence?) {
// Handle failure
}
}, null)
}
Remark: This isn't completed answer since I don't know what's exactly filter-mask for each device type, however at least it can help to filter the Android smartwatch
I decompiled the Google WearOS app and looking at source file FindDeviceControllerSdk26.java. Here are interested things:
private static final byte[] BLE_WEAR_MATCH_DATA = { 0, 20 };
private static final byte[] BLE_WEAR_MATCH_DATA_LEGACY = { 0, 19 };
private static final byte[] BLE_WEAR_MATCH_MASK = { 0, -1 };
private final void showAssociationDialog()
{
Object localObject1 = new ArrayList();
Object localObject3 = new FindDeviceDeviceFilterBuilder();
medium = 1;
Object localObject4 = new ScanFilter.Builder();
byte[] arrayOfByte = BLE_WEAR_MATCH_DATA;
Object localObject2 = BLE_WEAR_MATCH_MASK;
scanFilter = ((ScanFilter.Builder)localObject4).setManufacturerData(224, arrayOfByte, (byte[])localObject2).build();
FindDeviceAssociationRequestBuilder.unbox_addDeviceFilter$ar$ds((FindDeviceDeviceFilterBuilder)localObject3, (List)localObject1);
localObject3 = new FindDeviceDeviceFilterBuilder();
medium = 1;
scanFilter = new ScanFilter.Builder().setManufacturerData(224, BLE_WEAR_MATCH_DATA_LEGACY, (byte[])localObject2).build();
FindDeviceAssociationRequestBuilder.unbox_addDeviceFilter$ar$ds((FindDeviceDeviceFilterBuilder)localObject3, (List)localObject1);
if (!deviceWhitelist.shouldShowAllDevices())
{
localObject2 = (GServicesFindDeviceWhitelist)deviceWhitelist;
((GServicesFindDeviceWhitelist)localObject2).initializeIfNecessary();
localObject2 = cachedDeviceWhitelistRegularExpression;
if (!TextUtils.isEmpty((CharSequence)localObject2))
{
localObject3 = new FindDeviceDeviceFilterBuilder();
medium = 0;
namePatternString = ((String)localObject2);
FindDeviceAssociationRequestBuilder.unbox_addDeviceFilter$ar$ds((FindDeviceDeviceFilterBuilder)localObject3, (List)localObject1);
}
}
else
{
localObject2 = new FindDeviceDeviceFilterBuilder();
medium = 0;
namePatternString = ".*";
FindDeviceAssociationRequestBuilder.unbox_addDeviceFilter$ar$ds((FindDeviceDeviceFilterBuilder)localObject2, (List)localObject1);
}
localObject2 = new AssociationRequest.Builder().setSingleDevice(false);
localObject3 = ((List)localObject1).iterator();
while (((Iterator)localObject3).hasNext())
{
localObject1 = (FindDeviceDeviceFilterBuilder)((Iterator)localObject3).next();
if (medium != 0)
{
localObject4 = new BluetoothLeDeviceFilter.Builder();
localObject1 = scanFilter;
if (localObject1 != null) {
((BluetoothLeDeviceFilter.Builder)localObject4).setScanFilter((ScanFilter)localObject1);
}
localObject1 = ((BluetoothLeDeviceFilter.Builder)localObject4).build();
}
else
{
localObject4 = new BluetoothDeviceFilter.Builder();
if (!Platform.stringIsNullOrEmpty(namePatternString)) {
((BluetoothDeviceFilter.Builder)localObject4).setNamePattern(Pattern.compile(namePatternString));
}
localObject1 = ((BluetoothDeviceFilter.Builder)localObject4).build();
}
((AssociationRequest.Builder)localObject2).addDeviceFilter((DeviceFilter)localObject1);
}
localObject1 = ((AssociationRequest.Builder)localObject2).build();
viewClient.hideRefreshButton();
cwEventLogger.incrementCounter(Counter.COMPANION_PAIR_CDM_ASSOCIATE_REQUEST);
companionDeviceManager.associate((AssociationRequest)localObject1, deviceManagerCallback, null);
}
I then rewrite my scan function as following
private val BLE_WEAR_MATCH_DATA = byteArrayOf(0, 20)
private val BLE_WEAR_MATCH_DATA_LEGACY = byteArrayOf(0, 19)
private val BLE_WEAR_MATCH_MASK = byteArrayOf(0, -1)
private fun scanLeDevice(enable: Boolean) {
val scanFilter = ScanFilter.Builder().setManufacturerData(224,BLE_WEAR_MATCH_DATA_LEGACY,BLE_WEAR_MATCH_MASK).build()
val deviceFilter: BluetoothLeDeviceFilter = BluetoothLeDeviceFilter.Builder()
.setScanFilter(scanFilter)
.setNamePattern(Pattern.compile(".*"))
.build()
// The argument provided in setSingleDevice() determines whether a single
// device name or a list of device names is presented to the user as
// pairing options.
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
.setSingleDevice(false)
.addDeviceFilter(deviceFilter)
.build()
// When the app tries to pair with the Bluetooth device, show the
// appropriate pairing request dialog to the user.
deviceManager.associate(pairingRequest,
object : CompanionDeviceManager.Callback() {
override fun onDeviceFound(chooserLauncher: IntentSender) {
startIntentSenderForResult(chooserLauncher,
Companion.SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
}
override fun onFailure(error: CharSequence?) {
// Handle failure
}
}, null)
}
I would hope someone can help to describe what do these maskes stand for, and then I will help to update to make this answer completely.
private val BLE_WEAR_MATCH_DATA = byteArrayOf(0, 20)
private val BLE_WEAR_MATCH_DATA_LEGACY = byteArrayOf(0, 19)
private val BLE_WEAR_MATCH_MASK = byteArrayOf(0, -1)
Please look at that article: https://medium.com/@martijn.van.welie/making-android-ble-work-part-1-a736dcd53b02
TL;TR
String[] names = new String[]{"Polar H7 391BB014"};
List<ScanFilter> filters = null;
if(names != null) {
filters = new ArrayList<>();
for (String name : names) {
ScanFilter filter = new ScanFilter.Builder()
.setDeviceName(name)
.build();
filters.add(filter);
}
}
scanner.startScan(filters, scanSettings, scanCallback);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With