Skip to content

Commit

Permalink
Changed|Fixed: Always request `MANAGE_EXTERNAL_STORAGE if on Android …
Browse files Browse the repository at this point in the history
…`>= 11` when running `termux-setup-storage`

Requesting `MANAGE_EXTERNAL_STORAGE` should additionally grant access to unreliable/removable volumes like USB OTG devices under the `/mnt/media_rw/XXXX-XXXX` paths on `Android >= 12`, so request that if possible. Check #71 (comment) for more info.

Fixes issue on Android `14`, where using `targetSdkVersion=28`, that requests the legacy `WRITE_EXTERNAL_STORAGE` will actually request the `photos, music, video, and other files` permissions (`READ_MEDIA_AUDIO`/`READ_MEDIA_IMAGES`/`READ_MEDIA_VIDEO`) and apparently access to full external storage `/sdcard` is not available for some users, maybe because `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` permissions are not granted for those device automatically in addition to `READ_MEDIA_*` permission. The issue is not reproducible on Android `13-15` avd. To solve this, we request the singular `MANAGE_EXTERNAL_STORAGE` permission instead so that full access is always available.

Related: #3647 (comment)

See also:
- https://developer.android.com/training/data-storage/shared/media#access-other-apps-files
- https://developer.android.com/reference/android/Manifest.permission#READ_MEDIA_IMAGES
  • Loading branch information
agnostic-apollo committed Jun 17, 2024
1 parent 3ae0d60 commit 8cdeb55
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 10 deletions.
2 changes: 1 addition & 1 deletion app/src/main/java/com/termux/app/TermuxActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ public void run() {

// If permission is granted, then also setup storage symlinks.
if(PermissionUtils.checkAndRequestLegacyOrManageExternalStoragePermission(
TermuxActivity.this, requestCode, !isPermissionCallback)) {
TermuxActivity.this, requestCode, true, !isPermissionCallback)) {
if (isPermissionCallback)
Logger.logInfoAndShowToast(TermuxActivity.this, LOG_TAG,
getString(com.termux.shared.R.string.msg_storage_permission_granted_on_request));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,31 +209,44 @@ public static List<String> getPermissionsNotRequested(@NonNull Context context,

/** If path is under primary external storage directory and storage permission is missing,
* then legacy or manage external storage permission will be requested from the user via a call
* to {@link #checkAndRequestLegacyOrManageExternalStoragePermission(Context, int, boolean)}.
* to {@link #checkAndRequestLegacyOrManageExternalStoragePermission(Context, int, boolean, boolean)}.
*
* @param context The context for operations.
* @param filePath The path to check.
* @param requestCode The request code to use while asking for permission.
* @param prioritizeManageExternalStoragePermission If {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* permission should be requested if on
* Android `>= 11` instead of getting legacy
* storage permission.
* @param showErrorMessage If an error message toast should be shown if permission is not granted.
* @return Returns {@code true} if permission is granted, otherwise {@code false}.
*/
@SuppressLint("SdCardPath")
public static boolean checkAndRequestLegacyOrManageExternalStoragePermissionIfPathOnPrimaryExternalStorage(
@NonNull Context context, String filePath, int requestCode, boolean showErrorMessage) {
@NonNull Context context, String filePath, int requestCode,
boolean prioritizeManageExternalStoragePermission, boolean showErrorMessage) {
// If path is under primary external storage directory, then check for missing permissions.
if (!FileUtils.isPathInDirPaths(filePath,
Arrays.asList(Environment.getExternalStorageDirectory().getAbsolutePath(), "/sdcard"), true))
return true;

return checkAndRequestLegacyOrManageExternalStoragePermission(context, requestCode, showErrorMessage);
return checkAndRequestLegacyOrManageExternalStoragePermission(context, requestCode, prioritizeManageExternalStoragePermission, showErrorMessage);
}

/**
* Check if legacy or manage external storage permissions has been granted. If
* {@link #isLegacyExternalStoragePossible(Context)} returns {@code true}, them it will be
* checked if app has has been granted {@link Manifest.permission#READ_EXTERNAL_STORAGE} and
* {@link Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions, otherwise it will be checked
* if app has been granted the {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission.
* Check if legacy or manage external storage permissions has been granted.
*
* - If `prioritizeManageExternalStoragePermission` is `true and running on Android `>= 11`, then
* it will be checked if app has been granted the
* {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}.
* - If `prioritizeManageExternalStoragePermission` is `false` and running on Android `>= 11`, then
* if {@link #isLegacyExternalStoragePossible(Context)} returns `true`, them it will be
* checked if app has has been granted {@link Manifest.permission#READ_EXTERNAL_STORAGE} and
* {@link Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions, otherwise it will be checked
* if app has been granted the {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission.
* - If running on Android `< 11`, then it will only be checked if app has been granted
* {@link Manifest.permission#READ_EXTERNAL_STORAGE} and
* {@link Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions.
*
* If storage permission is missing, it will be requested from the user if {@code context} is an
* instance of {@link Activity} or {@link AppCompatActivity} and {@code requestCode}
Expand All @@ -256,16 +269,34 @@ public static boolean checkAndRequestLegacyOrManageExternalStoragePermissionIfPa
*}
* @param context The context for operations.
* @param requestCode The request code to use while asking for permission.
* @param prioritizeManageExternalStoragePermission If {@link Manifest.permission#MANAGE_EXTERNAL_STORAGE}
* permission should be requested if on
* Android `>= 11` instead of getting legacy
* storage permission.
* @param showErrorMessage If an error message toast should be shown if permission is not granted.
* @return Returns {@code true} if permission is granted, otherwise {@code false}.
*/
public static boolean checkAndRequestLegacyOrManageExternalStoragePermission(@NonNull Context context,
int requestCode,
boolean prioritizeManageExternalStoragePermission,
boolean showErrorMessage) {
Logger.logVerbose(LOG_TAG, "Checking storage permission");

String errmsg;
boolean requestLegacyStoragePermission = isLegacyExternalStoragePossible(context);
Boolean requestLegacyStoragePermission = null;

if (prioritizeManageExternalStoragePermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
requestLegacyStoragePermission = false;

if (requestLegacyStoragePermission == null)
requestLegacyStoragePermission = isLegacyExternalStoragePossible(context);

boolean checkIfHasRequestedLegacyExternalStorage = checkIfHasRequestedLegacyExternalStorage(context);

Logger.logVerbose(LOG_TAG, "prioritizeManageExternalStoragePermission=" + prioritizeManageExternalStoragePermission +
", requestLegacyStoragePermission=" + requestLegacyStoragePermission +
", checkIfHasRequestedLegacyExternalStorage=" + checkIfHasRequestedLegacyExternalStorage);

if (requestLegacyStoragePermission && checkIfHasRequestedLegacyExternalStorage) {
// Check if requestLegacyExternalStorage is set to true in app manifest
if (!hasRequestedLegacyExternalStorage(context, showErrorMessage))
Expand Down

0 comments on commit 8cdeb55

Please sign in to comment.