Closed tiagoblackcode closed 3 years ago
I can't say I understand android permissions very well yet, but what about this bit about the companion device api?
Because discoverable devices might reveal information about the user's location, the device discovery process requires location access. If your app is being used on a device that runs Android 8.0 (API level 26) or higher, use the Companion Device Manager API. This API performs device discovery on your app's behalf, so your app doesn't need to request location permissions.
Is that not viable here?
I think using the Companion Device Manager API
as you suggested might be suitable for most use cases but, according to the docs and guide, it doesn't allow for continuous scan, which might prevent some apps from functioning properly:
Companion device pairing also isn't intended for continuous scanning. It is a means for implementing custom protocols over Bluetooth, BLE, and Wi-Fi that are required for the companion device to function.
it's too bad that FINE_LOCATION is the only way to support all cases :(
Yes, definitely.
But it left me thinking there could be an additional method for a non-continuous, discrete scan using the Companion Device Manager API in the library. Something like performScan()
that would trigger the callback only once. Or adding an option to the startScanWithOptions()
method.
EDIT: rename startScan()
into startScanWithOptions()
to avoid confusion.
Nice idea @tiagoblackcode, can you develop the feature ?
I can help you if you want...
@rolloaxel let's wait for @don feedback. We also need to settle for the option vs method if this is going to be implemented.
@tiagoblackcode I am using your branch 1.2.5-android-10, and it works better. But I encounter the following issue on a Samsung A7 running Android 10 :
Thanks anyway you and @don for your work !
Appendix :
OK here are some thoughts @don and @tiagoblackcode :
I have pushed a version slightly different from the one of @tiagoblackcode implementing this : https://github.com/QuentinFarizon/cordova-plugin-ble-central/commit/5daa4b3075f13933868a0cd0afbc90b012797b99
Ideally, I think it should be configurable wether ACCESS_BACKGROUND_LOCATION should be required or not, because some application may never need to do background scanning.
Hello @QuentinFarizon,
so BLE only requires ACCESS_FINE_LOCATION
in Android 10 unless you want to do a background scan right? I can update my PR with that change. However, we need to support the use-case where the user (of the library) wants to perform a background scan (which is common when interacting with Bluetooth beacons). To do that, I suggest we add an option to the startScanWithOptions
's options argument to request background permissions as well:
const options = {enableBackgroundScan: true};
ble.startScanWithOptions([], options, success, failure);
Regarding:
But I encounter the following issue on a Samsung A7 running Android 10 :
- launch scan
- permission granting UI is displayed, click "only when in use"
- scanning returns an error
- launch scan again
- scan succeeds this time, strangely
- permission granting UI is launched by your code change, because ACCESS_BACKGROUND_LOCATION is not granted The interaction between this phone/android10 and bluetooth are so strange, not sure if something can be done on your side ?
I'll try to reproduce your issue since I've also been having reports of a similar problem.
@tiagoblackcode
so BLE only requires ACCESS_FINE_LOCATION in Android 10 unless you want to do a background scan right? I ... think so, but this should be further tested
I'm now using https://github.com/QuentinFarizon/cordova-plugin-ble-central/commit/5daa4b3075f13933868a0cd0afbc90b012797b99 in production and it works fine. As you can see, it still requestPermissions initially for ACCESS_BACKGROUND_LOCATION, but not again if it is nos granted as long as ACCESS_FINE_LOCATION is granted (which was the issue I had with @tiagoblackcode 's version)
Also, I have a check+request method to await before running the scan, using the https://github.com/dpa99c/cordova-diagnostic-plugin plugin. I have found a particular sequence of checks using this plugin, that correctly manages all cases of android10/ble interactions around location, and also works fine with previous versions of Android :
private async checkPermissions() {
const bluetoothAvailable = await this.diagnostic.isBluetoothAvailable();
if (!bluetoothAvailable) {
return Promise.reject('bluetooth_not_granted');
}
// Retrieve status of previous authorizations
const locationAuthorizationStatus = await this.diagnostic.getLocationAuthorizationStatus();
switch (locationAuthorizationStatus) {
case this.diagnostic.permissionStatus.GRANTED:
case this.diagnostic.permissionStatus.GRANTED_WHEN_IN_USE:
// already granted previously
return Promise.resolve();
case this.diagnostic.permissionStatus.NOT_REQUESTED:
// continue below to request authorization
break;
case this.diagnostic.permissionStatus.DENIED_ALWAYS:
default:
// User previously clicked 'always denied', maybe prompt him to go to its settings (eventually using Diagnostics plugin method for that)
return Promise.reject('location_not_granted');
}
// Synchronously request location, it prompts a dialog to the user, and we await his response.
const status = await this.diagnostic.requestLocationAuthorization(this.diagnostic.locationAuthorizationMode.ALWAYS);
switch (status) {
case this.diagnostic.permissionStatus.GRANTED:
case this.diagnostic.permissionStatus.GRANTED_WHEN_IN_USE:
// user granted permission
return Promise.resolve();
case this.diagnostic.permissionStatus.NOT_REQUESTED:
case this.diagnostic.permissionStatus.DENIED_ALWAYS:
default:
// user denied permission, prompt him that he should accept, and why, and to go to its settings (eventually using Diagnostics plugin method for that)
return Promise.reject('location_not_granted');
}
}
Once user clicked always_deny, the app should call requestLocationAuthorization again, it wont work at all. The app should give indication to the user at what to do.
When a function request a scan, I call :
this.checkPermissions().then(() => {
// use cordova-plugin-ble-central scanning methods
This sequence of checks should probably be integrated in this library, don't you think @don ?
Thanks for your patience. I'm finally getting a chance to look at this after a long delay. I've been distracted by other work.
Generally I don't like asking for more permissions than necessary. Allowing ACCESS_FINE_LOCATION is unfortunate seems to be required. I'm torn on ACCESS_BACKGROUND_LOCATION. Not everyone needs this but background scanning is very useful. Maybe that doesn't matter as much now since Android users have more control to enable and disable permissions.
I need to run in a test app but @tiagoblackcode patch looks like it will fix a lot of issues. I like @QuentinFarizon suggestion about integrating more checks like checkPermissions
.
These changes were published to NPM as v1.3.0
@don beware, by merging this PR as-is, I think you will hit this bug : https://github.com/don/cordova-plugin-ble-central/pull/771#issuecomment-667193474
@QuentinFarizon I think I solved that problem with this commit https://github.com/don/cordova-plugin-ble-central/commit/5183e5389394f9812700dc78c4f31d2a6e7e110a
@don Nice ! I'll check it on my project.
By the way, I had to modify my "checkPermissions" method due to changes in iOS 14.0.1 and then again for iOS 14.1. Here it is now, yes it's long but every bit is necessary ! :
export enum BlePermissionsErrorType {
BLE_NOT_AVAILABLE = 'BLE_NOT_AVAILABLE',
LOCATION_NOT_ENABLED = 'LOCATION_NOT_ENABLED',
LOCATION_NOT_GRANTED = 'LOCATION_NOT_GRANTED'
}
@Injectable({
providedIn: 'root'
})
export class BlePermissionsService {
private bluetoothState = 'unknown';
constructor(
private ble: BLE,
private diagnostic: Diagnostic,
private platform: Platform
) {
}
public async checkBleAndLocationPermissions() {
console.log('checkPermissions enter');
const bluetoothAvailable = await this.diagnostic.isBluetoothAvailable();
console.log('checkPermissions bluetoothAvailable ' + bluetoothAvailable);
if (!bluetoothAvailable) {
return Promise.reject(BlePermissionsErrorType.BLE_NOT_AVAILABLE);
}
const locationEnabled = await this.diagnostic.isLocationEnabled();
if (!locationEnabled) {
return Promise.reject(BlePermissionsErrorType.LOCATION_NOT_ENABLED);
}
if (this.platform.is('ios')) {
console.log('checkPermissions startBleStateNotification');
this.startBleStateNotification();
}
try {
const locationAuthorizationStatus = await this.diagnostic.getLocationAuthorizationStatus();
console.log('checkPermissions locationAuthorizationStatus ' + locationAuthorizationStatus);
switch (locationAuthorizationStatus) {
case this.diagnostic.permissionStatus.GRANTED:
case this.diagnostic.permissionStatus.GRANTED_WHEN_IN_USE:
if (this.platform.is('ios')) {
await this.waitForBluetoothStateOn();
}
return Promise.resolve();
case this.diagnostic.permissionStatus.NOT_REQUESTED:
break;
case this.diagnostic.permissionStatus.DENIED_ALWAYS:
default:
return Promise.reject(BlePermissionsErrorType.LOCATION_NOT_GRANTED);
}
const status = await this.diagnostic.requestLocationAuthorization(this.diagnostic.locationAuthorizationMode.ALWAYS);
console.log('checkPermissions statusAfterAuthorizationRequest ' + status);
switch (status) {
case this.diagnostic.permissionStatus.GRANTED:
case this.diagnostic.permissionStatus.GRANTED_WHEN_IN_USE:
if (this.platform.is('ios')) {
console.log('checkPermissions waitForBluetoothStateOn');
await this.waitForBluetoothStateOn();
}
console.log('checkPermissions location granted');
return Promise.resolve();
case this.diagnostic.permissionStatus.NOT_REQUESTED:
case this.diagnostic.permissionStatus.DENIED_ALWAYS:
default:
return Promise.reject(BlePermissionsErrorType.LOCATION_NOT_GRANTED);
}
} finally {
if (this.platform.is('ios')) {
await this.stopBleStateNotification();
}
}
}
private startBleStateNotification() {
this.ble.startStateNotifications()
.subscribe(state => {
console.log('checkPermissions ble state changed ' + state);
this.bluetoothState = state;
});
}
private async waitForBluetoothStateOn() {
let nbRetries = 30;
console.log('checkPermissions bluetoothState init ' + this.bluetoothState);
while(!(this.bluetoothState === 'on') && nbRetries) {
console.log('checkPermissions bluetoothState ' + nbRetries + ' ' + this.bluetoothState);
await new Promise(r => setTimeout(r, 100));
nbRetries--;
}
}
private async stopBleStateNotification() {
await this.ble.stopStateNotifications();
}
Hi @don I have upgraded to v1.3.1 on my app and I'm still not receiving any events when building with sdk 29. Any idea that could help me ? i'm not able to debug java code..
We tried on OnePlus7 Oxygen Os 10, Huawei P20 Pro another Redmi Note 8 Pro. Thanks
My cordova app is currently being rejected from Google Play due to the request for Background Location in the cordova-plugin-ble-central 1.3.1 "BLE" . This is a huge issue for my team as we need bluetooth support and we are have a production issue we are fixing. Any ideas on how to disable this ASAP so it will be approved???
My cordova app is currently being rejected from Google Play due to the request for Background Location in the cordova-plugin-ble-central 1.3.1 "BLE" . This is a huge issue for my team as we need bluetooth support and we are have a production issue we are fixing. Any ideas on how to disable this ASAP so it will be approved???
See my reply here: https://github.com/don/cordova-plugin-ble-central/issues/821#issuecomment-778545615
Thank you. I will give this a try!! Sincerely, Scott
Sent from my iPhone
On Feb 12, 2021, at 9:13 PM, Hank Lloyd Right notifications@github.com wrote:
My cordova app is currently being rejected from Google Play due to the request for Background Location in the cordova-plugin-ble-central 1.3.1 "BLE" . This is a huge issue for my team as we need bluetooth support and we are have a production issue we are fixing. Any ideas on how to disable this ASAP so it will be approved???
See my reply here: #821 (comment)
— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.
Im abit worried to downgrade (ignoring severals commits from @don) to make it work.
isn't it also possible to just delete uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" line in manifest?
Im abit worried to downgrade (ignoring severals commits from @don) to make it work.
There are only two versions between 1.2.5 and 1.3.1.
And the 1.3.0 is what created this problem to begin with. So you're only really missing the 1.3.1 changes (below).
I'm using the modified 1.2.5 on android-cordova in two published apps without any problems.
= 1.3.1 = Android updated to BluetoothLeScanner removing deprecated LeScanCallback #796 Android updated to work with android-cordova@8 and android-cordova@9 #819 iOS has new plugin variable IOS_INIT_ON_LOAD to delay plugin initialization. Defaults to false. #739 #769 = 1.3.0 = Add new location permissions Android 10 (API29) #771
Since Android API Level 29 (Android 10) Bluetooth discovery now needs permissions for fine location as per update notes.
This change conditionally checks and requests for both fine and background location (apps with fine location are granted background location on Android 10) when running on API level 29 onwards.
I've made a pre-release tagged as 1.2.5-android-10 to test the change. I've only tested on Ionic Capacitor and seems to work correctly on Android 8 and 10.
Fixes #754 and some issues related with the scan not finding any devices on Android.
Thanks to @RoopeHakulinen for also investigating the issue.