porlicus
porlicus

Reputation: 871

How to check whether Uri points to external or internal storage?

While developing for Android at some point I have a Uri which points to a Notification sound file previously selected by a user. This file may be located on internal or external storage. Is there a universal method (which works for all API levels) to check whether this Uri points to external or internal storage?

Why do I need this? Well, after Google introduced new permission policy in API23 I have the following problem. If Uri points to internal storage my App doesn't have to get READ_EXTERNAL_STORAGE permission to access that Uri. However if a Uri points to some sound file located on external storage, the App must have READ_EXTERNAL_STORAGE permission to access that file.

So I want to show different dialogs depending on the source of Uri.

Upvotes: 2

Views: 1681

Answers (1)

Bö macht Blau
Bö macht Blau

Reputation: 13019

The way they put it at developer.android.com:

  • Internal Storage
    Store private data on the device memory.
  • External Storage
    Store public data on the shared external storage.
  • So internal means "owned by your app and thus unreadable for the other third party apps" (in contrast to system apps) and will always be located in the device memory whereas external storage means "readable by all" and stored no matter where.

    If your app somehow (install or download) caused the ringtone to be saved on internal storage, you don't need any permissions.

    If on the other hand the ringtone is in the shared public memory, you need READ_EXTERNAL_STORAGE to access it.

    It depends on the target SDK as well as the current API level when the user will be asked to grant the permission:

  • device running 5.1 or older:
    permissions have to be granted at install time, can't be revoked
  • device running 6.0+ (= API level 23) and targeting API level 22 or lower:
    permissions have to be granted at install time, can be revoked later, no SecurityException if app tries to proceed without asking but of course some parts may fail (Content Provider queries may return null etc.)
  • device running 6.0+ (= API level 23) and targeting API level 23 or higher:
    permissions don't have to be granted at install time, can be revoked later, app has to ask for permissions and will run into a SecurityException if it tries to proceed in spite of a missing permission
  • EDIT To make things a little more complicated, there is a kind of "internal" external storage. The documentation calls this "app-private" data. It will not be accessed by the MediaStore content provider if the query comes from third party apps but it is readable for all apps holding the READ_EXTERNAL_STORAGE permission.

    Your app will always be able to access this type of storage, for example using ContextCompat.getExternalFilesDirs() which will return paths to both the built-in device memory as well as an SD-card, if they exist.

    BUT one should not count on a Uri being something like an absolute path (see for instance the Commonsware blog)

    Instead, it is recommended to use a ContentResolver query, in your case with MediaStore.Audio.Media.EXTERNAL_CONTENT_URI respectively MediaStore.Audio.Media.INTERNAL_CONTENT_URI.

    If you start with the query for internal content, you won't need a permission. If you don't find the file, it may have been deleted or the SD-card may have been removed. Or it is stored in the "public" external storage, for which you won't get any information at all if you don't have the permission...

    So you can check the permission state (ContextCompat.checkSelfPermission() if you're targeting API level 23 or higher, PermissionChecker.checkSelfPermission() when targeting lower API levels), query the ContentResolver if you have permission and display a dialog according to the results.

    Upvotes: 1

    Related Questions