diff --git a/libsuperuser/.classpath b/libsuperuser/.classpath
new file mode 100644
index 0000000..a662f00
--- /dev/null
+++ b/libsuperuser/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/libsuperuser/.project b/libsuperuser/.project
new file mode 100644
index 0000000..c7bc2fd
--- /dev/null
+++ b/libsuperuser/.project
@@ -0,0 +1,33 @@
+
+
+ libsuperuser
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/libsuperuser/AndroidManifest.xml b/libsuperuser/AndroidManifest.xml
new file mode 100644
index 0000000..a829063
--- /dev/null
+++ b/libsuperuser/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/libsuperuser/proguard-project.txt b/libsuperuser/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/libsuperuser/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libsuperuser/project.properties b/libsuperuser/project.properties
new file mode 100644
index 0000000..b5ebc37
--- /dev/null
+++ b/libsuperuser/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-4
+android.library=true
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/Application.java b/libsuperuser/src/eu/chainfire/libsuperuser/Application.java
new file mode 100644
index 0000000..07feb48
--- /dev/null
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/Application.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Jorrit "Chainfire" Jongma
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package eu.chainfire.libsuperuser;
+
+import android.content.Context;
+import android.os.Handler;
+import android.widget.Toast;
+
+/**
+ * Base application class to extend from, solving some issues with
+ * toasts and AsyncTasks you are likely to run into
+ */
+public class Application extends android.app.Application {
+ /**
+ * Shows a toast message
+ *
+ * @param context Any context belonging to this application
+ * @param message The message to show
+ */
+ public static void toast(Context context, String message) {
+ // this is a static method so it is easier to call,
+ // as the context checking and casting is done for you
+
+ if (context == null) return;
+
+ if (!(context instanceof Application)) {
+ context = context.getApplicationContext();
+ }
+
+ if (context instanceof Application) {
+ final Context c = context;
+ final String m = message;
+
+ ((Application)context).runInApplicationThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(c, m, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+ }
+
+ private static Handler mApplicationHandler = new Handler();
+
+ /**
+ * Run a runnable in the main application thread
+ *
+ * @param r Runnable to run
+ */
+ public void runInApplicationThread(Runnable r) {
+ mApplicationHandler.post(r);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ try {
+ // workaround bug in AsyncTask, can show up (for example) when you toast from a service
+ // this makes sure AsyncTask's internal handler is created from the right (main) thread
+ Class.forName("android.os.AsyncTask");
+ } catch (ClassNotFoundException e) {
+ }
+ }
+}
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/Debug.java b/libsuperuser/src/eu/chainfire/libsuperuser/Debug.java
new file mode 100644
index 0000000..eb6ceae
--- /dev/null
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/Debug.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 Jorrit "Chainfire" Jongma
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package eu.chainfire.libsuperuser;
+
+import android.util.Log;
+
+/**
+ * Utility class that intentionally does nothing when not in debug mode
+ */
+public class Debug {
+ /**
+ * Log a message if we are in debug mode
+ * @param message The message to log
+ */
+ public static void log(String message) {
+ if (BuildConfig.DEBUG) {
+ Log.d("libsuperuser", "[libsuperuser]" + (!message.startsWith("[") && !message.startsWith(" ") ? " " : "") + message);
+ }
+ }
+}
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java b/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java
new file mode 100644
index 0000000..e3c5571
--- /dev/null
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012 Jorrit "Chainfire" Jongma
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package eu.chainfire.libsuperuser;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.os.Looper;
+
+/**
+ * Class providing functionality to execute commands in a (root) shell
+ */
+public class Shell {
+ /**
+ * Runs commands using the supplied shell, and returns the output, or null in
+ * case of errors.
+ *
+ * Note that due to compatibility with older Android versions,
+ * wantSTDERR is not implemented using redirectErrorStream, but rather appended
+ * to the output. STDOUT and STDERR are thus not guaranteed to be in the correct
+ * order in the output.
+ *
+ * Note as well that this code will intentionally crash when run in debug mode
+ * from the main thread of the application. You should always execute shell
+ * commands from a background thread.
+ *
+ * When in debug mode, the code will also excessively log the commands passed to
+ * and the output returned from the shell.
+ *
+ * @param shell The shell to use for executing the commands
+ * @param commands The commands to execute
+ * @param wantSTDERR Return STDERR in the output ?
+ * @return Output of the commands, or null in case of an error
+ */
+ public static List run(String shell, String[] commands, boolean wantSTDERR) {
+ if (BuildConfig.DEBUG) {
+ // check if we're running in the main thread, and if so, crash if we're in debug mode,
+ // to let the developer know attention is needed here.
+
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ Debug.log("Application attempted to run a shell command from the main thread");
+ throw new ShellOnMainThreadException();
+ }
+
+ Debug.log(String.format("[%s%%] START", shell.toUpperCase()));
+ }
+
+ List res = new ArrayList();
+
+ try {
+ Process process = Runtime.getRuntime().exec(shell);
+ DataOutputStream STDIN = new DataOutputStream(process.getOutputStream());
+ BufferedReader STDOUT = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ BufferedReader STDERR = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+ for (String write : commands) {
+ if (BuildConfig.DEBUG) Debug.log(String.format("[%s+] %s", shell.toUpperCase(), write));
+ STDIN.writeBytes(write + "\n");
+ STDIN.flush();
+ }
+ STDIN.writeBytes("exit\n");
+ STDIN.flush();
+
+ process.waitFor();
+ if (process.exitValue() == 255) {
+ // in case of su, probably denied
+ return null;
+ }
+
+ while (STDOUT.ready()) {
+ String read = STDOUT.readLine();
+ if (BuildConfig.DEBUG) Debug.log(String.format("[%s-] %s", shell.toUpperCase(), read));
+ res.add(read);
+ }
+ while (STDERR.ready()) {
+ String read = STDERR.readLine();
+ if (BuildConfig.DEBUG) Debug.log(String.format("[%s*] %s", shell.toUpperCase(), read));
+ if (wantSTDERR) res.add(read);
+ }
+
+ process.destroy();
+ } catch (IOException e) {
+ // shell probably not found
+ return null;
+ } catch (InterruptedException e) {
+ // this should really be re-thrown
+ return null;
+ }
+
+ if (BuildConfig.DEBUG) Debug.log(String.format("[%s%%] END", shell.toUpperCase()));
+ return res;
+ }
+
+ /**
+ * This class provides utility functions to easily execute commands using SH
+ */
+ public static class SH {
+ /**
+ * Runs command and return output
+ *
+ * @param command The command to run
+ * @return Output of the command, or null in case of an error
+ */
+ public static List run(String command) {
+ return Shell.run("sh", new String[] { command }, false);
+ }
+
+ /**
+ * Runs commands and return output
+ *
+ * @param commands The commands to run
+ * @return Output of the commands, or null in case of an error
+ */
+ public static List run(List commands) {
+ return Shell.run("sh", commands.toArray(new String[commands.size()]), false);
+ }
+
+ /**
+ * Runs command and return output
+ *
+ * @param commands The commands to run
+ * @return Output of the commands, or null in case of an error
+ */
+ public static List run(String[] commands) {
+ return Shell.run("sh", commands, false);
+ }
+ }
+
+ /**
+ * This class provides utility functions to easily execute commands using SU
+ * (root shell), as well as detecting whether or not root is available, and
+ * if so which version.
+ */
+ public static class SU {
+ /**
+ * Runs command as root (if available) and return output
+ *
+ * @param command The command to run
+ * @return Output of the command, or null if root isn't available or in case of an error
+ */
+ public static List run(String command) {
+ return Shell.run("su", new String[] { command }, false);
+ }
+
+ /**
+ * Runs commands as root (if available) and return output
+ *
+ * @param command The commands to run
+ * @return Output of the commands, or null if root isn't available or in case of an error
+ */
+ public static List run(List commands) {
+ return Shell.run("su", commands.toArray(new String[commands.size()]), false);
+ }
+
+ /**
+ * Runs commands as root (if available) and return output
+ *
+ * @param command The commands to run
+ * @return Output of the commands, or null if root isn't available or in case of an error
+ */
+ public static List run(String[] commands) {
+ return Shell.run("su", commands, false);
+ }
+
+ /**
+ * Detects whether or not superuser access is available, by checking the output
+ * of the "id" command if available, checking if a shell runs at all otherwise
+ *
+ * @return True if superuser access available
+ */
+ public static boolean available() {
+ // this is only one of many ways this can be done
+
+ List ret = run(new String[] {
+ "id",
+ "echo -EOC-"
+ });
+ if (ret == null) return false;
+
+ for (String line : ret) {
+ if (line.contains("uid=")) {
+ // id command is working, let's see if we are actually root
+ return line.contains("uid=0");
+ } else if (line.contains("-EOC-")) {
+ // if we end up here, the id command isn't present, but at
+ // least the su commands starts some kind of shell, let's
+ // hope it has root priviliges - no way to know without
+ // additional native binaries
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 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.
+ *
+ * @param internal Request human-readable version or application internal version
+ * @return String containing the su version or null
+ */
+ public static String version(boolean internal) {
+ // we add an additional exit call, because the command
+ // line options are not available in all su versions,
+ // thus potentially launching a shell instead
+
+ List ret = Shell.run("sh", new String[] {
+ internal ? "su -V" : "su -v",
+ "exit"
+ }, false);
+ if (ret == null) return null;
+
+ for (String line : ret) {
+ if (!internal) {
+ if (line.contains(".")) return line;
+ } else {
+ try {
+ if (Integer.parseInt(line) > 0) return line;
+ } catch(NumberFormatException e) {
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/ShellOnMainThreadException.java b/libsuperuser/src/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
new file mode 100644
index 0000000..eda8d1d
--- /dev/null
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
@@ -0,0 +1,12 @@
+package eu.chainfire.libsuperuser;
+
+/**
+ * Exception class used to crash application when shell commands are executed
+ * from the main thread, and we are in debug mode.
+ */
+@SuppressWarnings("serial")
+public class ShellOnMainThreadException extends RuntimeException {
+ public ShellOnMainThreadException() {
+ super("Application attempted to run a shell command from the main thread");
+ }
+}