由于项目需要root安装软件,并且希望在合适的时候引导用户去开启root安装,故需要检测手机是否root。
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 ? null
100 : successMsg.toString(), errorMsg == null ? null
101 : 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 command
110 * result</li>
111 * <li>{@link CommandResult#errorMsg} means error message of command result</li>
112 * </ul>
113 *
114 * @author Trinea 2013-5-16
115 */
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 msg
136 *
137 * @param command
138 * command
139 * @param isRoot
140 * whether need to run with root
141 * @return
142 * @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 }
但是这会带来一个问题,每次判断是否root都会弹出一个root请求框。这是十分不友好的一种交互方式,而且,用户如果选择取消,有部分手机是判断为非root的。
这是方法一。交互不友好,而且有误判。
在这个情况下,为了不弹出确认框,考虑到一般root手机都会有一些的特殊文件夹,比如/system/bin/su,/system/xbin/su,里面存放有相关的权限控制文件。
因此只要手机中有一个文件夹存在就判断这个手机root了。
然后经过测试,这种方法在大部分手机都可行。
代码如下:
/** 判断是否具有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 }
这是方法二。交互友好,但是有误判。
后来测试的过程中发现部分国产,比如小米系列,有这个文件夹,但是系统是未root的,判断成了已root。经过分析,这是由于小米有自身的权限控制系统而导致。
考虑到小米手机有大量的用户群,这个问题必须解决,所以不得不寻找第三种方案。
从原理着手,小米手机无论是否root,应该都是具有相关文件的。但是无效的原因应该是,文件设置了相关的权限。导致用户组无法执行相关文件。
从这个角度看,就可以从判断文件的权限入手。
未root手机,大部分手机没有这两个文件夹,小米手机有这个文件夹。未root小米手机权限如下(由于手头暂时没有小米手机,过几天补上,或者有同学帮忙补上,那真是感激不尽)。
【等待补充图片】
代码如下:
/** 判断手机是否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 }
这种方法基本可以判断所有的手机,而且不弹出root请求框。这才是我们需要的,perfect。
方法三,交互友好,基本没有误判。