Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Read and write to app-specific external storage #39414

Closed
shafy opened this issue Jun 9, 2020 · 14 comments · Fixed by #49435
Closed

Android: Read and write to app-specific external storage #39414

shafy opened this issue Jun 9, 2020 · 14 comments · Fixed by #49435

Comments

@shafy
Copy link

shafy commented Jun 9, 2020

Godot version:
3.2.1.rc1

OS/device including version:
Oculus Quest

Issue description:
Per Android documentation, app can store data in external storage without requesting additional permissions as long as it's in the app-specific folder. On the Oculus Quest, this folder is /sdcard/Android/data/com.yourpackage.name/.

Now, this works if that folder already exists (e.g., created manually with adb). However, I can't create that folder through Godot using make_dir() or `make_dir_recursive(). It returns Error 20 (ERR_CANT_CREATE).

I would expect that either I can create the folder at that the folder is created automatically once read and write external storage permissions are grated in the Android Manifest.

I'm not sure if this is related to issue #38913 ?

Steps to reproduce:

  • Enable read and write external storage in your Android export settings.
  • Make sure your Android device (preferrable Oculus Quest) is attached to your computer
  • Run this in a script's ready function:
if not dir.dir_exists("/sdcard/Android/data/com.yourpackage.name/"):
	var err = dir.make_dir_recursive("/sdcard/Android/data/com.yourpackage.name/")
	print("err ", err)


var open_err = dir.open("/sdcard/Android/data/com.yourpackage.name/")
print("open_err ", open_err)
  • You'll get an error.
  • Create that folder manually in adb:
adb mkdir "/sdcard/Android/data/com.yourpackage.name/"
  • Run the script again - Success!
@Constannnnnt
Copy link

I met this issue several days before and it seemed that I walked around it. I had to say I did not know what actually solved the issue but it could possibly be the cause, mentioned by @volzhs at #23004. So, let me repeat what I did.


First Shot

Godot version:
3.2.1.stable (the version I used at that moment)

OS/device including version:
Oculus Quest

Results:
I followed the steps you said and was able to reproduce the issue. And the weird thing to me was that although I enabled the read and write external storage at Android export settings, when I check the permissions at the runtime,

var perm = OS.get_granted_permissions()
if perm does not have android read/write external permission:
OS.request_permission(read/write external storage)

the outputs were that not only did it fail to have those two expected permissions, but also it still did not have the permissions even I requested them successfully ("successfully" here means the output returned OK, but when I checked granted_permissions again, permissions not exist).


Second Shot

I thought maybe it was just the version, and the latest version might solve this issue.

Godot version:
3.2.2.stable

OS/device including version:
Oculus Quest

Results:
Repeat those steps. The same issues exist.


Third Shot: Issue Solved

I looked around the issues and pulls (e.g. #32600) at the repo regarding read&write permission issues and it came to me that if I did not have those permissions even I enabled them at the android export setting, can I see and edit the AndroidManifest.xml directly to ensure that I do have these permissions? Basically, by following this tutorial, I used the default android build and I only need to click the little android button at the top-right corner to compile, build and run on the Quest. Therefore, I then followed the custom android build.

Godot version:
3.2.2.stable

OS/device including version:
Oculus Quest

Results:
Issues resolved. I was able to create a directory and write a CSV file and save it on the Quest. I just and only followed the custom build and even did not modify the AndroidManifest.xml at all. I checked the granted_permissions and this time, it returned that the project had the read&write permissions.


Forth Shot

So then, I was wondering this walkaround could work on 3.2.1.stable

Godot version:
3.2.1.stable

OS/device including version:
Oculus Quest

Results:
Unfortunately, it failed and returned that read&write permissions were not granted.


In a word, check my third shot and sorry that I did not have enough time to explore what was really happening there with the issue, and what I could help is just a walkaround. If you had enough time, probably check all four shots to reproduce my experiments.

@shafy
Copy link
Author

shafy commented Jul 23, 2020

Thanks @Constannnnnt - I'll have a look.

@Yesyoor
Copy link

Yesyoor commented Oct 3, 2020

Hey I ran into this problem in Godot 3.2.1 stable version.
I am unable to read/write to any path on Android.
I tried adding permissions in the Manifest and I used the export settings in the export GUI.
I also tried to use a FileDialog, which would always stay empty.

@shafy
Copy link
Author

shafy commented Oct 6, 2020

Yeah @Railwanderer it's still not fixed. You can try @Constannnnnt 's workaround if you need it.

@eternaldensity
Copy link

Yeah I was having this problem: even using adb to give the permission to read external files (and confirming in my game that it had that permission) didn't help and it still would not open files (though it would confirm that they existed, since it would report the 'can't open' error code rather than 'not found'). I'd been using Godot 3.2 and updgrading to 3.2.3 didn't alter anything.

So I followed Constannnnnt's solution of enabling the custom android build checkbox. That told me that my Android SDK path was wrong (so I fixed that) and I was missing the JDK (so I fixed that too) and then it built and now loading files from the Download directory just works. So that's good.

@viindicater
Copy link

viindicater commented Dec 22, 2020

I've figured out the cause, there just needs to be a call to getExternalFilesDir (which creates such directories automatically without storage permissions) ( https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String) )

@EXOMODE
Copy link

EXOMODE commented Dec 27, 2020

To implement a mechanism for loading content from the server side and saving game settings, I ran into the same problems on the Android platform. I managed to solve the read/write access rights issue, but this solution is relevant for older versions of the system. When trying to write on the Android 11 emulator (API 30), Directory.make_dir_recursive() returns an error even after granting access rights. I suspect that it may be really in the new security requirements of the platform, but unfortunately I do not have time for a deeper study of the issue now.

