Search code examples
androidpermissionssd-cardandroid-4.4-kitkatpermission-denied

How to check permission is granted for a directory path and won't thorow EACCES error?


I have a photo editing android app that users can choose the output directory of the the result photos. Problem is Google made a change on sdcard write permission with the KITKAT version and devices with Android KITKAT version won't allow apps to write secondary sdcards. Now I need to check if the choosen directory by user has granted the permission and won't throw EACCES error. I am already checking canRead and canWrite but these won't help. Could you please tell me how can I check if the choosen directory won't throw EACCES. My only solution is trying to write a file in a try catch, however I am hoping there is better way to do it.

[update k3b 2016-09-19]

i tried this on my android-4.4 but without success

Uri uri = Uri.fromFile(file);
int permissionCode = 
     context.checkCallingOrSelfUriPermission(uri,
     Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (permissionCode == PackageManager.PERMISSION_DENIED) {
   // on my android-4.4 i always get PERMISSION_DENIED even 
   // if i can overwrite the file
   return false;
}

Solution

  • try {
        Process p = new ProcessBuilder("ls", "-l", "-s", dir.getCanonicalPath()).start();
    
        String line;
        ArrayList<String> lineOut = new ArrayList<>();
    
        BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        while ((line = error.readLine()) != null) {
            Log.e(TAG, "ls error = "+line);
        }
        error.close();
    
        BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
        while ((line = input.readLine()) != null) {
            lineOut.add(line);
        }
    
        input.close();
    
        String[] strings = lineOut.toArray(new String[]{});
        List<FilesLS.FileEntry> fileEntries = FilesLS.processNewLines(strings);
        for(FilesLS.FileEntry file : fileEntries){
            Log.d(TAG, file.name +" = " + file.permissions);
        }
    
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    And some edits to this class

    import java.util.ArrayList;
    import java.util.List;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public final class FilesLS {
        /**
         * Entry type: File
         */
        public static final int TYPE_FILE = 0;
        /**
         * Entry type: Directory
         */
        public static final int TYPE_DIRECTORY = 1;
        /**
         * Entry type: Directory Link
         */
        public static final int TYPE_DIRECTORY_LINK = 2;
        /**
         * Entry type: Block
         */
        public static final int TYPE_BLOCK = 3;
        /**
         * Entry type: Character
         */
        public static final int TYPE_CHARACTER = 4;
        /**
         * Entry type: Link
         */
        public static final int TYPE_LINK = 5;
        /**
         * Entry type: Socket
         */
        public static final int TYPE_SOCKET = 6;
        /**
         * Entry type: FIFO
         */
        public static final int TYPE_FIFO = 7;
        /**
         * Entry type: Other
         */
        public static final int TYPE_OTHER = 8;
        /**
         * Device side file separator.
         */
        public static final String FILE_SEPARATOR = "/"; //$NON-NLS-1$
        /**
         * Regexp pattern to parse the result from ls.
         */
        private static Pattern sLsPattern = Pattern
            .compile("^([bcdlsp-][-r][-w][-xsS][-r][-w][-xsS][-r][-w][-xstST])\\s+(\\S+)\\s+   (\\S+)\\s+(\\d{4}-\\d\\d-\\d\\d)\\s+(\\d\\d:\\d\\d)\\s+(.*)$"); //$NON-NLS-1$  \s+([\d\s,]*)
    
        public static List<FileEntry> processNewLines(String[] lines) {
            List<FileEntry> listOfFiles = new ArrayList<FileEntry>();
            for (String line : lines) {
                // no need to handle empty lines.
                if (line.length() == 0) {
                    continue;
                }
                // run the line through the regexp
                Matcher m = sLsPattern.matcher(line);
                if (m.matches() == false) {
                    continue;
                }
                // get the name
                String name = m.group(6);
                // get the rest of the groups
                String permissions = m.group(1);
                String owner = m.group(2);
                String group = m.group(3);
                // String size = m.group(4);
                String date = m.group(4);
                String time = m.group(5);
                String info = null;
                // and the type
                int objectType = TYPE_OTHER;
                switch (permissions.charAt(0)) {
                    case '-':
                        objectType = TYPE_FILE;
                        break;
                    case 'b':
                        objectType = TYPE_BLOCK;
                        break;
                    case 'c':
                        objectType = TYPE_CHARACTER;
                        break;
                    case 'd':
                        objectType = TYPE_DIRECTORY;
                        break;
                    case 'l':
                        objectType = TYPE_LINK;
                        break;
                    case 's':
                        objectType = TYPE_SOCKET;
                        break;
                    case 'p':
                        objectType = TYPE_FIFO;
                        break;
                }
                // now check what we may be linking to
                if (objectType == TYPE_LINK) {
                    String[] segments = name.split("\\s->\\s"); //$NON-NLS-1$
                    // we should have 2 segments
                    if (segments.length == 2) {
                        // update the entry name to not contain the link
                        name = segments[0];
                        // and the link name
                        info = segments[1];
                        // now get the path to the link
                        String[] pathSegments = info.split(FILE_SEPARATOR);
                        if (pathSegments.length == 1) {
                            // the link is to something in the same directory,
                            // unless the link is ..
                            if ("..".equals(pathSegments[0])) { //$NON-NLS-1$
                                // set the type and we're done.
                                objectType = TYPE_DIRECTORY_LINK;
                            } else {
                                // either we found the object already
                                // or we'll find it later.
                            }
                        }
                    }
                    // add an arrow in front to specify it's a link.
                    info = "-> " + info; //$NON-NLS-1$;
                }
                FileEntry entry = new FileEntry();
                entry.permissions = permissions;
                entry.name = name;
                // entry.size = size;
                entry.date = date;
                entry.time = time;
                entry.owner = owner;
                entry.group = group;
                if (objectType == TYPE_LINK) {
                    entry.info = info;
                }
                listOfFiles.add(entry);
            }
            return listOfFiles;
        }
    
        public final static class FileEntry {
            String name;
            String info;
            String permissions;
            String size;
            String date;
            String time;
            String owner;
            String group;
            int type;
        }
    }