/*
* Copyright (C) 2015-2016 Willi Ye <williye97@gmail.com>
*
* This file is part of Kernel Adiutor.
*
* Kernel Adiutor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Kernel Adiutor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kernel Adiutor. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.grarak.kerneladiutor.utils.root;
import android.util.Log;
import com.grarak.kerneladiutor.utils.Utils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* Created by willi on 30.12.15.
*/
public class RootUtils {
private static SU su;
public static boolean rootAccess() {
SU su = getSU();
su.runCommand("echo /testRoot/");
return !su.denied;
}
public static boolean busyboxInstalled() {
return existBinary("busybox") || existBinary("toybox");
}
private static boolean existBinary(String binary) {
String paths;
if (System.getenv("PATH") != null) {
paths = System.getenv("PATH");
} else {
paths = "/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin";
}
for (String path : paths.split(":")) {
if (!path.endsWith("/")) path += "/";
if (Utils.existFile(path + binary, false) || Utils.existFile(path + binary)) {
return true;
}
}
return false;
}
public static void chmod(String file, String permission) {
chmod(file, permission, getSU());
}
public static void chmod(String file, String permission, SU su) {
su.runCommand("chmod " + permission + " " + file);
}
public static String getProp(String prop) {
return runCommand("getprop " + prop);
}
public static void mount(boolean writeable, String mountpoint) {
mount(writeable, mountpoint, getSU());
}
public static void mount(boolean writeable, String mountpoint, SU su) {
su.runCommand(writeable ? "mount -o remount,rw " + mountpoint + " " + mountpoint :
"mount -o remount,ro " + mountpoint + " " + mountpoint);
su.runCommand(writeable ? "mount -o remount,rw " + mountpoint :
"mount -o remount,ro " + mountpoint);
}
public static String runScript(String text, String... arguments) {
RootFile script = new RootFile("/data/local/tmp/kerneladiutortmp.sh");
script.mkdir();
script.write(text, false);
return script.execute(arguments);
}
public static void closeSU() {
if (su != null) su.close();
su = null;
}
public static String runCommand(String command) {
return getSU().runCommand(command);
}
public static SU getSU() {
if (su == null || su.closed || su.denied) {
if (su != null && !su.closed) {
su.close();
}
su = new SU();
}
return su;
}
/*
* Based on AndreiLux's SU code in Synapse
* https://github.com/AndreiLux/Synapse/blob/master/src/main/java/com/af/synapse/utils/Utils.java#L238
*/
public static class SU {
private Process mProcess;
private BufferedWriter mWriter;
private BufferedReader mReader;
private final boolean mRoot;
private final String mTag;
private boolean closed;
private boolean denied;
private boolean firstTry;
public SU() {
this(true, null);
}
public SU(boolean root, String tag) {
mRoot = root;
mTag = tag;
try {
if (mTag != null) {
Log.i(mTag, String.format("%s initialized", root ? "SU" : "SH"));
}
firstTry = true;
mProcess = Runtime.getRuntime().exec(root ? "su" : "sh");
mWriter = new BufferedWriter(new OutputStreamWriter(mProcess.getOutputStream()));
mReader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
} catch (IOException e) {
if (mTag != null) {
Log.e(mTag, root ? "Failed to run shell as su" : "Failed to run shell as sh");
}
denied = true;
closed = true;
}
}
public synchronized String runCommand(final String command) {
synchronized (this) {
try {
StringBuilder sb = new StringBuilder();
String callback = "/shellCallback/";
mWriter.write(command + "\necho " + callback + "\n");
mWriter.flush();
int i;
char[] buffer = new char[256];
while (true) {
sb.append(buffer, 0, mReader.read(buffer));
if ((i = sb.indexOf(callback)) > -1) {
sb.delete(i, i + callback.length());
break;
}
}
firstTry = false;
if (mTag != null) {
Log.i(mTag, "run: " + command + " output: " + sb.toString().trim());
}
return sb.toString().trim();
} catch (IOException e) {
closed = true;
e.printStackTrace();
if (firstTry) denied = true;
} catch (ArrayIndexOutOfBoundsException e) {
denied = true;
} catch (Exception e) {
e.printStackTrace();
denied = true;
}
return null;
}
}
public void close() {
try {
if (mWriter != null) {
mWriter.write("exit\n");
mWriter.flush();
mWriter.close();
}
if (mReader != null) {
mReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (mProcess != null) {
try {
mProcess.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
mProcess.destroy();
if (mTag != null) {
Log.i(mTag, String.format("%s closed: %d", mRoot ? "SU" : "SH", mProcess.exitValue()));
}
}
closed = true;
}
}
}