boolean
to specify whether to close the root shell or the standard shell.
+ */
+ public static void closeShell(boolean root) throws IOException {
+ if (root) {
+ Shell.closeRootShell();
+ } else {
+ Shell.closeShell();
+ }
+ }
+
+ /**
+ * Use this to check whether or not a file exists on the filesystem.
+ *
+ * @param file String that represent the file, including the full path to the
+ * file and its name.
+ * @return a boolean that will indicate whether or not the file exists.
+ */
+ public static boolean exists(final String file) {
+ return exists(file, false);
+ }
+
+ /**
+ * Use this to check whether or not a file OR directory exists on the filesystem.
+ *
+ * @param file String that represent the file OR the directory, including the full path to the
+ * file and its name.
+ * @param isDir boolean that represent whether or not we are looking for a directory
+ * @return a boolean that will indicate whether or not the file exists.
+ */
+ public static boolean exists(final String file, boolean isDir) {
+ final ListList
containing the locations the binary was found at.
+ */
+ public static ListString
that represent the binary to find.
+ * @param searchPaths List
which contains the paths to search for this binary in.
+ * @param singlePath boolean that represents whether to return a single path or multiple.
+ *
+ * @return List
containing the locations the binary was found at.
+ */
+ public static ListString
to Indicate the path to the shell that you want to open.
+ * @param timeout an int
to Indicate the length of time before giving up on opening a shell.
+ * @throws TimeoutException
+ * @throws com.stericson.RootShell.exceptions.RootDeniedException
+ * @throws IOException
+ */
+ public static Shell getCustomShell(String shellPath, int timeout) throws IOException, TimeoutException, RootDeniedException
+ {
+ return Shell.startCustomShell(shellPath, timeout);
+ }
+
+ /**
+ * This will return the environment variable PATH
+ *
+ * @return List
A List of Strings representing the environment variable $PATH
+ */
+ public static Listboolean
to Indicate whether or not you want to open a root shell or a standard shell
+ * @param timeout an int
to Indicate the length of time to wait before giving up on opening a shell.
+ * @param shellContext the context to execute the shell with
+ * @param retry a int
to indicate how many times the ROOT shell should try to open with root priviliges...
+ */
+ public static Shell getShell(boolean root, int timeout, Shell.ShellContext shellContext, int retry) throws IOException, TimeoutException, RootDeniedException {
+ if (root) {
+ return Shell.startRootShell(timeout, shellContext, retry);
+ } else {
+ return Shell.startShell(timeout);
+ }
+ }
+
+ /**
+ * This will open or return, if one is already open, a shell, you are responsible for managing the shell, reading the output
+ * and for closing the shell when you are done using it.
+ *
+ * @param root a boolean
to Indicate whether or not you want to open a root shell or a standard shell
+ * @param timeout an int
to Indicate the length of time to wait before giving up on opening a shell.
+ * @param shellContext the context to execute the shell with
+ */
+ public static Shell getShell(boolean root, int timeout, Shell.ShellContext shellContext) throws IOException, TimeoutException, RootDeniedException {
+ return getShell(root, timeout, shellContext, 3);
+ }
+
+ /**
+ * This will open or return, if one is already open, a shell, you are responsible for managing the shell, reading the output
+ * and for closing the shell when you are done using it.
+ *
+ * @param root a boolean
to Indicate whether or not you want to open a root shell or a standard shell
+ * @param shellContext the context to execute the shell with
+ */
+ public static Shell getShell(boolean root, Shell.ShellContext shellContext) throws IOException, TimeoutException, RootDeniedException {
+ return getShell(root, 0, shellContext, 3);
+ }
+
+ /**
+ * This will open or return, if one is already open, a shell, you are responsible for managing the shell, reading the output
+ * and for closing the shell when you are done using it.
+ *
+ * @param root a boolean
to Indicate whether or not you want to open a root shell or a standard shell
+ * @param timeout an int
to Indicate the length of time to wait before giving up on opening a shell.
+ */
+ public static Shell getShell(boolean root, int timeout) throws IOException, TimeoutException, RootDeniedException {
+ return getShell(root, timeout, Shell.defaultContext, 3);
+ }
+
+ /**
+ * This will open or return, if one is already open, a shell, you are responsible for managing the shell, reading the output
+ * and for closing the shell when you are done using it.
+ *
+ * @param root a boolean
to Indicate whether or not you want to open a root shell or a standard shell
+ */
+ public static Shell getShell(boolean root) throws IOException, TimeoutException, RootDeniedException {
+ return RootShell.getShell(root, 0);
+ }
+
+ /**
+ * @return true
if your app has been given root access.
+ * @throws TimeoutException if this operation times out. (cannot determine if access is given)
+ */
+ public static boolean isAccessGiven() {
+ return isAccessGiven(0, 3);
+ }
+
+ /**
+ * Control how many time of retries should request
+ *
+ * @param timeout The timeout
+ * @param retries The number of retries
+ *
+ * @return true
if your app has been given root access.
+ * @throws TimeoutException if this operation times out. (cannot determine if access is given)
+ */
+ public static boolean isAccessGiven(int timeout, int retries) {
+ final Settrue
if BusyBox was found.
+ */
+ public static boolean isBusyboxAvailable()
+ {
+ return isBusyboxAvailable(false);
+ }
+
+ /**
+ * @return true
if BusyBox or Toybox was found.
+ */
+ public static boolean isBusyboxAvailable(boolean includeToybox)
+ {
+ if(includeToybox) {
+ return (findBinary("busybox", true)).size() > 0 || (findBinary("toybox", true)).size() > 0;
+ } else {
+ return (findBinary("busybox", true)).size() > 0;
+ }
+ }
+
+ /**
+ * @return true
if su was found.
+ */
+ public static boolean isRootAvailable() {
+ return (findBinary("su", true)).size() > 0;
+ }
+
+ /**
+ * This method allows you to output debug messages only when debugging is on. This will allow
+ * you to add a debug option to your app, which by default can be left off for performance.
+ * However, when you need debugging information, a simple switch can enable it and provide you
+ * with detailed logging.
+ *
+ * This method handles whether or not to log the information you pass it depending whether or
+ * not RootShell.debugMode is on. So you can use this and not have to worry about handling it
+ * yourself.
+ *
+ * @param msg The message to output.
+ */
+ public static void log(String msg) {
+ log(null, msg, LogLevel.DEBUG, null);
+ }
+
+ /**
+ * This method allows you to output debug messages only when debugging is on. This will allow
+ * you to add a debug option to your app, which by default can be left off for performance.
+ * However, when you need debugging information, a simple switch can enable it and provide you
+ * with detailed logging.
+ *
+ * This method handles whether or not to log the information you pass it depending whether or
+ * not RootShell.debugMode is on. So you can use this and not have to worry about handling it
+ * yourself.
+ *
+ * @param TAG Optional parameter to define the tag that the Log will use.
+ * @param msg The message to output.
+ */
+ public static void log(String TAG, String msg) {
+ log(TAG, msg, LogLevel.DEBUG, null);
+ }
+
+ /**
+ * This method allows you to output debug messages only when debugging is on. This will allow
+ * you to add a debug option to your app, which by default can be left off for performance.
+ * However, when you need debugging information, a simple switch can enable it and provide you
+ * with detailed logging.
+ *
+ * This method handles whether or not to log the information you pass it depending whether or
+ * not RootShell.debugMode is on. So you can use this and not have to worry about handling it
+ * yourself.
+ *
+ * @param msg The message to output.
+ * @param type The type of log, 1 for verbose, 2 for error, 3 for debug, 4 for warn
+ * @param e The exception that was thrown (Needed for errors)
+ */
+ public static void log(String msg, LogLevel type, Exception e) {
+ log(null, msg, type, e);
+ }
+
+ /**
+ * This method allows you to check whether logging is enabled.
+ * Yes, it has a goofy name, but that's to keep it as short as possible.
+ * After all writing logging calls should be painless.
+ * This method exists to save Android going through the various Java layers
+ * that are traversed any time a string is created (i.e. what you are logging)
+ *
+ * Example usage:
+ * if(islog) {
+ * StrinbBuilder sb = new StringBuilder();
+ * // ...
+ * // build string
+ * // ...
+ * log(sb.toString());
+ * }
+ *
+ * @return true if logging is enabled
+ */
+ public static boolean islog() {
+ return debugMode;
+ }
+
+ /**
+ * This method allows you to output debug messages only when debugging is on. This will allow
+ * you to add a debug option to your app, which by default can be left off for performance.
+ * However, when you need debugging information, a simple switch can enable it and provide you
+ * with detailed logging.
+ *
+ * This method handles whether or not to log the information you pass it depending whether or
+ * not RootShell.debugMode is on. So you can use this and not have to worry about handling it
+ * yourself.
+ *
+ * @param TAG Optional parameter to define the tag that the Log will use.
+ * @param msg The message to output.
+ * @param type The type of log, 1 for verbose, 2 for error, 3 for debug
+ * @param e The exception that was thrown (Needed for errors)
+ */
+ public static void log(String TAG, String msg, LogLevel type, Exception e) {
+ if (msg != null && !msg.equals("")) {
+ if (debugMode) {
+ if (TAG == null) {
+ TAG = version;
+ }
+
+ switch (type) {
+ case VERBOSE:
+ Log.v(TAG, msg);
+ break;
+ case ERROR:
+ Log.e(TAG, msg, e);
+ break;
+ case DEBUG:
+ Log.d(TAG, msg);
+ break;
+ case WARN:
+ Log.w(TAG, msg);
+ break;
+ }
+ }
+ }
+ }
+
+ // --------------------
+ // # Public Methods #
+ // --------------------
+
+ private static void commandWait(Shell shell, Command cmd) throws Exception {
+ while (!cmd.isFinished()) {
+
+ RootShell.log(version, shell.getCommandQueuePositionString(cmd));
+ RootShell.log(version, "Processed " + cmd.totalOutputProcessed + " of " + cmd.totalOutput + " output from command.");
+
+ synchronized (cmd) {
+ try {
+ if (!cmd.isFinished()) {
+ cmd.wait(2000);
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (!cmd.isExecuting() && !cmd.isFinished()) {
+ if (!shell.isExecuting && !shell.isReading) {
+ RootShell.log(version, "Waiting for a command to be executed in a shell that is not executing and not reading! \n\n Command: " + cmd.getCommand());
+ Exception e = new Exception();
+ e.setStackTrace(Thread.currentThread().getStackTrace());
+ e.printStackTrace();
+ } else if (shell.isExecuting && !shell.isReading) {
+ RootShell.log(version, "Waiting for a command to be executed in a shell that is executing but not reading! \n\n Command: " + cmd.getCommand());
+ Exception e = new Exception();
+ e.setStackTrace(Thread.currentThread().getStackTrace());
+ e.printStackTrace();
+ } else {
+ RootShell.log(version, "Waiting for a command to be executed in a shell that is not reading! \n\n Command: " + cmd.getCommand());
+ Exception e = new Exception();
+ e.setStackTrace(Thread.currentThread().getStackTrace());
+ e.printStackTrace();
+ }
+ }
+
+ }
+ }
+}
diff --git a/wrappers/android/mynteye/libshell/src/main/java/com/stericson/RootShell/containers/RootClass.java b/wrappers/android/mynteye/libshell/src/main/java/com/stericson/RootShell/containers/RootClass.java
new file mode 100644
index 0000000..9cf7659
--- /dev/null
+++ b/wrappers/android/mynteye/libshell/src/main/java/com/stericson/RootShell/containers/RootClass.java
@@ -0,0 +1,348 @@
+package com.stericson.RootShell.containers;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/* #ANNOTATIONS @SupportedAnnotationTypes("com.stericson.RootShell.containers.RootClass.Candidate") */
+/* #ANNOTATIONS @SupportedSourceVersion(SourceVersion.RELEASE_6) */
+public class RootClass /* #ANNOTATIONS extends AbstractProcessor */ {
+
+ /* #ANNOTATIONS
+ @Override
+ public boolean process(Set extends TypeElement> typeElements, RoundEnvironment roundEnvironment) {
+ processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "I was invoked!!!");
+
+ return false;
+ }
+ */
+
+ static String PATH_TO_DX = "/Users/Chris/Projects/android-sdk-macosx/build-tools/18.0.1/dx";
+
+ enum READ_STATE {
+ STARTING, FOUND_ANNOTATION;
+ }
+
+ public RootClass(String[] args) throws ClassNotFoundException, NoSuchMethodException,
+ IllegalAccessException, InvocationTargetException, InstantiationException {
+
+ // Note: rather than calling System.load("/system/lib/libandroid_runtime.so");
+ // which would leave a bunch of unresolved JNI references,
+ // we are using the 'withFramework' class as a preloader.
+ // So, yeah, russian dolls: withFramework > RootClass > actual method
+
+ String className = args[0];
+ RootArgs actualArgs = new RootArgs();
+ actualArgs.args = new String[args.length - 1];
+ System.arraycopy(args, 1, actualArgs.args, 0, args.length - 1);
+ Class> classHandler = Class.forName(className);
+ Constructor> classConstructor = classHandler.getConstructor(RootArgs.class);
+ classConstructor.newInstance(actualArgs);
+ }
+
+ public @interface Candidate {
+
+ }
+
+ ;
+
+ public class RootArgs {
+
+ public String args[];
+ }
+
+ static void displayError(Exception e) {
+ // Not using system.err to make it easier to capture from
+ // calling library.
+ System.out.println("##ERR##" + e.getMessage() + "##");
+ e.printStackTrace();
+ }
+
+ // I reckon it would be better to investigate classes using getAttribute()
+ // however this method allows the developer to simply select "Run" on RootClass
+ // and immediately re-generate the necessary jar file.
+ static public class AnnotationsFinder {
+
+ private final String AVOIDDIRPATH = "stericson" + File.separator + "RootShell" + File.separator;
+
+ private List+ * Detects the version of the su binary installed (if any), if supported + * by the binary. Most binaries support two different version numbers, + * the public version that is displayed to users, and an internal + * version number that is used for version number comparisons. Returns + * null if su not available or retrieving the version isn't supported. + *
+ *+ * Note that su binary version and GUI (APK) version can be completely + * different. + *
+ *+ * This function caches its result to improve performance on multiple + * calls + *
+ * + * @param internal Request human-readable version or application + * internal version + * @return String containing the su version or null + */ + private synchronized String getSuVersion(boolean internal) { + int idx = internal ? 0 : 1; + if (suVersion[idx] == null) { + String version = null; + + // Replace libsuperuser:Shell.run with manual process execution + Process process; + try { + process = Runtime.getRuntime().exec(internal ? "su -V" : "su -v", null); + process.waitFor(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } catch (InterruptedException e) { + e.printStackTrace(); + return null; + } + + // From libsuperuser:StreamGobbler + List