Magisk/app/src/main/java/com/topjohnwu/magisk/utils/Shell.java

182 lines
5.1 KiB
Java
Raw Normal View History

package com.topjohnwu.magisk.utils;
2017-07-15 17:20:39 +00:00
import android.content.Context;
2017-08-06 16:15:46 +00:00
import android.text.TextUtils;
2017-08-03 15:33:08 +00:00
import com.topjohnwu.magisk.MagiskManager;
2017-07-17 19:34:06 +00:00
import java.io.BufferedReader;
2017-07-15 17:20:39 +00:00
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
2017-07-17 19:34:06 +00:00
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Modified by topjohnwu, based on Chainfire's libsuperuser
*/
public class Shell {
// -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted
2016-08-27 11:02:41 +00:00
public static int rootStatus;
2017-07-17 19:34:06 +00:00
private final Process shellProcess;
private final DataOutputStream STDIN;
private final DataInputStream STDOUT;
2017-07-15 19:32:29 +00:00
private boolean isValid;
2017-07-15 17:20:39 +00:00
private Shell() {
2017-07-17 19:34:06 +00:00
rootStatus = 1;
Process process = null;
DataOutputStream in = null;
DataInputStream out = null;
try {
2017-07-15 17:20:39 +00:00
process = Runtime.getRuntime().exec("su");
2017-07-17 19:34:06 +00:00
in = new DataOutputStream(process.getOutputStream());
out = new DataInputStream(process.getInputStream());
2017-07-15 17:20:39 +00:00
} catch (IOException e) {
rootStatus = 0;
}
2017-07-17 19:34:06 +00:00
while (true) {
if (rootAccess()) {
try {
in.write(("id\n").getBytes("UTF-8"));
in.flush();
String s = new BufferedReader(new InputStreamReader(out)).readLine();
2017-08-06 16:15:46 +00:00
if (TextUtils.isEmpty(s) || !s.contains("uid=0")) {
2017-07-17 19:34:06 +00:00
in.close();
out.close();
process.destroy();
throw new IOException();
}
} catch (IOException e) {
rootStatus = -1;
continue;
}
break;
} else {
// Try to gain non-root sh
try {
process = Runtime.getRuntime().exec("sh");
in = new DataOutputStream(process.getOutputStream());
out = new DataInputStream(process.getInputStream());
} catch (IOException e) {
// Nothing works....
shellProcess = null;
STDIN = null;
STDOUT = null;
isValid = false;
return;
}
break;
}
}
2017-07-17 19:34:06 +00:00
isValid = true;
shellProcess = process;
STDIN = in;
STDOUT = out;
sh_raw("umask 022");
}
2017-07-17 19:34:06 +00:00
public static Shell getShell() {
2017-07-15 17:20:39 +00:00
return new Shell();
}
2017-07-17 19:34:06 +00:00
public static Shell getShell(Context context) {
2017-08-03 15:33:08 +00:00
MagiskManager magiskManager = Utils.getMagiskManager(context);
2017-08-11 17:31:34 +00:00
if (magiskManager.shell == null || !magiskManager.shell.isValid) {
2017-08-03 15:33:08 +00:00
// Get new shell if needed
magiskManager.shell = getShell();
}
return magiskManager.shell;
2017-07-15 17:20:39 +00:00
}
2016-08-25 10:08:07 +00:00
public static boolean rootAccess() {
2017-07-15 17:20:39 +00:00
return rootStatus > 0;
2016-08-25 10:08:07 +00:00
}
2017-07-17 19:34:06 +00:00
public List<String> sh(String... commands) {
2017-07-15 17:20:39 +00:00
List<String> res = new ArrayList<>();
2017-07-17 19:34:06 +00:00
if (!isValid) return res;
sh(res, commands);
2017-07-15 17:20:39 +00:00
return res;
}
2017-07-17 19:34:06 +00:00
public void sh_raw(String... commands) {
2017-08-03 15:33:08 +00:00
sh_raw(false, commands);
}
public void sh_raw(boolean stdout, String... commands) {
2017-07-15 19:32:29 +00:00
if (!isValid) return;
2017-07-17 19:34:06 +00:00
synchronized (shellProcess) {
2017-07-15 17:20:39 +00:00
try {
for (String command : commands) {
2017-07-30 16:21:18 +00:00
Logger.shell(command);
2017-08-03 15:33:08 +00:00
STDIN.write((command + (stdout ? "\n" : " >/dev/null\n")).getBytes("UTF-8"));
2017-07-17 19:34:06 +00:00
STDIN.flush();
}
2017-07-15 19:32:29 +00:00
} catch (IOException e) {
2017-07-15 17:20:39 +00:00
e.printStackTrace();
2017-07-17 19:34:06 +00:00
shellProcess.destroy();
2017-07-15 19:32:29 +00:00
isValid = false;
}
}
}
2017-07-15 19:32:29 +00:00
public void sh(Collection<String> output, String... commands) {
2017-07-15 19:32:29 +00:00
if (!isValid) return;
try {
2017-07-17 19:34:06 +00:00
shellProcess.exitValue();
2017-07-15 19:32:29 +00:00
isValid = false;
return; // The process is dead, return
} catch (IllegalThreadStateException ignored) {
// This should be the expected result
}
2017-07-17 19:34:06 +00:00
synchronized (shellProcess) {
2017-07-30 16:21:18 +00:00
StreamGobbler out = new StreamGobbler(STDOUT, output);
2017-07-17 19:34:06 +00:00
out.start();
2017-08-03 15:33:08 +00:00
sh_raw(true, commands);
sh_raw(true, "echo \'-shell-done-\'");
2017-07-17 19:34:06 +00:00
try { out.join(); } catch (InterruptedException ignored) {}
2017-07-15 19:32:29 +00:00
}
}
2017-07-17 19:34:06 +00:00
public List<String> su(String... commands) {
if (!rootAccess()) return sh();
return sh(commands);
}
public void su_raw(String... commands) {
if (!rootAccess()) return;
sh_raw(commands);
}
public void su(Collection<String> output, String... commands) {
2017-07-17 19:34:06 +00:00
if (!rootAccess()) return;
sh(output, commands);
}
public static abstract class AbstractList<E> extends java.util.AbstractList<E> {
@Override
public abstract boolean add(E e);
@Override
public E get(int i) {
return null;
}
@Override
public int size() {
return 0;
}
}
}