【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 ? 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,应该都是具有相关文件的。但是无效的原因应该是,文件设置了相关的权限。导致用户组无法执行相关文件。

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

先看下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小米手机权限如下(由于手头暂时没有小米手机,过几天补上,或者有同学帮忙补上,那真是感激不尽)。

【等待补充图片】
代码如下:

/** 判断手机是否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。

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

参考链接:http://www.cnblogs.com/waylife/p/3846440.html

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

时间: 2024-08-04 11:23:24

【Android】不弹root请求框检测手机是否root的相关文章

Android的弹出登陆框的实现

最近在做一个项目,要用到登陆框,几经波折,最后用的是直接将Activity的Theme属性设置成Dialog,然后达到了我想要的效果. 下面是我的实现经历: 1.首先,我是直接使用AlertDialog来实现,确定是,形状有点难看,而且获得Dialog里面的控件略显麻烦(因为我要做的登陆框有一定的布局),然后就给我就放弃了,可能因为我太水了,不能很好的使用它 2.然后我就使用PopupWindow来实现,界面是达到了我的要求,控件的获得通过Inflater就可以获得了相对较简单,但是有一个缺点就

Android 底部弹出提示框的解决办法(使用Activity以及PopupWindow)

本片文章主要谈探讨了如何实现在底部弹出提示框背景为半透明效果的实现.想要实现此种效果一般有两种方式一个是使用Activity设置Theme另一种方式就是使用PopupWindow设置样式实现效果. 一,使用Activity 首先是此activity的布局文件: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.andro

android 透明弹出搜索框

1.在QQ一些APP当中有是弹出一个半透明的搜索框的,其实这种效果就是很多种方法,自定义一个Dialog,或者直接将activity的背景变成半透明的也可以的. 下面就是将activity变成半透明的方法.效果图如下: 2.代码如下: 在styles.xml文件中添加自定义Theme 1 <style name="MyTranspaent"> 2 3 <item name="android:windowBackground">@color/t

Android:Toast 弹出信息框

toast的主要方法: cancel 方法:关闭Toast视图 getDuration 方法:获取持续时间 getGravity 方法:获取Toast视图的位置 makeText 方法:生成标准Toast setView 方法:设置显示的View物件 getView 方法:获取View对象 setGravity 方法:设置显示位置 getXOffset 方法:获取水平方向偏移量 getYOffset 方法:获取垂直方向偏移量 setDuration 方法:设置持续时间 setText 方法:设置

如何实现android蓝牙开发 自动配对连接,并不弹出提示框

如何实现android蓝牙开发 自动配对连接,并不弹出提示框 之前做一个android版的蓝牙,遇到最大的难题就是自动配对. 上网查资料说是用反射createBond()和setPin(),但测试时进行配对还是会出现提示,但配对是成功了 我就开始查找怎么关闭这个蓝牙配对提示框,后面还是伟大的android源码帮助了我. 在源码 BluetoothDevice 类中还有两个隐藏方法 cancelBondProcess()和cancelPairingUserInput() 这两个方法一个是取消配对进

Android底部弹出iOS7风格对话选项框

<Android底部弹出iOS7风格对话选项框> 效果图如下: 网上流传的Android底部弹出iOS7风格的对话选项框开源代码,原作者不详.我在网上流传的代码基础上改进了一些地方,把原来作为Application发布的代码整理成一个Android的Library,如果在未来的Android项目中需要这样的对话选项框样式,则只需要下载我上次到CSDN的完整库项目(完整的Android库项目代码我已经上传到CSDN,下载地址:http://download.csdn.net/download/z

Android APP 分享图片文字到微信刚开始正常,后面就不弹出分享框了

按照官方的流程走了一遍,一切顺利,分享成功了,本来以为可以大功告成了,结果睡了一觉,第二天要给客户演示了,才发现TMD坑爹了,不能分享了,第三方的分享栏弹不出来了,我一阵惊慌,还好很快找到了解决办法:原因是我进行了代码混淆,但是没有对新添加的这部分分享到微信的代码进行处理,所以...解决问题的办法很简单:需要在混淆配置文件proguard.cfg中,增加如下两行代码: -keep class com.tencent.mm.sdk.openapi.WXMediaMessage { *;} -kee

[Phonegap+Sencha Touch] 移动开发19 某些安卓手机上弹出消息框 点击后不消失的解决办法

Ext.Msg.alert等弹出框在某些安卓手机上,点击确定后不消失. 原因是: 消息框点击确定后有一段css3 transform动画,动画完成后才会隐藏(display:none).有些奇葩手机就是不一样. 解决办法就是禁用消息框的动画: 方法一: 在app.js的launch方法里面加上 Ext.Msg.defaultAllowedConfig.showAnimation = false Ext.Msg.defaultAllowedConfig.hideAnimation = false

Android Studio 打开弹出警告框

1.Android Studio打开后,自己的项目没有打开,就弹出了警告框,重启之后依然弹出警告框: 警告框内容:"Cannot load project: java.lang.IllegalStateException: @NotNull method com/intellij/openapi/progress/ProgressManager.getInstance must not return null". 解决:在电脑的任务管理器里面找到Android Studio运行项,选中