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

scanForDevices fails on Android 12. #496

Closed
pgiacomo69 opened this issue Jan 17, 2022 · 11 comments
Closed

scanForDevices fails on Android 12. #496

pgiacomo69 opened this issue Jan 17, 2022 · 11 comments

Comments

@pgiacomo69
Copy link

pgiacomo69 commented Jan 17, 2022

My App is running on almost 3000 Devices without any problem until Android 12 Release, on this OS version it is imposible to execute a scan.
Example: the app was working fine on a Samsung Galaxy S21 Ultra with Android 11, the user upgraded the OS as soon it was available from Samsung, and now, when executing:

_ble.scanForDevices( withServices: [sUuid], ).listen((device) { add(BleEventConnectDevice(deviceId: device.id)); }, onError: ((error) { add(BleEventError(state: state, error: 'Errore Connessione: ' + error.toString())); }));
OnError is called and error description is:

GenericFailure<ScanFailure>(code: ScanFailure.unknown, message: "Bluetooth cannot start (code 0)")

This is from the App's Merged Manifest :

'
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" / >
<uses-permission-sdk-23
android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30" / >
<uses-permission-sdk-23
android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" / >
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" / >
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" / >'

Same problem on a Google Pixel 5, and other Samsung smartphones.
How can I solve this, before more users loose ability to use my app?

@remonh87
Copy link
Contributor

I am able to run the example app on my pixel4a with Android 12. Can you try to run our example app on your phone? Also we need some more logical output because the only thing what I can see is that the library correctly propagates a native error.

@ozzy1873
Copy link

ozzy1873 commented Jan 17, 2022

This is probably because you did not request permission from the user. This is now required on Android 12:

if (Platform.isAndroid) {
Map<Permission, PermissionStatus> statuses = await [
Permission.location,
Permission.bluetoothScan,
Permission.bluetoothConnect
].request();

  for (var status in statuses.entries) {
    if (status.key == Permission.location) {
      if (status.value.isGranted) {
        debugPrint('Location permission granted');
      } else {
        debugPrint(
            "Location permission not granted");
      }
    } else if (status.key == Permission.bluetoothScan) {
      if (status.value.isGranted) {
        debugPrint('Bluetooth scan permission granted');
      } else {
        debugPrint('Bluetooth scan permission not granted');
      }
    } else if (status.key == Permission.bluetoothConnect) {
      if (status.value.isGranted) {
        debugPrint('Bluetooth connect permission granted');
      } else {
        debugPrint('Bluetooth connect permission not granted');
      }
    }
  }
}

@pgiacomo69
Copy link
Author

I am able to run the example app on my pixel4a with Android 12. Can you try to run our example app on your phone? Also we need some more logical output because the only thing what I can see is that the library correctly propagates a native error.

As soon as I can put again my hands on an Android 12 Phone, I will try to run the Example.
What other logical output can I provide?
I know that the output comes from underlying library, I thought that this could be a known error.

@pgiacomo69
Copy link
Author

pgiacomo69 commented Jan 17, 2022

This is probably because you did not request permission from the user. This is now required on Android 12:

if (Platform.isAndroid) { Map<Permission, PermissionStatus> statuses = await [ Permission.location, Permission.bluetoothScan, Permission.bluetoothConnect ].request();

  for (var status in statuses.entries) {
    if (status.key == Permission.location) {
      if (status.value.isGranted) {
        debugPrint('Location permission granted');
      } else {
        debugPrint(
            "Location permission not granted");
      }
    } else if (status.key == Permission.bluetoothScan) {
      if (status.value.isGranted) {
        debugPrint('Bluetooth scan permission granted');
      } else {
        debugPrint('Bluetooth scan permission not granted');
      }
    } else if (status.key == Permission.bluetoothConnect) {
      if (status.value.isGranted) {
        debugPrint('Bluetooth connect permission granted');
      } else {
        debugPrint('Bluetooth connect permission not granted');
      }
    }
  }
}

Here, as I told, on Android 7 to 11 it works:

