Update Shell.rootAccess() implementation

This commit is contained in:
topjohnwu 2022-02-27 22:09:23 -08:00
parent 47c8d2c9c2
commit 9c7025130e
4 changed files with 45 additions and 10 deletions

View File

@ -28,6 +28,7 @@ import androidx.annotation.Nullable;
import com.topjohnwu.superuser.internal.BuilderImpl;
import com.topjohnwu.superuser.internal.MainShell;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import com.topjohnwu.superuser.internal.Utils;
import java.io.Closeable;
import java.io.IOException;
@ -206,17 +207,17 @@ public abstract class Shell implements Closeable {
}
/**
* {@code Shell.getShell().isRoot()}
* Whether the application has access to root.
* <p>
* Please refer to {@link #getShell()} for info about concerns on blocking.
* This method would NEVER produce false negatives, but false positives can be returned before
* actually constructing a root shell. A {@code false} returned is guaranteed to be
* 100% accurate, while {@code true} may be returned if the device is rooted, but the user
* 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.
*/
public static boolean rootAccess() {
try {
return getShell().isRoot();
} catch (NoShellException e) {
return false;
}
return Utils.isAppGrantedRoot();
}
/* ************

View File

@ -89,8 +89,12 @@ public final class BuilderImpl extends Shell.Builder {
if (shell == null && !hasFlags(FLAG_NON_ROOT_SHELL)) {
try {
shell = build("su");
if (shell.getStatus() != ROOT_SHELL)
if (shell.getStatus() != ROOT_SHELL) {
shell = null;
synchronized (Utils.class) {
Utils.confirmedRootState = false;
}
}
} catch (NoShellException ignore) {}
}

View File

@ -141,8 +141,12 @@ class ShellImpl extends Shell {
STDIN.write(("id\n").getBytes(UTF_8));
STDIN.flush();
s = br.readLine();
if (!TextUtils.isEmpty(s) && s.contains("uid=0"))
if (!TextUtils.isEmpty(s) && s.contains("uid=0")) {
status = ROOT_SHELL;
synchronized (Utils.class) {
Utils.confirmedRootState = true;
}
}
if (status == ROOT_SHELL) {
STDIN.write(("readlink /proc/self/ns/mnt\n").getBytes(UTF_8));

View File

@ -20,6 +20,7 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.os.Process;
import android.util.ArraySet;
import android.util.Log;
@ -42,6 +43,7 @@ public final class Utils {
public static Context context;
private static Class<?> synchronizedCollectionClass;
private static final String TAG = "LIBSU";
static Boolean confirmedRootState = null;
public static void log(Object log) {
log(TAG, log);
@ -123,4 +125,28 @@ public final class Utils {
return new HashSet<>();
}
}
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;
}
try {
Runtime.getRuntime().exec("su --version");
// Even if the execution worked, 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.
return true;
} catch (IOException e) {
// Cannot run program "su": error=2, No such file or directory
confirmedRootState = false;
return false;
}
}
}