NearDrop icon indicating copy to clipboard operation
NearDrop copied to clipboard

Advertising BLE service data from macOS

Open avaidyam opened this issue 2 years ago • 10 comments

I'm fairly sure you should be able to do this on macOS (not iOS perhaps?). Here's an example. Specifically, CBAdvertisementDataServiceDataKey should be what you need.

avaidyam avatar Sep 26 '23 20:09 avaidyam

I modified my test app to pass a Data for that key (the docs say that its value is a [CBUUID:Data] dictionary so that's what I was trying), it's still complaining that it's not an allowed key. Снимок экрана 2023-09-26 в 23 09 24

The service as seen by Android doesn't have any service data either: Screenshot_20230926-230948

grishka avatar Sep 26 '23 20:09 grishka

Ah, I see. It looks like the nearby library actually recognizes this limitation too with a workaround:

/**
 * Starts advertising service data in a way that is supported by CoreBluetooth.
 *
 * Since CoreBluetooth doesn't support setting the @c CBAdvertisementDataServiceDataKey key, the
 * service list is advertised using @c CBAdvertisementDataServiceUUIDsKey and the associated data is
 * advertised using @c CBAdvertisementDataLocalNameKey. Since @c CBAdvertisementDataLocalNameKey
 * does not support binary data, the value is base64 encoded and truncated if the resulting value is
 * longer than 22 bytes. This also means we can only support advertising a single service.
 *
 * @param serviceData A dictionary that contains service-specific advertisement data.
 * @param completionHandler Called on a private queue with @c nil if successfully started
 *                          advertising or an error if one has occured.
 */
- (void)startAdvertisingData:(NSDictionary<CBUUID *, NSData *> *)serviceData
           completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler;

Another relevant snippet:

  // Apple devices don't support advertising service data, so Apple devices advertise a base64
  // encoded local name, while other devices advertise service data. Here we attempt to reconstruct
  // service data by decoding the local name. If successful, this is possibly a Nearby advertisement
  // on an Apple device.
  NSString *localName = advertisementData[CBAdvertisementDataLocalNameKey];

  // ...  

  // A Nearby Apple advertisement should only have a single service, so simply grab the first one if
  // it exists.
  NSArray<CBUUID *> *serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey];

Yet another relevant snippet:

  // Maps service UUIDs to their service data.
  //
  // For each platform should follow to set the service UUID(key) and service
  // data(value):
  //
  // iOS    : 16 bit service UUID (type=0x03) + LocalName data (type=0x08)
  // Windows: Service data (type=0x16)
  // Android: 16 bit service UUID (type=0x03) + Service data (type=0x16)
  absl::flat_hash_map<Uuid, nearby::ByteArray> service_data;

Can you see if this method works instead?

avaidyam avatar Sep 26 '23 21:09 avaidyam

I tried this: Screenshot_20230927-004716 Didn't trigger neither the notification nor the MDNS service.

grishka avatar Sep 26 '23 21:09 grishka

It looks like iOS/macOS BLE advertisement was only added to the google/nearby project last month by @bourdakos1 actually, so perhaps the Android/Play services system library isn't ready for it yet. Of note, it looks like migration of the Nearby Share code to the public project only started 2 weeks ago as well.

avaidyam avatar Sep 26 '23 22:09 avaidyam

Okay then. Maybe they're preparing to ship their (inevitably worse lol) Nearby Share app for macOS.

Another thing to keep in mind is that google/nearby is their universal cross-platform implementation in C++ that is maybe used by projects like Chromium, but Play Services uses another, closed-source (as far as I can tell) pure-Java implementation.

grishka avatar Sep 26 '23 22:09 grishka

I decompiled the latest (as of now) version of Play Services and found this: Снимок экрана 2024-03-21 в 01 29 58 So this fallback is implemented, but it's behind what looks like a server flag, that defaults to false: Снимок экрана 2024-03-21 в 01 28 41 (I renamed things here and there to make the code easier to understand)

grishka avatar Mar 20 '24 22:03 grishka

In case someone wants to look at it themselves, the bulk of code dealing with the reception of these BLE advertisements is in com.google.android.gms.nearby.sharing.discovery.FastInitiation

grishka avatar Mar 20 '24 22:03 grishka

Would using private apis be off the menu? The BluetoothHCILESetAdvertisingParameters https://github.com/seemoo-lab/internalblue/blob/master/macos/IOBluetoothExtended/IOBluetoothExtended/IOBluetoothHostController.h#L126C8-L126C40

eg https://github.com/RadiusNetworks/scanbeacon-gem/blob/f3f9ff9123593ff821b3dba7f253d9735e10082f/ext/core_bluetooth/core_bluetooth.m#L213-L222 ?

EionRobb avatar Aug 26 '24 00:08 EionRobb

I'm totally fine with using private APIs, I'll test this one, although I don't have my hopes up

grishka avatar Aug 26 '24 15:08 grishka

Oh these APIs are the ones I've already tested and they didn't work on modern macOS versions https://github.com/grishka/NearDrop/issues/4#issuecomment-1714705145

grishka avatar Aug 26 '24 16:08 grishka