Skip to content
This repository has been archived by the owner on Mar 16, 2019. It is now read-only.

[Android] Cannot write new files to storage in AVD emulator #231

Open
BrendanFDMoore opened this issue Jan 14, 2017 · 6 comments
Open

[Android] Cannot write new files to storage in AVD emulator #231

BrendanFDMoore opened this issue Jan 14, 2017 · 6 comments

Comments

@BrendanFDMoore
Copy link

BrendanFDMoore commented Jan 14, 2017

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:

  1. See no errors
  2. See path logged out on success
  3. Find file at path

Actual Result:

  1. See no errors
  2. See path logged out on success
  3. Find no file at path 😢

Other Considerations:

  • I do have WRITE_EXTERNAL_STORAGE permission in AndroidManifest.xml and on startup I verify the permission explicitly via PermissionsAndroid.checkPermission(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE) which returns true 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

@wkh237
Copy link
Owner

wkh237 commented Jan 15, 2017

@BrendanFDMoore , did you create the folder ${RNFetchBlob.fs.dirs.DocumentDir}/files before download the file ?

@BrendanFDMoore
Copy link
Author

@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 as RNFS.DocumentDirectoryPath (from react-native-fs which I use for reading the files/directories). The paths are in fact the same.

@BrendanFDMoore
Copy link
Author

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 by react-native or is a construct of react-native-ignite - in any case, it existed. To seed some tests, I used adb push to put a test file (say, 123.mp3) to the full path in subdir above. Using the File Explorer in Android Device Monitor, I can now see that that subdir was created with permissions drwxrwxr-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 using RNFetchBlob at /data/user/0/com.myappname/files/testdir and I can see in ADM it has permissions drwx------. 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, using createFile 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
Copy link
Author

BrendanFDMoore commented Jan 15, 2017

Ah, it's most likely the ownership. Using adb shell and ls -l I can see that subdir (created via adb push) has root as its owner, while testdir (created in app with RNFetchBlob) has u0_a65 as the owner. I imagine that the root 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 with root ownership. I don't know enough about linux permissions to speculate on that aspect of the issue.

@wkh237
Copy link
Owner

wkh237 commented 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 by chmond. Does this solve the problem ?

@BrendanFDMoore
Copy link
Author

BrendanFDMoore commented Jan 15, 2017

Yes, I can correct the issue with any of the following from adb shell after navigating to the files dir:

  • chmod 777 subdir
  • chgrp 10065 subdir
  • chown 10065 subdir

Where 10065 is the uid associated with my app. You can learn this by examining data/system/packages.xml on the android device. Run adb 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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.