Write External Storage in AndroidTest

Rafael T :

I wanted to write an AndroidTest for an App which involves writing to external storage but somehow I am not able to make it work. (Testing on several devices like Pixel2, Lg G6, Huawai P8)

So I simplified everything just to test if writing to external storage is possible at all. I experimented with GrantPermissionRule as well as writing my on Granter with no success at all.

I probably ended up doing way too much unnecessary stuff, so here is what I have done:

  1. Create new Project with no Activity in AndroidStudio
  2. added permission to Manifest
  3. added AndroidManifest with uses-permission tag
  4. Wrote my own PermissionRequester
  5. Wrote test to try to add a Folder in /sdcard/Download and delete it

Following are snippets referenced by numbered List above

2.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company.android.testpermissions">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="com.company.TestPermissions"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme" />
</manifest>

3.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.company.android.testpermissions.test">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

</manifest>

Also tried

<manifest package="com.company.android.testpermissions">...

omitting the 'test' postfix

4.

public class MyPermissionRequester {

    public static final String TAG = MyPermissionRequester.class.getSimpleName();

    public static void request(String... permissions) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            UiAutomation auto = InstrumentationRegistry.getInstrumentation().getUiAutomation();
            String cmd = "pm grant " + InstrumentationRegistry.getTargetContext().getPackageName() + " %1$s";
            String cmdTest = "pm grant " + InstrumentationRegistry.getContext().getPackageName() + " %1$s";
            String currCmd;
            for (String perm : permissions) {
                execute(String.format(cmd, perm), auto);
                execute(String.format(cmdTest, perm), auto);
            }
        }
        GrantPermissionRule.grant(permissions);
    }

    private static void execute(String currCmd, UiAutomation auto){
        Log.d(TAG, "exec cmd: " + currCmd);
        auto.executeShellCommand(currCmd);
    }
}

5.

@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    @Test
    public void sdcardTest(){

        //check sdcard availability 
        Assert.assertEquals("Media is not mounted!", Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
       //get and check Permissions
MyPermissionRequester.request(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        int grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getTargetContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
        Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);
        grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
        Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);

        //finally try to create folder
        File f = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "CompanyTest");
        if (!f.exists()){
            boolean mkdir = f.mkdirs();
            Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' not Present!", mkdir);
        }
        boolean delete = f.delete();
        Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' is Present!", delete);
    }
}

The result of the Test is:

junit.framework.AssertionFailedError: Folder ' /storage/emulated/0/Download/CompanyTest' not Present! at

junit.framework.Assert.fail(Assert.java:50) at

junit.framework.Assert.assertTrue(Assert.java:20) at

com.company.android.testpermissions.ExampleInstrumentedTest.sdcardTest(ExampleInstrumentedTest.java:69)

at java.lang.reflect.Method.invoke(Native Method) at ... (omitted rest coming from the TestRunner)

What am I missing here or doing wrong?

Rafael T :

After a lot of pain trying stuff which doesn't work I finally got the solution:

Google's claims that

if your application already requests write access, it will automatically get read access as well.

https://developer.android.com/about/versions/android-4.1.html#Permissions

or in WriteExternalStorage docs:

Note: If your app uses the WRITE_EXTERNAL_STORAGE permission, then it implicitly has permission to read the external storage as well.

https://developer.android.com/training/data-storage/files.html#WriteExternalStorage

Or in manifest Permissions:

Any app that declares the WRITE_EXTERNAL_STORAGE permission is implicitly granted this permission.

https://developer.android.com/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE

except it doesn't!

If I explicitly add READ_EXTERNAL_STORAGE to my manifest as well as requesting it on Runtime, it works like I was used to.

So 3. must add

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.company.android.testpermissions.test">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
</manifest>

and the Test should look like this:

@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    @Test
    public void sdcardTest(){

        //check sdcard availability 
        Assert.assertEquals("Media is not mounted!", Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
       //get and check Permissions
MyPermissionRequester.request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);
        int grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getTargetContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
        Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);
        grantResult = ActivityCompat.checkSelfPermission(InstrumentationRegistry.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
        Assert.assertEquals(PackageManager.PERMISSION_GRANTED, grantResult);

        //finally try to create folder
        File f = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "CompanyTest");
        if (!f.exists()){
            boolean mkdir = f.mkdirs();
            Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' not Present!", mkdir);
        }
        boolean delete = f.delete();
        Assert.assertTrue("Folder '"+f.getAbsolutePath() + "' is Present!", delete);
    }
}

and everything works.

Only question which is left is if/why this is intended behavior since Android 8.1

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=475193&siteId=1