`
FutureLocationPermission(
{bool addEvents = true, BuildContext? context}) async {

  bool locationService =
      await Permission.locationWhenInUse.serviceStatus.isEnabled;
  if (!locationService) {
    if (addEvents) {
      add(BleEventTerminated(result: BleSenderResult.positionNotEnabled));
    }
    if (context != null) {
      await AppDialogs.messageAsyncDlg(
          parentContext: context,
          messaggio: AppLocalizations.of(context)!
              .globalAndroidLocationServiceNotEnabled);
    }
    debugPrint('*******  Location Service Disabled!!!!');
    return false;
  }
  bool locationPermission =
      await Permission.locationWhenInUse.request().isGranted;
  if (!locationPermission) {
    if (addEvents) {
      add(BleEventTerminated(result: BleSenderResult.positionNotGranted));
    }
    if (context != null) {
      await AppDialogs.permissionMessageAsyncDlg(
          parentContext: context,
          messaggio: AppLocalizations.of(context)!
              .globalAndroidLocationServiceNotGranted);
    }
    debugPrint('*******  Location When In Use not Granted!!!!');
    return false;
  }

  locationPermission = await Permission.locationAlways.request().isGranted;
  if (!locationPermission) {
    if (addEvents) {
      add(BleEventTerminated(result: BleSenderResult.positionNotGranted));
    }
    if (context != null) {
      await AppDialogs.permissionMessageAsyncDlg(
          parentContext: context,
          messaggio: AppLocalizations.of(context)!
              .globalAndroidLocationServiceNotGranted);
    }
    debugPrint('*******  Location Always not Granted!!!!');
    return false;
  }
  return true;

}

Future bleRequestPermissions(
{bool addEvents = true, BuildContext? context}) async {
if (Platform.isAndroid && await LocationPermission(addEvents: addEvents, context: context)) {
if (!await Permission.bluetoothScan.request().isGranted) {
if (addEvents) {
add(BleEventTerminated(result: BleSenderResult.bleNotGranted));
}
if (context != null) {
await AppDialogs.permissionMessageAsyncDlg(
parentContext: context,
messaggio: AppLocalizations.of(context)!
.globalBluetoothNotGrantedScan); //TODO: Messaggio bluetooth Android
}
debugPrint('******* Bluetooth Scan not Granted!!!!');
return false;
}

  if (!await Permission.bluetoothConnect.request().isGranted) {
    if (addEvents) {
      add(BleEventTerminated(result: BleSenderResult.bleNotGranted));
    }
    if (context != null) {
      await AppDialogs.permissionMessageAsyncDlg(
          parentContext: context,
          messaggio: AppLocalizations.of(context)!
              .globalBluetoothNotGrantedConnect);
    }
    debugPrint('*******  Bluetooth Connect not Granted!!!!');
    return false;
  }
}`

@ffamar
Copy link

ffamar commented Jan 18, 2022

Hi Giacomo, I had the same issue after Samsung released Android 12 to my S21.
Below the steps I did for fix it:

1)Change uses-permission in android manifest ( removed android:maxSdkVersion="30"):

    <!-- required for API 18 - 30 -->
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

    <!-- required for API 23 - 30 -->
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>

    <!-- API 31+ -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"       android:usesPermissionFlags="neverForLocation" />

2) Change android\build.glade (kotlin_version = 1.5.31)

