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

Various API updates #166

Closed
wants to merge 117 commits into from
Closed

Conversation

gedgygedgy
Copy link
Contributor

These changes are being made to accommodate the Android Bluetooth API.

qwandor and others added 30 commits June 2, 2021 18:14
Also move utils modules to separate files.
Also, a better name for string_to_maybe_string, and avoid the dead code
warning for str_to_nsstring which is only used in tests.
By re-exporting the platform specific `Manager` and `Adapter` saves the user
of the api from doing so.
By type-aliasing `Adapter `as `ConnectedAdapter` for mac and windows, and
adding a no-op `connect` the user doesn't have to care that only linux
has a difference between the two.
the crate _serde_bytes_ is added as it's needed for Vec<u8>
Corrected the inverse store order.
Added more display and parse options.
Is replaced by general tests on BDAddr.
@@ -284,6 +284,9 @@ pub trait Central: Send + Sync + Clone {

/// Returns a particular [`Peripheral`] by its address if it has been discovered.
async fn peripheral(&self, address: BDAddr) -> Result<Self::Peripheral>;

/// Add a [`Peripheral`] from a MAC address without a scan result. Not supported on all Bluetooth systems.
async fn add_peripheral(&self, address: BDAddr) -> Result<Self::Peripheral>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What does it mean to add a peripheral? Add to what?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, let me explain the rationale behind this.

On Android, scanning for Bluetooth devices requires permission to access location information (though it looks like they're fixing this in Android 12.) To make this less cumbersome for developers, Android provides the Companion Device Manager API, which basically tells the Android OS "scan for Bluetooth devices yourself, prompt the user to select one, and tell me which one they selected." Unlike the scanning API, this does not require location permissions.

Once the app obtains this handle (which contains the MAC address information), either by prompting the user through the Companion Device Manager or by saving the MAC address and then retrieving it from storage later on, it can then connect to the device simply by providing the MAC address:

BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice("12:34:56:78:90:AB");

This new API is intended to make this functionality possible for btleplug clients on Android.

Copy link
Contributor

Choose a reason for hiding this comment

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

This also becomes an interesting question in WebBluetooth with the upcoming getDevices() (or whatever it is) API, which I believe may allow similar?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would the app call the Companion Device Manager API directly, without btleplug involvement? And does this need to be a separate method from peripheral, just above? Either way, what you're really doing is getting a peripheral which the system already knows about, it's just a question of whether you are able to know about it beforehand. Perhaps they could be the same method?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would the app call the Companion Device Manager API directly, without btleplug involvement?

Yes. The Companion Device Manager involves the use of an Activity and other things which are far above the level of btleplug.

My Android port also has an Adapter::report_scan_result() method, which takes an android.bluetooth.le.ScanResult (which can be obtained from the CDM) and applies it to the device as if the information in it had come from its own scan.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Either way, what you're really doing is getting a peripheral which the system already knows about, it's just a question of whether you are able to know about it beforehand. Perhaps they could be the same method?

In this branch, Adapter::peripheral() assumes the peripheral is already in the cache, while Adapter::add_peripheral() adds it if it's not already there. I suppose they could be merged into one method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose they could be merged into one method.

On second thought, I think it's better to be able to make the distinction between "get the peripheral only if it's already in the cache" and "add it by MAC address if it doesn't already exist". I'd prefer to keep add_peripheral() after all (I'm open to feedback on better names.)

@@ -147,7 +147,7 @@ pub struct PeripheralProperties {
/// The address of this peripheral
pub address: BDAddr,
/// The type of address (either random or public)
pub address_type: AddressType,
pub address_type: Option<AddressType>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Having an address without an AddressType seems problematic, isn't it required to know how to interpret the address?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is that not all Bluetooth systems report the address type. Android does not, and in fact, neither do WinRTBLE and CoreBluetooth - out of the systems we use, the only one that does is BlueZ. Rather than pretending to know the type and guessing, BTLEPlug should simply be honest that it doesn't know, and let the client decide what to do with that information.

I've updated WinRTBLE and CoreBluetooth to reflect this.

@@ -186,7 +186,7 @@ pub trait Peripheral: Send + Sync + Clone + Debug {

/// Returns the set of properties associated with the peripheral. These may be updated over time
/// as additional advertising reports are received.
async fn properties(&self) -> Result<PeripheralProperties>;
async fn properties(&self) -> Result<Option<PeripheralProperties>>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you don't know the properties yet, can't you just use PeripheralProperties::default()? The fields (other than the address) are all either optional or can be empty or 0 anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My rationale behind this was to honestly report "we don't know yet" instead of "it's empty". Though, now that I look more closely at PeripheralProperties, I do see the has_scan_response field, which I guess reports this already. Still, in order to conform to Rust conventions, I'd argue in favor of making PeripheralProperties an Option and removing the has_scan_response field.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm in agreement on Option versus default. I'd rather know the surrounding state than just getting back something with nothing in it and not knowing if that's what the device reported.

@codecov
Copy link

codecov bot commented Jul 1, 2021

Codecov Report

Merging #166 (18cdd24) into dev (ca0a177) will increase coverage by 5.78%.
The diff coverage is 4.54%.

Impacted file tree graph

@@            Coverage Diff             @@
##              dev     #166      +/-   ##
==========================================
+ Coverage   14.75%   20.54%   +5.78%     
==========================================
  Files          24       12      -12     
  Lines        3836     2473    -1363     
==========================================
- Hits          566      508      -58     
+ Misses       3270     1965    -1305     
Impacted Files Coverage Δ
examples/discover_adapters_peripherals.rs 0.00% <0.00%> (ø)
examples/lights.rs 0.00% <0.00%> (ø)
examples/subscribe_notify_characteristic.rs 0.00% <0.00%> (ø)
src/api/mod.rs 0.00% <ø> (ø)
src/bluez/adapter.rs 0.00% <0.00%> (ø)
src/bluez/peripheral.rs 0.00% <0.00%> (ø)
src/lib.rs 17.17% <50.00%> (-0.05%) ⬇️
src/common/adapter_manager.rs
src/corebluetooth/utils/mod.rs
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ca0a177...18cdd24. Read the comment docs.

@gedgygedgy gedgygedgy mentioned this pull request Jul 2, 2021
blandger and others added 9 commits July 3, 2021 16:59
Some Bluetooth systems allow you to obtain a handle to a peripheral
without getting a scan result from it, just by providing a MAC
address. Make properties() optional to reflect the fact that the
properties may not be known.
Not all Bluetooth systems provide information about the address type.
Make it optional, and return None instead of pretending to know.
Some Bluetooth systems allow you to obtain a handle to a peripheral
without getting a scan result from it, just by providing a MAC
address. Add an optional method to do this.
Converting an Error into a String loses information such as source
and downcasting. Use a Box<dyn Error> instead to preserve this
information.
@qdot
Copy link
Contributor

qdot commented Jul 26, 2021

I realize everything might not be perfect here, but perfect has currently been the enemy of done for 6 months now and I'd like to slide this into 0.8.0 so we can possibly add android as either 0.8.x or maybe 0.9 if there's enough stuff in front of it to need surface changes.

Cherry-picking onto dev and closing.

@qdot qdot closed this Jul 26, 2021
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.

9 participants