0

I have to develop a little function that, besides other things, writes a file in the SD card. I have to use it for two specific Android tablets provided by a supplier. One tablet uses Android 5 and the other uses Android 7. The application that I am modifying is a system app and it doesn't have UI. I'm calling the code from a Service, and I want to call it from a FirebaseMessagingService. I have problems to write a file only in Android 7 tablet.

I have no problems with the Android 5 tablet, I identified the external storage folder and I can create files in it. But I do have problems in Android 7, I identified the external storage folder and I have a problem: Permission denied.

I have this permissions in the manifest:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

This is the piece of code that is giving me problems:

public void myFunction()
{
    String sdPath;
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
        sdPath = "/storage/extsd";
    else
        sdPath = "/storage/0665-3426";

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (my_context.checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
            Log.d(TAG, "Permission is granted");
        else
            Log.d(TAG, "Permission is denied");
    }

    File folder = new File(sdPath);
    if (folder.exists()) {
        Log.d(TAG, sdPath + " exists, can write: " + folder.canWrite());
        File file = new File(sdPath + "/new_file");
        boolean fileExists = file.exists();
        Log.d(TAG, file.getAbsolutePath() + " file exists: " + fileExists + ", can write: " + file.canWrite());

        if (!fileExists) {
            try {
                file.createNewFile();
                Log.d(TAG, "Can write in " + sdPath);
            }
            catch (Exception exception) {
                Log.e(TAG, "Cannot write in " + sdPath + ": " + exception.getMessage());
            }
        }
    }
    else
        Log.e(TAG, sdPath + " does not exist.");

    ...
}

Here the logs in Android 5 tablet:

10-22 14:44:51.271 10450-10450/com.my.app D/MY_TAG: /storage/extsd exists, can write: true
10-22 14:44:51.368 10450-10450/com.my.app D/MY_TAG: /storage/extsd/new_file file exists: false, can write: false
10-22 14:44:51.479 10450-10450/com.my.app D/MY_TAG: Can write in /storage/extsd

And here the logs in Android 7 tablet:

2020-10-22 15:11:56.383 19689-19689/com.my.app D/MY_TAG: Permission is granted
2020-10-22 15:11:59.037 19689-19689/com.my.app D/MY_TAG: /storage/0665-3426 exists, can write: false
2020-10-22 15:12:07.956 19689-19689/com.my.app D/MY_TAG: /storage/0665-3426/new_file file exists: false, can write: false
2020-10-22 15:12:07.957 19689-19689/com.my.app E/MY_TAG: Cannot write in /storage/0665-3426: Permission denied

As you can see, even if permission is granted, canWrite() method returns false in Android 7. Do you know the reason? How can I solve this problem?

I have read some other questions from stack overflow but I didn't find the solution.

Jon
  • 891
  • 13
  • 32

3 Answers3

0

I'm referring to one of the answers in this Stack Overflow thread.

I am not aware of the target SDK version in your case, but if you're building for version 29, try adding this to your AndroidManifest.xml file:

<manifest>
    <application
        <!-- other stuff -->
        android:requestLegacyExternalStorage="true">
    <!-- other stuff -->
    </application>
</manifest>

Also, are you requesting permissions at runtime correctly?

  • I'm not requesting permissions at runtime. I think that I need an activity for that, and I want to call ```myFunction()``` from a ```Service```. – Jon Oct 22 '20 at 14:05
0

Removable micro sd cards are read only since Android Kitkat/Lollipop.

Hence you cannot write to paths like "/storage/0665-3426".

Only one app specific directory is writable on a removable micro sd card.

To determine the path of that folder have a look at the second item returned by

getExternalFilesDirs()
blackapps
  • 8,011
  • 2
  • 11
  • 25
  • But in the Android 5 (Lollipop) I can write to "/storage/extsd", is it any difference with Android 7? – Jon Oct 23 '20 at 06:01
  • What is /storage/extsd? Not every device has such. The same as /storage/sdcard? Did you check if that is the removable micro sd card? I think it is not. But the last word is to the device maker. That can do it wants and unlock all. – blackapps Oct 23 '20 at 08:27
  • Yes, "/storage/extsd" is the folder where removable micro SD card is mounted, in my specific Android 5 device. I'm not developing for all devices, only specific 2 devices with privileges, one is Android 5 and the other is Android 7. – Jon Oct 23 '20 at 08:33
0

I have decided not to use the Android API. Since the application has elevated privileges, I have created the file by executing a shell command. This is the code to create a file (works with removable SD card folder):

public static String createFile(String filePath)
{
    String returnValue = "";

    try
    {
        Runtime runtime = Runtime.getRuntime();

        String[] command = new String[]{ "su", "0", "touch", filePath};

        Process p = runtime.exec(command);
        p.waitFor();

        java.io.BufferedReader errorIn = new java.io.BufferedReader(
                new java.io.InputStreamReader(p.getErrorStream()));

        String line = "";
        while ((line = errorIn.readLine()) != null)
            returnValue += line + "\n";

    }
    catch (IOException | InterruptedException e)
    {
        e.printStackTrace();
    }

    return returnValue;
}
Jon
  • 891
  • 13
  • 32