2016-08-24 21:58:15 +00:00
|
|
|
package com.topjohnwu.magisk.utils;
|
|
|
|
|
2017-07-15 17:20:39 +00:00
|
|
|
import android.content.Context;
|
2017-06-05 19:06:23 +00:00
|
|
|
|
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;
|
2016-08-24 21:58:15 +00:00
|
|
|
import java.io.DataOutputStream;
|
|
|
|
import java.io.IOException;
|
2017-07-17 19:34:06 +00:00
|
|
|
import java.io.InputStreamReader;
|
2016-08-24 21:58:15 +00:00
|
|
|
import java.util.ArrayList;
|
2017-07-19 10:01:22 +00:00
|
|
|
import java.util.Collection;
|
2016-08-24 21:58:15 +00:00
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modified by topjohnwu, based on Chainfire's libsuperuser
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class Shell {
|
|
|
|
|
2016-10-03 02:24:59 +00:00
|
|
|
// -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted
|
2016-08-27 11:02:41 +00:00
|
|
|
public static int rootStatus;
|
2016-08-24 21:58:15 +00:00
|
|
|
|
2017-07-17 19:34:06 +00:00
|
|
|
private final Process shellProcess;
|
|
|
|
private final DataOutputStream STDIN;
|
|
|
|
private final DataInputStream STDOUT;
|
2016-08-24 21:58:15 +00:00
|
|
|
|
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;
|
|
|
|
|
2016-08-24 21:58:15 +00:00
|
|
|
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) {
|
2016-10-03 02:24:59 +00:00
|
|
|
rootStatus = 0;
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
if (s.isEmpty() || !s.contains("uid=0")) {
|
|
|
|
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;
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-17 19:34:06 +00:00
|
|
|
|
|
|
|
isValid = true;
|
|
|
|
shellProcess = process;
|
|
|
|
STDIN = in;
|
|
|
|
STDOUT = out;
|
|
|
|
sh_raw("umask 022");
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
if (!magiskManager.shell.isValid) {
|
|
|
|
// 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;
|
2016-09-25 13:31:38 +00:00
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
|
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();
|
2016-09-25 13:31:38 +00:00
|
|
|
}
|
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;
|
2016-09-25 13:31:38 +00:00
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|
2017-06-05 19:06:23 +00:00
|
|
|
}
|
2017-07-15 19:32:29 +00:00
|
|
|
|
2017-07-19 10:01:22 +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);
|
|
|
|
}
|
|
|
|
|
2017-07-19 10:01:22 +00:00
|
|
|
public void su(Collection<String> output, String... commands) {
|
2017-07-17 19:34:06 +00:00
|
|
|
if (!rootAccess()) return;
|
|
|
|
sh(output, commands);
|
|
|
|
}
|
2017-07-19 10:01:22 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-08-24 21:58:15 +00:00
|
|
|
}
|