@EXOMODE
Copy link

EXOMODE commented Dec 27, 2020

I found a very useful comment on stackoverflow. As expected, the fact is that starting with Android 10, there is no longer access to the device's file system outside of the application context. But for now, it is possible to return to the old scheme by adding in AndroidManifest.xml custom build the following attribute in the application tag:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting Android Q. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

@viindicater
Copy link

viindicater commented Jan 1, 2021

@EXOMODE

I found a very useful comment on stackoverflow. As expected, the fact is that starting with Android 10, there is no longer access to the device's file system outside of the application context. But for now, it is possible to return to the old scheme by adding in AndroidManifest.xml custom build the following attribute in the application tag:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting Android Q. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Godot already has that in android builds by default afaik, and also

To implement a mechanism for loading content from the server side and saving game settings, I ran into the same problems on the Android platform. I managed to solve the read/write access rights issue, but this solution is relevant for older versions of the system. When trying to write on the Android 11 emulator (API 30), Directory.make_dir_recursive() returns an error even after granting access rights. I suspect that it may be really in the new security requirements of the platform, but unfortunately I do not have time for a deeper study of the issue now.

I've tested it (with android studio making apps), and the only way to be able to make that directory is by https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String) . I've been trying to make a plugin to make this API call but for some reason my plugin doesn't work (Engine.has_singleton(myPluginName) returns false idk why). Guess I'm stuck with needlessly asking for storage perms til someone makes a plugin and/or Godot gets this API call built in.

@EXOMODE
Copy link

EXOMODE commented Jan 1, 2021

@EXOMODE

Я нашел очень полезный комментарий к stackoverflow. Как и ожидалось, дело в том, что начиная с Android 10, больше нет доступа к файловой системе устройства вне контекста приложения. Но пока можно вернуться к старой схеме, добавив в нее AndroidManifest.xml пользовательское построение следующего атрибута в теге приложения:

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting Android Q. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

Годо уже имеет то, что в android строит по умолчанию afaik, а также

Чтобы реализовать механизм загрузки контента со стороны сервера и сохранения настроек игры, я столкнулся с теми же проблемами на платформе Android. Мне удалось решить проблему прав доступа на чтение и запись, но это решение актуально для более старых версий системы. При попытке записи на эмулятор Android 11 (API 30) Directory.make_dir_recursive()возвращается ошибка даже после предоставления прав доступа. Я подозреваю, что это действительно может быть связано с новыми требованиями безопасности платформы, но, к сожалению, у меня сейчас нет времени для более глубокого изучения этого вопроса.

Я протестировал его (с помощью приложений для создания android studio), и единственный способ сделать этот каталог-это https://developer.android.com/reference/android/content/Context#getExternalFilesDir (java. lang.Строка). Я пытался сделать плагин, чтобы сделать этот вызов API, но по какой-то причине мой плагин не работает (Engine.has_singleton(myPluginName) возвращает false idk why). Наверное, я застрял с бесполезным запросом завивки хранилища, пока кто-то не сделает плагин и/или Godot не получит встроенный вызов API.

In other words, Android 11 and higher need a platform unification patch. Well, we are waiting :) fortunately, this is not relevant yet, because the transition to a new version of Android is just beginning and the current capabilities of the engine are enough to work correctly with the file system on all previous versions

@viindicater
Copy link

viindicater commented Jan 1, 2021

@EXOMODE but you said it yourself, "When trying to write on the Android 11 emulator (API 30), Directory.make_dir_recursive() returns an error even after granting access rights." This happens to me on my Android 10 phone, so this works on previous versions (9 and under) without permissions?

@EXOMODE
Copy link

EXOMODE commented Jan 2, 2021

@EXOMODE but you said it yourself, "When trying to write on the Android 11 emulator (API 30), Directory.make_dir_recursive() returns an error even after granting access rights." This happens to me on my Android 10 phone, so this works on previous versions (9 and under) without permissions?

This works on Android 9 and below with the standard permission request via OS.request_permissions(). On Android 10, I got it working only after adding the attribute to AndroidManifest.xml, and on version 11 it refused to work at all, but in the next branch they report that this now works on 11. Perhaps the other day a system update was released that solves the access problem on the most recent version, I personally solved this problem for myself by simply building the project on targetSdk=29 and no higher

@viindicater
Copy link

viindicater commented Jan 2, 2021

@EXOMODE but you said it yourself, "When trying to write on the Android 11 emulator (API 30), Directory.make_dir_recursive() returns an error even after granting access rights." This happens to me on my Android 10 phone, so this works on previous versions (9 and under) without permissions?

This works on Android 9 and below with the standard permission request via OS.request_permissions(). On Android 10, I got it working only after adding the attribute to AndroidManifest.xml, and on version 11 it refused to work at all, but in the next branch they report that this now works on 11. Perhaps the other day a system update was released that solves the access problem on the most recent version, I personally solved this problem for myself by simply building the project on targetSdk=29 and no higher

My entire point is WITHOUT permissions. It works fine when permission is requested/granted. But what I said here is this API call allows the directory to be made WITHOUT permissions needed.

I've tested it (with android studio making apps), and the only way to be able to make that directory is by https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String) . I've been trying to make a plugin to make this API call but for some reason my plugin doesn't work (Engine.has_singleton(myPluginName) returns false idk why). Guess I'm stuck with needlessly asking for storage perms til someone makes a plugin and/or Godot gets this API call built in.

@krishan147
Copy link

Was this problem solves as to how to successfully read and write to files onto Oculus Quest 2 using the Android Export with Godot?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants