Dynamically request Android permission - android

Is it possible to dynamically request Android permission?

Android M introduces new run-time permission model which allows you to do this.

Yes,If the device is running Android 6.0 or higher, and your app's target SDK is 23 or higher.
code snippet:
if(ContextCompat.checkSelfPermission(getApplicationContext(),Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_PHONE_STATE}, 1);
}
int result = ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_PHONE_STATE);
if (result == 0)
// function which uses the permission
}

From targetapi>22 android need to have runtime permissions for performing some operations,Following is the code for requesting permission at run time in android
//checking wether the permission is already granted
if(ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS) ==PackageManager.PERMISSION_GRANTED){
// permission is already granted
}else{
//persmission is not granted yet
//Asking for permission
ActivityCompat.requestPermissions(this,new String[] {
Manifest.permission.READ_CONTACTS},REQUEST_CODE);
}
The above code is for requesting read contact permissions. Similarly you can request other permissions also based on your requirement.

This solution address devices targeting API < 23, for 23 and above see Aun answer.
While many will answer this question with NO, and will tell you that it is a security risk, I found a way!
Take a look at ASTRO File Manager, one of the famouse file manager application for android. It can brows your SD-CARD, show your images, videos and the most interesting thing, it can install APKs!
So theoretically if I created an app that did not request for internet access during the install time, I could attach an APK inside my Raw library and install it in run time, and place all my permissions there. After that it's easy only a meter of applications communications. You can do this with broadcast or shared application id(read about it first it might be risky)
From user experience it will looks like you are asking a permission in run time.

Related

Why READ_PHONE_STATE permission asked "make and manage phone call"?

For getting the IMEI i using from this code:
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, 1);
} else {
iMEI = tm.getDeviceId();
}
But when my app is running this dialog box coming up:
The program asks to grant permission To make and manage phone calls which can scary users from using app.
And now my question is:
Why READ_PHONE_STATE permission asked "make and manage phone call"? While I have not make phone call and manage phone call in my
app.
READ_PHONE_STATE permission is listed as Dangerous permission and provides access to read phone state. It comes under the Phone permission group. If dangerous permission is asked, the system shows dialog related to its group. in your case, Phone. and That is the reason- the user is asked for "make and manage phone call" permission. This is how the permissions are asked-
https://developer.android.com/training/permissions/requesting
To make it more clear, see
https://developer.android.com/guide/topics/permissions/overview
It says -
If the device is running Android 6.0 (API level 23) and the app's
targetSdkVersion is 23 or higher, the following system behavior
applies when your app requests a dangerous permission:
If the app doesn't currently have any permissions in the permission group, the system shows the permission request dialog to the user
describing the permission group that the app wants access to. The
dialog doesn't describe the specific permission within that group.
For example, if an app requests the READ_CONTACTS permission, the
system dialog just says the app needs access to the device's
contacts. If the user grants approval, the system gives the app just
the permission it requested.
If the app has already been granted another dangerous permission in the same permission group, the system immediately grants the
permission without any interaction with the user. For example, if an
app had previously requested and been granted the READ_CONTACTS
permission, and it then requests WRITE_CONTACTS, the system
immediately grants that permission without showing the permissions
dialog to the user.
There are a lot of better ways to get a unique identifier. For example-
String android_id = Settings.Secure.getString(getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
Why READ_PHONE_STATE permission asked "make and manage phone call"?
While I have not make phone call and manage phone call in my app.
After Marshmallow we need to explicitly call the permissions which come under Dangerous permission.
READ_PHONE_STATE comes under Permission Group Called Phone
Because google got lazy. It is the same with READ_EXTERNAL_STORAGE

Is there any difference between ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions between sdk 22 and 25?

I'm trying to get the current location from either GPS or from network provider for which I'm using these permissions by declaring it in AndroidManifest.xml,
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
I'm validating the permission at runtime as,
ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)
and
ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
It works fine for me with Android SDK version 22 whereas in SDK version 25 though the permission is declared within AndroidManifest.xml it's not getting reflected at runtime.
I checked whether these permissions are granted or not by,
adb shell dumpsys package com.abc.xyz
The permissions were not granted for my app which runs on SDK-25 when I tried to grant the permission with adb shell as,
adb shell pm grant com.abc.xyz
android.Manifest.permission.ACCESS_COARSE_LOCATION
I'm suspecting Android made these permissions to be granted from SDK version 25 (Please correct me If I'm wrong). With just googling I'm not able to figure out how the permission level/severity changed between SDK Versions.
Please help me know about it.
On all versions of Android, your app needs to declare both the normal and the dangerous permissions it needs in its app manifest, as described in Declaring Permissions. However, the effect of that declaration is different depending on the system version and your app's target SDK level:
If your app's target SDK is 22 or lower: If you list a dangerous permission in your
manifest, the user has to grant the permission when they install the
app; if they do not grant the permission, the system does not install the app at all.
If your app's target SDK is 23 or higher: The app has to list the permissions in the manifest, and it must request each dangerous permission it needs while the app is running. The user can grant or deny each permission, and the app can continue to run with limited
capabilities even if the user denies a permission request.
you can refer to this Link
Try this one out.
private boolean RequestPermissions() {
int camera = ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.CAMERA);
int storage = ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
List<String> listPermissionsNeeded = new ArrayList<>();
if (camera != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add(CAMERA);
}
if (storage != PackageManager.PERMISSION_GRANTED) {
listPermissionsNeeded.add(WRITE_EXTERNAL_STORAGE);
listPermissionsNeeded.add(READ_EXTERNAL_STORAGE);
}
if (!listPermissionsNeeded.isEmpty()) {
ActivityCompat.requestPermissions(getActivity(), listPermissionsNeeded.toArray
(new String[listPermissionsNeeded.size()]), REQUEST_ID_MULTIPLE_PERMISSIONS);
return false;
}
return true;
}
this is the function that checks if the permission is needed and if they are not available it will ask from user.
Another thing you should keep in mind that ACCESS_FINE_LOCATION gives you better and accurate location and ACCESS_COARSE_LOCATION gives you less accurate location.
ACCESS_FINE_LOCATION includes ACCESS_COARSE_LOCATION. However, there is a catch:
ACCESS_COARSE_LOCATION gives you last-known location which is battery friendly https://developer.android.com/training/location/retrieve-current.html#setup
However, if you need something like live/ real-time location like Pokemon Go, use ACCESS_FINE_LOCATION
It gives you live/ real-time location.

