Add new public API isAppGrantedRoot

This commit is contained in:
topjohnwu 2022-04-14 00:23:12 -07:00
parent 270aa6d426
commit 19126657f7
5 changed files with 54 additions and 29 deletions

View File

@ -15,7 +15,7 @@ buildscript {
}
dependencies {
classpath("com.android.tools.build:gradle:7.1.2")
classpath("com.android.tools.build:gradle:7.1.3")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@ -35,6 +35,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -205,6 +206,21 @@ public abstract class Shell implements Closeable {
return MainShell.getCached();
}
/**
* Whether the application has access to root.
* <p>
* This method returns {@code null} when it is currently unable to determine whether
* root access has been granted to the application. A non-null value meant that the root
* permission grant state has been accurately determined and finalized. The application
* must have at least 1 root shell created to have this method return {@code true}.
* This method will not block the calling thread; results will be returned immediately.
* @return whether the application has access to root, or {@code null} when undetermined.
*/
@Nullable
public static Boolean isAppGrantedRoot() {
return Utils.isAppGrantedRoot();
}
/**
* Whether the application has access to root.
* <p>
@ -214,9 +230,11 @@ public abstract class Shell implements Closeable {
* did not grant root access to your application. However, after any root shell is constructed,
* this method will accurately return {@code true}.
* @return whether the application has access to root.
* @deprecated please switch to {@link #isAppGrantedRoot()}
*/
@Deprecated
public static boolean rootAccess() {
return Utils.isAppGrantedRoot();
return Objects.equals(isAppGrantedRoot(), Boolean.TRUE);
}
/* ************

View File

@ -97,9 +97,7 @@ public final class BuilderImpl extends Shell.Builder {
// Try normal non-root shell
if (shell == null) {
if (!hasFlags(FLAG_NON_ROOT_SHELL)) {
synchronized (Utils.class) {
Utils.confirmedRootState = false;
}
Utils.setConfirmedRootState(false);
}
shell = build("sh");
}

View File

@ -150,9 +150,7 @@ class ShellImpl extends Shell {
s = br.readLine();
if (!TextUtils.isEmpty(s) && s.contains("uid=0")) {
status = ROOT_SHELL;
synchronized (Utils.class) {
Utils.confirmedRootState = true;
}
Utils.setConfirmedRootState(true);
// noinspection ConstantConditions
String cwd = ShellUtils.escapedString(System.getProperty("user.dir"));
STDIN.write(("cd " + cwd + "\n").getBytes(UTF_8));

View File

@ -43,9 +43,14 @@ public final class Utils {
private static Class<?> synchronizedCollectionClass;
private static final String TAG = "LIBSU";
// -1: uninitialized
// 0: checked, no root
// 1: checked, undetermined
// 2: checked, root access
private static int currentRootState = -1;
@SuppressLint("StaticFieldLeak")
static Context context;
static Boolean confirmedRootState;
public static void log(Object log) {
log(TAG, log);
@ -128,28 +133,34 @@ public final class Utils {
}
}
public synchronized static boolean isAppGrantedRoot() {
if (confirmedRootState != null) {
// This confirmed root state will also be set in BuilderImpl
// and ShellImpl when new shells are getting constructed.
return confirmedRootState;
}
if (Process.myUid() == 0) {
// The current process is a root service
confirmedRootState = true;
return true;
}
// noinspection ConstantConditions
for (String path : System.getenv("PATH").split(":")) {
File su = new File(path + "/su");
if (su.canExecute()) {
// We don't actually know whether the app has been granted root access.
// As a heuristic, let's return true here,
// but do NOT set the value as a confirmed state.
public synchronized static Boolean isAppGrantedRoot() {
if (currentRootState < 0) {
if (Process.myUid() == 0) {
// The current process is a root service
currentRootState = 2;
return true;
}
// noinspection ConstantConditions
for (String path : System.getenv("PATH").split(":")) {
File su = new File(path, "su");
if (su.canExecute()) {
// We don't actually know whether the app has been granted root access.
// Do NOT set the value as a confirmed state.
currentRootState = 1;
return null;
}
}
currentRootState = 0;
return false;
}
confirmedRootState = false;
return false;
switch (currentRootState) {
case 0 : return false;
case 2 : return true;
default: return null;
}
}
synchronized static void setConfirmedRootState(boolean value) {
currentRootState = value ? 2 : 0;
}
}