Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specify a Bluetooth Scanning API. #239

Merged
merged 6 commits into from
Aug 2, 2016

Conversation

jyasskin
Copy link
Member

@jyasskin jyasskin commented May 9, 2016

</p>
<ol>
<li>
If |event| doesn't <a for="BluetoothLEScanFilter">match</a>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this leaves no way to do a completely open scan. It has to be filtered for some UUID or Manufacturer ID. Is that OK, or do we need an extra filter type to match everything? (I don't want to make no-filters the sign for completely-open because that makes the API non-monotonic: reducing the number of filters would match less, less, less, and then more advertisements.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to #234 I think developers will have good use cases for matching everything.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beaufortfrancois
Copy link
Member

I wonder why developers would use requestDevice rather than requestLEScan. The latter seems more powerful and better in terms of UX. Could you comment on the drawbacks?

@jyasskin
Copy link
Member Author

@beaufortfrancois At the moment, requestLEScan gets you a bunch of BluetoothDevice objects, but all of their .gatt fields are null (unless you already had permission to communicate with them), so you have to use requestDevice to communicate. I think it's worthwhile to give folks a way to upgrade, but we need to ask the security UI folks what it should look like, and it'll probably be designed to push people toward just using requestDevice() if they don't really need to control the scan.

@beaufortfrancois
Copy link
Member

beaufortfrancois commented May 13, 2016

This makes more sense now ;)
Thank you @jyasskin!

So once you have a BluetoothDevice object, the way to know if you have to use requestDevice is to check device.gatt... but how do you make sure that only this device will be shown in the bluetooth chooser?

navigator.bluetooth.requestLEScan({
  filters: [{manufacturerData: 0x004C}]
}).then(() => {
  navigator.bluetooth.addEventListener('advertisementreceived', event => {
    if (!event.device.gatt) {
      // How do I connect to this device specifically?
    }
  })
})

@jyasskin
Copy link
Member Author

There's no API for that yet, and I've mailed the security UI folks to ask their opinion on it. The benefits of the chooser are that 1) the user has to pay enough attention to make a choice, and 2) the user can override the page's idea of which device is best. If we ensure that exactly one device appears in the chooser that opens as a result of an advertisement, then we lose both of those. On the other hand, we might be able to add a hint field to RequestDeviceOptions, which could ensure that the preferred device is listed first if it matches the other filters.

@Vudentz
Copy link

Vudentz commented May 20, 2016

Some devices may use non-connectable flag, e.g. broadcaster, in that case requestDevice might actually not work in the end so perhaps we need some field to indicate this to the application.

@beaufortfrancois
Copy link
Member

beaufortfrancois commented May 25, 2016

The connectable field would be indeed valuable to add to the BluetoothDevice object. Thanks @Vudentz!
I wonder however if that would be as simple as a boolean field. There are actually two kinds of connectable there: ADV_IND and ADV_DIRECT_IND
@jyasskin Any thoughts?

@jyasskin
Copy link
Member Author

I think we don't need to distinguish between ADV_IND and ADV_DIRECT_IND: the difference is whether the controller can send a SCAN_REQ in response, but the advertising event should just combine data from the advertisement with the SCAN_RSP, so websites don't need to react to the difference.

Do we want to mark this in the advertising event or on the device? I'd initially thought the advertising event, but since we always have to wait for a subsequent advertisement in order to connect, it probably makes sense to turn it to 'true' on the device as soon as we see any connectable advertisement from that device.

