我的应用程序有一个特定的功能,只能在根可用的设备上工作。与其让这个特性在使用时失败(然后向用户显示适当的错误消息),我更希望能够先静默地检查根目录是否可用,如果不可用,则首先隐藏相应的选项。

有办法做到这一点吗?


当前回答

if [[ "`adb shell which su | grep -io "permission denied"`" != "permission denied" ]]; then
   echo "Yes. Rooted device."
 else
   echo "No. Device not rooted. Only limited tasks can be performed. Done."
    zenity --warning --title="Device Not Rooted" --text="The connected Android Device is <b>NOT ROOTED</b>. Only limited tasks can be performed." --no-wrap
fi

其他回答

RootTools库提供了简单的方法来检查根:

RootTools.isRootAvailable()

参考

你可以使用isAccessGiven()代替isRootAvailable()。直接从RootTools wiki:

if (RootTools.isAccessGiven()) {
    // your app has been granted root access
}

RootTools.isAccessGiven()不仅检查设备是否已根,它还检查设备是否已根 也为你的应用程序调用su,请求权限,如果返回true 您的应用程序已成功授予根权限。可以使用这个 作为在你的应用程序中的第一个检查,以确保你将被授予 在需要的时候访问。

参考

    public static boolean isRootAvailable(){
            Process p = null;
            try{
               p = Runtime.getRuntime().exec(new String[] {"su"});
               writeCommandToConsole(p,"exit 0");
               int result = p.waitFor();
               if(result != 0)
                   throw new Exception("Root check result with exit command " + result);
               return true;
            } catch (IOException e) {
                Log.e(LOG_TAG, "Su executable is not available ", e);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Root is unavailable ", e);
            }finally {
                if(p != null)
                    p.destroy();
            }
            return false;
        }
 private static String writeCommandToConsole(Process proc, String command, boolean ignoreError) throws Exception{
            byte[] tmpArray = new byte[1024];
            proc.getOutputStream().write((command + "\n").getBytes());
            proc.getOutputStream().flush();
            int bytesRead = 0;
            if(proc.getErrorStream().available() > 0){
                if((bytesRead = proc.getErrorStream().read(tmpArray)) > 1){
                    Log.e(LOG_TAG,new String(tmpArray,0,bytesRead));
                    if(!ignoreError)
                        throw new Exception(new String(tmpArray,0,bytesRead));
                }
            }
            if(proc.getInputStream().available() > 0){
                bytesRead = proc.getInputStream().read(tmpArray);
                Log.i(LOG_TAG, new String(tmpArray,0,bytesRead));
            }
            return new String(tmpArray);
        }

这里列出的许多答案都有内在的问题:

Checking for test-keys is correlated with root access but doesn't necessarily guarantee it "PATH" directories should be derived from the actual "PATH" environment variable instead of being hard coded The existence of the "su" executable doesn't necessarily mean the device has been rooted The "which" executable may or may not be installed, and you should let the system resolve its path if possible Just because the SuperUser app is installed on the device does not mean the device has root access yet

Stericson的RootTools库似乎可以更合理地检查root。它还有很多额外的工具和实用程序,所以我强烈推荐它。然而,并没有解释它是如何专门检查根目录的,而且它可能比大多数应用程序真正需要的要重一些。

我已经创建了几个基于RootTools库的实用程序方法。如果你只是想检查“su”可执行文件是否在设备上,你可以使用以下方法:

public static boolean isRootAvailable(){
    for(String pathDir : System.getenv("PATH").split(":")){
        if(new File(pathDir, "su").exists()) {
            return true;
        }
    }
    return false;
}

这个方法简单地遍历“PATH”环境变量中列出的目录,并检查其中是否存在“su”文件。

为了真正检查root访问权限,必须实际运行“su”命令。如果安装了一个像SuperUser这样的应用程序,那么在这一点上它可能会要求root访问,或者如果它已经被授予/拒绝,吐司可能会显示是否授予/拒绝访问。一个很好的命令是“id”,这样您可以验证用户id实际上是0(根)。

下面是一个示例方法来确定是否授予了根访问权:

public static boolean isRootGiven(){
    if (isRootAvailable()) {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[]{"su", "-c", "id"});
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String output = in.readLine();
            if (output != null && output.toLowerCase().contains("uid=0"))
                return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (process != null)
                process.destroy();
        }
    }

    return false;
}

实际测试运行"su"命令是很重要的,因为一些模拟器已经预安装了"su"可执行文件,但只允许某些用户像adb shell一样访问它。

在尝试运行“su”可执行文件之前检查它的存在也是很重要的,因为android已经知道不会正确地处理试图运行缺失命令的进程。随着时间的推移,这些幽灵进程会消耗大量内存。

如果你已经在使用Fabric/Firebase Crashlytics,你可以打电话

CommonUtils.isRooted(context)

这是该方法的当前实现:

public static boolean isRooted(Context context) {
    boolean isEmulator = isEmulator(context);
    String buildTags = Build.TAGS;
    if (!isEmulator && buildTags != null && buildTags.contains("test-keys")) {
        return true;
    } else {
        File file = new File("/system/app/Superuser.apk");
        if (file.exists()) {
            return true;
        } else {
            file = new File("/system/xbin/su");
            return !isEmulator && file.exists();
        }
    }
}

public static boolean isEmulator(Context context) {
    String androidId = Secure.getString(context.getContentResolver(), "android_id");
    return "sdk".equals(Build.PRODUCT) || "google_sdk".equals(Build.PRODUCT) || androidId == null;
}