4

I know a question almost like this was asked several times but that problem is different. I need to write data to the external storage. I know I need to ask for permissions at runtime on Android 6. All this works fine so far. Just one thing is weird. The granted permission only seems to work the second time I start the app.

My example looks like this:

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists() && !root.mkdirs()) {
            Log.e(TAG, "Directory not created");
            return;
        }

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }

Pressing the button calls the onClick() which is checking for permissions. If granted it calls the write() method - if not it asks for permission.

So when pressing the button first time, the permission popup opens, I click "yes" and assumed, it would write my test file. Still, I get the following error message and the file or even the directory was not created:

D/LOG: Has no permission! Ask! 
D/LOG: Permission granted! 
D/LOG: Trying to write 
E/LOG: Directory not created

If I click again (with already given permission) it only shows

D/LOG: Permission already given!
D/LOG: Trying to write
E/LOG: Directory not created

In fact neither the directory nor the file exist yet. So I close the app and start it again and press the button. The console shows this:

D/LOG: Permission already given!
D/LOG: Trying to write
D/LOG: Write successful

Voila, now the directory and file exist.

So why does it only work the second time I start the app? It appears the permissions are only refreshed on app start. Any ideas?

---- EDIT ----

Related to the comment, I separated exists() and mkdirs().

Still the method looks like this:

private void write(){
        Log.d(TAG, "Trying to write");
        // Get the directory for the user's public pictures directory.
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        if (! root.exists()){
            if(!root.mkdirs()) {
                Log.e(TAG, "Directory not created");
                return;
            } else {
                Log.e(TAG, "Directory created");
            }
        }
        ...

Still, first time the app starts I get Directory not created. Next time I start the app, the directory was created. So this doesn't seem to be the problem.

Also, I added a method checking if Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) exists. This one is true, even at first start. Whats failing is the mkdirs() for my sub folder.

Aleksandar G
  • 963
  • 2
  • 19
  • 23
Tobias Reich
  • 4,414
  • 2
  • 42
  • 77
  • Try putting the `exists()` and `mkdirs()` calls to separate if clauses to narrow down the issue. – 1615903 May 09 '16 at 08:36

3 Answers3

1

As posted here: https://stackoverflow.com/a/37135078/1565635 I believe I found the solution - or at least an explanation.

It turns out this seems to be a bigger issue. Changing the permission to write to the external storage changes the Linux GID for this process. In order to change the ID the process has to be restarted. Next time the app opens, the new groupID is set and the permission is granted. That leads to the conclusion that this is not a bug but in fact a bigger issue with Linux and Android.

I "solved" this by asking for permission the first time the app is executed and restarting it when the permission is given like this:

PackageManager packageManager = getPackageManager();
        Intent intent = packageManager.getLaunchIntentForPackage(getPackageName());
        ComponentName componentName = intent.getComponent();
        Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName);
        startActivity(mainIntent);
        System.exit(0);

A solution I didn't try was to create a service running in the background (thus having another process id) and giving it the permission. That way only the service needs to be restarted but not the complete app. On the down side this might make more work for communication between the processes. If I understand it right, a content provider might work, too but also means more work.

--- EDIT ---

The user M66B (https://stackoverflow.com/a/32473449/1565635) found a list of the related gids. Further information can be found here: https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml

Community
  • 1
  • 1
Tobias Reich
  • 4,414
  • 2
  • 42
  • 77
  • This issue may be old but i just had the same behaviour on ONE device with LineageOS 7.1.2. All other emulators and devices do NOT show it. The LinOS runs with SELinux strict, i am not a Linux guy but this could be a reason ?! – NikkyD Feb 25 '20 at 16:19
0

Try this code:-

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_WRITE_STORAGE: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Permission granted!");
                    write();
                } else {
                    Log.d(TAG, "No permission granted!");
                }
            }
        }
    }


    @Override
    public void onClick(View v) {
        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
        dircheck(root);
        filecheck(new File(root, "TEST.tmp"));
        boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
        if (!hasPermission) {
            Log.d(TAG, "Has no permission! Ask!");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
        } else {
            Log.d(TAG, "Permission already given!");
            write();
        }
    }

    private void dircheck(File file) {

            if (!file.exists()) {
                file.mkdirs();
            }

    }

    private void filecheck(File file){
        if(!file.exists()){
            file.createNewFile();
        }
    }
    private void write(){

        File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");

        File testFile = new File(root, "TEST.tmp");
        FileWriter writer;
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("TEST TEST TEST");
            writer = new FileWriter(testFile, false);
            writer.append(sb.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Write successful");
    }
Ankesh kumar Jaisansaria
  • 1,463
  • 4
  • 22
  • 43
  • 2
    Please provide some sort of explanation of _why_ this would solve the issue. – Michael May 09 '16 at 08:51
  • Previously @Tobias Reich was making exist() and mkdir() at the same time in if statement. So now I have created a separate method to check if dir exists and if not then create it. – Ankesh kumar Jaisansaria May 09 '16 at 08:54
  • Well, at least this seems to work. So obviously I have to create every directory separately? I assumed mkdirs() would do the job of creating all sub directories. – Tobias Reich May 09 '16 at 09:08
  • @TobiasReich If its working for you then please don't forget to mark answer as accepted. Thanks.....Happy coding – Ankesh kumar Jaisansaria May 09 '16 at 09:13
  • Hm, sorry, I thought it would work but obviously it didn't. Only change I get is an exception after "Permission granted" saying: "FileNotFoundException: /storage/emulated/0/Pictures/test/TEST.tmp: open failed: ENOENT (No such file or directory)" – Tobias Reich May 09 '16 at 09:37
  • @TobiasReich Sorry, Some minnor change in code. I have edited my solution. Please go throught it and let me now if it does not work. – Ankesh kumar Jaisansaria May 09 '16 at 09:45
  • @TobiasReich Sorry, One more correction made and this time its final, and its working at my system. – Ankesh kumar Jaisansaria May 09 '16 at 09:58
  • Hm, sorry, still not working. Besides, you are checking the directories before asking for permission. This seems wrong somehow. – Tobias Reich May 09 '16 at 10:13
  • Ah, alright. I found the problem. Changing the permission changes the GID on the linux side. This requires to restart the process. So it looks like there is no way around restarting the app when this permission got changed. – Tobias Reich May 09 '16 at 11:04
0

Edited answer :-

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case REQUEST_WRITE_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d(TAG, "Permission granted!");
                write();
            } else {
                Log.d(TAG, "No permission granted!");
            }
        }
    }
}


@Override
public void onClick(View v) {
    boolean hasPermission = (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
    if (!hasPermission) {
        Log.d(TAG, "Has no permission! Ask!");
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE);
    } else {
        Log.d(TAG, "Permission already given!");
        write();
    }
}

private void dircheck(File file) {
    if (!file.exists()) {
        file.mkdirs();
    }

}

private void filecheck(File file){
    if(!file.exists()){
        try {
            file.createNewFile();    
        }catch (Exception e){e.printStackTrace();}

    }
}
private void write(){
    File root = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "test");
    dircheck(root);
    filecheck(new File(root, "TEST.tmp"));

    File testFile = new File(root, "TEST.tmp");
    FileWriter writer;
    try {
        StringBuilder sb = new StringBuilder();
        sb.append("TEST TEST TEST");
        writer = new FileWriter(testFile, false);
        writer.append(sb.toString());
        writer.flush();
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    Log.d(TAG, "Write successful");
}
Ankesh kumar Jaisansaria
  • 1,463
  • 4
  • 22
  • 43