Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to filter nearby bluetooth devices by type

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)
}

enter image description here

like image 851
Nguyen Minh Binh Avatar asked Sep 01 '25 20:09

Nguyen Minh Binh


2 Answers

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)

like image 162
Nguyen Minh Binh Avatar answered Sep 07 '25 22:09

Nguyen Minh Binh


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);
like image 29
Maxim Firsoff Avatar answered Sep 08 '25 00:09

Maxim Firsoff