From d3b07aa263bcf081d5447446fcb0affc76560256 Mon Sep 17 00:00:00 2001
From: Kevin Cernekee
Date: Sat, 7 Dec 2013 15:57:26 -0800
Subject: [PATCH] Run dos2unix on *.java
Currently these source files have a mix of CRLF and LF (even inside the
same file), which causes problems with patching/merging. Pick one format
for consistency's sake.
---
.../chainfire/libsuperuser/Application.java | 158 +-
.../src/eu/chainfire/libsuperuser/Debug.java | 432 ++--
.../src/eu/chainfire/libsuperuser/Shell.java | 2036 ++++++++---------
.../libsuperuser/ShellNotClosedException.java | 58 +-
.../ShellOnMainThreadException.java | 64 +-
.../chainfire/libsuperuser/StreamGobbler.java | 204 +-
.../BackgroundIntentService.java | 202 +-
.../BootCompleteReceiver.java | 86 +-
8 files changed, 1620 insertions(+), 1620 deletions(-)
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/Application.java b/libsuperuser/src/eu/chainfire/libsuperuser/Application.java
index 128f2d0..454740a 100644
--- a/libsuperuser/src/eu/chainfire/libsuperuser/Application.java
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/Application.java
@@ -1,79 +1,79 @@
-/*
- * Copyright (C) 2012-2013 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) {
- }
- }
-}
+/*
+ * Copyright (C) 2012-2013 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
index 6ae704e..2d059e3 100644
--- a/libsuperuser/src/eu/chainfire/libsuperuser/Debug.java
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/Debug.java
@@ -1,102 +1,102 @@
-/*
- * Copyright (C) 2012-2013 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.os.Looper;
-import android.util.Log;
-
-/**
- * Utility class for logging and debug features that (by default) does nothing when not in debug mode
- */
-public class Debug {
-
- // ----- DEBUGGING -----
-
- private static boolean debug = BuildConfig.DEBUG;
-
- /**
- * Enable or disable debug mode
- *
- * By default, debug mode is enabled for development
- * builds and disabled for exported APKs - see
- * BuildConfig.DEBUG
- *
- * @param enabled Enable debug mode ?
- */
- public static void setDebug(boolean enable) {
- debug = enable;
- }
-
- /**
- * Is debug mode enabled ?
- *
- * @return Debug mode enabled
- */
- public static boolean getDebug() {
- return debug;
- }
-
- // ----- LOGGING -----
-
- public interface OnLogListener {
- public void onLog(int type, String typeIndicator, String message);
- }
-
- public static final String TAG = "libsuperuser";
-
- public static final int LOG_GENERAL = 0x0001;
- public static final int LOG_COMMAND = 0x0002;
- public static final int LOG_OUTPUT = 0x0004;
-
- public static final int LOG_NONE = 0x0000;
- public static final int LOG_ALL = 0xFFFF;
-
- private static int logTypes = LOG_ALL;
-
- private static OnLogListener logListener = null;
-
- /**
- * Log a message (internal)
- *
- * Current debug and enabled logtypes decide what gets logged -
- * even if a custom callback is registered
- *
- * @param type Type of message to log
- * @param typeIndicator String indicator for message type
- * @param message The message to log
- */
- private static void logCommon(int type, String typeIndicator, String message) {
- if (debug && ((logTypes & type) == type)) {
- if (logListener != null) {
- logListener.onLog(type, typeIndicator, message);
- } else {
- Log.d(TAG, "[" + TAG + "][" + typeIndicator + "]" + (!message.startsWith("[") && !message.startsWith(" ") ? " " : "") + message);
- }
- }
- }
-
- /**
+/*
+ * Copyright (C) 2012-2013 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.os.Looper;
+import android.util.Log;
+
+/**
+ * Utility class for logging and debug features that (by default) does nothing when not in debug mode
+ */
+public class Debug {
+
+ // ----- DEBUGGING -----
+
+ private static boolean debug = BuildConfig.DEBUG;
+
+ /**
+ * Enable or disable debug mode
+ *
+ * By default, debug mode is enabled for development
+ * builds and disabled for exported APKs - see
+ * BuildConfig.DEBUG
+ *
+ * @param enabled Enable debug mode ?
+ */
+ public static void setDebug(boolean enable) {
+ debug = enable;
+ }
+
+ /**
+ * Is debug mode enabled ?
+ *
+ * @return Debug mode enabled
+ */
+ public static boolean getDebug() {
+ return debug;
+ }
+
+ // ----- LOGGING -----
+
+ public interface OnLogListener {
+ public void onLog(int type, String typeIndicator, String message);
+ }
+
+ public static final String TAG = "libsuperuser";
+
+ public static final int LOG_GENERAL = 0x0001;
+ public static final int LOG_COMMAND = 0x0002;
+ public static final int LOG_OUTPUT = 0x0004;
+
+ public static final int LOG_NONE = 0x0000;
+ public static final int LOG_ALL = 0xFFFF;
+
+ private static int logTypes = LOG_ALL;
+
+ private static OnLogListener logListener = null;
+
+ /**
+ * Log a message (internal)
+ *
+ * Current debug and enabled logtypes decide what gets logged -
+ * even if a custom callback is registered
+ *
+ * @param type Type of message to log
+ * @param typeIndicator String indicator for message type
+ * @param message The message to log
+ */
+ private static void logCommon(int type, String typeIndicator, String message) {
+ if (debug && ((logTypes & type) == type)) {
+ if (logListener != null) {
+ logListener.onLog(type, typeIndicator, message);
+ } else {
+ Log.d(TAG, "[" + TAG + "][" + typeIndicator + "]" + (!message.startsWith("[") && !message.startsWith(" ") ? " " : "") + message);
+ }
+ }
+ }
+
+ /**
* Log a "general" message
*
* These messages are infrequent and mostly occur at startup/shutdown or on error
- *
- * @param message The message to log
- */
- public static void log(String message) {
+ *
+ * @param message The message to log
+ */
+ public static void log(String message) {
logCommon(LOG_GENERAL, "G", message);
}
@@ -107,7 +107,7 @@ public static void log(String message) {
*
* @param message The message to log
*/
- public static void logCommand(String message) {
+ public static void logCommand(String message) {
logCommon(LOG_COMMAND, "C", message);
}
@@ -118,123 +118,123 @@ public static void logCommand(String message) {
*
* @param message The message to log
*/
- public static void logOutput(String message) {
+ public static void logOutput(String message) {
logCommon(LOG_OUTPUT, "O", message);
- }
-
- /**
- * Enable or disable logging specific types of message
- *
- * You may | (or) LOG_* constants together. Note that
- * debug mode must also be enabled for actual logging to
- * occur.
- *
- * @param type LOG_* constants
- * @param enabled Enable or disable
- */
- public static void setLogTypeEnabled(int type, boolean enable) {
- if (enable) {
- logTypes |= type;
- } else {
- logTypes &= ~type;
- }
- }
-
- /**
- * Is logging for specific types of messages enabled ?
- *
- * You may | (or) LOG_* constants together, to learn if
- * all passed message types are enabled for logging. Note
- * that debug mode must also be enabled for actual logging
- * to occur.
- *
- * @param type LOG_* constants
- */
- public static boolean getLogTypeEnabled(int type) {
- return ((logTypes & type) == type);
- }
-
- /**
- * Is logging for specific types of messages enabled ?
- *
- * You may | (or) LOG_* constants together, to learn if
- * all message types are enabled for logging. Takes
- * debug mode into account for the result.
- *
- * @param type LOG_* constants
- */
- public static boolean getLogTypeEnabledEffective(int type) {
- return getDebug() && getLogTypeEnabled(type);
- }
-
- /**
- * Register a custom log handler
- *
- * Replaces the log method (write to logcat) with your own
- * handler. Whether your handler gets called is still dependent
- * on debug mode and message types being enabled for logging.
- *
- * @param onLogListener Custom log listener or NULL to revert to default
- */
- public static void setOnLogListener(OnLogListener onLogListener) {
- logListener = onLogListener;
- }
-
- /**
- * Get the currently registered custom log handler
- *
- * @return Current custom log handler or NULL if none is present
- */
- public static OnLogListener getOnLogListener() {
- return logListener;
- }
-
- // ----- SANITY CHECKS -----
-
- private static boolean sanityChecks = true;
-
- /**
- * Enable or disable sanity checks
- *
- * Enables or disables the library crashing when su is called
- * from the main thread.
- *
- * @param enabled Enable or disable
- */
- public static void setSanityChecksEnabled(boolean enable) {
- sanityChecks = enable;
- }
-
- /**
- * Are sanity checks enabled ?
- *
- * Note that debug mode must also be enabled for actual
- * sanity checks to occur.
- *
- * @return True if enabled
- */
- public static boolean getSanityChecksEnabled() {
- return sanityChecks;
- }
-
- /**
- * Are sanity checks enabled ?
- *
- * Takes debug mode into account for the result.
- *
- * @return True if enabled
- */
- public static boolean getSanityChecksEnabledEffective() {
- return getDebug() && getSanityChecksEnabled();
- }
-
- /**
- * Are we running on the main thread ?
- *
- * @return Running on main thread ?
- */
- public static boolean onMainThread() {
- return ((Looper.myLooper() != null) && (Looper.myLooper() == Looper.getMainLooper()));
- }
-
-}
+ }
+
+ /**
+ * Enable or disable logging specific types of message
+ *
+ * You may | (or) LOG_* constants together. Note that
+ * debug mode must also be enabled for actual logging to
+ * occur.
+ *
+ * @param type LOG_* constants
+ * @param enabled Enable or disable
+ */
+ public static void setLogTypeEnabled(int type, boolean enable) {
+ if (enable) {
+ logTypes |= type;
+ } else {
+ logTypes &= ~type;
+ }
+ }
+
+ /**
+ * Is logging for specific types of messages enabled ?
+ *
+ * You may | (or) LOG_* constants together, to learn if
+ * all passed message types are enabled for logging. Note
+ * that debug mode must also be enabled for actual logging
+ * to occur.
+ *
+ * @param type LOG_* constants
+ */
+ public static boolean getLogTypeEnabled(int type) {
+ return ((logTypes & type) == type);
+ }
+
+ /**
+ * Is logging for specific types of messages enabled ?
+ *
+ * You may | (or) LOG_* constants together, to learn if
+ * all message types are enabled for logging. Takes
+ * debug mode into account for the result.
+ *
+ * @param type LOG_* constants
+ */
+ public static boolean getLogTypeEnabledEffective(int type) {
+ return getDebug() && getLogTypeEnabled(type);
+ }
+
+ /**
+ * Register a custom log handler
+ *
+ * Replaces the log method (write to logcat) with your own
+ * handler. Whether your handler gets called is still dependent
+ * on debug mode and message types being enabled for logging.
+ *
+ * @param onLogListener Custom log listener or NULL to revert to default
+ */
+ public static void setOnLogListener(OnLogListener onLogListener) {
+ logListener = onLogListener;
+ }
+
+ /**
+ * Get the currently registered custom log handler
+ *
+ * @return Current custom log handler or NULL if none is present
+ */
+ public static OnLogListener getOnLogListener() {
+ return logListener;
+ }
+
+ // ----- SANITY CHECKS -----
+
+ private static boolean sanityChecks = true;
+
+ /**
+ * Enable or disable sanity checks
+ *
+ * Enables or disables the library crashing when su is called
+ * from the main thread.
+ *
+ * @param enabled Enable or disable
+ */
+ public static void setSanityChecksEnabled(boolean enable) {
+ sanityChecks = enable;
+ }
+
+ /**
+ * Are sanity checks enabled ?
+ *
+ * Note that debug mode must also be enabled for actual
+ * sanity checks to occur.
+ *
+ * @return True if enabled
+ */
+ public static boolean getSanityChecksEnabled() {
+ return sanityChecks;
+ }
+
+ /**
+ * Are sanity checks enabled ?
+ *
+ * Takes debug mode into account for the result.
+ *
+ * @return True if enabled
+ */
+ public static boolean getSanityChecksEnabledEffective() {
+ return getDebug() && getSanityChecksEnabled();
+ }
+
+ /**
+ * Are we running on the main thread ?
+ *
+ * @return Running on main thread ?
+ */
+ public static boolean onMainThread() {
+ return ((Looper.myLooper() != null) && (Looper.myLooper() == Looper.getMainLooper()));
+ }
+
+}
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java b/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java
index 264af69..37baac3 100644
--- a/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java
@@ -1,168 +1,168 @@
-/*
- * Copyright (C) 2012-2013 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.DataOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
+/*
+ * Copyright (C) 2012-2013 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.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
-import java.util.Map;
-import java.util.UUID;
+import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import eu.chainfire.libsuperuser.StreamGobbler.OnLineListener;
-
-/**
- * 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.
- *
- * This method is deprecated and only provided for backwards compatibility.
- * Use {@link #run(String, String[], String[], boolean)} instead, and see that
- * same method for usage notes.
- *
- * @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
- */
- @Deprecated
- public static List run(String shell, String[] commands, boolean wantSTDERR) {
- return run(shell, commands, null, wantSTDERR);
- }
-
- /**
- * 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.
- *
- * Though this function uses background threads to gobble STDOUT and STDERR so
- * a deadlock does not occur if the shell produces massive output, the output is
- * still stored in a List<String>, and as such doing something like 'ls -lR /'
- * will probably have you run out of memory.
- *
- * @param shell The shell to use for executing the commands
- * @param commands The commands to execute
- * @param environment List of all environment variables (in 'key=value' format) or null for defaults
- * @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, String[] environment, boolean wantSTDERR) {
+
+import android.os.Handler;
+import android.os.Looper;
+
+import eu.chainfire.libsuperuser.StreamGobbler.OnLineListener;
+
+/**
+ * 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.
+ *
+ * This method is deprecated and only provided for backwards compatibility.
+ * Use {@link #run(String, String[], String[], boolean)} instead, and see that
+ * same method for usage notes.
+ *
+ * @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
+ */
+ @Deprecated
+ public static List run(String shell, String[] commands, boolean wantSTDERR) {
+ return run(shell, commands, null, wantSTDERR);
+ }
+
+ /**
+ * 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.
+ *
+ * Though this function uses background threads to gobble STDOUT and STDERR so
+ * a deadlock does not occur if the shell produces massive output, the output is
+ * still stored in a List<String>, and as such doing something like 'ls -lR /'
+ * will probably have you run out of memory.
+ *
+ * @param shell The shell to use for executing the commands
+ * @param commands The commands to execute
+ * @param environment List of all environment variables (in 'key=value' format) or null for defaults
+ * @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, String[] environment, boolean wantSTDERR) {
String shellUpper = shell.toUpperCase(Locale.ENGLISH);
-
- if (Debug.getSanityChecksEnabledEffective() && Debug.onMainThread()) {
- // 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.
-
- Debug.log(ShellOnMainThreadException.EXCEPTION_COMMAND);
- throw new ShellOnMainThreadException(ShellOnMainThreadException.EXCEPTION_COMMAND);
- }
+
+ if (Debug.getSanityChecksEnabledEffective() && Debug.onMainThread()) {
+ // 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.
+
+ Debug.log(ShellOnMainThreadException.EXCEPTION_COMMAND);
+ throw new ShellOnMainThreadException(ShellOnMainThreadException.EXCEPTION_COMMAND);
+ }
Debug.logCommand(String.format("[%s%%] START", shellUpper));
-
- List res = Collections.synchronizedList(new ArrayList());
-
- try {
- // Combine passed environment with system environment
- if (environment != null) {
- Map newEnvironment = new HashMap();
- newEnvironment.putAll(System.getenv());
- int split;
- for (String entry : environment) {
- if ((split = entry.indexOf("=")) >= 0) {
- newEnvironment.put(entry.substring(0, split), entry.substring(split + 1));
- }
- }
- int i = 0;
- environment = new String[newEnvironment.size()];
- for (Map.Entry entry : newEnvironment.entrySet()) {
- environment[i] = entry.getKey() + "=" + entry.getValue();
- i++;
- }
- }
-
- // setup our process, retrieve STDIN stream, and STDOUT/STDERR gobblers
- Process process = Runtime.getRuntime().exec(shell, environment);
- DataOutputStream STDIN = new DataOutputStream(process.getOutputStream());
- StreamGobbler STDOUT = new StreamGobbler(shellUpper + "-", process.getInputStream(), res);
- StreamGobbler STDERR = new StreamGobbler(shellUpper + "*", process.getErrorStream(), wantSTDERR ? res : null);
-
- // start gobbling and write our commands to the shell
- STDOUT.start();
- STDERR.start();
- for (String write : commands) {
+
+ List res = Collections.synchronizedList(new ArrayList());
+
+ try {
+ // Combine passed environment with system environment
+ if (environment != null) {
+ Map newEnvironment = new HashMap();
+ newEnvironment.putAll(System.getenv());
+ int split;
+ for (String entry : environment) {
+ if ((split = entry.indexOf("=")) >= 0) {
+ newEnvironment.put(entry.substring(0, split), entry.substring(split + 1));
+ }
+ }
+ int i = 0;
+ environment = new String[newEnvironment.size()];
+ for (Map.Entry entry : newEnvironment.entrySet()) {
+ environment[i] = entry.getKey() + "=" + entry.getValue();
+ i++;
+ }
+ }
+
+ // setup our process, retrieve STDIN stream, and STDOUT/STDERR gobblers
+ Process process = Runtime.getRuntime().exec(shell, environment);
+ DataOutputStream STDIN = new DataOutputStream(process.getOutputStream());
+ StreamGobbler STDOUT = new StreamGobbler(shellUpper + "-", process.getInputStream(), res);
+ StreamGobbler STDERR = new StreamGobbler(shellUpper + "*", process.getErrorStream(), wantSTDERR ? res : null);
+
+ // start gobbling and write our commands to the shell
+ STDOUT.start();
+ STDERR.start();
+ for (String write : commands) {
Debug.logCommand(String.format("[%s+] %s", shellUpper, write));
- STDIN.write((write + "\n").getBytes("UTF-8"));
- STDIN.flush();
- }
- STDIN.write("exit\n".getBytes("UTF-8"));
- STDIN.flush();
-
- // wait for our process to finish, while we gobble away in the background
- process.waitFor();
-
- // make sure our threads are done gobbling, our streams are closed, and the process is
- // destroyed - while the latter two shouldn't be needed in theory, and may even produce
- // warnings, in "normal" Java they are required for guaranteed cleanup of resources, so
- // lets be safe and do this on Android as well
- try {
- STDIN.close();
- } catch (IOException e) {
- }
- STDOUT.join();
- STDERR.join();
- process.destroy();
-
- // in case of su, 255 usually indicates access denied
- if (shell.equals("su") && (process.exitValue() == 255)) {
- res = null;
- }
- } catch (IOException e) {
- // shell probably not found
- res = null;
- } catch (InterruptedException e) {
- // this should really be re-thrown
- res = null;
- }
-
+ STDIN.write((write + "\n").getBytes("UTF-8"));
+ STDIN.flush();
+ }
+ STDIN.write("exit\n".getBytes("UTF-8"));
+ STDIN.flush();
+
+ // wait for our process to finish, while we gobble away in the background
+ process.waitFor();
+
+ // make sure our threads are done gobbling, our streams are closed, and the process is
+ // destroyed - while the latter two shouldn't be needed in theory, and may even produce
+ // warnings, in "normal" Java they are required for guaranteed cleanup of resources, so
+ // lets be safe and do this on Android as well
+ try {
+ STDIN.close();
+ } catch (IOException e) {
+ }
+ STDOUT.join();
+ STDERR.join();
+ process.destroy();
+
+ // in case of su, 255 usually indicates access denied
+ if (shell.equals("su") && (process.exitValue() == 255)) {
+ res = null;
+ }
+ } catch (IOException e) {
+ // shell probably not found
+ res = null;
+ } catch (InterruptedException e) {
+ // this should really be re-thrown
+ res = null;
+ }
+
Debug.logCommand(String.format("[%s%%] END", shell.toUpperCase(Locale.ENGLISH)));
- return res;
- }
+ return res;
+ }
protected static String[] availableTestCommands = new String[] {
"echo -BOC-",
@@ -197,146 +197,146 @@ protected static boolean parseAvailableResult(List ret, boolean checkFor
return echo_seen;
}
- /**
- * 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 }, null, 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()]), null, 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(String[] commands) {
- return Shell.run("sh", commands, null, 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 }, null, false);
- }
-
- /**
- * Runs commands as root (if available) and return output
- *
- * @param commands 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()]), null, false);
- }
-
- /**
- * Runs commands as root (if available) and return output
- *
- * @param commands 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, null, 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
-
+ /**
+ * 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 }, null, 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()]), null, 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(String[] commands) {
+ return Shell.run("sh", commands, null, 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 }, null, false);
+ }
+
+ /**
+ * Runs commands as root (if available) and return output
+ *
+ * @param commands 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()]), null, false);
+ }
+
+ /**
+ * Runs commands as root (if available) and return output
+ *
+ * @param commands 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, null, 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(Shell.availableTestCommands);
return Shell.parseAvailableResult(ret, true);
- }
-
- /**
- * 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"
- }, null, 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;
- }
- }
-
- /**
- * Command result callback, notifies the recipient of the completion of a command
- * block, including the (last) exit code, and the full output
- */
- public interface OnCommandResultListener {
- /**
- * Command result callback
- *
- * Depending on how and on which thread the shell was created, this callback
- * may be executed on one of the gobbler threads. In that case, it is important
- * the callback returns as quickly as possible, as delays in this callback may
- * pause the native process or even result in a deadlock
- *
- * See {@link Shell.Interactive} for threading details
- *
- * @param commandCode Value previously supplied to addCommand
- * @param exitCode Exit code of the last command in the block
- * @param output All output generated by the command block
- */
- public void onCommandResult(int commandCode, int exitCode, List output);
+ }
+
+ /**
+ * 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"
+ }, null, 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;
+ }
+ }
+
+ /**
+ * Command result callback, notifies the recipient of the completion of a command
+ * block, including the (last) exit code, and the full output
+ */
+ public interface OnCommandResultListener {
+ /**
+ * Command result callback
+ *
+ * Depending on how and on which thread the shell was created, this callback
+ * may be executed on one of the gobbler threads. In that case, it is important
+ * the callback returns as quickly as possible, as delays in this callback may
+ * pause the native process or even result in a deadlock
+ *
+ * See {@link Shell.Interactive} for threading details
+ *
+ * @param commandCode Value previously supplied to addCommand
+ * @param exitCode Exit code of the last command in the block
+ * @param output All output generated by the command block
+ */
+ public void onCommandResult(int commandCode, int exitCode, List output);
// for any onCommandResult callback
public static final int WATCHDOG_EXIT = -1;
@@ -346,190 +346,190 @@ public interface OnCommandResultListener {
public static final int SHELL_EXEC_FAILED = -3;
public static final int SHELL_WRONG_UID = -4;
public static final int SHELL_RUNNING = 0;
- }
-
- /**
+ }
+
+ /**
* Internal class to store command block properties
- */
- private static class Command {
- private static int commandCounter = 0;
-
- private final String[] commands;
- private final int code;
- private final OnCommandResultListener onCommandResultListener;
- private final String marker;
-
- public Command(String[] commands, int code, OnCommandResultListener onCommandResultListener) {
- this.commands = commands;
- this.code = code;
- this.onCommandResultListener = onCommandResultListener;
- this.marker = UUID.randomUUID().toString() + String.format("-%08x", ++commandCounter);
- }
- }
-
- /**
- * Builder class for {@link Shell.Interactive}
- */
- public static class Builder {
- private Handler handler = null;
- private boolean autoHandler = true;
- private String shell = "sh";
- private boolean wantSTDERR = false;
- private List commands = new LinkedList();
- private Map environment = new HashMap();
- private OnLineListener onSTDOUTLineListener = null;
- private OnLineListener onSTDERRLineListener = null;
+ */
+ private static class Command {
+ private static int commandCounter = 0;
+
+ private final String[] commands;
+ private final int code;
+ private final OnCommandResultListener onCommandResultListener;
+ private final String marker;
+
+ public Command(String[] commands, int code, OnCommandResultListener onCommandResultListener) {
+ this.commands = commands;
+ this.code = code;
+ this.onCommandResultListener = onCommandResultListener;
+ this.marker = UUID.randomUUID().toString() + String.format("-%08x", ++commandCounter);
+ }
+ }
+
+ /**
+ * Builder class for {@link Shell.Interactive}
+ */
+ public static class Builder {
+ private Handler handler = null;
+ private boolean autoHandler = true;
+ private String shell = "sh";
+ private boolean wantSTDERR = false;
+ private List commands = new LinkedList();
+ private Map environment = new HashMap();
+ private OnLineListener onSTDOUTLineListener = null;
+ private OnLineListener onSTDERRLineListener = null;
private int watchdogTimeout = 0;
-
- /**
- * Set a custom handler that will be used to post all callbacks to
- *
- * See {@link Shell.Interactive} for further details on threading and handlers
- *
- * @param handler Handler to use
- * @return This Builder object for method chaining
- */
- public Builder setHandler(Handler handler) { this.handler = handler; return this; }
-
- /**
- * Automatically create a handler if possible ? Default to true
- *
- * See {@link Shell.Interactive} for further details on threading and handlers
- *
- * @param autoHandler Auto-create handler ?
- * @return This Builder object for method chaining
- */
- public Builder setAutoHandler(boolean autoHandler) { this.autoHandler = autoHandler; return this; }
-
- /**
- * Set shell binary to use. Usually "sh" or "su", do not use a full path
- * unless you have a good reason to
- *
- * @param shell Shell to use
- * @return This Builder object for method chaining
- */
- public Builder setShell(String shell) { this.shell = shell; return this; }
-
- /**
- * Convenience function to set "sh" as used shell
- *
- * @return This Builder object for method chaining
- */
- public Builder useSH() { return setShell("sh"); }
-
- /**
- * Convenience function to set "su" as used shell
- *
- * @return This Builder object for method chaining
- */
- public Builder useSU() { return setShell("su"); }
-
- /**
- * Set if error output should be appended to command block result output
- *
- * @param wantSTDERR Want error output ?
- * @return This Builder object for method chaining
- */
- public Builder setWantSTDERR(boolean wantSTDERR) { this.wantSTDERR = wantSTDERR; return this; }
-
- /**
- * Add or update an environment variable
- *
- * @param key Key of the environment variable
- * @param value Value of the environment variable
- * @return This Builder object for method chaining
- */
- public Builder addEnvironment(String key, String value) { environment.put(key, value); return this; }
-
- /**
- * Add or update environment variables
- *
- * @param addEnvironment Map of environment variables
- * @return This Builder object for method chaining
- */
- public Builder addEnvironment(Map addEnvironment) { environment.putAll(addEnvironment); return this; }
-
- /**
- * Add a command to execute
- *
- * @param command Command to execute
- * @return This Builder object for method chaining
- */
- public Builder addCommand(String command) { return addCommand(command, 0, null); }
-
- /**
- * Add a command to execute, with a callback to be called on completion
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param command Command to execute
- * @param code User-defined value passed back to the callback
- * @param onCommandResultListener Callback to be called on completion
- * @return This Builder object for method chaining
- */
- public Builder addCommand(String command, int code, OnCommandResultListener onCommandResultListener) { return addCommand(new String[] { command }, code, onCommandResultListener); }
-
- /**
- * Add commands to execute
- *
- * @param commands Commands to execute
- * @return This Builder object for method chaining
- */
- public Builder addCommand(List commands) { return addCommand(commands, 0, null); }
-
- /**
- * Add commands to execute, with a callback to be called on completion (of all commands)
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param commands Commands to execute
- * @param code User-defined value passed back to the callback
- * @param onCommandResultListener Callback to be called on completion (of all commands)
- * @return This Builder object for method chaining
- */
- public Builder addCommand(List commands, int code, OnCommandResultListener onCommandResultListener) { return addCommand(commands.toArray(new String[commands.size()]), code, onCommandResultListener); }
-
- /**
- * Add commands to execute
- *
- * @param commands Commands to execute
- * @return This Builder object for method chaining
- */
- public Builder addCommand(String[] commands) { return addCommand(commands, 0, null); }
-
- /**
- * Add commands to execute, with a callback to be called on completion (of all commands)
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param commands Commands to execute
- * @param code User-defined value passed back to the callback
- * @param onCommandResultListener Callback to be called on completion (of all commands)
- * @return This Builder object for method chaining
- */
- public Builder addCommand(String[] commands, int code, OnCommandResultListener onCommandResultListener) { this.commands.add(new Command(commands, code, onCommandResultListener)); return this; }
-
- /**
- * Set a callback called for every line output to STDOUT by the shell
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param onLineListener Callback to be called for each line
- * @return This Builder object for method chaining
- */
- public Builder setOnSTDOUTLineListener(OnLineListener onLineListener) { this.onSTDOUTLineListener = onLineListener; return this; }
-
- /**
- * Set a callback called for every line output to STDERR by the shell
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param onLineListener Callback to be called for each line
- * @return This Builder object for method chaining
- */
- public Builder setOnSTDERRLineListener(OnLineListener onLineListener) { this.onSTDERRLineListener = onLineListener; return this; }
-
- /**
+
+ /**
+ * Set a custom handler that will be used to post all callbacks to
+ *
+ * See {@link Shell.Interactive} for further details on threading and handlers
+ *
+ * @param handler Handler to use
+ * @return This Builder object for method chaining
+ */
+ public Builder setHandler(Handler handler) { this.handler = handler; return this; }
+
+ /**
+ * Automatically create a handler if possible ? Default to true
+ *
+ * See {@link Shell.Interactive} for further details on threading and handlers
+ *
+ * @param autoHandler Auto-create handler ?
+ * @return This Builder object for method chaining
+ */
+ public Builder setAutoHandler(boolean autoHandler) { this.autoHandler = autoHandler; return this; }
+
+ /**
+ * Set shell binary to use. Usually "sh" or "su", do not use a full path
+ * unless you have a good reason to
+ *
+ * @param shell Shell to use
+ * @return This Builder object for method chaining
+ */
+ public Builder setShell(String shell) { this.shell = shell; return this; }
+
+ /**
+ * Convenience function to set "sh" as used shell
+ *
+ * @return This Builder object for method chaining
+ */
+ public Builder useSH() { return setShell("sh"); }
+
+ /**
+ * Convenience function to set "su" as used shell
+ *
+ * @return This Builder object for method chaining
+ */
+ public Builder useSU() { return setShell("su"); }
+
+ /**
+ * Set if error output should be appended to command block result output
+ *
+ * @param wantSTDERR Want error output ?
+ * @return This Builder object for method chaining
+ */
+ public Builder setWantSTDERR(boolean wantSTDERR) { this.wantSTDERR = wantSTDERR; return this; }
+
+ /**
+ * Add or update an environment variable
+ *
+ * @param key Key of the environment variable
+ * @param value Value of the environment variable
+ * @return This Builder object for method chaining
+ */
+ public Builder addEnvironment(String key, String value) { environment.put(key, value); return this; }
+
+ /**
+ * Add or update environment variables
+ *
+ * @param addEnvironment Map of environment variables
+ * @return This Builder object for method chaining
+ */
+ public Builder addEnvironment(Map addEnvironment) { environment.putAll(addEnvironment); return this; }
+
+ /**
+ * Add a command to execute
+ *
+ * @param command Command to execute
+ * @return This Builder object for method chaining
+ */
+ public Builder addCommand(String command) { return addCommand(command, 0, null); }
+
+ /**
+ * Add a command to execute, with a callback to be called on completion
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param command Command to execute
+ * @param code User-defined value passed back to the callback
+ * @param onCommandResultListener Callback to be called on completion
+ * @return This Builder object for method chaining
+ */
+ public Builder addCommand(String command, int code, OnCommandResultListener onCommandResultListener) { return addCommand(new String[] { command }, code, onCommandResultListener); }
+
+ /**
+ * Add commands to execute
+ *
+ * @param commands Commands to execute
+ * @return This Builder object for method chaining
+ */
+ public Builder addCommand(List commands) { return addCommand(commands, 0, null); }
+
+ /**
+ * Add commands to execute, with a callback to be called on completion (of all commands)
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param commands Commands to execute
+ * @param code User-defined value passed back to the callback
+ * @param onCommandResultListener Callback to be called on completion (of all commands)
+ * @return This Builder object for method chaining
+ */
+ public Builder addCommand(List commands, int code, OnCommandResultListener onCommandResultListener) { return addCommand(commands.toArray(new String[commands.size()]), code, onCommandResultListener); }
+
+ /**
+ * Add commands to execute
+ *
+ * @param commands Commands to execute
+ * @return This Builder object for method chaining
+ */
+ public Builder addCommand(String[] commands) { return addCommand(commands, 0, null); }
+
+ /**
+ * Add commands to execute, with a callback to be called on completion (of all commands)
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param commands Commands to execute
+ * @param code User-defined value passed back to the callback
+ * @param onCommandResultListener Callback to be called on completion (of all commands)
+ * @return This Builder object for method chaining
+ */
+ public Builder addCommand(String[] commands, int code, OnCommandResultListener onCommandResultListener) { this.commands.add(new Command(commands, code, onCommandResultListener)); return this; }
+
+ /**
+ * Set a callback called for every line output to STDOUT by the shell
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param onLineListener Callback to be called for each line
+ * @return This Builder object for method chaining
+ */
+ public Builder setOnSTDOUTLineListener(OnLineListener onLineListener) { this.onSTDOUTLineListener = onLineListener; return this; }
+
+ /**
+ * Set a callback called for every line output to STDERR by the shell
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param onLineListener Callback to be called for each line
+ * @return This Builder object for method chaining
+ */
+ public Builder setOnSTDERRLineListener(OnLineListener onLineListener) { this.onSTDERRLineListener = onLineListener; return this; }
+
+ /**
* Enable command timeout callback
*
* This will invoke the onCommandResult() callback with exitCode WATCHDOG_EXIT if a command takes longer than watchdogTimeout
@@ -551,14 +551,14 @@ public static class Builder {
* @param useMinimal true for reduced output, false for full output
* @return This Builder object for method chaining
*/
- public Builder setMinimalLogging(boolean useMinimal) {
- Debug.setLogTypeEnabled(Debug.LOG_COMMAND | Debug.LOG_OUTPUT, !useMinimal);
+ public Builder setMinimalLogging(boolean useMinimal) {
+ Debug.setLogTypeEnabled(Debug.LOG_COMMAND | Debug.LOG_OUTPUT, !useMinimal);
return this;
}
/**
- * Construct a {@link Shell.Interactive} instance, and start the shell
- */
+ * Construct a {@link Shell.Interactive} instance, and start the shell
+ */
public Interactive open() { return new Interactive(this, null); }
/**
@@ -570,112 +570,112 @@ public Builder setMinimalLogging(boolean useMinimal) {
public Interactive open(OnCommandResultListener onCommandResultListener) {
return new Interactive(this, onCommandResultListener);
}
- }
-
- /**
- *
An interactive shell - initially created with {@link Shell.Builder} - that
- * executes blocks of commands you supply in the background, optionally calling
- * callbacks as each block completes.
- *
- * STDERR output can be supplied as well, but 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.
- *
+ }
+
+ /**
+ * An interactive shell - initially created with {@link Shell.Builder} - that
+ * executes blocks of commands you supply in the background, optionally calling
+ * callbacks as each block completes.
+ *
+ * STDERR output can be supplied as well, but 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 the close() and waitForIdle() methods will intentionally
* crash when run in debug mode from the main thread of the application. Any blocking
* call should be run 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.
- *
- * Though this function uses background threads to gobble STDOUT and STDERR so
- * a deadlock does not occur if the shell produces massive output, the output is
- * still stored in a List<String>, and as such doing something like 'ls -lR /'
- * will probably have you run out of memory when using a
- * {@link Shell.OnCommandResultListener}. A work-around is to not supply this callback,
- * but using (only) {@link Shell.Builder#setOnSTDOUTLineListener(OnLineListener)}. This
- * way, an internal buffer will not be created and wasting your memory.
- *
- * Callbacks, threads and handlers
- *
- * On which thread the callbacks execute is dependent on your initialization. You can
- * supply a custom Handler using {@link Shell.Builder#setHandler(Handler)} if needed.
- * If you do not supply a custom Handler - unless you set {@link Shell.Builder#setAutoHandler(boolean)}
- * to false - a Handler will be auto-created if the thread used for instantiation
- * of the object has a Looper.
- *
- * If no Handler was supplied and it was also not auto-created, all callbacks will
- * be called from either the STDOUT or STDERR gobbler threads. These are important
- * threads that should be blocked as little as possible, as blocking them may in rare
- * cases pause the native process or even create a deadlock.
- *
+ *
+ * When in debug mode, the code will also excessively log the commands passed to
+ * and the output returned from the shell.
+ *
+ * Though this function uses background threads to gobble STDOUT and STDERR so
+ * a deadlock does not occur if the shell produces massive output, the output is
+ * still stored in a List<String>, and as such doing something like 'ls -lR /'
+ * will probably have you run out of memory when using a
+ * {@link Shell.OnCommandResultListener}. A work-around is to not supply this callback,
+ * but using (only) {@link Shell.Builder#setOnSTDOUTLineListener(OnLineListener)}. This
+ * way, an internal buffer will not be created and wasting your memory.
+ *
+ * Callbacks, threads and handlers
+ *
+ * On which thread the callbacks execute is dependent on your initialization. You can
+ * supply a custom Handler using {@link Shell.Builder#setHandler(Handler)} if needed.
+ * If you do not supply a custom Handler - unless you set {@link Shell.Builder#setAutoHandler(boolean)}
+ * to false - a Handler will be auto-created if the thread used for instantiation
+ * of the object has a Looper.
+ *
+ * If no Handler was supplied and it was also not auto-created, all callbacks will
+ * be called from either the STDOUT or STDERR gobbler threads. These are important
+ * threads that should be blocked as little as possible, as blocking them may in rare
+ * cases pause the native process or even create a deadlock.
+ *
* The main thread must certainly have a Looper, thus if you call {@link Shell.Builder#open()}
- * from the main thread, a handler will (by default) be auto-created, and all the callbacks
- * will be called on the main thread. While this is often convenient and easy to code with,
- * you should be aware that if your callbacks are 'expensive' to execute, this may negatively
- * impact UI performance.
- *
- * Background threads usually do not have a Looper, so calling {@link Shell.Builder#open()}
- * from such a background thread will (by default) result in all the callbacks being executed
- * in one of the gobbler threads. You will have to make sure the code you execute in these callbacks
- * is thread-safe.
- */
- public static class Interactive {
- private final Handler handler;
- private final boolean autoHandler;
- private final String shell;
- private final boolean wantSTDERR;
- private final List commands;
- private final Map environment;
- private final OnLineListener onSTDOUTLineListener;
- private final OnLineListener onSTDERRLineListener;
+ * from the main thread, a handler will (by default) be auto-created, and all the callbacks
+ * will be called on the main thread. While this is often convenient and easy to code with,
+ * you should be aware that if your callbacks are 'expensive' to execute, this may negatively
+ * impact UI performance.
+ *
+ * Background threads usually do not have a Looper, so calling {@link Shell.Builder#open()}
+ * from such a background thread will (by default) result in all the callbacks being executed
+ * in one of the gobbler threads. You will have to make sure the code you execute in these callbacks
+ * is thread-safe.
+ */
+ public static class Interactive {
+ private final Handler handler;
+ private final boolean autoHandler;
+ private final String shell;
+ private final boolean wantSTDERR;
+ private final List commands;
+ private final Map environment;
+ private final OnLineListener onSTDOUTLineListener;
+ private final OnLineListener onSTDERRLineListener;
private int watchdogTimeout;
-
- private Process process = null;
- private DataOutputStream STDIN = null;
- private StreamGobbler STDOUT = null;
- private StreamGobbler STDERR = null;
+
+ private Process process = null;
+ private DataOutputStream STDIN = null;
+ private StreamGobbler STDOUT = null;
+ private StreamGobbler STDERR = null;
private ScheduledThreadPoolExecutor watchdog = null;
-
- private volatile boolean running = false;
- private volatile boolean idle = true; // read/write only synchronized
- private volatile boolean closed = true;
- private volatile int callbacks = 0;
+
+ private volatile boolean running = false;
+ private volatile boolean idle = true; // read/write only synchronized
+ private volatile boolean closed = true;
+ private volatile int callbacks = 0;
private volatile int watchdogCount;
-
- private Object idleSync = new Object();
- private Object callbackSync = new Object();
-
- private volatile int lastExitCode = 0;
- private volatile String lastMarkerSTDOUT = null;
- private volatile String lastMarkerSTDERR = null;
- private volatile Command command = null;
- private volatile List buffer = null;
-
- /**
- * The only way to create an instance: Shell.Builder::open()
- *
- * @param builder Builder class to take values from
- */
+
+ private Object idleSync = new Object();
+ private Object callbackSync = new Object();
+
+ private volatile int lastExitCode = 0;
+ private volatile String lastMarkerSTDOUT = null;
+ private volatile String lastMarkerSTDERR = null;
+ private volatile Command command = null;
+ private volatile List buffer = null;
+
+ /**
+ * The only way to create an instance: Shell.Builder::open()
+ *
+ * @param builder Builder class to take values from
+ */
private Interactive(final Builder builder, final OnCommandResultListener onCommandResultListener) {
- autoHandler = builder.autoHandler;
- shell = builder.shell;
- wantSTDERR = builder.wantSTDERR;
- commands = builder.commands;
- environment = builder.environment;
- onSTDOUTLineListener = builder.onSTDOUTLineListener;
- onSTDERRLineListener = builder.onSTDERRLineListener;
+ autoHandler = builder.autoHandler;
+ shell = builder.shell;
+ wantSTDERR = builder.wantSTDERR;
+ commands = builder.commands;
+ environment = builder.environment;
+ onSTDOUTLineListener = builder.onSTDOUTLineListener;
+ onSTDERRLineListener = builder.onSTDERRLineListener;
watchdogTimeout = builder.watchdogTimeout;
-
- // If a looper is available, we offload the callbacks from the gobbling threads
- // to whichever thread created us. Would normally do this in open(),
- // but then we could not declare handler as final
- if ((Looper.myLooper() != null) && (builder.handler == null) && autoHandler) {
- handler = new Handler();
- } else {
- handler = builder.handler;
- }
+
+ // If a looper is available, we offload the callbacks from the gobbling threads
+ // to whichever thread created us. Would normally do this in open(),
+ // but then we could not declare handler as final
+ if ((Looper.myLooper() != null) && (builder.handler == null) && autoHandler) {
+ handler = new Handler();
+ } else {
+ handler = builder.handler;
+ }
boolean ret = open();
if (onCommandResultListener == null) {
@@ -699,81 +699,81 @@ public void onCommandResult(int commandCode, int exitCode, List output)
onCommandResultListener.onCommandResult(0, exitCode, output);
}
});
- }
-
- @Override
- protected void finalize() throws Throwable {
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
if (!closed && Debug.getSanityChecksEnabledEffective()) {
- // waste of resources
- Debug.log(ShellNotClosedException.EXCEPTION_NOT_CLOSED);
- throw new ShellNotClosedException();
- }
- super.finalize();
- }
-
- /**
- * Add a command to execute
- *
- * @param command Command to execute
- */
- public void addCommand(String command) { addCommand(command, 0, null); }
-
- /**
- * Add a command to execute, with a callback to be called on completion
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param command Command to execute
- * @param code User-defined value passed back to the callback
- * @param onCommandResultListener Callback to be called on completion
- */
- public void addCommand(String command, int code, OnCommandResultListener onCommandResultListener) { addCommand(new String[] { command }, code, onCommandResultListener); }
-
- /**
- * Add commands to execute
- *
- * @param commands Commands to execute
- */
- public void addCommand(List commands) { addCommand(commands, 0, null); }
-
- /**
- * Add commands to execute, with a callback to be called on completion (of all commands)
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param commands Commands to execute
- * @param code User-defined value passed back to the callback
- * @param onCommandResultListener Callback to be called on completion (of all commands)
- */
- public void addCommand(List commands, int code, OnCommandResultListener onCommandResultListener) { addCommand(commands.toArray(new String[commands.size()]), code, onCommandResultListener); }
-
- /**
- * Add commands to execute
- *
- * @param commands Commands to execute
- */
- public void addCommand(String[] commands) { addCommand(commands, 0, null); }
-
- /**
- * Add commands to execute, with a callback to be called on completion (of all commands)
- *
- * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
- *
- * @param commands Commands to execute
- * @param code User-defined value passed back to the callback
- * @param onCommandResultListener Callback to be called on completion (of all commands)
- */
- public synchronized void addCommand(String[] commands, int code, OnCommandResultListener onCommandResultListener) {
- this.commands.add(new Command(commands, code, onCommandResultListener));
- runNextCommand();
- }
-
- /**
- * Run the next command if any and if ready, signals idle state if no commands left
- */
- private void runNextCommand() {
- runNextCommand(true);
- }
+ // waste of resources
+ Debug.log(ShellNotClosedException.EXCEPTION_NOT_CLOSED);
+ throw new ShellNotClosedException();
+ }
+ super.finalize();
+ }
+
+ /**
+ * Add a command to execute
+ *
+ * @param command Command to execute
+ */
+ public void addCommand(String command) { addCommand(command, 0, null); }
+
+ /**
+ * Add a command to execute, with a callback to be called on completion
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param command Command to execute
+ * @param code User-defined value passed back to the callback
+ * @param onCommandResultListener Callback to be called on completion
+ */
+ public void addCommand(String command, int code, OnCommandResultListener onCommandResultListener) { addCommand(new String[] { command }, code, onCommandResultListener); }
+
+ /**
+ * Add commands to execute
+ *
+ * @param commands Commands to execute
+ */
+ public void addCommand(List commands) { addCommand(commands, 0, null); }
+
+ /**
+ * Add commands to execute, with a callback to be called on completion (of all commands)
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param commands Commands to execute
+ * @param code User-defined value passed back to the callback
+ * @param onCommandResultListener Callback to be called on completion (of all commands)
+ */
+ public void addCommand(List commands, int code, OnCommandResultListener onCommandResultListener) { addCommand(commands.toArray(new String[commands.size()]), code, onCommandResultListener); }
+
+ /**
+ * Add commands to execute
+ *
+ * @param commands Commands to execute
+ */
+ public void addCommand(String[] commands) { addCommand(commands, 0, null); }
+
+ /**
+ * Add commands to execute, with a callback to be called on completion (of all commands)
+ *
+ * The thread on which the callback executes is dependent on various factors, see {@link Shell.Interactive} for further details
+ *
+ * @param commands Commands to execute
+ * @param code User-defined value passed back to the callback
+ * @param onCommandResultListener Callback to be called on completion (of all commands)
+ */
+ public synchronized void addCommand(String[] commands, int code, OnCommandResultListener onCommandResultListener) {
+ this.commands.add(new Command(commands, code, onCommandResultListener));
+ runNextCommand();
+ }
+
+ /**
+ * Run the next command if any and if ready, signals idle state if no commands left
+ */
+ private void runNextCommand() {
+ runNextCommand(true);
+ }
/**
* Called from a ScheduledThreadPoolExecutor timer thread every second when there is an outstanding command
@@ -781,9 +781,9 @@ private void runNextCommand() {
private synchronized void handleWatchdog() {
final int exitCode;
- if (watchdog == null) return;
- if (watchdogTimeout == 0) return;
-
+ if (watchdog == null) return;
+ if (watchdogTimeout == 0) return;
+
if (!isRunning()) {
exitCode = OnCommandResultListener.SHELL_DIED;
Debug.log(String.format("[%s%%] SHELL_DIED", shell.toUpperCase(Locale.ENGLISH)));
@@ -834,196 +834,196 @@ private void stopWatchdog() {
watchdog = null;
}
}
-
- /**
- * Run the next command if any and if ready
- *
- * @param notifyIdle signals idle state if no commands left ?
- */
- private void runNextCommand(boolean notifyIdle) {
- // must always be called from a synchronized method
-
- boolean running = isRunning();
- if (!running) idle = true;
-
- if (running && idle && (commands.size() > 0)) {
- Command command = commands.get(0);
- commands.remove(0);
-
- buffer = null;
- lastExitCode = 0;
- lastMarkerSTDOUT = null;
- lastMarkerSTDERR = null;
-
- if (command.commands.length > 0) {
- try {
- if (command.onCommandResultListener != null) {
- // no reason to store the output if we don't have an OnCommandResultListener
- // user should catch the output with an OnLineListener in this case
- buffer = Collections.synchronizedList(new ArrayList());
- }
-
- idle = false;
- this.command = command;
+
+ /**
+ * Run the next command if any and if ready
+ *
+ * @param notifyIdle signals idle state if no commands left ?
+ */
+ private void runNextCommand(boolean notifyIdle) {
+ // must always be called from a synchronized method
+
+ boolean running = isRunning();
+ if (!running) idle = true;
+
+ if (running && idle && (commands.size() > 0)) {
+ Command command = commands.get(0);
+ commands.remove(0);
+
+ buffer = null;
+ lastExitCode = 0;
+ lastMarkerSTDOUT = null;
+ lastMarkerSTDERR = null;
+
+ if (command.commands.length > 0) {
+ try {
+ if (command.onCommandResultListener != null) {
+ // no reason to store the output if we don't have an OnCommandResultListener
+ // user should catch the output with an OnLineListener in this case
+ buffer = Collections.synchronizedList(new ArrayList());
+ }
+
+ idle = false;
+ this.command = command;
startWatchdog();
- for (String write : command.commands) {
+ for (String write : command.commands) {
Debug.logCommand(String.format("[%s+] %s", shell.toUpperCase(Locale.ENGLISH), write));
- STDIN.write((write + "\n").getBytes("UTF-8"));
- }
- STDIN.write(("echo " + command.marker + " $?\n").getBytes("UTF-8"));
- STDIN.write(("echo " + command.marker + " >&2\n").getBytes("UTF-8"));
- STDIN.flush();
- } catch (IOException e) {
- }
- } else {
- runNextCommand(false);
- }
- } else if (!running) {
- // our shell died for unknown reasons - abort all submissions
- while (commands.size() > 0) {
- postCallback(commands.remove(0), OnCommandResultListener.SHELL_DIED, null);
- }
- }
-
- if (idle && notifyIdle) {
- synchronized(idleSync) {
- idleSync.notifyAll();
- }
- }
- }
-
- /**
- * Processes a STDOUT/STDERR line containing an end/exitCode marker
- */
- private synchronized void processMarker() {
- if (command.marker.equals(lastMarkerSTDOUT) && (command.marker.equals(lastMarkerSTDERR))) {
- if (buffer != null) {
- postCallback(command, lastExitCode, buffer);
- }
-
+ STDIN.write((write + "\n").getBytes("UTF-8"));
+ }
+ STDIN.write(("echo " + command.marker + " $?\n").getBytes("UTF-8"));
+ STDIN.write(("echo " + command.marker + " >&2\n").getBytes("UTF-8"));
+ STDIN.flush();
+ } catch (IOException e) {
+ }
+ } else {
+ runNextCommand(false);
+ }
+ } else if (!running) {
+ // our shell died for unknown reasons - abort all submissions
+ while (commands.size() > 0) {
+ postCallback(commands.remove(0), OnCommandResultListener.SHELL_DIED, null);
+ }
+ }
+
+ if (idle && notifyIdle) {
+ synchronized(idleSync) {
+ idleSync.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Processes a STDOUT/STDERR line containing an end/exitCode marker
+ */
+ private synchronized void processMarker() {
+ if (command.marker.equals(lastMarkerSTDOUT) && (command.marker.equals(lastMarkerSTDERR))) {
+ if (buffer != null) {
+ postCallback(command, lastExitCode, buffer);
+ }
+
stopWatchdog();
- command = null;
- buffer = null;
- idle = true;
- runNextCommand();
- }
- }
-
- /**
- * Process a normal STDOUT/STDERR line
- *
- * @param line Line to process
- * @param listener Callback to call or null
- */
- private synchronized void processLine(String line, OnLineListener listener) {
- if (listener != null) {
- if (handler != null) {
- final String fLine = line;
- final OnLineListener fListener = listener;
-
- startCallback();
- handler.post(new Runnable() {
- @Override
- public void run() {
- try {
- fListener.onLine(fLine);
- } finally {
- endCallback();
- }
- }
- });
- } else {
- listener.onLine(line);
- }
- }
- }
-
- /**
- * Add line to internal buffer
- *
- * @param line Line to add
- */
- private synchronized void addBuffer(String line) {
- if (buffer != null) {
- buffer.add(line);
- }
- }
-
- /**
- * Increase callback counter
- */
- private void startCallback() {
- synchronized (callbackSync) {
- callbacks++;
- }
- }
-
- /**
- * Schedule a callback to run on the appropriate thread
- */
- private void postCallback(final Command fCommand, final int fExitCode, final List fOutput) {
- if (fCommand.onCommandResultListener == null) {
- return;
- }
- if (handler == null) {
- fCommand.onCommandResultListener.onCommandResult(fCommand.code, fExitCode, fOutput);
- return;
- }
- startCallback();
- handler.post(new Runnable() {
- @Override
- public void run() {
- try {
- fCommand.onCommandResultListener.onCommandResult(fCommand.code, fExitCode, fOutput);
- } finally {
- endCallback();
- }
- }
- });
- }
-
- /**
- * Decrease callback counter, signals callback complete state when dropped to 0
- */
- private void endCallback() {
- synchronized (callbackSync) {
- callbacks--;
- if (callbacks == 0) {
- callbackSync.notifyAll();
- }
- }
- }
-
- /**
+ command = null;
+ buffer = null;
+ idle = true;
+ runNextCommand();
+ }
+ }
+
+ /**
+ * Process a normal STDOUT/STDERR line
+ *
+ * @param line Line to process
+ * @param listener Callback to call or null
+ */
+ private synchronized void processLine(String line, OnLineListener listener) {
+ if (listener != null) {
+ if (handler != null) {
+ final String fLine = line;
+ final OnLineListener fListener = listener;
+
+ startCallback();
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ fListener.onLine(fLine);
+ } finally {
+ endCallback();
+ }
+ }
+ });
+ } else {
+ listener.onLine(line);
+ }
+ }
+ }
+
+ /**
+ * Add line to internal buffer
+ *
+ * @param line Line to add
+ */
+ private synchronized void addBuffer(String line) {
+ if (buffer != null) {
+ buffer.add(line);
+ }
+ }
+
+ /**
+ * Increase callback counter
+ */
+ private void startCallback() {
+ synchronized (callbackSync) {
+ callbacks++;
+ }
+ }
+
+ /**
+ * Schedule a callback to run on the appropriate thread
+ */
+ private void postCallback(final Command fCommand, final int fExitCode, final List fOutput) {
+ if (fCommand.onCommandResultListener == null) {
+ return;
+ }
+ if (handler == null) {
+ fCommand.onCommandResultListener.onCommandResult(fCommand.code, fExitCode, fOutput);
+ return;
+ }
+ startCallback();
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ fCommand.onCommandResultListener.onCommandResult(fCommand.code, fExitCode, fOutput);
+ } finally {
+ endCallback();
+ }
+ }
+ });
+ }
+
+ /**
+ * Decrease callback counter, signals callback complete state when dropped to 0
+ */
+ private void endCallback() {
+ synchronized (callbackSync) {
+ callbacks--;
+ if (callbacks == 0) {
+ callbackSync.notifyAll();
+ }
+ }
+ }
+
+ /**
* Internal call that launches the shell, starts gobbling, and starts executing commands.
- * See {@link Shell.Interactive}
- *
- * @return Opened successfully ?
- */
- private synchronized boolean open() {
+ * See {@link Shell.Interactive}
+ *
+ * @return Opened successfully ?
+ */
+ private synchronized boolean open() {
Debug.log(String.format("[%s%%] START", shell.toUpperCase(Locale.ENGLISH)));
-
- try {
- // setup our process, retrieve STDIN stream, and STDOUT/STDERR gobblers
- if (environment.size() == 0) {
- process = Runtime.getRuntime().exec(shell);
- } else {
- Map newEnvironment = new HashMap();
- newEnvironment.putAll(System.getenv());
- newEnvironment.putAll(environment);
- int i = 0;
- String[] env = new String[newEnvironment.size()];
- for (Map.Entry entry : newEnvironment.entrySet()) {
- env[i] = entry.getKey() + "=" + entry.getValue();
- i++;
- }
- process = Runtime.getRuntime().exec(shell, env);
- }
-
- STDIN = new DataOutputStream(process.getOutputStream());
+
+ try {
+ // setup our process, retrieve STDIN stream, and STDOUT/STDERR gobblers
+ if (environment.size() == 0) {
+ process = Runtime.getRuntime().exec(shell);
+ } else {
+ Map newEnvironment = new HashMap();
+ newEnvironment.putAll(System.getenv());
+ newEnvironment.putAll(environment);
+ int i = 0;
+ String[] env = new String[newEnvironment.size()];
+ for (Map.Entry entry : newEnvironment.entrySet()) {
+ env[i] = entry.getKey() + "=" + entry.getValue();
+ i++;
+ }
+ process = Runtime.getRuntime().exec(shell, env);
+ }
+
+ STDIN = new DataOutputStream(process.getOutputStream());
STDOUT = new StreamGobbler(shell.toUpperCase(Locale.ENGLISH) + "-", process.getInputStream(), new OnLineListener() {
- @Override
- public void onLine(String line) {
+ @Override
+ public void onLine(String line) {
synchronized (Interactive.this) {
if (command == null) {
return;
@@ -1038,13 +1038,13 @@ public void onLine(String line) {
} else {
addBuffer(line);
processLine(line, onSTDOUTLineListener);
- }
- }
- }
- });
+ }
+ }
+ }
+ });
STDERR = new StreamGobbler(shell.toUpperCase(Locale.ENGLISH) + "*", process.getErrorStream(), new OnLineListener() {
- @Override
- public void onLine(String line) {
+ @Override
+ public void onLine(String line) {
synchronized (Interactive.this) {
if (command == null) {
return;
@@ -1056,77 +1056,77 @@ public void onLine(String line) {
if (wantSTDERR) addBuffer(line);
processLine(line, onSTDERRLineListener);
}
- }
- }
- });
-
- // start gobbling and write our commands to the shell
- STDOUT.start();
- STDERR.start();
-
- running = true;
- closed = false;
-
- runNextCommand();
-
- return true;
- } catch (IOException e) {
- // shell probably not found
- return false;
- }
- }
-
- /**
- * Close shell and clean up all resources. Call this when you are done with the shell.
- * If the shell is not idle (all commands completed) you should not call this method
- * from the main UI thread because it may block for a long time. This method will
- * intentionally crash your app (if in debug mode) if you try to do this anyway.
- */
- public void close() {
- boolean _idle = isIdle(); // idle must be checked synchronized
-
- synchronized (this) {
- if (!running) return;
- running = false;
- closed = true;
- }
-
- // This method should not be called from the main thread unless the shell is idle
- // and can be cleaned up with (minimal) waiting. Only throw in debug mode.
+ }
+ }
+ });
+
+ // start gobbling and write our commands to the shell
+ STDOUT.start();
+ STDERR.start();
+
+ running = true;
+ closed = false;
+
+ runNextCommand();
+
+ return true;
+ } catch (IOException e) {
+ // shell probably not found
+ return false;
+ }
+ }
+
+ /**
+ * Close shell and clean up all resources. Call this when you are done with the shell.
+ * If the shell is not idle (all commands completed) you should not call this method
+ * from the main UI thread because it may block for a long time. This method will
+ * intentionally crash your app (if in debug mode) if you try to do this anyway.
+ */
+ public void close() {
+ boolean _idle = isIdle(); // idle must be checked synchronized
+
+ synchronized (this) {
+ if (!running) return;
+ running = false;
+ closed = true;
+ }
+
+ // This method should not be called from the main thread unless the shell is idle
+ // and can be cleaned up with (minimal) waiting. Only throw in debug mode.
if (!_idle && Debug.getSanityChecksEnabledEffective() && Debug.onMainThread()) {
- Debug.log(ShellOnMainThreadException.EXCEPTION_NOT_IDLE);
- throw new ShellOnMainThreadException(ShellOnMainThreadException.EXCEPTION_NOT_IDLE);
- }
-
- if (!_idle) waitForIdle();
-
- try {
- STDIN.write(("exit\n").getBytes("UTF-8"));
- STDIN.flush();
-
- // wait for our process to finish, while we gobble away in the background
- process.waitFor();
-
- // make sure our threads are done gobbling, our streams are closed, and the process is
- // destroyed - while the latter two shouldn't be needed in theory, and may even produce
- // warnings, in "normal" Java they are required for guaranteed cleanup of resources, so
- // lets be safe and do this on Android as well
- try {
- STDIN.close();
- } catch (IOException e) {
- }
- STDOUT.join();
- STDERR.join();
+ Debug.log(ShellOnMainThreadException.EXCEPTION_NOT_IDLE);
+ throw new ShellOnMainThreadException(ShellOnMainThreadException.EXCEPTION_NOT_IDLE);
+ }
+
+ if (!_idle) waitForIdle();
+
+ try {
+ STDIN.write(("exit\n").getBytes("UTF-8"));
+ STDIN.flush();
+
+ // wait for our process to finish, while we gobble away in the background
+ process.waitFor();
+
+ // make sure our threads are done gobbling, our streams are closed, and the process is
+ // destroyed - while the latter two shouldn't be needed in theory, and may even produce
+ // warnings, in "normal" Java they are required for guaranteed cleanup of resources, so
+ // lets be safe and do this on Android as well
+ try {
+ STDIN.close();
+ } catch (IOException e) {
+ }
+ STDOUT.join();
+ STDERR.join();
stopWatchdog();
- process.destroy();
- } catch (IOException e) {
- // shell probably not found
- } catch (InterruptedException e) {
- // this should really be re-thrown
- }
-
+ process.destroy();
+ } catch (IOException e) {
+ // shell probably not found
+ } catch (InterruptedException e) {
+ // this should really be re-thrown
+ }
+
Debug.log(String.format("[%s%%] END", shell.toUpperCase(Locale.ENGLISH)));
- }
+ }
/**
* Try to clean up as much as possible from a shell that's gotten itself wedged.
@@ -1140,114 +1140,114 @@ public synchronized void kill() {
try {
STDIN.close();
} catch (IOException e) {
- }
+ }
try {
- process.destroy();
- } catch (Exception e) {
+ process.destroy();
+ } catch (Exception e) {
}
}
- /**
+ /**
* Is our shell still running ?
- *
- * @return Shell running ?
- */
- public boolean isRunning() {
- try {
- // if this throws, we're still running
- process.exitValue();
- return false;
- } catch (IllegalThreadStateException e) {
- }
- return true;
- }
-
- /**
- * Have all commands completed executing ?
- *
- * @return Shell idle ?
- */
- public synchronized boolean isIdle() {
- if (!isRunning()) {
- idle = true;
- synchronized(idleSync) {
- idleSync.notifyAll();
- }
- }
- return idle;
- }
-
- /**
- * Wait for idle state. As this is a blocking call, you should not call it from the main UI thread.
- * If you do so and debug mode is enabled, this method will intentionally crash your app.
- *
- * If not interrupted, this method will not return until all commands have finished executing.
- * Note that this does not necessarily mean that all the callbacks have fired yet.
- *
- * If no Handler is used, all callbacks will have been executed when this method returns. If
- * a Handler is used, and this method is called from a different thread than associated with the
- * Handler's Looper, all callbacks will have been executed when this method returns as well.
- * If however a Handler is used but this method is called from the same thread as associated
- * with the Handler's Looper, there is no way to know.
- *
+ *
+ * @return Shell running ?
+ */
+ public boolean isRunning() {
+ try {
+ // if this throws, we're still running
+ process.exitValue();
+ return false;
+ } catch (IllegalThreadStateException e) {
+ }
+ return true;
+ }
+
+ /**
+ * Have all commands completed executing ?
+ *
+ * @return Shell idle ?
+ */
+ public synchronized boolean isIdle() {
+ if (!isRunning()) {
+ idle = true;
+ synchronized(idleSync) {
+ idleSync.notifyAll();
+ }
+ }
+ return idle;
+ }
+
+ /**
+ * Wait for idle state. As this is a blocking call, you should not call it from the main UI thread.
+ * If you do so and debug mode is enabled, this method will intentionally crash your app.
+ *
+ * If not interrupted, this method will not return until all commands have finished executing.
+ * Note that this does not necessarily mean that all the callbacks have fired yet.
+ *
+ * If no Handler is used, all callbacks will have been executed when this method returns. If
+ * a Handler is used, and this method is called from a different thread than associated with the
+ * Handler's Looper, all callbacks will have been executed when this method returns as well.
+ * If however a Handler is used but this method is called from the same thread as associated
+ * with the Handler's Looper, there is no way to know.
+ *
* In practice this means that in most simple cases all callbacks will have completed when this
- * method returns, but if you actually depend on this behavior, you should make certain this is
- * indeed the case.
- *
- * See {@link Shell.Interactive} for further details on threading and handlers
- *
- * @return True if wait complete, false if wait interrupted
- */
- public boolean waitForIdle() {
+ * method returns, but if you actually depend on this behavior, you should make certain this is
+ * indeed the case.
+ *
+ * See {@link Shell.Interactive} for further details on threading and handlers
+ *
+ * @return True if wait complete, false if wait interrupted
+ */
+ public boolean waitForIdle() {
if (Debug.getSanityChecksEnabledEffective() && Debug.onMainThread()) {
- Debug.log(ShellOnMainThreadException.EXCEPTION_WAIT_IDLE);
- throw new ShellOnMainThreadException(ShellOnMainThreadException.EXCEPTION_WAIT_IDLE);
- }
-
- if (isRunning()) {
- synchronized (idleSync) {
- while (!idle) {
- try {
- idleSync.wait();
- } catch (InterruptedException e) {
- return false;
- }
- }
- }
-
- if (
- (handler != null) &&
- (handler.getLooper() != null) &&
- (handler.getLooper() != Looper.myLooper())
- ) {
- // If the callbacks are posted to a different thread than this one, we can wait until
- // all callbacks have called before returning. If we don't use a Handler at all,
- // the callbacks are already called before we get here. If we do use a Handler but
- // we use the same Looper, waiting here would actually block the callbacks from being
- // called
-
- synchronized (callbackSync) {
- while (callbacks > 0) {
- try {
- callbackSync.wait();
- } catch (InterruptedException e) {
- return false;
- }
- }
- }
- }
- }
-
- return true;
- }
-
- /**
- * Are we using a Handler to post callbacks ?
- *
- * @return Handler used ?
- */
- public boolean hasHandler() {
- return (handler != null);
- }
- }
-}
+ Debug.log(ShellOnMainThreadException.EXCEPTION_WAIT_IDLE);
+ throw new ShellOnMainThreadException(ShellOnMainThreadException.EXCEPTION_WAIT_IDLE);
+ }
+
+ if (isRunning()) {
+ synchronized (idleSync) {
+ while (!idle) {
+ try {
+ idleSync.wait();
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+ }
+
+ if (
+ (handler != null) &&
+ (handler.getLooper() != null) &&
+ (handler.getLooper() != Looper.myLooper())
+ ) {
+ // If the callbacks are posted to a different thread than this one, we can wait until
+ // all callbacks have called before returning. If we don't use a Handler at all,
+ // the callbacks are already called before we get here. If we do use a Handler but
+ // we use the same Looper, waiting here would actually block the callbacks from being
+ // called
+
+ synchronized (callbackSync) {
+ while (callbacks > 0) {
+ try {
+ callbackSync.wait();
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Are we using a Handler to post callbacks ?
+ *
+ * @return Handler used ?
+ */
+ public boolean hasHandler() {
+ return (handler != null);
+ }
+ }
+}
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/ShellNotClosedException.java b/libsuperuser/src/eu/chainfire/libsuperuser/ShellNotClosedException.java
index ec34ff8..6b2d7ae 100644
--- a/libsuperuser/src/eu/chainfire/libsuperuser/ShellNotClosedException.java
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/ShellNotClosedException.java
@@ -1,29 +1,29 @@
-/*
- * Copyright (C) 2012-2013 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;
-
-/**
- * Exception class used to notify developer that a shell was not close()d
- */
-@SuppressWarnings("serial")
-public class ShellNotClosedException extends RuntimeException {
- public static final String EXCEPTION_NOT_CLOSED = "Application did not close() interactive shell";
-
- public ShellNotClosedException() {
- super(EXCEPTION_NOT_CLOSED);
- }
-}
+/*
+ * Copyright (C) 2012-2013 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;
+
+/**
+ * Exception class used to notify developer that a shell was not close()d
+ */
+@SuppressWarnings("serial")
+public class ShellNotClosedException extends RuntimeException {
+ public static final String EXCEPTION_NOT_CLOSED = "Application did not close() interactive shell";
+
+ public ShellNotClosedException() {
+ super(EXCEPTION_NOT_CLOSED);
+ }
+}
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/ShellOnMainThreadException.java b/libsuperuser/src/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
index 91bc57a..70cf23d 100644
--- a/libsuperuser/src/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/ShellOnMainThreadException.java
@@ -1,32 +1,32 @@
-/*
- * Copyright (C) 2012-2013 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;
-
-/**
- * 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 static final String EXCEPTION_COMMAND = "Application attempted to run a shell command from the main thread";
- public static final String EXCEPTION_NOT_IDLE = "Application attempted to wait for a non-idle shell to close on the main thread";
- public static final String EXCEPTION_WAIT_IDLE = "Application attempted to wait for a shell to become idle on the main thread";
-
- public ShellOnMainThreadException(String message) {
- super(message);
- }
-}
+/*
+ * Copyright (C) 2012-2013 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;
+
+/**
+ * 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 static final String EXCEPTION_COMMAND = "Application attempted to run a shell command from the main thread";
+ public static final String EXCEPTION_NOT_IDLE = "Application attempted to wait for a non-idle shell to close on the main thread";
+ public static final String EXCEPTION_WAIT_IDLE = "Application attempted to wait for a shell to become idle on the main thread";
+
+ public ShellOnMainThreadException(String message) {
+ super(message);
+ }
+}
diff --git a/libsuperuser/src/eu/chainfire/libsuperuser/StreamGobbler.java b/libsuperuser/src/eu/chainfire/libsuperuser/StreamGobbler.java
index 87ad3e4..893f75e 100644
--- a/libsuperuser/src/eu/chainfire/libsuperuser/StreamGobbler.java
+++ b/libsuperuser/src/eu/chainfire/libsuperuser/StreamGobbler.java
@@ -1,103 +1,103 @@
-/*
- * Copyright (C) 2012-2013 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.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.List;
-
-/**
- * Thread utility class continuously reading from an InputStream
- */
-public class StreamGobbler extends Thread {
- /**
- * Line callback interface
- */
- public interface OnLineListener {
- /**
- * Line callback
- *
- * This callback should process the line as quickly as possible.
- * Delays in this callback may pause the native process or even
- * result in a deadlock
- *
- * @param line String that was gobbled
- */
- public void onLine(String line);
- }
-
- private String shell = null;
- private BufferedReader reader = null;
- private List writer = null;
- private OnLineListener listener = null;
-
- /**
- * StreamGobbler constructor
- *
- * We use this class because shell STDOUT and STDERR should be read as quickly as
- * possible to prevent a deadlock from occurring, or Process.waitFor() never
- * returning (as the buffer is full, pausing the native process)
- *
- * @param shell Name of the shell
- * @param inputStream InputStream to read from
- * @param outputList List to write to, or null
- */
- public StreamGobbler(String shell, InputStream inputStream, List outputList) {
- this.shell = shell;
- reader = new BufferedReader(new InputStreamReader(inputStream));
- writer = outputList;
- }
-
- /**
- * StreamGobbler constructor
- *
- * We use this class because shell STDOUT and STDERR should be read as quickly as
- * possible to prevent a deadlock from occurring, or Process.waitFor() never
- * returning (as the buffer is full, pausing the native process)
- *
- * @param shell Name of the shell
- * @param inputStream InputStream to read from
- * @param onLineListener OnLineListener callback
- */
- public StreamGobbler(String shell, InputStream inputStream, OnLineListener onLineListener) {
- this.shell = shell;
- reader = new BufferedReader(new InputStreamReader(inputStream));
- listener = onLineListener;
- }
-
- @Override
- public void run() {
- // keep reading the InputStream until it ends (or an error occurs)
- try {
- String line = null;
- while ((line = reader.readLine()) != null) {
+/*
+ * Copyright (C) 2012-2013 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.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+
+/**
+ * Thread utility class continuously reading from an InputStream
+ */
+public class StreamGobbler extends Thread {
+ /**
+ * Line callback interface
+ */
+ public interface OnLineListener {
+ /**
+ * Line callback
+ *
+ * This callback should process the line as quickly as possible.
+ * Delays in this callback may pause the native process or even
+ * result in a deadlock
+ *
+ * @param line String that was gobbled
+ */
+ public void onLine(String line);
+ }
+
+ private String shell = null;
+ private BufferedReader reader = null;
+ private List writer = null;
+ private OnLineListener listener = null;
+
+ /**
+ * StreamGobbler constructor
+ *
+ * We use this class because shell STDOUT and STDERR should be read as quickly as
+ * possible to prevent a deadlock from occurring, or Process.waitFor() never
+ * returning (as the buffer is full, pausing the native process)
+ *
+ * @param shell Name of the shell
+ * @param inputStream InputStream to read from
+ * @param outputList List to write to, or null
+ */
+ public StreamGobbler(String shell, InputStream inputStream, List outputList) {
+ this.shell = shell;
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+ writer = outputList;
+ }
+
+ /**
+ * StreamGobbler constructor
+ *
+ * We use this class because shell STDOUT and STDERR should be read as quickly as
+ * possible to prevent a deadlock from occurring, or Process.waitFor() never
+ * returning (as the buffer is full, pausing the native process)
+ *
+ * @param shell Name of the shell
+ * @param inputStream InputStream to read from
+ * @param onLineListener OnLineListener callback
+ */
+ public StreamGobbler(String shell, InputStream inputStream, OnLineListener onLineListener) {
+ this.shell = shell;
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+ listener = onLineListener;
+ }
+
+ @Override
+ public void run() {
+ // keep reading the InputStream until it ends (or an error occurs)
+ try {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
Debug.logOutput(String.format("[%s] %s", shell, line));
- if (writer != null) writer.add(line);
- if (listener != null) listener.onLine(line);
- }
- } catch (IOException e) {
- }
-
- // make sure our stream is closed and resources will be freed
- try {
- reader.close();
- } catch (IOException e) {
- }
- }
-}
+ if (writer != null) writer.add(line);
+ if (listener != null) listener.onLine(line);
+ }
+ } catch (IOException e) {
+ }
+
+ // make sure our stream is closed and resources will be freed
+ try {
+ reader.close();
+ } catch (IOException e) {
+ }
+ }
+}
diff --git a/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BackgroundIntentService.java b/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BackgroundIntentService.java
index 4cafcca..79a6812 100644
--- a/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BackgroundIntentService.java
+++ b/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BackgroundIntentService.java
@@ -1,101 +1,101 @@
-/*
- * 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_example;
-
-import eu.chainfire.libsuperuser.Application;
-import eu.chainfire.libsuperuser.Shell;
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * Example IntentService based class, to execute tasks in a background
- * thread. This would typically be used by BroadcastReceivers and other
- * fire-and-forget type calls.
- *
- * For most background calls that would occur when the UI is visible, in
- * response to some user action and/or something you are waiting for,
- * you would typically use an AsyncTask instead of a service like this.
- * (See MainActivity.java for that example)
- *
- * Note that the IntentService's onHandleIntent call runs in a background
- * thread, while a normal service's calls would run in the main thread,
- * unless you put in the extra work. This is an important distinction
- * that is often overlooked by beginners.
- *
- * This service starts running when needed, and stops running when the
- * task is done, automagically.
- *
- * Please also see BootCompleteReceiver.java, and AndroidManifest.xml for
- * how and when this service is instantiated.
- *
- * This code leaves some room for extension - if you really wanted to
- * respond only to a single event that always does the same, this code
- * could have been a lot shorter.
- */
-public class BackgroundIntentService extends IntentService {
- // you could provide more options here, should you need them
- public static final String ACTION_BOOT_COMPLETE = "boot_complete";
-
- public static void performAction(Context context, String action) {
- performAction(context, action, null);
- }
-
- public static void performAction(Context context, String action, Bundle extras) {
- // this is utility call to easy starting the service and performing a task
- // pass parameters in an bundle to be added to the intent as extras
- // See BootCompleteReceiver.java
-
- if ((context == null) || (action == null) || action.equals("")) return;
-
- Intent svc = new Intent(context, BackgroundIntentService.class);
- svc.setAction(action);
- if (extras != null) svc.putExtras(extras);
- context.startService(svc);
- }
-
- public BackgroundIntentService() {
- // If you forget this one, the app will crash
- super("BackgroundIntentService");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- String action = intent.getAction();
- if ((action == null) || (action.equals(""))) return;
-
- if (action.equals(ACTION_BOOT_COMPLETE)) {
- onBootComplete();
- }
- // you can define more options here... pass parameters through the "extra" values
- }
-
- protected void onBootComplete() {
- // We are running in a background thread here!
-
- // This would crash (when debugging) if it was called from the main thread:
- Shell.SU.run("ls -l /");
-
- // Let's toast that we're done, using the work-arounds and utility function in
- // out Application class. Without those modifications there would be a very high
- // chance of crashing the app in various Android versions. The modifications are
- // simple and easily ported to your own Application class, if you can't use the
- // one from libsuperuser.
- Application.toast(this, "This toast will self-destruct in five seconds");
- }
-}
+/*
+ * 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_example;
+
+import eu.chainfire.libsuperuser.Application;
+import eu.chainfire.libsuperuser.Shell;
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Example IntentService based class, to execute tasks in a background
+ * thread. This would typically be used by BroadcastReceivers and other
+ * fire-and-forget type calls.
+ *
+ * For most background calls that would occur when the UI is visible, in
+ * response to some user action and/or something you are waiting for,
+ * you would typically use an AsyncTask instead of a service like this.
+ * (See MainActivity.java for that example)
+ *
+ * Note that the IntentService's onHandleIntent call runs in a background
+ * thread, while a normal service's calls would run in the main thread,
+ * unless you put in the extra work. This is an important distinction
+ * that is often overlooked by beginners.
+ *
+ * This service starts running when needed, and stops running when the
+ * task is done, automagically.
+ *
+ * Please also see BootCompleteReceiver.java, and AndroidManifest.xml for
+ * how and when this service is instantiated.
+ *
+ * This code leaves some room for extension - if you really wanted to
+ * respond only to a single event that always does the same, this code
+ * could have been a lot shorter.
+ */
+public class BackgroundIntentService extends IntentService {
+ // you could provide more options here, should you need them
+ public static final String ACTION_BOOT_COMPLETE = "boot_complete";
+
+ public static void performAction(Context context, String action) {
+ performAction(context, action, null);
+ }
+
+ public static void performAction(Context context, String action, Bundle extras) {
+ // this is utility call to easy starting the service and performing a task
+ // pass parameters in an bundle to be added to the intent as extras
+ // See BootCompleteReceiver.java
+
+ if ((context == null) || (action == null) || action.equals("")) return;
+
+ Intent svc = new Intent(context, BackgroundIntentService.class);
+ svc.setAction(action);
+ if (extras != null) svc.putExtras(extras);
+ context.startService(svc);
+ }
+
+ public BackgroundIntentService() {
+ // If you forget this one, the app will crash
+ super("BackgroundIntentService");
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent.getAction();
+ if ((action == null) || (action.equals(""))) return;
+
+ if (action.equals(ACTION_BOOT_COMPLETE)) {
+ onBootComplete();
+ }
+ // you can define more options here... pass parameters through the "extra" values
+ }
+
+ protected void onBootComplete() {
+ // We are running in a background thread here!
+
+ // This would crash (when debugging) if it was called from the main thread:
+ Shell.SU.run("ls -l /");
+
+ // Let's toast that we're done, using the work-arounds and utility function in
+ // out Application class. Without those modifications there would be a very high
+ // chance of crashing the app in various Android versions. The modifications are
+ // simple and easily ported to your own Application class, if you can't use the
+ // one from libsuperuser.
+ Application.toast(this, "This toast will self-destruct in five seconds");
+ }
+}
diff --git a/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BootCompleteReceiver.java b/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BootCompleteReceiver.java
index f85a159..8277125 100644
--- a/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BootCompleteReceiver.java
+++ b/libsuperuser_example/src/eu/chainfire/libsuperuser_example/BootCompleteReceiver.java
@@ -1,44 +1,44 @@
-/*
- * 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_example;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * Example BootCompleteReceiver that starts MyIntentService
- * (please see MyIntentService.java) to handle the task
- * in a background thread
- */
-public class BootCompleteReceiver extends BroadcastReceiver{
- @Override
- public void onReceive(Context context, Intent intent) {
- // What many beginners don't realize is that BroadcastReceivers like these
- // usually run in the application's main thread, and can thus generate
- // ANRs. This is increasingly likely with the BOOT_COMPLETED receiver, as
- // the system is likely very busy when this receiver is called.
-
- // In this example we are starting our MyIntentService to actually do the
- // work we want to happen, not only because "su" should specifically NEVER
- // be called from a BroadcastReceiver, but also because you should be doing
- // this even if you aren't calling "su". It's a good practise, and using
- // IntentService is really easy.
-
- BackgroundIntentService.performAction(context, BackgroundIntentService.ACTION_BOOT_COMPLETE);
- }
+/*
+ * 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_example;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Example BootCompleteReceiver that starts MyIntentService
+ * (please see MyIntentService.java) to handle the task
+ * in a background thread
+ */
+public class BootCompleteReceiver extends BroadcastReceiver{
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // What many beginners don't realize is that BroadcastReceivers like these
+ // usually run in the application's main thread, and can thus generate
+ // ANRs. This is increasingly likely with the BOOT_COMPLETED receiver, as
+ // the system is likely very busy when this receiver is called.
+
+ // In this example we are starting our MyIntentService to actually do the
+ // work we want to happen, not only because "su" should specifically NEVER
+ // be called from a BroadcastReceiver, but also because you should be doing
+ // this even if you aren't calling "su". It's a good practise, and using
+ // IntentService is really easy.
+
+ BackgroundIntentService.performAction(context, BackgroundIntentService.ACTION_BOOT_COMPLETE);
+ }
}
\ No newline at end of file