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:
AndroidStudio
AndroidManifest
with uses-permission
tagPermissionRequester
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?
To read and write data to external storage, the app required WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE system permission. These permissions are added to the AndroidManifest. xml file. Add these permissions just after the package name.
The Android SDK documentation has this to say in terms of a definition of “external storage”: Every Android-compatible device supports a shared “external storage” that you can use to save files. This can be a removable storage media (such as an SD card) or an internal (non-removable) storage.
External Storage: That means, both storage types like Nexus 6P's 64 GB internal memory and removable microSD card which we insert in phone's card slot are considered as External Storage. Removable Storage means just microSD card storage, not the internal memory.
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
Like I stated in my comments,
The only thing I can think of is that in both the examples that you cited, they seem to be referencing older versions of Android (it looks like they are referencing around Android 4.1). Here is a link explicitly stating that the user must explicitly approve each permission that doesn't fall within the "sandbox" of the application after Android 6.0:
https://developer.android.com/training/permissions/requesting.html
Also, they explicitly state in the link I provided above that you HAVE to include both READ and WRITE in your manifest. Then you can use run time permissions and that will cover both, but only if you included both in your manifest. (under the "Handle the permissions request response" section)
Since Android 6.0, you need to grant the runtime permission manually for running androidTest
adb shell pm grant <pkg_name> android.permission.READ_EXTERNAL_STORAGE
adb shell pm grant <pkg_name> android.permission.WRITE_EXTERNAL_STORAGE
You can verify the permissions by
adb shell dumpsys package <pkg_name>
The AndroidManifest.xml only request for those permissions, but not granted by default when install the androidTest APK.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With