I want to support MANAGE_EXTERNAL_STORAGE permission for my file manager app. I have followed the following steps to do that. I have added the following line into the manifest
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
and requested the permission on main activity using the following code:
if (Environment.isExternalStorageManager()) {
//todo when permission is granted
} else {
//request for the permission
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
My question is not regarding acquiring the permission. It is something else. When I accidentally used my old APK for Android 10 devices with legacy support for storage access, it actually worked on the Pixel 4 emulator with Android 11 in Android Studio. It asked for the write permission (WRITE_EXTERNAL_STORAGE) while I believe that permission is no longer available on Android 11. When I went into permissions, I did not see the storage (WRITE_EXTERNAL_STORAGE) permission (which I see on Android 6-10 devices), I saw the management (MANAGE_EXTERNAL_STORAGE) permission for Android 11. How does this happen? Did the system map WRITE_EXTERNAL_STORAGE into MANAGE_EXTERNAL_STORAGE permission?
The reason I am asking is that my users who upgraded their phones from Android 10 to 11, they reported that the app (which contains the WRITE_EXTERNAL_STORAGE only) is not responding on start. How does it work for me on my emulator with Pixel 4 Android 11? Is it possible to really get the same behavior my users get? I do not have a real Android 11 device.
Thank you.
From my own experience, starting from API 30 (android 11 and above), the below permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
is completely ignored(but MUST be included for API 29 and below in your manifest.
Having said that, users will be promoted to manually enable scoped storage on their phone settings as long as the below permission is available on your manifest.
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
what you must note
When targeting API 30 and above, please make sure to handle External storage gracefully else OS may denied this permission and your app may crash if try/catch is not equally implemented gracefully. Snippets below
if (Environment.isExternalStorageManager()) {
//todo when permission is granted
} else {
//request for the permission here
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
In addition, don't forget to also add android:requestLegacyExternalStorage="true" in your manifest, this will enable your app users to opt out of scoped storage (only when your target API was 29, android 10) and your user access your app with android 11 and above.
As stated here,
Apps that run on Android 11 but target Android 10 (API level 29) can still request the requestLegacyExternalStorage attribute. This flag allows apps to temporarily opt out of the changes associated with scoped storage, such as granting access to different directories and different types of media files. After you update your app to target Android 11, the system ignores the requestLegacyExternalStorage flag.
Since your old APK is targetting Android 10, having requestLegacyExternalStorage can be used by Android OS to support backward compatibility.
Android does this because all apps which do not target Android 11 will stop working all together which is a pain for all users. Why bother users that they cannot use their existing app after updating to Android 11. No one will update in that case till all devs update their app.
Related
I'm trying to achieve some clean up tools. More and more manufacturers have forbidden rooting devices due to some "security reason", it's forbidden NOT to request for unlock.
After API 28, This code will make error:
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, 1); // Request permission or not, Will got same result
File rootFolder = Environment.getExternalStorageDirectory(); // That is working fine
rootFolder.listFiles(); // That will return null
Sure, I can use this:
android:requestLegacyExternalStorage="true"
But I belive that will be killed in future.
So, Any elegant way to manage SDCard?
On Android 10 Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() will return storage paths but paths are not readable or writable.
For Android 10 you can continue to use paths provided by Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() if you add android:requestLegacyExternalStorage="true" to application tag in manifest file. At runtime your app can call Environment.isExternalStorageLegacy() to check if the request has been done.
Another (not known) possibility (only for Android 10) is to add <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> to manifest file.
The user has to go to the advanced settings of the app and enable from Advanced settings Install unknown apps | Allow from this source.
The nice thing with this is that the user can switch the access rights. You can make it easier for the user if you implement an intent for
Settings.ACTION_APPLICATION_DETAILS_SETTINGS where he can change the settings.
A funny thing is that Environment.isExternalStorageLegacy() returns true then too.
Compiling for Android 11 both options do not work on an Android 11 device. (But they continue to work for Android 10 devices). The paths of Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() are usable again in read mode and very often in write mode too. And this is great as one can simply list the contents of directories like Download or Pictures or DCIM/Camera again using the File class.
But adding <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> to manifest file and implementing an intent for
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION will give your app read/write access for all files even on removable micro sd card.
(Finally you can remove the google ban not being able to read/write your own micro sd card on your own Android device using your own app).
Environment.isExternalStorageManager() can be used to check if the permission is on/off.
As long as you do not try to upload your app to the play store you are fine.
use android:requestLegacyExternalStorage="true" in your Manifest below <application
I'm testing my app on an emulator. I have an export function where I create and write to a file in the external storage's downloads directory. And I also have an import function where I read a file from the external storage's downloads directory.
From Android documentation:
If the device is running Android 5.1 or lower, or 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 the device is running Android 6.0 or higher, and 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.
My emulator is running on Android 6.0 and my app's target SDK is 25, therefore I must also request each dangerous permission it needs while the app is running. I did so for the export functionality and everything works properly. However, when I'm implementing the import function I didn't request a permission during runtime. And the strange thing is I'm still able to read from my external storage's permission without READ_EXTERNAL_STORAGE being requested and granted at runtime. READ_EXTERNAL_STORAGE is a dangerous permission according to this Android documentation .
To verify, I made sure to disable permissions before I started using the feature and after it is completed, I verified again that the permission still wasn't granted. Although I'm happy with the behaviour since it's working without me requesting permission at runtime, but according to the documentations I don't believe this behaviour is expected. That's why I will like to know what's causing this and to figure out the problem before I publish any changes for the app.
Here's a code snippet of my manifest:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
The code snippet where I pick a file to read:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("text/*");
startActivityForResult(intent, GET_FILE_RESULT_CODE);
The code snippet where I read the file chosen from the code snippet above (exportFile is simply the URI from onActivityResult):
BufferedReader br;
try {
br = new BufferedReader(new InputStreamReader(context.getContentResolver().openInputStream(exportFile)));
String line;
// Skip first header line
br.readLine();
while ((line = br.readLine()) != null) {...}
Thanks!
There's a well explanation here,
READ_EXTERNAL_STORAGE
Provides protected read access to external storage. In Android 4.1 by
default all applications still have read access. This will be changed
in a future release to require that applications explicitly request
read access using this permission. If your application already
requests write access, it will automatically get read access as well.
There is a new developer option to turn on read access restriction,
for developers to test their applications against how Android will
behave in the future.
In short, READ_EXTERNAL_STORAGE only exists as of Jelly Bean (Level 16). So, unless you're using a Jelly Bean phone and set the developer option "Protect USB storage" it won't be a problem.
You know,Android Runtime Permissions are grouped, since you applied for WRITE_EXTERNAL_STORAGE permission in the manifest already, so there's no need to apply for READ_EXTERNAL_STORAGE permissions.Both of them are the same group.
This question already has answers here:
Is it available to set checkSelfPermission on minimum SDK < 23?
(5 answers)
Closed 6 years ago.
One of my app has a permission RECORD_AUDIO and my app's targetSdkVersion is 22. I cannot check permission at runtime. But I notice if I install my app in Android 6.0 and above. Users can manually deny permission in System App permissions Settings. So my question is how can I check whether my app is grand Audio permission under API level 23?
I have searched for quite a long time but only find some document about how to check permission in Android M.
I have noticed context has a function checkCallingOrSelfPermission, but it's behavior is strange under Android 6.0. I am using the solution from here. But I after I manually close app permission in System App settings. The result I get is not what I expect.
String permission = "android.permission.RECORD_AUDIO";
int ret = context.checkCallingPermission(permission);
Forget to mention:
minSdkVersion 9
targetSdkVersion 22
Edit:
I have noticed that there is a solution which is to upgrade v4 support lib to v23. It will be a little complex to upgrade my v4 support lib. So I want another solution.
In API 22 and below:
int permission = PermissionChecker.checkSelfPermission(context, permission);
if (permission == PermissionChecker.PERMISSION_GRANTED) {
// good to go
} else {
// permission not granted, you decide what to do
}
In API 23 and above:
From the docs:
If your app needs a dangerous permission, you must check whether you
have that permission every time you perform an operation that requires
that permission. The user is always free to revoke the permission, so
even if the app used the camera yesterday, it can't assume it still
has that permission today.
To check if you have a permission, call the
ContextCompat.checkSelfPermission() method. For example, this snippet
shows how to check if the activity has permission to write to the
calendar:
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
If the app has the permission, the method returns
PackageManager.PERMISSION_GRANTED, and the app can proceed with the
operation. If the app does not have the permission, the method returns
PERMISSION_DENIED, and the app has to explicitly ask the user for
permission.
https://developer.android.com/training/permissions/requesting.html
for Api 22 and lower versions, if i am not wrong, you don't need to check permission on runtime, you have to just add them at AndroidManifest and app will ask this permission just one time when first install period. So it's maybe you have to check your AndroidManifest, can you please add this to here.
In case of you want to use your application on Api 23 as well as lower versions:
You can try to use if statement to check Sdk version and request dungerous permission on runtime if version 23 or upper.
if (android.os.Build.VERSION.SDK_INT > 23) { /*Ask Dungerous Permissions here*/ }
and maybe it will solve the problem on Api 22 and lower versions.
You can check how to ask permission on runetime from Requesting Permissions at Run Time
Also you have to add Api 23 compile to your application and make your target to Api 23.
Edit : Now i see you mentioned that, it's complex to upgrade your support lib to v23 but i didn't understand why. You need to just install some SDK and add some Strings to gradle. Do you use something like custom support lib?
You need to look at System Permissions and
Requesting Permissions
Please Go through this link will guide you how permission is set and reduce your work to implement permission
Example of Dangerous Permissions and Special Permissions
https://github.com/henrychuangtw/AndroidRuntimePermission
Facebook, Evernote, Pocket - all apps get this permission on Android 6.0 automatically, even though they are targeting 23 (targetSdkVersion=23).
There has been a lot of documentation regarding the new Marshmallow permission model. One of them is SYSTEM_ALERT_WINDOW been 'promoted' to 'above dangerous' permission class thus requiring a special user intervention in order for apps to be granted with those. If the app has targetSdkVersion 22 or lower, app gets this permission automatically (if requested in the manifest).
However, I've noticed some apps that get this permission, without needing to send the user to the setting special page of Draw over other apps permission. I saw Facebook, Evernote, Pocket - and perhaps there are more.
Anyone knows how an app can be granted this permission without the user go through Settings -> Apps -> Draw over other apps?
Thanks
It is a new behaviour introduced in Marshmallow 6.0.1.
Every app that requests the SYSTEM_ALERT_WINDOW permission and that is installed through the Play Store (version 6.0.5 or higher is required), will have granted the permission automatically.
If instead the app is sideloaded, the permission is not automatically granted. You can try to download and install the Evernote APK from apkmirror.com. As you can see you need to manually grant the permission in Settings -> Apps -> Draw over other apps.
These are the commits [1] [2] that allow the Play Store to give the automatic grant of the SYSTEM_ALERT_WINDOW permission.
Yeh After Marshmallow come Android make security level more stick, But For SYSTEM_ALERT_WINDOW you can show floating action and anything You can Force user to give permission for it By Following Codes in your onCreate() method.
Put this code after setContentView:
// Check if Android M or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Show alert dialog to the user saying a separate permission is needed
// Launch the settings activity if the user prefers
Intent myIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(myIntent);
}
The action ACTION_MANAGE_OVERLAY_PERMISSION directly launches the 'Draw over other apps' permission screen.
Edit:
My Above Code works 100% Correct
But I just found that many guys are still searching that how can allow ACTION_MANAGE_OVERLAY_PERMISSION permanently like If user has allow Permission Once then don't ask it every time he open application so here is a solution for you:
Check if device has API 23+
if 23+ API then check if user has permit or not
if had permit once don't drive him to Settings.ACTION_MANAGE_OVERLAY_PERMISSION and if has not permit yet then ask for runtime permission check
Put below line in your onCreate() method. Put this after setContentView:
checkPermission();
Now put below code in onActivityResult:
#TargetApi(Build.VERSION_CODES.M)
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
if (!Settings.canDrawOverlays(this)) {
// You don't have permission
checkPermission();
} else {
// Do as per your logic
}
}
}
Now finally the checkPermission method code:
public void checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
}
}
And don't forget to declare this public variable in your class:
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
Now(2019) that Google offers an alternative API to SYSTEM_ALERT_WINDOW in the form of Bubbles in Android Q, Google has decided to eventually deprecate SYSTEM_ALERT_WINDOW in a future Android release.
And Android Go devices will no longer grant this permission
i.e Settings.canDrawOverlays() == false
For those who want to get this permission automatically when the app is downloaded from the Play Store, besides the SYSTEM_ALERT_WINDOW in the Manifest, you should go to this link and request this from Google.
You have to provide some additional information why you need this permission and Google will review and give you the automatically permission.
Bear in mind that before you ask for this, you have to:
Have the SYSTEM_ALERT_WINDOW permission in the Manifest
Prompt the user to grant the SYSTEM_ALERT_WINDOW permission within your app, when not already granted
If I miss something feel free to update the answer
If the app targets API 22 or lower, then Play Store will give the SYSTEM_ALERT_WINDOW permission and others when the user clicks to install (showing an alert) even if its device is Android 6.0
Otherwise, if the app targets API 23 or above, so that permission will be request to grant in run time.
Been doing some Android permission research and ran across an application that - according to the AndroidManifest.xml file - only declares WRITE_EXTERNAL_STORAGE as a permission. The Android Market only reports this as well. Using the aapt tool to dump the uses-permission it also only reports the one permission.
However, in code running on the Android device (or emulator), doing the following:
PackageManager pm = getPackageManager();
List<PackageInfo> pkgList = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
...
PackageInfo p = pkgList.get(i); // where i is the index of the apk in question
String[] perms = p.requestedPermissions;
I get 2 permissions for this APK, READ_PHONE_STATE and the one in the manifest, WRITE_EXTERNAL_STORAGE. Looking at the "Manage Apps" screen and selecting details for this also shows the additional READ_PHONE_STATE permission.
Are there cases where permissions can be/are 'implied' (in code, by feature use, etc) that would not be required in the Android Manifest? Or put another way, why does aapt return one set of permissions and the getPackageManager().getPackageInfo() API return a different set?
EDIT:
Searching with "more better" terms discovered the answer I was looking for: Android permissions: Phone Calls: read phone state and identity
In short, APKs compiled with earlier version of the SDK did inherit some permissions for free...
As far as I know permissions must always be explicitly set in the manifest.
If an application needs access to a feature protected by a permission, it must declare that it requires that permission with a element in the manifest. Then, when the application is installed on the device, the installer determines whether or not to grant the requested permission by checking the authorities that signed the application's certificates and, in some cases, asking the user. If the permission is granted, the application is able to use the protected features. If not, its attempts to access those features will simply fail without any notification to the user.
source
The difference you are seeing I believe is due to the protectionLevel attribute on permissions. Any permissions that are set to "normal" are not required to be OK'd by the user so they just show up in the Details section.