You often want to restrict user on a rooted device for doing any specific operation, like making an online payment in a Banking app or e-commerce App.
If you google around, there are tons of code which might help you determine whether a device is rooted or not. Hence, in order to make your life a little simpler, you can use the following code to determine the same.
public boolean isRooted() {
boolean isRootManagementApplicationInstalled = detectRootManagementApps();
boolean isPotentiallyDangerousAppsInstalled = detectPotentiallyDangerousApps();
boolean isRootCloakingApplicationInstalled = detectRootCloakingApps();
boolean isSuBinaryFileExists = isSuBinaryFileExists();
boolean isDangerousPropsExists = checkForDangerousProps();
boolean isRWPaths = checkForRWPaths();
boolean isTestKeysAvailable = detectTestKeys();
boolean isSuCommandExecutes = isSuCommandExecutes();
boolean isMagiskBinaryFileExists = isMagiskBinaryFileExists();
boolean isThirdPartyApp = isRootManagementApplicationInstalled || isPotentiallyDangerousAppsInstalled
|| isRootCloakingApplicationInstalled;
boolean isSuOrDangerousPath = isSuBinaryFileExists || isSuCommandExecutes || isDangerousPropsExists;
boolean isTestOrMagiskOrRW = isRWPaths || isTestKeysAvailable || isMagiskBinaryFileExists;
return isThirdPartyApp || isSuOrDangerousPath || isTestOrMagiskOrRW;
}
/**
* this function checks whether the kernel is signed with custom key generated by a third-party developer.
*
* @return true if signed with test-keys
*/
private boolean detectTestKeys() {
String buildTags = android.os.Build.TAGS;
return buildTags != null && buildTags.contains(TEST_KEYS);
}
/**
* This methods check for root apps using PackageManager.
*
* @return true if one of the rooting app is installed else false
*/
private boolean detectRootManagementApps() {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(ROOT_APPS_PACKAGES));
return isAnyPackageFromListInstalled(packages);
}
/**
* check for the apps which require roo
*
* @return true if root requiring app installed else false
*/
private boolean detectPotentiallyDangerousApps() {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(DANGEROUS_APPS_PACKAGES));
return isAnyPackageFromListInstalled(packages);
}
/**
* This function check for the root cloaking apps using PackageManager.
*
* @return true if one of the apps it's installed
*/
private boolean detectRootCloakingApps() {
ArrayList<String> packages = new ArrayList<>();
packages.addAll(Arrays.asList(ROOT_CLOAKING_PACKAGES));
return isAnyPackageFromListInstalled(packages);
}
/**
* This method check for common locations of SU binary
*
* @return true if found su binary else false
*/
private boolean isSuBinaryFileExists() {
return checkForBinary(SU);
}
/**
*
* @return true if @link {MAGISK} binary found else false
*/
private boolean isMagiskBinaryFileExists() {
return checkForBinary(MAGISK);
}
/**
* checks for binary in common su_Paths
*
* filename - check for this existence of this file
* @return true if found else false
*/
private boolean checkForBinary(String filename) {
boolean result = false;
for (String path : SU_PATHS) {
File f = new File(path, filename);
boolean fileExists = f.exists();
if (fileExists) {
result = true;
}
}
return result;
}
/**
* @return array of getProp splitted using "\\A"
*/
private String[] propsReader() {
try {
InputStream inputstream = Runtime.getRuntime().exec(GETPROP).getInputStream();
if (inputstream == null) {
return new String[0];
}
String propVal = new Scanner(inputstream).useDelimiter(DELIMETER_A).next();
return propVal.split("\n");
} catch (IOException | NoSuchElementException exception) {
Log.e(TAG, "RootHelper.propsReader : exception - " + exception);
return new String[0];
}
}
/**
* @return array of mount command splitted using "\\A" and "\n"
*/
private String[] mountReader() {
try {
InputStream inputstream = Runtime.getRuntime().exec(MOUNT).getInputStream();
if (inputstream == null) {
return new String[0];
}
String propVal = new Scanner(inputstream).useDelimiter(DELIMETER_A).next();
return propVal.split("\n");
} catch (IOException | NoSuchElementException exception) {
Log.e(TAG, "RootHelper.mountReader : exception - " + exception);
return new String[0];
}
}
/**
* Check if any package in the list is installed
*
* packages - list of packages to search for
* @return true if any of the packages are installed
*/
private boolean isAnyPackageFromListInstalled(List<String> packages) {
boolean result = false;
PackageManager pm = BaseApplication.getInstance().getPackageManager();
for (String packageName : packages) {
try {
pm.getPackageInfo(packageName, 0);
result = true;
} catch (PackageManager.NameNotFoundException exception) {
Log.e(TAG, "RootHelper.isAnyPackageFromListInstalled : exception - " + exception);
}
}
return result;
}
/**
* This method check for system's dangerous properties
*
* @return - true if dangerous props found else false
*/
private boolean checkForDangerousProps() {
final Map<String, String> dangerousProps = new ArrayMap<>();
dangerousProps.put(RO_DEBUGGABLE, "1");
dangerousProps.put(RO_SECURE, "0");
boolean result = false;
String[] lines = propsReader();
if (lines.length == 0) {
return false;
}
for (String line : lines) {
for (Map.Entry<String, String> entry : dangerousProps.entrySet()) {
result = isDangerousProps(line, entry);
}
}
return result;
}
/**
* line line
* entry map entry set
* @return rue if dangerous props found else false
*/
private boolean isDangerousProps(String line, Map.Entry<String, String> entry) {
if (line.contains(entry.getKey())) {
StringBuilder badValue = new StringBuilder(entry.getValue());
badValue.append(OPEN_BRACKET).append(badValue).append(CLOSING_BRACKET);
if (line.contains(badValue)) {
return true;
}
}
return false;
}
/**
* this method checks
* if any of the paths are writable.
*
* @return true any of the non writable directory is writable, else false
*/
public boolean checkForRWPaths() {
boolean result = false;
String[] lines = mountReader();
if (lines.length == 0) {
return false;
}
for (String line : lines) {
String[] args = line.split(" ");
if (args.length < 4) {
continue;
}
String mountPoint = args[1];
String mountOptions = args[3];
for (String pathToCheck : PATHS_THAT_SHOULD_NOT_BE_WRITABLE) {
result = checkForRWPathsExtended(mountPoint, mountOptions, pathToCheck);
}
}
return result;
}
/**
* checks if directory is writable
*
* mountPoint mount point
* mountOptions mount options
* pathToCheck path to check
* @return true if directory is writable else false
*/
private boolean checkForRWPathsExtended(String mountPoint, String mountOptions,
String pathToCheck) {
if (mountPoint.equalsIgnoreCase(pathToCheck)) {
for (String option : mountOptions.split(",")) {
if (option.equalsIgnoreCase(RW)) {
return true;
}
}
}
return false;
}
/**
*
* @return true if 'which su' command executes else false
*/
private boolean isSuCommandExecutes() {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]{WHICH, SU});
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
return in.readLine() != null;
} catch (IOException exception) {
Log.e(TAG, "RootHelper.isSuCommandExecutes : exception - " + exception);
} finally {
if (process != null) {
process.destroy();
}
}
return false;
}
}
Note: If you have any method which can be part of this Util class, feel free to leave a comment. You can download the sample app from here.
With increase vulnerabilities in security, it makes sense to check if the device is rooted or not.