[Android] Cannot write new files to storage in AVD emulator #231
Description
Hi there,
Thanks for maintaining this package with great API docs and examples. It's amazingly helpful.
While trying to implement file download functionality I encountered some frustrating behaviour I cannot explain while running a virtual android device: I can't write new files. Worse, react-native-fetch-blob
thinks it can write the file, reports no error, and responds with the expected path. But the file isn't written there. Overwriting an existing file works fine, so this isn't strictly a write permission issue. This problem does not happen when running on a real device.
Action:
Try to download a file to the local DocumentDirectory
on a virtual/emulated Android device as follows
const id = 123
const fileUrl = `http://some.example.com/stuff/${id}.mp3`
const fileDest = `${RNFetchBlob.fs.dirs.DocumentDir}/files/${id}.mp3`
yield RNFetchBlob
.config({
path: fileDest
})
.fetch('GET', fileUrl)
.then((resp) => {
console.log('RNFetchBlob result', resp.path())
})
.catch((rnfbErr) => {
console.log('RNFetchBlob error', rnfbErr)
})
which yields the expected output of: RNFetchBlob result /data/user/0/com.myappname/files/123.mp3
and no errors.
Expected result:
- See no errors
- See path logged out on success
- Find file at path
Actual Result:
- See no errors
- See path logged out on success
- Find no file at path 😢
Other Considerations:
- I do have
WRITE_EXTERNAL_STORAGE
permission inAndroidManifest.xml
and on startup I verify the permission explicitly viaPermissionsAndroid.checkPermission(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE)
which returnstrue
as expected. - This is reinforced by the fact that I am able to overwrite existing files. If I supply a
path
that already exists, the downloaded file will replace the pre-existing file. - This only happens on the AVD emulator. New files are created as expected on an actual device.
- I have tried different configurations of SD card storage in the AVD, such as "managed by studio" and supplying an image manually:
mksdcard -l e 128M sdcard.img
emulator -avd Nexus_5X_API_23 -sdcard sdcard.img
Target SDK: 23
Android Device & Version: Nexus 5X @ 6.0
RN: 0.34.1
RNFB: 0.10.2-beta.3
Activity
wkh237 commentedon Jan 15, 2017
@BrendanFDMoore , did you create the folder
${RNFetchBlob.fs.dirs.DocumentDir}/files
before download the file ?BrendanFDMoore commentedon Jan 15, 2017
@wkh237 While I do not create it immediately prior to downloading, the directory of the target path already exists, and contains other test files. Using
RNFetchBlob
I am able to overwrite those existing files if I supply one of them as the target path.I double checked that I hadn't made an error assuming the
RNFetchBlob.fs.dirs.DocumentDir
returned the same path asRNFS.DocumentDirectoryPath
(fromreact-native-fs
which I use for reading the files/directories). The paths are in fact the same.BrendanFDMoore commentedon Jan 15, 2017
So, before you posted this I had a theory, and it turns out my theory was at least partially right. This is something related to the particular directory of this particular image. I think it is directory permissions related, but I don't know specifically why. I'll try to describe to help you understand if there is a useful error case to detect here.
I am actually using a sub-directory of
/files/
which I removed for simplification, but the full path would be better represented as/data/user/0/com.myappname/files/subdir/123.mp3
.When I was setting up my test data, the
/data/user/0/com.myappname/files
directory already existed, it was part of the installed bundle created either byreact-native
or is a construct ofreact-native-ignite
- in any case, it existed. To seed some tests, I usedadb push
to put a test file (say,123.mp3
) to the full path insubdir
above. Using the File Explorer in Android Device Monitor, I can now see that thatsubdir
was created with permissionsdrwxrwxr-x
. The pushed file has permissions-rw-rw-rw-
. This is the file I was successfully overwriting, but could not create siblings to.I just created a different directory under
files
from within my app usingRNFetchBlob
at/data/user/0/com.myappname/files/testdir
and I can see in ADM it has permissionsdrwx------
. I am able to download files as expected to this directory, and they get created with permissions-rw-------
according to ADM.So, I can write files in general, and my issue is different than I understood 24h ago. I cannot write files to directories that were created using
adb push
with the permissions as noted above.RNFetchBlob
seems to think that it can write files, but it silently fails to do so. Interestingly, usingcreateFile
does lead to an error, I think because it tries to read the (non-existent) file afterwards.Sorry that ended up much longer than expected, but I'm just writing this up as I'm discovering the more specific problem. I think I can now work around my specific issue, but I'm happy to help test if you think
RNFetchBlob
should detect this situation and throw an error.BrendanFDMoore commentedon Jan 15, 2017
Ah, it's most likely the ownership. Using
adb shell
andls -l
I can see thatsubdir
(created viaadb push
) hasroot
as its owner, whiletestdir
(created in app withRNFetchBlob
) hasu0_a65
as the owner. I imagine that theroot
owner is the source of the problem here.Is this something that
RNFetchBlob
can or should inspect when trying to write files?I also find it interesting that I was able to overwrite files that have
root
ownership, but not create new files in directories withroot
ownership. I don't know enough about linux permissions to speculate on that aspect of the issue.wkh237 commentedon Jan 15, 2017
Thanks so much for the detailed information, IMO if the error only happens on the files created by
adb push
you may try changing their permission bychmond
. Does this solve the problem ?BrendanFDMoore commentedon Jan 15, 2017
Yes, I can correct the issue with any of the following from
adb shell
after navigating to thefiles
dir:chmod 777 subdir
chgrp 10065 subdir
chown 10065 subdir
Where
10065
is theuid
associated with my app. You can learn this by examiningdata/system/packages.xml
on the android device. Runadb shell
to connect and then run:cat /data/system/packages.xml | grep com.myappname
Then just look at the value in
userId="10065"
on the line with your app name. Use the value for your own app in the permissions commands above.@wkh237 - I've resolved my issue, so feel free to close this. I think it would be better if
react-native-fetch-blob
were to detect directory permission issues like this that will prevent write operations, but it's likely a narrow edge case that isn't worth much time/effort.