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 4bae0f0..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 ListRuns 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 ListRuns 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 ListRuns 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 ListDetects 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 - - ListCommand 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, ListDetects 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 + + ListCommand 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, ListSet 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(MapAdd 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(ListAdd 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(ListAdd 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(MapAdd 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(ListAdd 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(ListAdd 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.
- * - *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.
+ * + *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 ListBackground 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 ListAdd 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(ListAdd 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(ListAdd 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) { - if (running) { - 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(ListAdd 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(ListAdd 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 @@ -783,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))); @@ -797,19 +795,7 @@ private synchronized void handleWatchdog() { } if (handler != null) { - final Command fCommand = command; - final ListWait 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 ListStreamGobbler 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 ListStreamGobbler 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 ListStreamGobbler 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 ListStreamGobbler 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