Requesting multiple Bluetooth permissions in Android Marshmallow - android

I'm developing an app with connectivity which connects to a Bluetooth device with SDK 23 as compile with. I'm having problems with requesting multiple permissions for Bluetooth. This is what I have done so far:
#Override
public void onStart() {
super.onStart();
if (D)
Log.e(TAG, "++ ON START ++");
if (ContextCompat.checkSelfPermission(MyBlueToothClientActivity.this,
Manifest.permission.BLUETOOTH)
!= PackageManager.PERMISSION_GRANTED) {
} else {
ActivityCompat.requestPermissions(MyBlueToothClientActivity.this,
new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN},
REQUEST_ENABLE_BT);
}
if (ContextCompat.checkSelfPermission(MyBlueToothClientActivity.this,
Manifest.permission.BLUETOOTH)
!= PackageManager.PERMISSION_GRANTED) {
} else {
ActivityCompat.requestPermissions(MyBlueToothClientActivity.this,
new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN},
REQUEST_CONNECT_DEVICE_INSECURE);
}
}
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_ENABLE_BT: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay!
Intent enableIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
if (CommonData.mChatService == null)
setupChat();
Toast.makeText(MyBlueToothClientActivity.this, "Permission denied for bluetooth", Toast.LENGTH_SHORT).show();
}
return;
}
case REQUEST_CONNECT_DEVICE_INSECURE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay!
Intent enableIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_CONNECT_DEVICE_INSECURE);
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
if (CommonData.mChatService == null)
setupChat();
Toast.makeText(MyBlueToothClientActivity.this, "Permission denied for bluetooth", Toast.LENGTH_SHORT).show();
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Although I'm able to get the dialogue box for requesting enabling the Bluetooth, I don't get the second permission, i.e. to connect to a device. In the logcat, I get:
01-01 06:41:24.334 25473-25473 E/BluetoothChat: ++ ON START ++
01-01 06:41:24.344 25473-25473 W/Activity: Can reqeust only one set of permissions at a time
And since I'm not able to connect to the device, I just get stuck here. And this code works fine on Android version up to Lollipop, just causes problem on the Marshmallow version.

BLUETOOTH and BLUETOOTH_ADMIN are normal permissions and are therefore they are automatically granted. Only permissions in the table of dangerous permissions need to requested at runtime.
However, as mentioned in the Android 6.0 changes: Access to Hardware Identifier:
To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions:
WifiManager.getScanResults()
BluetoothDevice.ACTION_FOUND
BluetoothLeScanner.startScan()
If you're using any of those methods, you'll need to request at least ACCESS_COARSE_LOCATION at runtime (as it is a dangerous permission).

Related

android Build.GetSerial() throwing exception