below 23 android xiaomi devices permission

So I have camera permission in manifest , Still when app goes to start camera it crashes .this happens beacuse user had denied the permission in permission manager for camera that comes with xiaomi devices
So the app Crashes , can someone help about how to handle this.
with the normal way of getting permisions , it does not give correct result
String permission = "android.permission.CAMERA";
int res = getContext().checkCallingOrSelfPermission(permission);
res is always 0(has Permission) for below 23 devices , if user has manually denied permission by going to permission manager then also
Revoking permissions on android devices below 23 is non-standard behavior and is afaik only possible through customized OS versions (like Cyanogen mod or in your case, the Xiaomi modified version). Users should be aware, that revoking permissions that way may cause error ins apps.
Prior to Android 6.0, you could reasonably assume that if your app is running at all, it has all the permissions it declares in the app manifest.
https://developer.android.com/training/permissions/best-practices.html#testing
Therefore I suggest you run your methods that require a certain permission with a try/catch. If the api lvl is below 23 and your method call fails, you know for sure if you have the permission or not.

BroadcastReceiver for Bluetooth device discovery works on one device but not on another

Code:
I use the following code taken from here with a target API level 23 (and minimum API level 18).
private final BroadcastReceiver mReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action))
{
bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
deviceNameTextView.setText(bluetoothDevice.getName());
}
}
};
On a button pressed event I call:
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
mBluetoothAdapter.startDiscovery(); // was initialized successsfully
My AndroidManifest.xml contains:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
Devices:
Samsung Galaxy S III (API level 18)
Sony Xperia Z3 (API level 23)
Facts:
both devices are running the exact same code
device 1 can discover device 2 (and any other Bluetooth device)
device 2 cannot discover device 1 (or any other Bluetooth device)
both devices are discoverable
tested discoverability with the standard system dialog for pairing for both devices successfully
both devices were unpaired at all times (I don't want to pair)
No exceptions are thrown.
What is wrong?
Update 1:
Since API level 23 permissions may have to be requested at run time. Yvette pointed me to that, thank you! Unfortunately it didn't solve my problem.
What speaks against her theory is the following:
mBluetoothAdapter.startDiscovery() returns true, which means success (see here).
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity , Manifest.permission.BLUETOOTH_ADMIN);
if(permissionCheck == PackageManager.PERMISSION_GRANTED)
Log.i("info", "Permission granted!");
else
Log.i("info", "Permission not granted!");
Running this code with BLUETOOTH_ADMIN and BLUETOOTH returns both times:
Permission granted!
When doing some research, I found the following article from the official documentation regarding changes in Android 6.0 (API level 23).
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()
So, I was missing the permissions ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION all along. But just adding them in the AndroidManifest.xml file is not enough. You have to request those privileges at run time like Yvette suggested.
You can find here how you can do that or just use this piece of code I wrote to get the permissions needed for Bluetooth discovery.
final int CODE = 5; // app defined constant used for onRequestPermissionsResult
String[] permissionsToRequest =
{
Manifest.permission.BLUETOOTH_ADMIN,
Manifest.permission.BLUETOOTH,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
};
boolean allPermissionsGranted = true;
for(String permission : permissionsToRequest)
{
allPermissionsGranted = allPermissionsGranted && (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED);
}
if(!allPermissionsGranted)
{
ActivityCompat.requestPermissions(this, permissionsToRequest, CODE);
}
mBluetoothAdapter.startDiscovery();
This code is assuming that the user grants the permissions (for simplicity). If you want your app to behave differently when the permissions are not granted see "Handle the permissions request response" in this article.
Sounds like you're not managing runtime permissions for sdk 23 and higher. The phone with the sdk of 23 will either silently ignore any requests that require runtime permissions or crash.
Also see in the docs System Permissions.
If your app lists normal permissions in its manifest (that is, permissions that don't pose much risk to the user's privacy or the device's operation), the system automatically grants those permissions. If your app lists dangerous permissions in its manifest (that is, permissions that could potentially affect the user's privacy or the device's normal operation), the system asks the user to explicitly grant those permissions. The way Android makes the requests depends on the system version, and the system version targeted by your app:
If the device is running Android 6.0 (API level 23) or higher, and the app's targetSdkVersion is 23 or higher, the app requests permissions from the user at run-time. The user can revoke the permissions at any time, so the app needs to check whether it has the permissions every time it runs. For more information about requesting permissions in your app, see the Working with System Permissions training guide.
If the device is running Android 5.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions when the user installs the app. If you add a new permission to an updated version of the app, the system asks the user to grant that permission when the user updates the app. Once the user installs the app, the only way they can revoke the permission is by uninstalling the app.
Often times a permission failure will result in a SecurityException being thrown back to the application. However, this is not guaranteed to occur everywhere. For example, the sendBroadcast(Intent) method checks permissions as data is being delivered to each receiver, after the method call has returned, so you will not receive an exception if there are permission failures. In almost all cases, however, a permission failure will be printed to the system log.
The permissions provided by the Android system can be found at Manifest.permission. Any application may also define and enforce its own permissions, so this is not a comprehensive list of all possible permissions.
A particular permission may be enforced at a number of places during
your program's operation:
At the time of a call into the system, to prevent an application from
executing certain functions.
When starting an activity, to prevent
applications from launching activities of other applications.
Both
sending and receiving broadcasts, to control who can receive your
broadcast or who can send a broadcast to you.
When accessing and
operating on a content provider.
Binding to or starting a service.
As for the app crashing:
Everything every Android Developer must know about new Android's Runtime Permission
Next question in your head right now. So will my application crash?
Such a kindness sent from god delivered through the Android team. When we call a function that requires a permission user revoked on application with targetSdkVersion less than 23, no any Exception will be thrown. Instead it will just simply do nothing. For the function that return value, it will return either null or 0 depends on the case.
But don't be too happy. Although application would not be crashed from calling a function. It may still can crash from what that application does next with those returned value.
Some more details in these answers Require dangerous permissions during installation When asking for runtime permission for location

Location needs to be enabled for Bluetooth Low Energy Scanning on Android 6.0

After upgrading to Android version 6.0 Bluetooth Low Energy (BLE) scanning will only work if Location services are enabled on the device. See here for reference: Bluetooth Low Energy startScan on Android 6.0 does not find devices
Basically, you need to have the permission enabled for the app as well as on for the phone. Is this a bug? Is it possible to scan without location services actually enabled? I don't want to have to have location for all my apps.
EDIT
I failed to mention that I am using the startScan() method in BluetoothLeScanner provided in API 21. I am okay with the course and fine location permissions in the manifest that this method require. I just don't want the users of my app to have to enable location services on their device (GPS, etc.) to use my app.
Previously, the startScan() method would run and return results with the Location services disabled on the phone. On Marshmallow, however, the same application would "scan" but silently failed and returned no results when location services were not enabled on the phone and course/fine location permissions were still in the manifest.
No, this is not a bug.
This issue was brought up to Google where they responded saying that this was the intended behavior and they won't fix it. They directed developers to this site where it points out that location permission is now needed for hardware identifier access. It is now the developer's responsibility to make their users aware of the requirement.
In the issue, however, it doesn't address why Location services (GPS, etc.) are required and it doesn't seem like they are going to revisit the issue to explain this since it has been marked as the intended behavior.
To answer the second part of the question: Yes, it is possible to scan without enabling Location services. You can do a Bluetooth classic scan using BluetoothAdapter.getDefaultAdapter().startDiscovery() and that will work with Location services off. This will discover all Bluetooth devices, BLE and otherwise. However, BLE devices won't have a scan record that they would have had if they were seen as a result of startScan().
I solved this by setting targetSdkVersion to 22 in the Gradle file.
You must declare ACCESS_COARSE_LOCATION in the manifest but, BLE scanning will work even if the user denies this permission from App Settings.
This is just a hack to avoid requesting location permission. It's better to target the latest android versions.
Edit
This solution should no longer be used as Google Play will require that new apps target at least Android 8.0 (API level 26). Apps should request for location permission for BLE scanning.
What I found is that after Android 6 you must grant ACCESS_COARSE_LOCATION permission. But on some devices is also necessary your phone location service (GPS) to be switched on, so you can discover peripheral devices. I found that using Nexus 5x, with Android 7.0.
I also tried this on manifest but did not request permission, not sure why. Is you app prompting for Location permission on startup? If it's not, we need to request for permission on runtime.
Also you can check this to test if your app is working fine:
Open Settings > Apps > YourApplication > Permissions
and enable Location and then try to scan for results.
Location will be listed here only if you have provided ACCESS_COARSE_LOCATION on manifest.
You can use BluetoothAdapter.startDiscovery().
It will scan for both Bluetooth Smart and classic Bluetooth devices, but location services do not need to be enabled.
(You still need ACCESS_COARSE_LOCATION permissions on Android 6.)
You can call BluetoothDevice.getType on found devices to filter for Bluetooth Smart / Low Energy devices.
after you add ACCESS_COARSE_LOCATION to Manifest,
ask for permission on runtime:
public void checkPermission() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
} else {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,}, 1);
}
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
} else {
checkPermission();
}
}
worked for me!
You can scan BLE devices without location access using CompanionDeviceManager (API26).
https://developer.android.com/reference/android/companion/CompanionDeviceManager.
Well, I have looked at my code written in Eclipse and I use there the startScan (API 21) function without declaring location stuff in manifest file. I still get the proper callback.
Have you tried running the code without the location declaration?
In the other hand - you can use the deprecated startLeScan (API 18) which does not require these permissions. However, in my opinion searching and reading desired characteristic in service is more complicated with API 18 methods.
From what I recently noticed on android 8.0, it is not required to turn on your GPS to do a BLE Scan, but you have to declare it in the manifest, but the user must allow the permission.
Android will prompt the user to allow location permission when you attempt to do a scan with startScan() method. Your scan will fail if the permission is not allowed.
Starting with API 31, you can use a new BLUETOOTH_SCAN permission instead of location permission.
If the app does not derive physical locations, you can add the android:usesPermissionFlags="neverForLocation" attribute to the BLUETOOTH_SCAN permission declaration:
<manifest ...>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
...
</manifest>
And then:
val requiredPermissions = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
} else {
arrayOf(Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN)
}
requestPermissions(requiredPermissions, 9999)
More: https://xizzhu.me/post/2021-10-05-android-12-bluetooth-permissions/

Categories

Resources