Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are some ContextCompat.getExternalFilesDirs not accessible?

ContextCompat.getExternalFilesDirs(context, null) says:

Returns absolute paths to application-specific directories on all external storage devices where the application can place persistent files it owns.

For example a Huawei Honor running Android 5.1.1 returns the following from that method:

/storage/emulated/0/Android/data/my.package.name/files
/storage/sdcard1/Android/data/my.package.name/files

The second directory is a removable sd card. However, if I attempt to read or write to that directory I get an Exception: android.system.ErrnoException: open failed: EACCES (Permission denied)

The app does have WRITE_EXTERNAL_STORAGE. And this works just fine on a Samsung Galaxy Note 4 running Android 4.4.4. This is not related to Android 6.0's optional permissions, since this is a problem showing on 5.1.1.

The API says the application can place persistent files it owns yet it doesn't appear to be the case.

I've heard reports from other devices as well, including more modern Samsung devices. Is this just OEMs not implementing this correctly or is there something I'm missing with Android's complicated storage frameworks?

Here is some code that will demonstrate this on this and some devices.

File removable = ContextCompat.getExternalFilesDirs(context, null)[1];
if (removable.exists() && removable.canRead() && removable.canWrite()) {
    File test = new File(removable, "test");
    test.createNewFile(); // Throws the exception mentioned above
}

Any attempts to create, move, mkdir files in that directory fails.

like image 315
cottonBallPaws Avatar asked May 03 '17 18:05

cottonBallPaws


2 Answers

Well, I wrapped your code in this activity:

package com.commonsware.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    File removable = ContextCompat.getExternalFilesDirs(this, null)[1];
    if (removable.exists() && removable.canRead() && removable.canWrite()) {
      File test = new File(removable, "test");
      try {
        test.createNewFile(); // Throws the exception mentioned above
      }
      catch (IOException e) {
        Log.e(getClass().getSimpleName(), "Exception creating file", e);
        Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
      }
    }
  }
}

It runs fine on a Huawei Honor 5X, running Android 6.0.1:

$ adb shell ls -al /storage/1A30-3598/Android/data/com.commonsware.myapplication/files
-rwxrwx--x u0_a29   sdcard_rw        0 2017-05-03 18:02 test

So, my guess is that this is a device-specific incompatibility.

like image 157
CommonsWare Avatar answered Nov 11 '22 10:11

CommonsWare


For a number of reason I would like to inquire how @CommonsWare got the code to run without a ArrayIndexOutOfBoundsException: length=1; index=1 ERROR the [0] needs to be set to [0] We have been testing this concept for sometime now to write a method that lets the user select Internal or External Storage Can someone explain how this works or why it does not work for us The above answer needs to be formatted this way

        File removable = ContextCompat.getExternalFilesDirs(this,null)[0];
    if (removable.exists() && removable.canRead() && removable.canWrite()) {

        THE_PATH = String.valueOf(removable);
        THE_PATH = THE_PATH + "/Documents/";

    }else {
        Toast.makeText(getApplicationContext(),"NO SD CARD", 
    Toast.LENGTH_LONG).show();

    }
like image 44
Vector Avatar answered Nov 11 '22 12:11

Vector