I am working on BLE Bluetooth scanning is working on all devices except the Android 10. Android 10 is not working, anyone please answer the question for the version 10 issues for scanning BLE Bluetooth
To make BLE scanning work on Android apps targeting Android 10 you need to ask the user for
ACCESS_BACKGROUND_LOCATION
along with ACCESS_FINE_LOCATION and also don't forget to add the permission in the manifest:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Here's the explanation why:
Android 10 (API level 29) introduces a number of features and behavior changes to better protect users' privacy. These changes extend the transparency and control that users have over their data and the capabilities they give to apps.
Your problem in short:
Access to device location in the background requires permission
To support the additional control that users have over an app's access to location information, Android 10 introduces the ACCESS_BACKGROUND_LOCATION permission.
Unlike the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions, the ACCESS_BACKGROUND_LOCATION permission only affects an app's access to location when it runs in the background. An app is considered to be accessing location in the background unless one of the following conditions is satisfied:
An activity belonging to the app is visible.
The app is running a foreground service that has declared a foreground service type of location.
To declare the foreground service type for a service in your app, set your app's targetSdkVersion or compileSdkVersion to 29 or higher. Learn more about how foreground services can continue user-initiated actions that require access to location.
Hope this fixes your problem :)
You need to have location enabled to perform a BLE scan on android 10.
To check if location is enabled and ask user to enable location service (Kotlin):
val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!LocationManagerCompat.isLocationEnabled(lm)) {
// Start Location Settings Activity, you should explain to the user why he need to enable location before.
startActivity(Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS))
}
LocationManagerCompat class is available in androidx.appcompat:appcompat:1.1.0 dependency, if you don't use androidx, you can use instead :
lm.isLocationEnabled()
Adding the following permissions to AndroidManifest.xml is not enough
android.permission.ACCESS_FINE_LOCATION,
android.permission.ACCESS_COARSE_LOCATION,
android.permission.ACCESS_BACKGROUND_LOCATION.
Add permissions to MainActivity such as
ActivityCompat.requestPermissions(
this,
new String[]
{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE
}, 0);
I faced the same problem, and I have just fixed it using cordova-plugin-android-permissions.
Android 10 (SDK 29) requires ACCESS_FINE_LOCATION, while previous versions require only ACCESS_COARSE_LOCATION. (more info in this Android Developers page)
Unlike some other answers state, the ACCESS_BACKGROUD_LOCATION is not required. It's better not to require it unless you really need it. Play Store considers background location a dangerous permission, and will ask you to fill in some forms to justify why do you need it.
Old plugins would ask only ACCESS_COARSE_LOCATION permission, which was enough for Android 9 (SDK 28) and older versions. The best solution is to update the plugin. But if it is hard to do, there is an easy workaround.
Add cordova-plugin-android-permissions (npm link) to your project, and use it to ask ACCESS_FINE_LOCATION permission.
Add this code to your "device ready" function to check if your app already has this permission or not:
permissions = cordova.plugins.permissions;
permissions.checkPermission(permissions.ACCESS_FINE_LOCATION, checkPermissionSuccessCallback, checkPermissionErrorCallback);
And then create the following callback function to ask user permission:
function checkPermissionSuccessCallback(status) {
if (status.hasPermission) {
console.log("Permission OK");
} else {
console.log("Permission not OK");
// ask user permission
permissions.requestPermission(permissions.ACCESS_FINE_LOCATION, requestPermissionSuccessCallback, requestPermissionErrorCallback);
}
}
You don't need to add anything to your "config.xml" to get this permission.
try this
//gradle
dependencies {
implementation 'no.nordicsemi.android.support.v18:scanner:1.4.3'
}
//import
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
//init scan setting
final ScanSettings settings = new ScanSettings.Builder().setReportDelay(1000).setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
//start scan
scanner.startScan(null, settings, scanCallback);
//scan callback
private final ScanCallback scanCallback = new ScanCallback() {
#Override
public void onScanResult(final int callbackType, #NonNull final ScanResult result) {
super.onScanResult(callbackType, result);
}
public void onBatchScanResults(#NonNull final List<ScanResult> results) {
for (final ScanResult result : results) {
//result.getDevice() is scanned device
}
}
#Override
public void onScanFailed(final int errorCode) {
// should never be called
}
};
For me it is working if i use fine permission in AndroidManifest. Switch on the location in Settings of the phone manually, and set app permission to location.
I suppose in android 10 having just the location permission is not enough. Your location service should be turned on while you are scanning for your device. So, ask the user for turning on the location service if it is turned off.
Alternative Solution:
You can use Companion device pairing for android 8 and higher. Mind you, In my opinion, it is not well documented, and it might be a bit unstable. For example, when I assigned a scan filter (BLE Service UUID) the Companion device pairing crashed, but without that filter, it worked.
Try setting the callbackType to CALLBACK_TYPE_ALL_MATCHES, it solves the problem for me.
val scanSettings = ScanSettings.Builder()
scanSettings.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
scanSettings.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
scanSettings.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
val filter = ScanFilter.Builder()
filter.setServiceUuid(ParcelUuid(Service_UUID))
bluetooth.bluetoothLeScanner.startScan(mutableListOf<ScanFilter>(filter.build()), scanSettings.build(), callback)
User need to allow "Location" - (GPS) on Device
Related
Right now I am trying to ask the user for permission to track his/her fine location, following the docs. I was playing around with the code, and it seems that I'm able to request a single permission from the user no problem, but when trying to request multiple permissions (Fine, Course, Background location) the permission UI doesn't even show. What the heck gives? The code is nearly the same.
The code for multiple permissions is here
private fun requestPermissions() {
val requestPermissionLauncher =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { granted ->
Timber.tag("ASDASD").e(granted.toString())
// We can check if either the FINE or APPROX permission location has been
// granted.
}
if (TrackingUtility.hasLocationPermissions(requireContext())) {
return
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
requestPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
} else {
requestPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
)
)
}
}
https://imgur.com/a/AIDCy4Q - Single permission request works
https://imgur.com/a/T8sTznX - Multiple permission doesn't show anything
I tried following the documentation to a T, but it seems that nothing wants to work unless I use a SingleRequestPermission
According to the documentation:
Even if several features in your app require location access, it's likely that only some of them require background location access. Therefore, it's recommended that your app performs incremental requests for location permissions, asking for foreground location access and then background location access. By performing incremental requests, you give users more control and transparency because they can better understand which features in your app need background location access.
And then then afterwards it highlights:
Caution: If your app targets Android 11 (API level 30) or higher, the system enforces this best practice. If you request a foreground location permission and the background location permission at the same time, the system ignores the request and doesn't grant your app either permission.
So for For Android 11 and up, if you request background location together with fine or coarse location the system will ignore the request. You need to request for background location separately (After being granted the fine or coarse location access)
I have an Android app that requires permission to read/write from/to external storage. When the app starts, it prompts the user to allow permission for external storage (and a bunch of other services - location, camera, etc).
Here's how we do it:
ActivityCompat.requestPermissions(this, new String[]{WRITE_EXTERNAL_STORAGE, CAMERA,ACCESS_FINE_LOCATION,INTERNET,WRITE_SETTINGS,ACCESS_WIFI_STATE,READ_PHONE_STATE}, PERMISSION_REQUEST_CODE)
Now, there is also an authentication service for which we need to read from external storage. We call this authentication service during startup.
Now, problem is: When you run the app for the 1st time after install, this call to authentication service may happen often before the permission to external storage is given. Because the authentication service has no permission to external storage, it fails authentication.
Workaround we do now: manually enable permission after install and then run the app. Not a long term solution though.
I did some reading and it seems in older API's we can force the permissions to be done during apk install itself. I am on API 29. Is it possible here to prompt the user to give permissions during install itself?
I also understand that this is considered a critical service and we can't bypass permissions. Rather we cannot force permissions to be default.
What I've done for now:
Initialized the authentication service later in the code so it runs only after permissions are done.
Is there a better solution available? Like some way to either force permissions for external storage by default or some other way? From what I have read, it doesnt look possible for the latest Android version. However, if there is a way to do this, pls let me know.
Thanks,
Anand
In order to know if the user has allowed or not the application with your requested permissions, you should override onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray).
This way, you can do something like this :
if (requestCode == PERMISSION_REQUEST_CODE) {
for (elem in grantResults) {
if (elem != PackageManager.PERMISSION_GRANTED)
// Abort your feature here
}
// Do your feature here
}
If every permission has been passed you can then do what you want.
The Android Developer Documentation has a nice tutorial, more complete than I've done above, but you get the idea.
Not possible to force permission at installation time, you would need to target API level < 23 but now you need to use 28/29 to create/update apps on PlayStore. You could consider to use an alternative storage location like getCacheDir() moving files where you need later when your app has permission or just getExternalFilesDir() path where you can read/write without additional permissions.
Is it possible here to prompt the user to give permissions during install itself?
From official doc:
If the device is running Android 6.0 (API level 23) or higher, and the app's targetSdkVersion is 23 or higher, the user isn't notified of any app permissions at install time.
Also:
If the device is running Android 5.1.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower while running on any version of Android, the system automatically asks the user to grant all dangerous permissions for your app at install-time (see figure 2).
In any case pay attention to this point:
Beginning with Android 6.0 (API level 23), users can revoke permissions from any app at any time, even if the app targets a lower API level.
In other word it means that today you have to check for and request permissions in your code.
Initialized the authentication service later in the code so it runs only after permissions are done.
You can use the ContextCompat.checkSelfPermission also in a Service.
When you start the service you can check if the permission is granted and avoid to fail.
You can use ActivityCompat.requestPermissions only in an Activity, but you can use the callback onRequestPermissionsResult to start your service.
Is there a better solution available?
You can't force the permission but for example your Service can check for permissions it needs and if it hasn't been granted yet, you can create a friendly notification to inform user.
Critical permissions like read/write external storage, camera, SMS, contacts, and a few others cannot be forcefully asked for prior to installation. So, they've to be asked for at runtime. The best practice is to ask for them just when they're actually needed instead of asking for them all at a time in the beginning.
If your service uses one of those permissions, you've to check for that permission every time before starting the service and then start the service only if the permission is granted. Otherwise, ask for permission, override onActivityResult and then start service only if the permission is granted.
Also, keep in mind to handle the scenario when the user marks the checkbox "Don't ask again" and denies permission. In that case, the user won't see the prompt again. So, you'll have to open permission settings using intent.
I'm trying to get the current location of the device using the plugin location but Android never asks for permissions. I get this error:
Exception has occurred. PlatformException
(PlatformException(PERMISSION_DENIED_NEVER_ASK, Location permission
denied forever- please open app settings, null))
I have added the line below in every `AndroidManifest.xml´ file that I could find in the Android folder (no idea which one I should use actually, I found 3 of them)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
I tried the plugin permission_handler to request them manually but still, no pop-up.
I'm completely lost and I find nothing similar on the net. Here is the code :
Future<LocationData> _getLocation() async {
LocationData currentLocation;
// returns a map with unreadable numbers which make no sense
var t = await PermissionHandler()
.requestPermissions([PermissionGroup.location]);
// returns false
var test = await location.requestPermission();
try {
currentLocation = await location.getLocation();
} catch (e) {
currentLocation = null;
}
return currentLocation;
}
Edit: I tried this on my device (OnePlus 6) and on an emulator (Pixel XL API 28). I've also tried to uninstall/reinstall the app.
After asking on the git of location plugin, I did this :
flutter clean
And I got the pop up asking for permissions.
Thank to this guy.
You can handle this kind of permission behavior in your code. Even if you never had installed your app on emulator this behavior can happen and you must handle this in your code because this can happen when your app are in final user hands.
This exception means that you're getting PermissionStatus.denied from checkPermissionStatus permission handler plugin method and this can happen when user hits Never ask again checkbox in android permission dialog and deny the request.
What is the most simple way to handle this?
If you get PermissionStatus.denied from checkPermissionStatus method you can show a dialog telling to the user that your app needs of that permission to provide a better experience and in this dialog you can redirect the user to Android permission settings where the user can enable the requested permission manually.
You can do this using openAppSettings() method from permission handler plugin.
Maybe you should read this article, it's a little bit old but it shows the correct flow about how to ask and handle user permissions in android. This article doesn't talks about the new permissions changes in newer Android Q.
The error message tries to make it clear: Your device (you or someone else on the device you are testing on) has selected Never when Android asked for the permission previously.
This means that Android will not ask for the permission again.
Just try it on a different device or uninstall the app again.
add this
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
I'm working with Heart Rate sensor on Samsung S6. In order access this sensor, starting from SDK Version > 23, BODY_SENSORS permission must be granted by the user https://stackoverflow.com/a/32636039/8204927
So far I've done the following
Added BODY_SENSORS permission in Manifest
Since targeted SDK Version is 23, first I check if BODY_SENSORS permission is granted to the Application, and if not, display Prompt asking user to grant the permission
Once user grants the permission I access the HeartRateSensor in the following way
SensorManager sensorManager =
((SensorManager)getSystemService(SENSOR_SERVICE));
Sensor heartRateSensor =
sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
sensorManager.registerListener(this, heartRateSensor,
SensorManager.SENSOR_DELAY_NORMAL);
And it all works fine, I'm getting bpm readings as expected.
However, when I move this logic to another Activity I'm getting
heartRateSensor == null, even though BODY_SENSORS has been granted in the same way as described above. What I discovered is that Android's SystemSensorManager has cached all the available Sensor Type in mSensorListByType list before the BODY_SENSORS permission has been granted, and that list isn't refreshed after granting permission.
By debugging I've discovered that second Activity overrides onResume
#Override
protected void onResume() {
super.onResume();
...
}
and that super.onResume(); calls
public List<Sensor> getSensorList(int type) {
// cache the returned lists the first time
...
}
of the SensorManager class, which populates mSensorListByType, and that list is not updated until the app restarts.
Is there any way to force SensorManager to update this list after the BODY_SENSOR permission is granted in run time, or I will have to asked the user to grant the BODY_SENSOR permission before any Activity that overrides onResume is started?
I found a workaround for this issue.
MixPanel API subscribes to the Application.ActivityLifecycleCallbacks, and
and when first Activity that overrides onResume is created, MixPanel will
access ACCELEROMETER Sensor in a ViewCrawler class. This will cause SensorManager to cache all available Sensors at the moment, and this list of
Sensors will not be updated even after BODY_SENSOR permission is granted.
By adding following meta-data tag under application tag in Manifest
android:name="com.mixpanel.android.MPConfig.DisableGestureBindingUI"
android:value="true" />
MixPanel API is prevented from accessing TYPE_ACCELEROMETER Sensor and thus caching of all available Sensors on first call of onResume.
Overall, this was not the solution I was hoping for. By looking at the documentation and the SDK code, it appears that this is an Android bug. This Sensor caching behavior was added to the API 1, and that changes in permission policy that were added to API 23 were not anticipated.
As #pavleku already found out the reason of the problem in the comments, I would like to have some additions to that.
Sadly, the suggested workaround did not work for me. In my use case, I was trying to implement reading heart rate sensor from a Samsung phone, in which I was experiencing the same behaviour.
Strangely this behaviour only appeared in Marshmallow and Nougat versions. Above API level 26 the sensor list has been updated automagically and the behaviour was gone.
There is also an alternative way to check if a device has BODY_SENSOR capabilities, without checking it from SensorManager. You can also do that via PackageManager.hasSystemFeature(FEATURE_SENSOR_HEART_RATE)
So all in all, after hours of trying to update the sensor list manually after granting permission, I have also failed miserably :)
Will the Android permissions WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE trigger the new grant permission dialog of Android M?
I agree with Guillaume Perrot 's answer. I have met the similar question when I write the permission of READ_WRITE_EXTERNAL_STORAGE in AndroidManifest.xml
with no permissions showing up in the app by default , people need to switch the toggle button of storage in the app permissions.Then I modify my targetSdkVersion in build.gradle to less than 23(MNC) and other number related with sdkVersion, the app installed with the permissions on.
The other way is to write requestpermission function in the place that you need the permisson. The code is as follow:
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)==
PackageManager.PERMISSION_GRANTED) {
//do the things} else {
requestPermissions(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
AnyNumber);
Because I have less than 15 reputation so I can't vote for the Guillaume Perrot 's answer.Just use this way to show my idea.
I solved add this if check version for Android M
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
requestPermissions(new String[]{android.Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
My answer is based on my tests on M Preview SDK version 2, using an emulator.
If you target MNC preview API level, WRITE_EXTERNAL_STORAGE is not granted by default and will be part of the new dynamic permission API.
You will see the storage permission as a toggle button in the new app permissions menu in device settings, and you can use Activity.requestPermissions to show the popup for that permission.
However if you target api level < MNC, it won't be classified as a dangerous permission, and thus will be granted without a way for the user to disable it (not showing up in permission settings), and you will not be able to compile code using Activity.requestPermissions anyway as the preview SDK enforces minSdkVersion="MNC" to use the new APIs.
This is a different behavior than location permissions: whatever the API level you target, the user will be able to turn location off in permission menu.
For the permission menu itself, the permission toggle state is ON by default if:
Target API level < MNC.
Target API level = MNC but you upgrade app on device from a previous install where target API level was less than MNC.
Otherwise you will see the toggle as OFF by default.
Hope it helps.
According to the docs:
Limited Permissions Granted at Install Time: When the user installs or updates the app, the system grants the app all permissions that the app requests that fall under PROTECTION_NORMAL.
So because READ_EXTERNAL_STORAGE is falling under PROTECTION_NORMAL , it won't trigger the dialog.
But because the level of WRITE_EXTERNAL_STORAGE is PROTECTION_DANGEROUS, it will fall under this behavior as described in docs:
User Grants Permissions at Run-Time: When the app requests a permission, the system shows a dialog to the user, then calls the app's callback function to notify it whether the permission was granted. If a user grants a permission, the app is given all permissions in that permission's functional area that were declared in the app manifest
Here is the sources for the protection level:
detailed list
According to Android docs you don't need to request permission about read and write external storage.
Edit: in the latest Android M release you need to ask for both read and write permissions
Storage permission falls under dangerous protection level, So all the dangerous protection level permissions will not be granted at install time in Android M, if App target SDK is set to 23. They will be given at run time.
And yes these permissions can be revoked at run time also.
No permission dialog will not be triggered automatically, you need to do a request by using API such as requestPermissions() method to show that native dialog.
Please check the dangerous level permission list here