I'm developing a Xamarin.Android project,where I need to get the Device Serial number.
I have implemented it the way it is shown below.
Also added the permissions in the manifest file:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
string serial;
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
serial = Build.GetSerial();
}
else
{
serial = Build.Serial;
}
I have tried it on two different devices(both android 9.0).Sadly i get the following exception when the GetSerial() function is called(huawei p10): Java.Lang.SecurityException: <Timeout exceeded getting exception details> .
On an other device(galaxy s8) I get this exception:
Java.Lang.SecurityException: getSerial requires READ_PHONE_STATE or
READ_PRIVILEGED_PHONE_STATE permission
I really dont understand what the problem is,because I have added both permissions in the manifest,which the exception sais...
Any idea what am I doing wrong?
You should faced a persmission issue. And since Android Marshmallow, you need to ask the user for the permissions.
Besides adding the permission in android manifest file, you can also add runtime permissions like this:
static readonly int REQUEST_PHONE_STATE = 1;
public void checkPermission()
{
Log.Info(TAG, "Checking permission.");
// Check if the permission is already available.
if (ActivityCompat.CheckSelfPermission(this, Manifest.Permission.ReadPhoneState) != (int)Permission.Granted)
{
// permission has not been granted
RequestPhoneStatePermission();
}
else
{
// permissions is already available, show the camera preview.
Log.Info(TAG, " permission has already been granted.");
getInfo();
}
}
Method RequestPhoneStatePermission
private void RequestPhoneStatePermission()
{
Log.Info(TAG, "PhoneState permission has NOT been granted. Requesting permission.");
if (ActivityCompat.ShouldShowRequestPermissionRationale(this, Manifest.Permission.ReadPhoneState))
{
Log.Info(TAG, "Displaying PhoneState permission rationale to provide additional context.");
Snackbar.Make(layout, Resource.String.permission_phonestate_rationale,
Snackbar.LengthIndefinite).SetAction(Resource.String.ok, new Action<View>(delegate (View obj) {
ActivityCompat.RequestPermissions(this, new String[] { Manifest.Permission.ReadPhoneState }, REQUEST_PHONE_STATE);
})).Show();
}
else
{
// PhoneState permission has not been granted yet. Request it directly.
ActivityCompat.RequestPermissions(this, new String[] { Manifest.Permission.ReadPhoneState }, REQUEST_PHONE_STATE);
}
}
Method OnRequestPermissionsResult
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
if (requestCode == REQUEST_PHONE_STATE)
{
// Received permission result for camera permission.
Log.Info(TAG, "Received response for phone state permission request.");
// Check if the only required permission has been granted
if (grantResults.Length == 1 && grantResults[0] == Permission.Granted)
{
// Camera permission has been granted, preview can be displayed
Log.Info(TAG, "phonestate permission has now been granted. Showing preview.");
Snackbar.Make(layout, Resource.String.permission_available_phonestate, Snackbar.LengthShort).Show();
getInfo();
}
else
{
Log.Info(TAG, "phonestate permission was NOT granted.");
Snackbar.Make(layout, Resource.String.permissions_not_granted, Snackbar.LengthShort).Show();
}
}
else
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
method getInfo
private void getInfo() {
string serial;
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
serial = Build.GetSerial();
}
else
{
serial = Build.Serial;
}
Log.Info(TAG, "serial = " + serial);
}
Here is a full demo, you can check it.
After that, you can get the effect:
For more details,you can check:
https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/permissions?tabs=windows
https://devblogs.microsoft.com/xamarin/requesting-runtime-permissions-in-android-marshmallow/

Automatically detect to show allow permission after user deny in the first time asking

