react-native-ble-manager
react-native-ble-manager copied to clipboard
[RFC] Implement companion device manager support
Android's CompanionDeviceManager
is used to get access[1,2] (association) to a Bluetooth LE (and classic Bluetooth) devices without requiring too broad permission set (notably location on older android versions).
Using CompanionDeviceManager
is also a requirement for implementing background connection management, tho' it also requires the application to implement CompationDeviceService
[3].
CompationDeviceManager
availability is not guaranteed, hence the few API version and feature detection checks.
iOS stubs are missing at the moment.
1: https://developer.android.com/develop/connectivity/bluetooth/companion-device-pairing 2: https://developer.android.com/reference/android/companion/CompanionDeviceManager 3: https://developer.android.com/reference/android/companion/CompanionDeviceService
Assuming this feature is welcome, TODO before ready:
- [x] iOS stubs
- [ ] Check the error handling (at least the
onDeviceFound
exception handling gives perhaps too long error message) - [x] docs
I managed to adapt our application to use the companion scan - so at least its somewhat working.
~My motivation to get this in is to then use the CompationDeviceSerivce
for managing bluetooth devices while the app is not in foreground.~ Scratch that, the companion device service is just for the newer android versions. The additional permissions might or might not help with keeping the app alive in the background.
Hi @vhakulinen , interesting feature, do you test it?
Yes, I'm working on this in our app.
Connecting to a device with the companion scanner works. The current iteration requires to subscribe to BleManagerCompanionPeripheral
to receive the selected peripheral from the companion manager (compared to BleManagerConnectPeripheral
for "manual" scanning).
I haven't tested with older phones, but based on the documentation you shouldn't need any location permissions for API version >=26.
I also managed to keep our app running in the background (i.e. after the user closes the app) through combination of permissions/uses-feature and a foreground service:
Permissions:
<!-- We're using companion device manager, if available. -->
<uses-feature android:name="android.software.companion_device_setup"/>
<!-- With companion devices, we're exempted from background limitations.
See https://developer.android.com/guide/components/foreground-services#bg-access-restrictions -->
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
<!-- TODO: is this required? Probably? -->
<uses-permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND" />
<!-- To keep our ConnectivityService running. -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"/>
Service declaration in manifest:
<service
android:name=".ConnectivityService"
android:foregroundServiceType="connectedDevice"
android:exported="false">
</service>
Then the service it self is basically this, except with the foreground service type changed to the connected device one: https://developer.android.com/guide/components/foreground-services#start.
As I mentioned earlier, I ditched any plans to use the CompationDeviceService
because its only for the latest API version and I need to support older devices too. Perhaps I'll get back to it at later time. It would allow waking the app when a companion device "appears" (i.e. someone's(?) scan notices its advertisement packets).
Edit: correct link
Connecting to a device with the companion scanner works. The current iteration requires to subscribe to BleManagerCompanionPeripheral to receive the selected peripheral from the companion manager (compared to BleManagerConnectPeripheral for "manual" scanning).
Sorry, should be BleManagerDiscoverPeripheral
instead of BleManagerConnectPeripheral
Slight issue with devices that don't support/require bonding: if you've associated a device and dont bond with it, you'll need to scan for the device again after the bluetooth adapter gets turned off/on. In that case, you already have a associated device but the bluetooth adapter can't connect to it without scanning first (except maybe with the latest android API version). And its a bit pointless to scan for the device with the companion device manager because that would prompt the user to pick the device again.
To solve this, the scan
function would need to decide whether to use the companion scanner or the default one.
Updated based on the earlier comment.
The companion device manager thingy is now behind different API (i.e. scan
vs. companionScan
).
Still missing iOS stubs.
Now would be good time for feedback (even if its just "looks good").
Two things that are missing: documentation (i.e. updates to README) and iOS stubs.
Hi @vhakulinen , thanks for your time. I'll take a look the next week, it seems everything ok. I can do the iOS stubs. Maybe you can add a little paragraph in the doc to explain how the "companion device" works.
Hi @vhakulinen you can integrate https://github.com/innoveit/react-native-ble-manager/tree/feat/ios-companion for the iOS stubs.
Sorry for the delay, I'm busy with other urgent things at the moment. I'll continue this once I'm able to.
There.
Rebased, added docs and applied the iOS stubs.
In 62ac6221eb574a966152e618b38769fc52467c94, you removed the BleManagerCompanionPeripheral
event.
This causes diverge between code thats using "normal" methods of connecting, which have the BleManagerConnectPeripheral
event available. Can you consider adding either the companion specific event back, or chain up the companion scanner to the BleManagerConnectPeripheral
event (possibly with some flag indicating that it actually is a companion device)?
Hi, from my tests that I have done the companionScan
does not also connect the device so the logic remains the same.
In your case does the device connect automatically after it is chosen?
Oh sorry, my bad. You're right, it doesn't automatically connect. So same argument, but with BleManagerConnectPeripheral
replaced with discover event.
So are you suggesting to add also the BleManagerDiscoverPeripheral
to the companionScan success? I'm not sure that is better if we mix that thing. You can scan normally and have the same result with the companion device, is not filtered by the standard scan.
Thats why it would need to have some flag in the returned object. Or restore the event that was removed.
Ok, you think that is better to know if a discovered peripheral is a companion. You can check the associated peripheral but maybe is easier with a flag. I'll take a look. Thank for the PR.
api version should be checked before loading companion device manager... now the module will cause app crash in android 7.1.1
It is being checked, or did I miss it on some code path?
public CompanionDeviceManager getCompanionDeviceManager()
will cause the problem. Change it to public Object getCompanionDeviceManager()
, add @RequiresApi(api = Build.VERSION_CODES.O)
on getAssociatedPeripherals
and removeAssociatedPeripheral
, and cast Object
to CompanionDeviceManager
on each getCompanionDeviceManager()
call could solve the crash problem.
the getCompanionDeviceManager
is only called after the API checks. Isn't that enough?
I don't know why... but the cast operation in getCompanionDeviceManager
did cause the NoClassDefFound
error when loading the module.
below is the patch i did, it fixed the problem in emulator 25. [email protected]
API check on getAssociatedPeripherals
and removeAssociatedPeripheral
are not necessary. Not casting Context.COMPANION_DEVICE_SERVICE
to CompanionDeviceManager
in getCompanionDeviceManager
fixed everything.
Hi, I confirm the problem, probably at runtime is trying to load the class because is in the method signature. I'll make a PR soon.
I think also the code is only working with API >= 33.
@iamfat can you check the PR https://github.com/innoveit/react-native-ble-manager/pull/1205 ?
Any chance to get the events removed in 62ac6221eb574a966152e618b38769fc52467c94 back?
If its just matter of adding them back, or if you have an alternative design in mind but lack the time, I can make a PR for it. Our app is structured around those (and the other discovered) events, and I'm currently stuck with our own fork form this PR.
Hi @vhakulinen , we can restore it but I don't understand why this event is useful and the result of the promise and the getAssociatedPeripherals is not enough to figure out if a device is a companion device. Sorry, I don't want to be a pain in the ass it's just to understand if maybe it can be solved in another way.
Sorry, I might've been unclear what I'm asking for.
What I want is to have a event based solution for discovering a companion device, i.e. BleManagerDiscoverPeripheral
but for the companion scanner. This way you can limit the divergence between code paths on devices that support companion device manager and devices that dont.