READ_ / WRITE_EXTERNAL_STORAGE permissions / app-Specific External Storage

3,198 views
Skip to first unread message
Assigned to ewpa...@gmail.com by taifu...@gmail.com

BodyMindPower

unread,
Mar 11, 2019, 8:37:54 AM3/11/19
to mitappinv...@googlegroups.com

Hello especially to the developers,

 

I would like to save files in the so-called "app-specific (private) external storage". According to Google, this does not require READ_ / WRITE_EXTERNAL STORAGE permissions.
https://developer.android.com/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE

The path of the app-specific (private) external storage is:
/storage/emulated/0/Android/<packageName>/files
In my case it is:
/storage/emulated/0/Android/data/appinventor.ai_bodymindpower.TestFileAI/files

There are many misconceptions about the terms Internal Storage and External Storage. Here is a very good overview on this topic:
https://www.dev2qa.com/android-read-write-external-storage-file-example/.

The File component and the TaifunFile extension requires READ_ / WRITE_EXTERNAL STORAGE. Therefore, the permission must be denied or previously removed from the Manifest. These permissions should only be needed if you want to save to a non-app-specific directory.

Steps to test the app (API 
19 !):

1. Download AIA (maybe possible later)

(Note: The new method of the TaifunTools extension (ApplicationSpecificDirectory) is not yet published. I recently received it only for test purposes from Taifun. Taifun has been helping me with my apps for several years. So I have to wait until Taifun gives me his OK to publish the aia. But you don't really need the aia for testing. So first go to step 2 and download the APK)

https://drive.google.com/file/ ... is coming later ...

 

2. Open AIA with AI2 or download the APK from here:

https://www.bodymindpower.de//pages/download.php

3. (Create APK) install APK and open the app

Do nothing else!


4. Check if the app-specific (private) external storage folder has been created on your device:

Android - data - <packageName> - files - priv.txt
(If the device has a MicroSD card, the directory should also be created on this SD card)


5. Press the "save file
..." button.

If a permission is required, deny it. You should then get a message "Error 908: The permission WRITE ... has been denied ..."


6. Click on the "read file ..." button.

Deny permission! Message: "Error: 908: The permission READ ... has been denied ... "

 

7. Grant permissions

now everything should work as it should do without these permissions

 

8. Uninstall the app

check if the app-specific (private) directory has been deleted. This is an indication that this is an app-specific (private) directory.

 

On Kodular and Thunkable I get similar issues. Only on AppyBuilder the app runs without permissions correctly.

(test the AppyBuilder version (APK): https://www.bodymindpower.de//pages/download.php )

Therefore, I assume that there is a bug here with App Inventor. The app-specific (private) external storage may NOT be declared correctly; see Google Developer Documentation (link see above):

"Also starting in API level 19, this permission is not required to read / write files in your application-specific directories returned by Context.getExternalFilesDir (String) and Context.getExternalCacheDir ()."

 

Thanks and regards,

Anke

blocks.JPG
Screen1.png

Taifun

unread,
Mar 11, 2019, 9:27:17 AM3/11/19
to MIT App Inventor Forum
thank you very much for bringing this up
now with the Google permission system obviously each app developer will try to use as less permissions as necessary
and reading from/writing to the  app-specific (private) external storage folder does not need any permissions.
/storage/emulated/0/Android/<packageName>/files

Unfortunately App Inventor currently does not consider this exception. Therefore this should be updated accordingly not only for the file component, but also for other components which currently require  READ_ / WRITE_EXTERNAL STORAGE permissions.

Taifun  

Trying to push the limits of App Inventor! Snippets, Tutorials and Extensions from Pura Vida Apps by Taifun.        



Evan Patton

unread,
Mar 11, 2019, 3:50:10 PM3/11/19
to MIT App Inventor Forum
The challenge here is that internal/external file paths are specified at runtime, and so there's no general way to know whether the File component will need to request permission at compile time for external files. Therefore, we chose a conservative default to always ask permission before commencing a file operation. What we could possibly do is provide a designer property for the File component called "AccessExternalFiles" or something along those lines that when true adds the permissions and when false doesn't, but then that requires the developer to be cognizant of the difference between the two directories and always be correct in their usage. That's probably more than what we would want for most of our users who won't care that their apps ask for external storage (remember the vast majority of apps made with App Inventor are not intended for the Play Store or even other people).

Regards,
Evan

Taifun

unread,
Mar 11, 2019, 7:10:50 PM3/11/19
to MIT App Inventor Forum
for some reason, the answer from Anke went into the spam folder... here it is...
Taifun


Thanks for the explanations. Obviously, it is also possible to do this without a query in the designer. AppyBuilder works the way it should.

Anke

BodyMindPower

unread,
Mar 11, 2019, 8:46:00 PM3/11/19
to MIT App Inventor Forum

BodyMindPower

unread,
Mar 12, 2019, 8:43:05 AM3/12/19
to mitappinv...@googlegroups.com

"...and so there's no general way to know whether the File component will need to request permission at compile time for external files."

This is not true, because the file is not an external file, but a file in an app-specific (private) folder that is already created when the app is opened.

see point 4 of my Thread:
4. Check if the app-specific (private) folder has been created on your device:

Android - data - <packageName> - files - priv.txt
the file (priv.txt) is created in this folder (tested on several devices with Android 8.x and 9)

Obviously, the app at this time (when the app is opend the first time) has permission to write / save to this app-specific folder.

When I then press the "save file" button (to save this file again) the permission WRITE_EXTERNAL_STORAGE is falsely required. This is shurely not an issue at compiling ...

This file should have the same properties as a file that is in the assets
(/data /data/<app package name>/) and therefore should have the same permissions.

Anke

PS: "Internal and external is only a logical classification." see: https://www.dev2qa.com/android-read-write-external-storage-file-example/

Evan Patton

unread,
Mar 12, 2019, 5:02:08 PM3/12/19
to MIT App Inventor Forum
It might be true in your specific case, but I'm not arguing a specific case. I'm talking about the general case. For example, consider the following code:

global PublicAndPrivate = ["priv.txt', "/public.txt"]
File1.WriteFile(PublicAndPrivate[randint(1,2)], "foo")

Should File1 force the inclusion of permissions to read external storage or write external storage in this case? Recall that the app in addition to asking user permission at runtime must also include the permission at compile time in AndroidManifest.xml. Internal vs external may be a logical classification, but it has real world implications for systems that create Android apps because of the permissions model and the fact that permissions must be declared at compile time.

The challenge is that determining whether to include the READ/WRITE external storage permission is functionally equivalent to the halting problem (which is undecidable) because of the possibility that one could determine whether to ask the permission as a function of whether the app will halt or not. Note that I expect in the AppyBuilder case, they are just including the permission and then not asking for permission, which is something that we can do, but that is different than not including the permission at all. Note that for SDK levels before 23, the app will be granted the permission regardless of whether it is accessing private/public directories because of this.

Regards,
Evan

BodyMindPower

unread,
Mar 14, 2019, 5:28:19 PM3/14/19
to mitappinv...@googlegroups.com

Hello Evan,

 

sorry, but I will not let up yet.

 

It may apply in your particular case, but I do not dispute any specific case.

I do not really understand that my case should be specific.

 

I'm talking about the general case. For example, consider the following code:

global PublicAndPrivate = ["priv.txt", "/public.txt"]

File1.WriteFile (PublicAndPrivate [randint (1,2)], "foo")

Also, I do not understand what the Python code (random query for a single file) should explain here. The declaration of a private directory as app-specific is the question here. And this should actually be done at compile time (install-time).


Recall that the app in addition to asking user permission at runtime must also include the permission at compile time in AndroidManifest.xml.

No, as far as I know, this should be only necessary for Pre-Marshmallow devices, for API > 22 the permission are always requested at runtime. You can catch this by declaring:

 

<uses-permission

     android:name="android.permission.WRITE_EXTERNAL_STORAGE"

     android:maxSdkVersion="22" />

 

Note that I expect in the AppyBuilder case, they are just including the permission and then not asking for permission, which is something that we can do, but that is different than not including the permission at all. Note that for SDK levels before 23, the app will be granted the permission regardless of whether it is accessing private/public directories because of this.

No, the permission in this case (API < 23) is requested (only for public / non-app-specific directories) at install-time and you have to grant it with all the other permissions listed at this time. Otherwise the app is not installed.

 

To ensure that app-specific directories do not require any permissions, the following must be declared in the Manifest: minSdkVersion ≥ 19 .

I think, I found a workaround for this issue with another extension, but why should this not be possible with the File component?

 

Regards, Anke

Evan Patton

unread,
Mar 15, 2019, 9:49:25 AM3/15/19
to MIT App Inventor Forum
My understanding based on this document in the Android Developer Portal is that you need to specify the permission regardless of whether it is a dangerous permission (which additionally requires a runtime prompt). They use SEND_SMS as the dangerous permission, but my understanding is that it equally would apply to WRITE_EXTERNAL_STORAGE. This is how App Inventor's component library and compiler are implemented today.

For completeness, I thought I would build an app with Android Studio using the approach you propose by specifying a maxSdkVersion. Please test this app on a phone/tablet running Android 6.0 or higher. On my Google Pixel running Android 7.0, I was not asked for permission and the system automatically denies the request for WRITE_EXTERNAL_STORAGE. This is the fundamental issue with your proposal. You absolutely need to have the permission in the manifest if you want to be able to request it at runtime; specifying a maxSdkVersion degrades the app's capabilities.

Remember that we have millions of users and tens of millions of projects. Just changing one feature so that your app behaves the way you want potentially breaks the other millions of projects that might assume the location on the SD card where the file is being written, and more unexpectedly if this location changes based on the version of Android running the app. This returns me back to my original proposal, which is that we include a property on the File component that allows this to be controlled by the user. Existing apps would not need to be modified and would continue to keep the existing behavior, but for folks like yourself who want finer control over the permissions requested by the app could do so.

Also note that if you want to write to the app's private data directory under /data, all you need to do is specify an undecorated filename, e.g., "foo.txt". In a compiled app this will write to the app's private directory (although at the moment I will admit that there is an issue where we ask for permission without checking the target location and this should be addressed). The companion will still write to the external storage for debugging purposes.

Regards,
Evan

BodyMindPower

unread,
Mar 16, 2019, 6:18:06 AM3/16/19
to MIT App Inventor Forum

"You absolutely need to have the permission in the manifest if you want to be able to request it at runtime; ... "

 

At this point you are right. Thanks for clarifying that. So, as long as I can not write in an app-specific folder without WRITE permissions with App Inventor, I will go ahead with my workaround to resolve this issue (without the File compontent). Or I have to switch to AppyBuilder ...

 

Thanks again and regards,

Anke


Reply all
Reply to author
Forward
0 new messages