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

Failed to create the directory for tapes. Is your sdcard directory read-only? #42

Open
christopherperry opened this issue Jun 23, 2017 · 7 comments

Comments

@christopherperry
Copy link

christopherperry commented Jun 23, 2017

Getting the message:

Failed to create the directory for tapes. Is your sdcard directory read-only?

on my Nexus 6P, and the Pixel. I tried to debug, the relevant source is in AndroidTapeRoot.kt

internal fun grantPermissionsIfNeeded() {
    val res = assetManager.context.checkCallingOrSelfPermission(WRITE_EXTERNAL_STORAGE)
    if (res != PackageManager.PERMISSION_GRANTED) {
      throw RuntimeException("We need WRITE_EXTERNAL_STORAGE permission for OkReplay. " +
          "Please add `adbOptions { installOptions \"-g\" }` to your build.gradle file.")
    }
    root.mkdirs()
    if (!root.exists()) {
      throw RuntimeException("Failed to create the directory for tapes. "
          + "Is your sdcard directory read-only?")
    }
    setWorldWriteable(root)
  }

My SD card is writeable.

@christopherperry
Copy link
Author

I spent quite a lot of time looking at this yesterday. Even after root.mkdirs() the call to root.exists() returns false and I couldn't figure out why.

@felipecsl
Copy link
Collaborator

Very weird. I haven't seen this before, but will try on a physical Pixel device.
Funny that it works fine on Firebase Test lab using their Pixel devices as well.

@christopherperry
Copy link
Author

I have an update on this. It seems that you have to run this via gradle, or it doesn't work. I was able to get the sample to work via ./gradlew connectedAndroidTest which is fine, but that command in our app runs over 700 tests, so I was looking to run only a single test class. The first thing I tried was running the test class from the IDE, which failed with the problem I reported here. I also tried to run my test class via adb shell am instrument which also failed. I was finally able to get this to work via:

./gradlew :app:cDAT -Pandroid.testInstrumentationRunnerArguments.class=com.example.MyTestClass

While it's great I got it to run, what's not so great is not being able to simply run the test class via the IDE, or via the adb shell am instrument command. Perhaps we can make this a feature request to make these work?

@malachid
Copy link

I still received the error message with cDAT from the command line.

@malachid
Copy link

root.mkdirs() is returning false indicating that it failed to create the directory; but AndroidTapeRoot is not checking the return value.

@malachid
Copy link

malachid commented Dec 13, 2017

After reviewing https://developer.android.com/guide/topics/data/data-storage.html#filesExternal I did some testing.

Using System.getenv or Environment.getExternalStorageDirectory, it attempts to write to /sdcard/okreplay/tapes/<packagename>/<type> and fails, though /sdcard/ does exist.

Using context.getExternalFilesDir(null) is succesfully creates a directory on the sdcard under the packagename. As such, the requested child path should not include it; resulting in /storage/emulated/0/Android/data/<packagename>/files/okreplay/tapes/<type> (or similar)... ie: it puts it under /sdcard/Android/data/<packagename> instead of /sdcard/ directly.

At least, mkdirs works this way.

@malachid
Copy link

I did a test using a replacement for AndroidTapeRoot (workaround to allow that here #64 ) and this successfully started writing files:

        private fun getSdcardDir(context: Context, type: String): File {
            if (!isExternalStorageWritable()) {
                throw RuntimeException("Unable to access external storage")
            }

            // ${context.packageName}/ not included because it's a parent by way of getExternalFilesDir
            val parent = File(context.getExternalFilesDir(null), "okreplay/tapes/")
            parent.mkdirs()
            return File(parent, type)
        }

        /* Checks if external storage is available for read and write */
        private fun isExternalStorageWritable(): Boolean {
            val state = Environment.getExternalStorageState()
            return Environment.MEDIA_MOUNTED == state
        }

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

No branches or pull requests

3 participants