navigator.bluetooth.<a idl for="Bluetooth" lt="requestLEScan()">requestLEScan</a>({
filters: [{manufacturerData: 0x004C}],
options: {
keepDuplicates: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/keepDuplicates/keepRepeatedDevices/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks.

@jyasskin jyasskin force-pushed the scanning branch 6 times, most recently from 04e61ff to d971187 Compare July 21, 2016 23:37
@Vudentz
Copy link

Vudentz commented Jul 22, 2016

Im quite concerned with these APIs, it is quite low level which may lead us to need to expose scanning intervals which depending on the duty cycle can block other traffic such as GATT to happen. Im also not quite sure how to interpret the aggregation of scan response, there could be other advertisements in the meantime shall the stacks hold the results until it gets a scan response, for requestDevice that may be fine because the API don't except individual reports just a device, but here the holding of events may change the sequence. Perhaps if this is done via device object, so the scan response wouldn't matter and only advertisement would actually cause events.

@krantik
Copy link

krantik commented Jul 22, 2016

How about timeout? Is the call expected to constantly scan or stop after sometime? Some of the devices(iOS for example) doesn't constantly scan and backs off after some period. Is the same expected here?

@krantik
Copy link

krantik commented Jul 22, 2016

Is the event expected to have parsed data with separate values for name etc., or raw data and JS side to interpret it?

@jyasskin
Copy link
Member Author

jyasskin commented Jul 22, 2016

@Vudentz

Im quite concerned with these APIs, it is quite low level which may lead us to need to expose scanning intervals which depending on the duty cycle can block other traffic such as GATT to happen.

In order to compete with [Android](https://developer.android.com/reference/android/bluetooth/le/ScanCallback.html#onScanResult%28int, android.bluetooth.le.ScanResult%29) and Mac, I think BlueZ is going to have to report information with this much detail. We can limit the load websites are allowed to put on the system, but beacon use cases definitely need to enable duplicate events, and seem to need the timing of the last advertisement in order to know when things are stale.

If BlueZ doesn't start reporting advertising events, we can infer some of this from changes in the Device object, but it's going to work less well than other platforms.

Im also not quite sure how to interpret the aggregation of scan response, there could be other advertisements in the meantime shall the stacks hold the results until it gets a scan response, for requestDevice that may be fine because the API don't except individual reports just a device, but here the holding of events may change the sequence. Perhaps if this is done via device object, so the scan response wouldn't matter and only advertisement would actually cause events.

It seems ok to delay the event until after the SCAN_RSP, although maybe we'll find use cases that prefer to get both events. The uses will have to be ok with whatever Android and Mac do, since it's harder to change them.

@krantik

How about timeout? Is the call expected to constantly scan or stop after sometime? Some of the devices(iOS for example) doesn't constantly scan and backs off after some period. Is the same expected here?

I could let the UA stop a scan if it's been running "too long". Advertisements aren't reliable anyway, so iOS's periodic scan probably already satisfies the spec, but I could add explicit wording to allow that.

I don't think having folks pass a timeout to requestLEScan() will help, since iOS will throttle the scan when it wants regardless of whether the site passed its own timeout. I expect folks to use setTimeout(()=>scan.stop(), xxx) to time out their scans.

Is the event expected to have parsed data with separate values for name etc., or raw data and JS side to interpret it?

Parsed data. See https://webbluetoothcg.github.io/web-bluetooth/#fire-an-advertisementreceived-event for the parsing. We're nervous about exposing the raw data, both because we can't implement that on Mac, and because some of the fields, like the public addresses, hold data that we're not sure websites should know.

@Vudentz
Copy link

Vudentz commented Jul 25, 2016

@jyasskin

In order to compete with Android and Mac, I think BlueZ is going to have to report information with this much detail. We can limit the load websites are allowed to put on the system, but beacon use cases definitely need to enable duplicate events, and seem to need the timing of the last advertisement in order to know when things are stale.

BlueZ is used in very different systems as Android, it is quite common to have multiple applications using the API in parallel, perhaps we could compare it to Mac but looking at its API it seems to be very similar to the kind of access BlueZ offers with StartDiscovery combined with SetDiscoveryFilter. Btw, it is already possibility to enable duplicated reports, in that case the RSSI property is emitted every time we receive a report from the peripheral, similar to what is stated in the Mac documentation.

It seems ok to delay the event until after the SCAN_RSP, although maybe we'll find use cases that prefer to get both events. The uses will have to be ok with whatever Android and Mac do, since it's harder to change them.

Well we do wait for the scan response so the applications can see the device name, etc, which is quite convenient for requestDevice but it perhaps wouldn't be for requestLEScan, anyway as long as it not strictly forbidden I guess we can live with that.

Btw, we also have to keep in mind there could be peripherals connected which makes requestLEScan even less reliable as it cannot use very high scan duty cycle in such cases.

</li>
<li>
If <code>|filter|.connectable</code> is `true`,
|event| has either the <a>ADV_IND</a>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@g-ortuno points out that this is unimplementable in the Android API.

@beaufortfrancois @jracle do you think it's better for this to work inconsistently depending on the platform, or to leave out the connectable bit until Android adds support for it?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jyasskin my proposal is to add a nullable boolean to BluetoothDevice object ( see #253 )

interface BluetoothDevice {
  ...
  readonly attribute boolean? connectable;
};

I'm frustrated by those platform inconsistencies, but due to them, I'm more comfortable leaving out the connectable filter for now, and adding above proposal instead. In that case, we would have a consistent behavior, checking for the boolean before attempting connection.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I'll leave it out of the filters for now, and we can keep figuring out what to do on #253.

@nik-markovic
Copy link

Hi,
I had a few questions/comments:

  1. BluetoothLEScanFilterInit.manufacturerData, shouldn't this be renamed into "manufacturerId"? Similarly serviceData to "serviceDataUUID".

  2. @jyasskin I agree that the API should not form such a tight association to "advertisement" packets. I believe that the LE Bluetooth spec does not restrict contents of scan response and advertisement indication. Though likely rare, for example, a device may have manufacturer data in scan response and service data in advertisement indication. I think that the events should be called "scan responses" (as in: response to user's request to scan for devices) or "scan events" in order to avoid this coupling.

  3. Shouldn't typical scan filters be applicable to this scan? For example, matching on a name.

  4. @jyasskin I don't see mention missing .gatt object on the returned device. Is there any thing else missing? I hope "id" is available, so the user's code can maintain a map of value. Can't we still expose .gatt object on returned devices and allow the code to initiate a connect request, requiring a user to answer a prompt?

@jyasskin
Copy link
Member Author

  1. BluetoothLEScanFilterInit.manufacturerData, shouldn't this be renamed into "manufacturerId"? Similarly serviceData to "serviceDataUUID".

My thought with those fields was that we could later extend them to whole objects with all the complexity of https://developer.android.com/reference/android/bluetooth/le/ScanFilter.Builder.html, so I wanted to use the same names as the AD field. That said, I can see some sense in using a different name for the shorthand. While I mull it over, 👍 this comment to prefer manufacturerId and serviceDataUUID or 👎 this comment to prefer manufacturerData and serviceData?

@jyasskin
Copy link
Member Author

  1. @jyasskin I agree that the API should not form such a tight association to "advertisement" packets. I believe that the LE Bluetooth spec does not restrict contents of scan response and advertisement indication. Though likely rare, for example, a device may have manufacturer data in scan response and service data in advertisement indication. I think that the events should be called "scan responses" (as in: response to user's request to scan for devices) or "scan events" in order to avoid this coupling.

I believe the spec consistently refers to advertising events instead of either advertising packets or scan responses, which comes from BT4.2 1.A.1.2 saying that an advertising event comprises an advertising packet and resulting scan requests and responses, on each of the three advertising channels. So I think this is ok as-is, and that using "scan events" would diverge too much from the Bluetooth spec.

@jyasskin
Copy link
Member Author

jyasskin commented Jul 27, 2016

  1. @jyasskin I don't see mention missing .gatt object on the returned device. Is there any thing else missing? I hope "id" is available, so the user's code can maintain a map of value.

Only .gatt is newly optional on BluetoothDevice. .id is still available and should be stable until the user clears their cookies or revokes permission. The wording about ""bluetooth"'s extra permission data" covers this while giving UAs enough freedom to reset IDs at the right times.

Can't we still expose .gatt object on returned devices and allow the code to initiate a connect request, requiring a user to answer a prompt?

Yes, I plan to add a way to get a BluetoothRemoteGATTServer from a scanned device in a future revision. I think we have agreement on what that interface should be, but I'd like to do it in a dedicated PR to make sure we get the right attention.

@nik-markovic
Copy link

  1. BluetoothLEScanFilterInit.manufacturerData, shouldn't this be renamed into "manufacturerId"? Similarly serviceData to "serviceDataUUID".

My thought with those fields was that we could later extend them to whole objects with all the complexity of https://developer.android.com/reference/android/bluetooth/le/ScanFilter.Builder.html, so I wanted to use the same names as the AD field. That said, I can see some sense in using a different name for the shorthand. While I mull it over, 👍 this comment to prefer manufacturerId and serviceDataUUID or 👎 this comment to prefer manufacturerData and serviceData?

I can definitely see the value in being able to specify a mask in manufacturer data. If you are planning on supporting that in the future. Maybe it's better that we define the .manufacturerData filter to be a structure that contains (at this time only) manufacturer ID? If you don't opt for this, then it may be better to still keep the manufacturerId as the name of the field, then you can do a conversion or "deprecated detection" once you have a new version of the API.

@nik-markovic
Copy link

nik-markovic commented Jul 28, 2016

  1. Shouldn't typical scan filters be applicable to this scan? For example, matching on a name.

I think the name and namePrefix filters are the only ones I have in requestDevice() but not requestLEScan(). Android provides an exact name match, but not a partial name match, which limits the efficiency gains we can get by specifying a partial match. (Of course, iOS has much weaker filters.) My impression of requestDevice() uses is that most have needed to use partial name matches.

So, I'm inclined not to include name filters in requestLEScan() until we find concrete use cases.

Isn't it implicit that requestLEScan filters are implemendted in software (browser) anyways? That way we are not limited to what the underlying bluetooth API implements. requestDevice is implemented in software, right? If we implement it in software, then we can have multiple sessions doing scans and filters of their own irrespective of what the adapter/native is filtering. Also, iOS and BluZ only supports limited (and global) filtering options, which typically include only service UUID and/or a couple of other minor things. How do we deal with that then? If this filter gets implemented in software, it seems to gravitate towards making filtering options and algorithm the same for both requestDevice and requestLEScan.

This doesn't cover scanning from a service worker yet.

Fixes WebBluetoothCG#191.
Before, filters: [{}] would have been a completely open filter, but that seems
too subtle.
@jyasskin
Copy link
Member Author

Isn't it implicit that requestLEScan filters are implemented in software (browser) anyways?

The browser has to fill in any filters that the platform doesn't provide, but the main reason to have a declarative system at all is so we can take advantage of OS and hardware support for the filters. Otherwise, we'd just let Javascript do the filtering.

On the other hand, having exactly the same filters for both systems would make them easier to understand, so I guess I'm not really opposed to doing that.

@jyasskin
Copy link
Member Author

jyasskin commented Aug 1, 2016

The latest commits rename manufacturerData to manufacturerId and serviceData to serviceDataUUID, and add name and namePrefix filters. Any more comments?

@Vudentz
Copy link

Vudentz commented Aug 2, 2016

Isn't it implicit that requestLEScan filters are implemented in software (browser) anyways?

Only doing filtering perhaps would be possible, but I guess if performance and security is a concern I would leave the filtering up to the underline stack so we don't spam the system with IPC messages containing advertisement reports that the browser may not be able to understand.

@jyasskin
Copy link
Member Author

jyasskin commented Aug 2, 2016

The diversity of platforms means we have to expect to do some filtering in the browser. We should try to pass off as much as possible to the underlying platform though.

Is the security issue just that processes that can intercept IPC messages will find out the advertisements? Can't such privileged processes create their own advertisement requests anyway?

@jyasskin
Copy link
Member Author

jyasskin commented Aug 2, 2016

I'm going to commit the current state. Please file issues when you notice them, and I'll fix them in smaller patches.

@jyasskin jyasskin merged commit 9c56bc4 into WebBluetoothCG:gh-pages Aug 2, 2016
@jyasskin jyasskin deleted the scanning branch August 2, 2016 18:29
@Vudentz
Copy link

Vudentz commented Aug 3, 2016

Is the security issue just that processes that can intercept IPC messages will find out the advertisements?

No, I meant that the browser itself is not a privileged application which shouldn't be given direct access for things that have shared control such as scanning.

This is actually acknowledge in the Android API:

https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html

  • Note: Most of the scan methods here require BLUETOOTH_ADMIN permission. (Btw, I wouldn't be surprised if this gets bumped to BLUETOOTH_PRIVILEGED if used with SCAN_MODE_LOW_LATENCY)

In Linux this would translate to have CAP_NET_ADMIN a.k.a root and then using the kernel Bluetooth Management socket to control the scanning parameters and results.

@jyasskin
Copy link
Member Author

jyasskin commented Aug 3, 2016

Ok, yes, I expect the non-system-supported filters to have to run at a lower power use and/or stop while the browser's in the background. Chrome does take the BLUETOOTH_ADMIN permission in order to make requestDevice() work at all.

@nikhilkashid
Copy link

Hi, I wanted to know if there is any method to get the UUID, Major, Minor and other services like Battery and signal strength of the signal received without requiring to connect with the device. We wanted to use beacons which would normally be in a unconnectable sleep mode. From what I understand requestLEScan would be a good method to use however in chrome console this method is not showing up and is undefined within navigator.bluethooth

@g-ortuno
Copy link
Contributor

@nikhilkashid Right, requestLEScan is the right method for this. Sadly it's not yet implemented in Chrome. https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md#scanning-api

@nikhilkashid
Copy link

Thank you @g-ortuno. We wanted to run a few experiments and view how requestLEScan would work. Is there a way we can try doing this?

@beaufortfrancois
Copy link
Member

@nikhilkashid You can't for now.

@f138150
Copy link

f138150 commented Nov 11, 2016

@jyasskin @beaufortfrancois HI, I've a question, devices without Peripheral mode also supports Web Bluetooth API ?

Does Web Bluetooth API works with the BLE phones with central mode only(without peripheral mode). I want to send sensor data to web page using Web Bluetooth API. My phone has 4.2 BLE (Not peripheral mode) is not detecting by API. I tried on some other phone which supports peripheral mode, and it appears in the list.

I am using Ubuntu 16.04 LTE, Google chrome 55 (developer edition), enabled Bluetooth flag, Also follow guidelines [(https://acassis.wordpress.com/2016/06/28/how-to-get-chrome-web-bluetooth-working-on-linux/)]

I am newbie, kindly need your help Please.

@g-ortuno
Copy link
Contributor

@f138150 Peripheral mode is how devices (heart rate monitors, beacons, etc.) tell nearby devices of their presence. If a device doesn't support Peripheral mode then it can't communicate its presence so the API won't be able to find it.

That said, it's surprising a 4.2 BLE device doesn't support Peripheral mode. What device is it?

@arashd
Copy link

arashd commented Jun 6, 2017

@beaufortfrancois would you be able to provide any rough estimate of when we could expect watchAdvertisements/scanning to be implemented?

Wouldn't this be incredibly useful in combination with a Physical Web referral in a use-case where the user wants to easily distinguish between several devices (say parking meters) by distance (rssi) and pinpoint the one he's right next to. I'm new to the space; please correct me if I'm misunderstanding.

@beaufortfrancois
Copy link
Member

@arashd I'm eager as well to see this coming ;)
However I can't share any rough ETA at the moment.

@arashd
Copy link

arashd commented Jun 9, 2017

@beaufortfrancois I understand!

@scheib
Copy link
Contributor

scheib commented Jun 14, 2017 via email

@arashd
Copy link

arashd commented Jun 20, 2017

Thank you for that clarification @scheib. I'll keep an eye out for the new OKRs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.