首页 > 代码库 > 【Android】不弹root请求框检测手机是否root

【Android】不弹root请求框检测手机是否root

由于项目需要root安装软件,并且希望在合适的时候引导用户去开启root安装,故需要检测手机是否root。

最基本的判断如下,直接运行一个底层命令。(参考https://github.com/Trinea/android-common/blob/master/src/cn/trinea/android/common/util/ShellUtils.java)

也可参考csdn http://blog.csdn.net/fm9333/article/details/12752415 

  1     /**  2      * check whether has root permission  3      *   4      * @return  5      */  6     public static boolean checkRootPermission() {  7         return execCommand("echo root", true, false).result == 0;  8     }  9      10  11     /** 12      * execute shell commands 13      *  14      * @param commands 15      *            command array 16      * @param isRoot 17      *            whether need to run with root 18      * @param isNeedResultMsg 19      *            whether need result msg 20      * @return <ul> 21      *         <li>if isNeedResultMsg is false, {@link CommandResult#successMsg} 22      *         is null and {@link CommandResult#errorMsg} is null.</li> 23      *         <li>if {@link CommandResult#result} is -1, there maybe some 24      *         excepiton.</li> 25      *         </ul> 26      */ 27     public static CommandResult execCommand(String[] commands, boolean isRoot, 28             boolean isNeedResultMsg) { 29         int result = -1; 30         if (commands == null || commands.length == 0) { 31             return new CommandResult(result, null, null); 32         } 33  34         Process process = null; 35         BufferedReader successResult = null; 36         BufferedReader errorResult = null; 37         StringBuilder successMsg = null; 38         StringBuilder errorMsg = null; 39  40         DataOutputStream os = null; 41         try { 42             process = Runtime.getRuntime().exec( 43                     isRoot ? COMMAND_SU : COMMAND_SH); 44             os = new DataOutputStream(process.getOutputStream()); 45             for (String command : commands) { 46                 if (command == null) { 47                     continue; 48                 } 49  50                 // donnot use os.writeBytes(commmand), avoid chinese charset 51                 // error 52                 os.write(command.getBytes()); 53                 os.writeBytes(COMMAND_LINE_END); 54                 os.flush(); 55             } 56             os.writeBytes(COMMAND_EXIT); 57             os.flush(); 58  59             result = process.waitFor(); 60             // get command result 61             if (isNeedResultMsg) { 62                 successMsg = new StringBuilder(); 63                 errorMsg = new StringBuilder(); 64                 successResult = new BufferedReader(new InputStreamReader( 65                         process.getInputStream())); 66                 errorResult = new BufferedReader(new InputStreamReader( 67                         process.getErrorStream())); 68                 String s; 69                 while ((s = successResult.readLine()) != null) { 70                     successMsg.append(s); 71                 } 72                 while ((s = errorResult.readLine()) != null) { 73                     errorMsg.append(s); 74                 } 75             } 76         } catch (IOException e) { 77             e.printStackTrace(); 78         } catch (Exception e) { 79             e.printStackTrace(); 80         } finally { 81             try { 82                 if (os != null) { 83                     os.close(); 84                 } 85                 if (successResult != null) { 86                     successResult.close(); 87                 } 88                 if (errorResult != null) { 89                     errorResult.close(); 90                 } 91             } catch (IOException e) { 92                 e.printStackTrace(); 93             } 94  95             if (process != null) { 96                 process.destroy(); 97             } 98         } 99         return new CommandResult(result, successMsg == null ? null100                 : successMsg.toString(), errorMsg == null ? null101                 : errorMsg.toString());102     }103 104     /**105      * result of command,106      * <ul>107      * <li>{@link CommandResult#result} means result of command, 0 means normal,108      * else means error, same to excute in linux shell</li>109      * <li>{@link CommandResult#successMsg} means success message of command110      * result</li>111      * <li>{@link CommandResult#errorMsg} means error message of command result</li>112      * </ul>113      * 114      * @author Trinea 2013-5-16115      */116     public static class CommandResult {117 118         /** result of command **/119         public int result;120         /** success message of command result **/121         public String successMsg;122         /** error message of command result **/123         public String errorMsg;124 125         public CommandResult(int result) {126             this.result = result;127         }128 129         public CommandResult(int result, String successMsg, String errorMsg) {130             this.result = result;131             this.successMsg = successMsg;132             this.errorMsg = errorMsg;133         }134     }    /**135      * execute shell command, default return result msg136      * 137      * @param command138      *            command139      * @param isRoot140      *            whether need to run with root141      * @return142      * @see ShellUtils#execCommand(String[], boolean, boolean)143      */144     public static CommandResult execCommand(String command, boolean isRoot) {145         return execCommand(new String[] { command }, isRoot, true);146     }
View Code

但是这会带来一个问题,每次判断是否root都会弹出一个root请求框。这是十分不友好的一种交互方式,而且,用户如果选择取消,有部分手机是判断为非root的。

这是方法一。交互不友好,而且有误判。

在这个情况下,为了不弹出确认框,考虑到一般root手机都会有一些的特殊文件夹,比如/system/bin/su,/system/xbin/su,里面存放有相关的权限控制文件。

因此只要手机中有一个文件夹存在就判断这个手机root了。

然后经过测试,这种方法在大部分手机都可行。

代码如下:

 1     /** 判断是否具有ROOT权限 ,此方法对有些手机无效,比如小米系列 */ 2     public static boolean isRoot() { 3  4         boolean res = false; 5  6         try { 7             if ((!new File("/system/bin/su").exists()) 8                     && (!new File("/system/xbin/su").exists())) { 9                 res = false;10             } else {11                 res = true;12             }13             ;14         } catch (Exception e) {15             res = false;16         }17         return res;18     }
View Code

这是方法二。交互友好,但是有误判。

后来测试的过程中发现部分国产,比如小米系列,有这个文件夹,但是系统是未root的,判断成了已root。经过分析,这是由于小米有自身的权限控制系统而导致。

考虑到小米手机有大量的用户群,这个问题必须解决,所以不得不寻找第三种方案。

从原理着手,小米手机无论是否root,应该都是具有相关文件的。但是无效的原因应该是,文件设置了相关的权限。导致用户组无法执行相关文件。

从这个角度看,就可以从判断文件的权限入手。

先看下linux的文件权限吧。

linux文件权限详细可参考《鸟叔的linux私房菜》http://vbird.dic.ksu.edu.tw/linux_basic/0210filepermission.php#filepermission_perm

只需要在第二种方法的基础上,再另外判断文件拥有者对这个文件是否具有可执行权限(第4个字符的状态),就基本可以确定手机是否root了。

在已root手机上(三星i9100 android 4.4),文件权限(x或者s,s权限,可参考http://blog.chinaunix.net/uid-20809581-id-3141879.html)如下

未root手机,大部分手机没有这两个文件夹,小米手机有这个文件夹。未root小米手机权限如下(由于手头暂时没有小米手机,过几天补上,或者有同学帮忙补上,那真是感激不尽)。

【等待补充图片】

代码如下:

 1     /** 判断手机是否root,不弹出root请求框<br/> */ 2     public static boolean isRoot() { 3         String binPath = "/system/bin/su"; 4         String xBinPath = "/system/xbin/su"; 5         if (new File(binPath).exists() && isExecutable(binPath)) 6             return true; 7         if (new File(xBinPath).exists() && isExecutable(xBinPath)) 8             return true; 9         return false;10     }11 12     private static boolean isExecutable(String filePath) {13         Process p = null;14         try {15             p = Runtime.getRuntime().exec("ls -l " + filePath);16             // 获取返回内容17             BufferedReader in = new BufferedReader(new InputStreamReader(18                     p.getInputStream()));19             String str = in.readLine();20             Log.i(TAG, str);21             if (str != null && str.length() >= 4) {22                 char flag = str.charAt(3);23                 if (flag == ‘s‘ || flag == ‘x‘)24                     return true;25             }26         } catch (IOException e) {27             e.printStackTrace();28         }finally{29             if(p!=null){30                 p.destroy();31             }32         }33         return false;34     }
View Code

这种方法基本可以判断所有的手机,而且不弹出root请求框。这才是我们需要的,perfect。

方法三,交互友好,基本没有误判。

 

以下是apk以及相关源代码,大家可以下载apk看下运行效果

ROOT检测APK下载地址:http://good.gd/3091610.htm

ROOT检测代码下载:http://good.gd/3091609.htm或者http://download.csdn.net/detail/waylife/7639017

如果有手机使用方法三无法判断,欢迎提出。

也欢迎大家提出其他的更好的办法。