I working with anroid app in Android M and need some permission that allow to access CAMERA, RECORD_AUDIO, WRITE_EXTERNAL_STORAGE, ... so I put my permission checking when user open the app in the first time. But if user deny it, how can I detect it automatically that user is using the feature that required permission then ask them again? Or I have to put my condition in every feature that need permission.
I know when we use some feature that required permission but not allowed already it will throw the exception, so do we have any class that handle this task?
I use this logic: Every Activity extends a BaseActivity, in which there is a method that check the permissions everytime the onCreate() is called.
The method that I use is:
public static boolean hasPermissions(Context context, String... permissions) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
So, everytime the user launch a new Activity, the application check the permissions and display which permissions are not granted yet.
To manage the result, use onRequestPermissionsResult()
BTW, this logic will ask the user the permission even if the activity's feature doesn't require any permission.
If you want to ask the user the permissions only where the feature that requires a permission, you must check it in every activity
onRequestPermissionsResult you can check particular Permission is granted or not you can call Permission dialog again from there if particular permission not granted for Example
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA) {
// BEGIN_INCLUDE(permission_result)
// Received permission result for camera permission.
Log.i(TAG, "Received response for Camera permission request.");
// Check if the only required permission has been granted
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Camera permission has been granted, preview can be displayed
Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
Snackbar.make(mLayout, R.string.permision_available_camera,
Snackbar.LENGTH_SHORT).show();
} else {
Log.i(TAG, "CAMERA permission was NOT granted.");
// Ask again for permission
}
// END_INCLUDE(permission_result)
} else if (requestCode == REQUEST_CONTACTS) {
Log.i(TAG, "Received response for contact permissions request.");
// We have requested multiple permissions for contacts, so all of them need to be
// checked.
if (PermissionUtil.verifyPermissions(grantResults)) {
// All required permissions have been granted, display contacts fragment.
Snackbar.make(mLayout, R.string.permision_available_contacts,
Snackbar.LENGTH_SHORT)
.show();
} else {
Log.i(TAG, "Contacts permissions were NOT granted.");
// Ask again for permission
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}

Avoid "Screen overlay detected" error when asking for user persmission

I am developing a GoogleMap application and thus require the users location. When asking this with the following code:
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
// all good, do my thing.
}else{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, this.MY_PERMISSIONS_REQUEST_ACCESS_LOCATION);
return false;
}
it will prompt the user to deny or allow it. When user Allows the permission then the "Screen overlay detected" error shows up.
I believe this happen because the newer androids will not let you change the permissions of an app when the app is opened and thus you need to close it an allow to permissions in the Setting->App.
My question is how would you program the app to ask user the permission without getting into trouble with the screen overlay and thus making the UX horrible.
The problem is that once the users is prompted to DENY/ALLOW the location permission then after ALLOW is pressed the screen overlay message appears immediately.
Here is the code which handles the location:
public boolean requestCurrentLocation(float zoomLevel){
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mZoomLevel = zoomLevel;
// Check if gps is enabled
LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean gpsEnabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(!gpsEnabled){
Log.d("GPS_TAG", "Gps not enabled");
showToastMessageShort(getResources().getString(R.string.cannot_get_location));
Intent gpsOptionsIntent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(gpsOptionsIntent);
return false;
}
try{
Log.d("GPS_TAG", "Calling FusedLocationApi request location updates");
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}catch(IllegalStateException ie){
Log.d("GPS_TAG", "Error requesting FusedLocationApi locationUpdates: " + ie);
return false;
}
}else{
Log.d("GPS_TAG", "Location access not granted, asking for grant");
showToastMessageShort(getResources().getString(R.string.cannot_get_location));
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, this.MY_PERMISSIONS_REQUEST_ACCESS_LOCATION);
return false;
}
return true;
}
I also added the onRequestPermissionResult() method:
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_ACCESS_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
requestCurrentLocation(ZOOM_LEVEL_BUILDING);
} else {
Log.e("GPS_", "Cannot get gps location data, permission not granted!");
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
You're going to hate this.
The problem is your call to "showToastMessageShort(...)".
You will see what you experienced if you Toast a message while a permissions request is pending.
To fix this, don't ever Toast in the code path of requesting permissions.
Yes, this is BS.
For more info: Screen overlay detected blocks Android permissions

Revoked permission android.permission.CALL_PHONE

I'm trying to programmatically call to a number with following code:
String number = ("tel:" + numTxt.getText());
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse(number));
startActivity(intent);
I've set the permission in the Manifest:
<uses-permission android:name="android.permission.CALL_PHONE"/>
I'm working with real device for testing and debugging, it is Nexus 5 with Android M, my compileSdkVersion is 23. I'm getting the following Security Exception:
error: Permission Denial: starting Intent { act=android.intent.action.CALL dat=tel:xxxxxxxxxx cmp=com.android.server.telecom/.components.UserCallActivity } from ProcessRecord{cbbd7c1 5228:com.dialerTest.DialerApp/u0a96} (pid=5228, uid=10096) with revoked permission android.permission.CALL_PHONE
I've searched the web and this community for similar Q/A and couldn't find the answer. Any help will be appreciated.
Permission CALL_PHONE belong to dangerous permission group.
So if your apps target SDK is 23 or higher and your device is running on Android 6.0 or higher, you must request for CALL_PHONE permission while the app is running.
Example :
String number = ("tel:" + numTxt.getText());
mIntent = new Intent(Intent.ACTION_CALL);
mIntent.setData(Uri.parse(number));
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.CALL_PHONE},
MY_PERMISSIONS_REQUEST_CALL_PHONE);
// MY_PERMISSIONS_REQUEST_CALL_PHONE is an
// app-defined int constant. The callback method gets the
// result of the request.
} else {
//You already have permission
try {
startActivity(mIntent);
} catch(SecurityException e) {
e.printStackTrace();
}
}
When your app requests permissions, the system presents a dialog box to the user. When the user responds, the system invokes your app's onRequestPermissionsResult() method, passing it the user response.
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_CALL_PHONE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the phone call
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
In android 6.0 (Api lvl 23) we have something called "Runtime Permissions". You have to read about it.
You can find documentation here.
You code could works only if you make an ACTION_DIAL, not an ACTION_CALL where you need to request a permission, so if you want to make a call, please follow this example:
MANIFEST:
<uses-permission android:name="android.permission.CALL_PHONE" />
Code:
import static android.Manifest.permission.CALL_PHONE;
Intent i = new Intent(Intent.ACTION_CALL);
i.setData(Uri.parse("tel:0612312312"));
/*
Intent i = new Intent(Intent.ACTION_DIAL);
i.setData(Uri.parse("tel:0612312312"));
if (i.resolveActivity(getPackageManager()) != null) {
startActivity(i);
}*/
if (ContextCompat.checkSelfPermission(getApplicationContext(), CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
startActivity(i);
} else {
requestPermissions(new String[]{CALL_PHONE}, 1);
}
in fragment class
Step 1:
import static android.Manifest.permission.CALL_PHONE;
step 2:Where your onclick button:
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:" +driver_no ));
if (ContextCompat.checkSelfPermission(getActivity(), CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
startActivity(callIntent);
} else {
requestPermissions(new String[]{CALL_PHONE}, 1);
}
[Or] you are using in activity class means change the getActivity to getApplicationContext()
FYI: If you're targeting Android 11, you need to add an Intent Query element for Intent.ACTION_DIAL in your manifest.
https://developer.android.com/training/basics/intents/package-visibility#intent-signature

Android 6 bluetooth

I upgraded to Android 6 and my applications who use Bluetooth doesn't work with this new API version. It's the same problem with application on Play Store: Bluetooth spp tools pro (good application to view if bluetooth works) which doesn't discovery of devices.
The problem seems to be in Bluetooth discovery:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.startDiscovery()
Log.i("BLUETOOTH", String.valueOf(mBluetoothAdapter.isDiscovering())); // Return false
My applications work well with Android 4/5 and I followed : http://developer.android.com/guide/topics/connectivity/bluetooth.html
Staring with Android 6.0 it is not enough to include permissions on manifest.
You have to ask the user explicitly about each permission that is considered "dangerous".
BluetoothDevice.ACTION_FOUND requires BLUETOOTH and ACCESS_COARSE_LOCATION permissions
http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#ACTION_FOUND
The ACCESS_COARSE_LOCATION
http://developer.android.com/reference/android/Manifest.permission.html#ACCESS_COARSE_LOCATION
is a "dangerous" permission and therefore you have to ask for it using requestPermission before doing actual discovery.
public void doDiscovery() {
int hasPermission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
if (hasPermission == PackageManager.PERMISSION_GRANTED) {
continueDoDiscovery();
return;
}
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{
android.Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_COARSE_LOCATION_PERMISSIONS);
}
then on you will get the user answer on onRequestPermissionsResult
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_COARSE_LOCATION_PERMISSIONS: {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
continueDoDiscovery();
} else {
Toast.makeText(this,
getResources().getString(R.string.permission_failure),
Toast.LENGTH_LONG).show();
cancelOperation();
}
return;
}
}
}
To work with previous versions of android you should use compatibility libraries and make the calls using ActivityCompat
I've spent some time investigating the problem.
Created bug report on Android bug tracker here
The problem is that system does not forward BluetoothDevice.ACTION_FOUND intents to the registered BroadcastReceiver. Logcat shows lines like this:
10-16 07:34:09.147 786-802/? W/BroadcastQueue﹕ Permission Denial: receiving Intent { act=android.bluetooth.device.action.FOUND flg=0x10 (has extras) } to ProcessRecord{5ce2d92 21736:com.example.mvl.bluetoothtest/u0a74} (pid=21736, uid=10074) requires android.permission.ACCESS_COARSE_LOCATION due to sender com.android.bluetooth (uid 1002)
Which themes for me that the application needs android.permission.ACCESS_COARSE_LOCATION permission to receive this intents. i personaly don't understand why I need that permission to get the Bluetooth devices around.
So if you add this permission to you Manifest, then it should work with one more precondition - You have to set target SDK and compile with SDK not higher, then 22.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
When checking the source code in GattService.java,you will find some code comments in method onScanResult:
// Do no report if location mode is OFF or the client has no location permission
// PEERS_MAC_ADDRESS permission holders always get results
if (hasScanResultPermission(client) && matchesFilters(client, result)) {
try {
ScanSettings settings = client.settings;
if ((settings.getCallbackType() &
ScanSettings.CALLBACK_TYPE_ALL_MATCHES) != 0) {
app.callback.onScanResult(result);
}
} catch (RemoteException e) {
Log.e(TAG, "Exception: " + e);
mClientMap.remove(client.clientIf);
mScanManager.stopScan(client);
}
}
this clarified what is needed to get a Bluetooth LE advertising report.

Categories

Resources