buildscript {
    ext.kotlin_version = '1.5.31'
    repositories {
        google()
        mavenCentral()        
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()   
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

3) change android\app\build.grade (compileSdkVersion 31, minSdkVersion 21, targetSdkVersion 31)

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion 31

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    lintOptions {
        disable 'InvalidPackage'
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

    defaultConfig {
        applicationId "com.signify.hue.reactivebleexample"
        minSdkVersion 21
        targetSdkVersion 31
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

4) Request the follow permissions

await Permission.locationWhenInUse.request();
await Permission.bluetooth.request();
await Permission.bluetoothScan.request();
await Permission.bluetoothConnect.request();

@tachmamedov
Copy link

tachmamedov commented Feb 1, 2022

My App is running on almost 3000 Devices without any problem until Android 12 Release, on this OS version it is imposible to execute a scan. Example: the app was working fine on a Samsung Galaxy S21 Ultra with Android 11, the user upgraded the OS as soon it was available from Samsung, and now, when executing:

_ble.scanForDevices( withServices: [sUuid], ).listen((device) { add(BleEventConnectDevice(deviceId: device.id)); }, onError: ((error) { add(BleEventError(state: state, error: 'Errore Connessione: ' + error.toString())); })); OnError is called and error description is:

GenericFailure<ScanFailure>(code: ScanFailure.unknown, message: "Bluetooth cannot start (code 0)")

This is from the App's Merged Manifest :

' <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" / > <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" / > <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" / > <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" / > <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" / >'

Same problem on a Google Pixel 5, and other Samsung smartphones. How can I solve this, before more users loose ability to use my app?

You have to add
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

@pgiacomo69
Copy link
Author

As now, I've solved with ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION. But I cannot understand why I need these permissions on Android 12, expecially while specifying "neverForLocation" on BLUETOOTH_SCAN.
Also, I dont know why in an App compiled with sdk 30 as target BLE should stop working on Android 12.
I think that these problems are not this library fault, but are due to Android SDKs idiosincrasies.

My App is running on almost 3000 Devices without any problem until Android 12 Release, on this OS version it is imposible to execute a scan. Example: the app was working fine on a Samsung Galaxy S21 Ultra with Android 11, the user upgraded the OS as soon it was available from Samsung, and now, when executing:
_ble.scanForDevices( withServices: [sUuid], ).listen((device) { add(BleEventConnectDevice(deviceId: device.id)); }, onError: ((error) { add(BleEventError(state: state, error: 'Errore Connessione: ' + error.toString())); })); OnError is called and error description is:
GenericFailure<ScanFailure>(code: ScanFailure.unknown, message: "Bluetooth cannot start (code 0)")
This is from the App's Merged Manifest :
' <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" / > <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" / > <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" / > <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" / > <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" / >'
Same problem on a Google Pixel 5, and other Samsung smartphones. How can I solve this, before more users loose ability to use my app?

You have to add <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

@dhanukaperera
Copy link

Hi Giacomo, I had the same issue after Samsung released Android 12 to my S21. Below the steps I did for fix it:

1)Change uses-permission in android manifest ( removed android:maxSdkVersion="30"):

    <!-- required for API 18 - 30 -->
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

    <!-- required for API 23 - 30 -->
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION"/>

    <!-- API 31+ -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"       android:usesPermissionFlags="neverForLocation" />

2) Change android\build.glade (kotlin_version = 1.5.31)

buildscript {
    ext.kotlin_version = '1.5.31'
    repositories {
        google()
        mavenCentral()        
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()   
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
    project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

3) change android\app\build.grade (compileSdkVersion 31, minSdkVersion 21, targetSdkVersion 31)

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion 31

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    lintOptions {
        disable 'InvalidPackage'
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }

    defaultConfig {
        applicationId "com.signify.hue.reactivebleexample"
        minSdkVersion 21
        targetSdkVersion 31
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

4) Request the follow permissions

await Permission.locationWhenInUse.request();
await Permission.bluetooth.request();
await Permission.bluetoothScan.request();
await Permission.bluetoothConnect.request();

I had the same issue with my Samsung Galaxy S10+ Android 12 Update. This resolves the issue for me. Thanks!

@PieterAelse
Copy link
Collaborator

Like mentioned in #498 the readme file explains what permission-setup you'd need when supporting Android X...12. Also see the manifest of the example app.

README: https:/PhilipsHue/flutter_reactive_ble#which-permissions-are-needed

@zalox
Copy link

zalox commented Feb 3, 2022

Hello good people,

I managed to get this working on Android 12.

  <uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:node="remove"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" tools:node="remove"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="remove"/>
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

So the gist is, use toos:node="remove" to remove any permission from the merged manifests so that we don't add any derivatives of the permissions that any of the dependencies might have.

To add the tools:node features add this to the top of your manifest:

xmlns:tools="http://schemas.android.com/tools"

@Taym95
Copy link
Collaborator

Taym95 commented Feb 15, 2022

Hello good people,

I managed to get this working on Android 12.

  <uses-permission android:name="android.permission.BLUETOOTH"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
                     android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:node="remove"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" tools:node="remove"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:node="remove"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="remove"/>
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

So the gist is, use toos:node="remove" to remove any permission from the merged manifests so that we don't add any derivatives of the permissions that any of the dependencies might have.

To add the tools:node features add this to the top of your manifest:

xmlns:tools="http://schemas.android.com/tools"

This is already documented in the readme:

https:/PhilipsHue/flutter_reactive_ble#which-permissions-are-needed

@Taym95 Taym95 closed this as completed Feb 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants