aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjruesga <jorge@ruesga.com>2012-10-13 23:13:02 +0200
committerjruesga <jorge@ruesga.com>2012-10-13 23:13:02 +0200
commit2d7b380448e3843a3949b6f08fba06bb0a4b86a7 (patch)
tree1967f6e106ed198a5b35afcbbc2adaed65966f8d
parentdc925f72d8829093e9c0f93721b9f07a3e9d7ce9 (diff)
downloadandroid_packages_apps_CMFileManager-2d7b380448e3843a3949b6f08fba06bb0a4b86a7.tar.gz
android_packages_apps_CMFileManager-2d7b380448e3843a3949b6f08fba06bb0a4b86a7.tar.bz2
android_packages_apps_CMFileManager-2d7b380448e3843a3949b6f08fba06bb0a4b86a7.zip
Various: new commands and buffer management
* Change default buffer to 4096 * New commands: Read, send signal, terminate and write * Background console now change to privileged when foreground console is or change to privileged * Fix cancel asynchronous programs launched on privileged consoles (non-privileged background console is not able to kill or signal) * Fix root directory default * Allow send signal and terminate, in addition to cancel, for AsyncResultExecutable programs * Passing console trace status to commands (allow programs to use user preference) * Allow to programs to access the stdout to respond to programs (xe: write to stdin of dd). * Fix ResolveLinkCommand when src is the root directory * Remove check of error code in checkStdErr of the shell. Determine the errors based on the returned text. Error code is not necessary because the check is over stderr, not stdin. * Create separate methods for create privileged console (one with no check and other for check and change to non-privileged on error) * Fix detection of start of program on Shell * No send string controls to asynchronous programs on parsePartial method (cut buffers prior to invoke) * Create static method on Permissions to load from raw and octals strings. * Fix disk usage and mount point info when no background console is available * Fix test app name. Changed to CMExplorerTest * Fix exec test, once there are a write command. Now write the shell program and execute to check results.
-rw-r--r--res/values/overlay.xml4
-rw-r--r--res/xml/command_list.xml19
-rw-r--r--src/com/cyanogenmod/explorer/ExplorerApplication.java43
-rw-r--r--src/com/cyanogenmod/explorer/commands/AsyncResultExecutable.java40
-rw-r--r--src/com/cyanogenmod/explorer/commands/ExecutableCreator.java46
-rw-r--r--src/com/cyanogenmod/explorer/commands/ReadExecutable.java22
-rw-r--r--src/com/cyanogenmod/explorer/commands/SIGNAL.java93
-rw-r--r--src/com/cyanogenmod/explorer/commands/SendSignalExecutable.java29
-rw-r--r--src/com/cyanogenmod/explorer/commands/WriteExecutable.java36
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgram.java54
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgramListener.java9
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/Command.java25
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/ExecCommand.java9
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/FindCommand.java13
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java9
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/Program.java28
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/ReadCommand.java108
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/ResolveLinkCommand.java6
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/SendSignalCommand.java85
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/Shell.java10
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java58
-rw-r--r--src/com/cyanogenmod/explorer/commands/shell/WriteCommand.java161
-rw-r--r--src/com/cyanogenmod/explorer/console/Console.java3
-rw-r--r--src/com/cyanogenmod/explorer/console/ConsoleBuilder.java52
-rw-r--r--src/com/cyanogenmod/explorer/console/shell/ShellConsole.java223
-rw-r--r--src/com/cyanogenmod/explorer/model/Permissions.java67
-rw-r--r--src/com/cyanogenmod/explorer/tasks/FilesystemAsyncTask.java12
-rw-r--r--src/com/cyanogenmod/explorer/util/CommandHelper.java156
-rw-r--r--src/com/cyanogenmod/explorer/util/FileHelper.java3
-rw-r--r--src/com/cyanogenmod/explorer/util/MountPointHelper.java7
-rw-r--r--src/com/cyanogenmod/explorer/util/StorageHelper.java2
-rw-r--r--tests/res/values/strings.xml2
-rw-r--r--tests/src/com/cyanogenmod/explorer/commands/shell/AbstractConsoleTest.java2
-rw-r--r--tests/src/com/cyanogenmod/explorer/commands/shell/ExecCommandTest.java25
-rw-r--r--tests/src/com/cyanogenmod/explorer/commands/shell/FindCommandTest.java4
-rw-r--r--tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java4
-rw-r--r--tests/src/com/cyanogenmod/explorer/commands/shell/ReadCommandTest.java102
-rw-r--r--tests/src/com/cyanogenmod/explorer/commands/shell/SendSignalCommandTest.java34
-rw-r--r--tests/src/com/cyanogenmod/explorer/commands/shell/WriteCommandTest.java120
39 files changed, 1617 insertions, 108 deletions
diff --git a/res/values/overlay.xml b/res/values/overlay.xml
index d82e964d..c326cb4d 100644
--- a/res/values/overlay.xml
+++ b/res/values/overlay.xml
@@ -28,7 +28,7 @@
<!-- The system directory -->
<string name="system_dir">/system</string>
- <!-- The size of the buffers use by the console (in bytes). Default: 0.5 Mb -->
- <integer name="buffer_size">512</integer>
+ <!-- The size of the buffers use by the console (in bytes). Default: 4k -->
+ <integer name="buffer_size">4096</integer>
</resources> \ No newline at end of file
diff --git a/res/xml/command_list.xml b/res/xml/command_list.xml
index e8c44e8d..d41f7cf2 100644
--- a/res/xml/command_list.xml
+++ b/res/xml/command_list.xml
@@ -15,15 +15,20 @@
** limitations under the License.
-->
-<!-- The <CommandList> element should contain one or more <command> elements.
- Every command should contain and unique id attribute an the follow
- requiered attributes:
+<!--
+ The <CommandList> element should contain one or more <command> elements.
+ Every command should contain and unique id attribute an the follow
+ requiered attributes:
path: (string) The fully qualified path to the executable command
args: (string) The arguments that invoke the executable
- The required exitcode element must have the required attributes commandId and commandPath
- with the expected command for retrieve the exit code of the executed command
+ The required exitcode element must have the required attributes commandId and commandPath
+ with the expected command for retrieve the exit code of the executed command
+
+ This file contains values that could be overlayed. This allow
+ configure special values for each device. Use overlay building folder on device tree
+ for overlay this values
-->
<CommandList xmlns="http://schemas.android.com/apk/res/com.cyanogenmod.explorer">
<!-- Start code (append to commands; for retrieve the exit code) -->
@@ -60,9 +65,13 @@
<command commandId="pid_cmd" commandPath="/system/bin/ps" commandArgs="| /system/xbin/grep %1$s | /system/xbin/grep -w %2$s | /system/xbin/awk '{print $2}'" />
<command commandId="pwd" commandPath="/system/xbin/pwd" commandArgs="-P" />
<command commandId="quickfoldersearch" commandPath="/system/bin/ls" commandArgs="-aFd %1$s.* %1$s* | /system/xbin/grep -e '^d' -e '^ld' | /system/xbin/awk '{print $2}'" />
+ <command commandId="read" commandPath="/system/bin/cat" commandArgs="&lt; %1$s" />
<command commandId="readlink" commandPath="cd" commandArgs="%2$s &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/xbin/dirname &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/bin/ls -ald" />
<command commandId="rm" commandPath="/system/bin/rm" commandArgs="%1$s" />
<command commandId="rmdir" commandPath="/system/bin/rm" commandArgs="-R %1$s" />
+ <command commandId="sendsignal" commandPath="/system/bin/kill" commandArgs="-%1$s %2$s" />
+ <command commandId="terminate" commandPath="/system/bin/kill" commandArgs="%1$s" />
<command commandId="touch" commandPath="/system/xbin/echo" commandArgs="-n '' >> %1$s" />
+ <command commandId="write" commandPath="/system/bin/dd" commandArgs="bs=4k of=%1$s" />
</CommandList> \ No newline at end of file
diff --git a/src/com/cyanogenmod/explorer/ExplorerApplication.java b/src/com/cyanogenmod/explorer/ExplorerApplication.java
index 5f5c56b9..63916fcb 100644
--- a/src/com/cyanogenmod/explorer/ExplorerApplication.java
+++ b/src/com/cyanogenmod/explorer/ExplorerApplication.java
@@ -26,8 +26,10 @@ import android.content.res.Configuration;
import android.util.Log;
import com.cyanogenmod.explorer.console.Console;
+import com.cyanogenmod.explorer.console.ConsoleAllocException;
import com.cyanogenmod.explorer.console.ConsoleBuilder;
import com.cyanogenmod.explorer.console.ConsoleHolder;
+import com.cyanogenmod.explorer.console.shell.PrivilegedConsole;
import com.cyanogenmod.explorer.preferences.ExplorerSettings;
import com.cyanogenmod.explorer.preferences.Preferences;
import com.cyanogenmod.explorer.util.ExceptionUtil;
@@ -67,7 +69,10 @@ public final class ExplorerApplication extends Application {
key.compareTo(ExplorerSettings.SETTINGS_SHOW_TRACES.getId()) == 0) {
// The debug traces setting has changed. Notify to consoles
- Console c = getBackgroundConsole();
+ Console c = null;
+ try {
+ c = getBackgroundConsole();
+ } catch (Exception e) {/**NON BLOCK**/}
if (c != null) {
c.reloadTrace();
}
@@ -168,6 +173,7 @@ public final class ExplorerApplication extends Application {
*/
private void init() {
//Sets the default preferences if no value is set yet
+ FileHelper.ROOT_DIRECTORY = getString(R.string.root_dir);
Preferences.loadDefaults();
//Create a non-privileged console for background non-privileged tasks
@@ -209,6 +215,41 @@ public final class ExplorerApplication extends Application {
return sBackgroundConsole.getConsole();
}
+ /**
+ * Method that changes the background console to a privileged console
+ *
+ * @throws ConsoleAllocException If the console can't be allocated
+ */
+ public static void changeBackgroundConsoleToPriviligedConsole()
+ throws ConsoleAllocException {
+ if (sBackgroundConsole == null ||
+ !(sBackgroundConsole.getConsole() instanceof PrivilegedConsole)) {
+ try {
+ if (sBackgroundConsole != null) {
+ sBackgroundConsole.dispose();
+ }
+ } catch (Throwable ex) {/**NON BLOCK**/}
+
+ // Change the privileged console
+ try {
+ sBackgroundConsole =
+ new ConsoleHolder(
+ ConsoleBuilder.createPrivilegedConsole(
+ getInstance().getApplicationContext(),
+ FileHelper.ROOT_DIRECTORY));
+ } catch (Exception e) {
+ try {
+ if (sBackgroundConsole != null) {
+ sBackgroundConsole.dispose();
+ }
+ } catch (Throwable ex) {/**NON BLOCK**/}
+ sBackgroundConsole = null;
+ throw new ConsoleAllocException(
+ "Failed to alloc background console", e); //$NON-NLS-1$
+ }
+ }
+ }
+
/**
diff --git a/src/com/cyanogenmod/explorer/commands/AsyncResultExecutable.java b/src/com/cyanogenmod/explorer/commands/AsyncResultExecutable.java
index 079e2b98..dc1bc5e6 100644
--- a/src/com/cyanogenmod/explorer/commands/AsyncResultExecutable.java
+++ b/src/com/cyanogenmod/explorer/commands/AsyncResultExecutable.java
@@ -22,15 +22,35 @@ package com.cyanogenmod.explorer.commands;
public interface AsyncResultExecutable extends Executable {
/**
+ * An interface that let to request the ending of the current
+ * execution of an {@link Executable}.
+ */
+ public interface OnEndListener {
+ /**
+ * Invoked when a request of ending of the current execution
+ *
+ * @param signal The signal to send
+ * @return boolean If the execution was ended
+ */
+ boolean onSendSignal(SIGNAL signal);
+
+ /**
+ * Invoked when a request of ending of the current execution
+ *
+ * @return boolean If the execution was ended
+ */
+ boolean onEnd();
+ }
+
+ /**
* An interface that let to request the cancellation of the current
* execution of an {@link Executable}.
*/
public interface OnCancelListener {
/**
- * Invoked when a request of cancellation of the current
- * execution is started.
+ * Invoked when a request of cancellation of the current execution
*
- * @return boolean If the execution was canceled
+ * @return boolean If the execution was canceled
*/
boolean onCancel();
}
@@ -50,6 +70,20 @@ public interface AsyncResultExecutable extends Executable {
boolean cancel();
/**
+ * Method that ends the execution of the program.
+ *
+ * @return boolean If the program was ended
+ */
+ boolean end();
+
+ /**
+ * Method that sets the end listener.
+ *
+ * @param onEndListener The end listener
+ */
+ void setOnEndListener(OnEndListener onEndListener);
+
+ /**
* Method that sets the cancel listener.
*
* @param onCancelListener The cancel listener
diff --git a/src/com/cyanogenmod/explorer/commands/ExecutableCreator.java b/src/com/cyanogenmod/explorer/commands/ExecutableCreator.java
index 4e61f57d..ec1d8e4c 100644
--- a/src/com/cyanogenmod/explorer/commands/ExecutableCreator.java
+++ b/src/com/cyanogenmod/explorer/commands/ExecutableCreator.java
@@ -323,6 +323,18 @@ public interface ExecutableCreator {
String regexp) throws CommandNotFoundException;
/**
+ * Method that creates an executable for read data from disk.
+ *
+ * @param file The file where to read the data
+ * @param asyncResultListener The listener where to return partial results
+ * @return ReadExecutable A {@link ReadExecutable} executable implementation reference
+ * @throws CommandNotFoundException If the executable can't be created
+ */
+ ReadExecutable createReadExecutable(
+ String file, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException;
+
+ /**
* Method that creates an executable for resolves the real
* path of a symlink or file system object.
*
@@ -333,4 +345,38 @@ public interface ExecutableCreator {
*/
ResolveLinkExecutable createResolveLinkExecutable(String fso) throws CommandNotFoundException;
+ /**
+ * Method that creates an executable for send a signal to the current process.
+ *
+ * @param process The process which to send the signal
+ * @param signal The signal to send
+ * @return SendSignalExecutable A {@link SendSignalExecutable} executable implementation reference
+ * @throws CommandNotFoundException If the executable can't be created
+ */
+ SendSignalExecutable createSendSignalExecutable(
+ int process, SIGNAL signal) throws CommandNotFoundException;
+
+ /**
+ * Method that creates an executable for send a kill signal to the current process.
+ *
+ * @param process The process which to send the signal
+ * @param signal The signal to send
+ * @return SendSignalExecutable A {@link SendSignalExecutable} executable implementation reference
+ * @throws CommandNotFoundException If the executable can't be created
+ */
+ SendSignalExecutable createKillExecutable(
+ int process) throws CommandNotFoundException;
+
+ /**
+ * Method that creates an executable for write data to disk.
+ *
+ * @param file The file where to write the data
+ * @param asyncResultListener The listener where to return partial results
+ * @return WriteExecutable A {@link WriteExecutable} executable implementation reference
+ * @throws CommandNotFoundException If the executable can't be created
+ */
+ WriteExecutable createWriteExecutable(
+ String file, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException;
+
}
diff --git a/src/com/cyanogenmod/explorer/commands/ReadExecutable.java b/src/com/cyanogenmod/explorer/commands/ReadExecutable.java
new file mode 100644
index 00000000..d5a90d41
--- /dev/null
+++ b/src/com/cyanogenmod/explorer/commands/ReadExecutable.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands;
+
+/**
+ * An interface that represents an executable for read files.
+ */
+public interface ReadExecutable extends AsyncResultExecutable {/**NON BLOCK**/}
diff --git a/src/com/cyanogenmod/explorer/commands/SIGNAL.java b/src/com/cyanogenmod/explorer/commands/SIGNAL.java
new file mode 100644
index 00000000..fd349e1a
--- /dev/null
+++ b/src/com/cyanogenmod/explorer/commands/SIGNAL.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands;
+
+/**
+ * An enumeration of allow signals that can send to programs.
+ */
+public enum SIGNAL {
+
+ /**
+ * Hangup (POSIX).
+ */
+ SIGHUP(1),
+ /**
+ * Interrupt (ANSI).
+ */
+ SIGINT(2),
+ /**
+ * Quit (POSIX).
+ */
+ SIGQUIT(3),
+ /**
+ * Illegal instruction (ANSI).
+ */
+ SIGILL(4),
+ /**
+ * Trace trap (POSIX).
+ */
+ SIGTRAP(5),
+ /**
+ * Abort (ANSI).
+ */
+ SIGABRT(6),
+ /**
+ * Floating-point exception (ANSI).
+ */
+ SIGFPE(8),
+ /**
+ * Kill, unblockable (POSIX).
+ */
+ SIGKILL(9),
+ /**
+ * Segmentation violation (ANSI).
+ */
+ SIGSEGV(11),
+ /**
+ * Broken pipe (POSIX).
+ */
+ SIGPIPE(13),
+ /**
+ * Alarm clock (POSIX).
+ */
+ SIGALRM(14),
+ /**
+ * Termination (ANSI).
+ */
+ SIGTERM(15);
+
+ private final int mSignal;
+
+ /**
+ * Constructor of <code>SIGNAL</code>
+ *
+ * @param signal The signal
+ */
+ private SIGNAL(int signal) {
+ this.mSignal = signal;
+ }
+
+ /**
+ * Method that returns the signal
+ *
+ * @return int The signal
+ */
+ public int getSignal() {
+ return this.mSignal;
+ }
+
+}
diff --git a/src/com/cyanogenmod/explorer/commands/SendSignalExecutable.java b/src/com/cyanogenmod/explorer/commands/SendSignalExecutable.java
new file mode 100644
index 00000000..4c74bbf9
--- /dev/null
+++ b/src/com/cyanogenmod/explorer/commands/SendSignalExecutable.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands;
+
+/**
+ * An interface that represents an executable for send signal to processes.
+ */
+public interface SendSignalExecutable extends SyncResultExecutable {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ Boolean getResult();
+}
diff --git a/src/com/cyanogenmod/explorer/commands/WriteExecutable.java b/src/com/cyanogenmod/explorer/commands/WriteExecutable.java
new file mode 100644
index 00000000..50d37df8
--- /dev/null
+++ b/src/com/cyanogenmod/explorer/commands/WriteExecutable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An interface that represents an executable for write data to the disk.
+ */
+public interface WriteExecutable extends AsyncResultExecutable {
+
+ /**
+ * Method that returns the stream where write the data.<br/>
+ * <br/>
+ * NOTE: Don't close this buffer. It is internally closed.
+ *
+ * @return OutputStream The stream where write the data
+ * @throws IOException If the buffer couldn't be created
+ */
+ public OutputStream createOutputStream() throws IOException;
+}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgram.java b/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgram.java
index c53ceb38..858c5e0e 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgram.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgram.java
@@ -18,6 +18,7 @@ package com.cyanogenmod.explorer.commands.shell;
import com.cyanogenmod.explorer.commands.AsyncResultExecutable;
import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.SIGNAL;
import com.cyanogenmod.explorer.util.FileHelper;
import java.util.ArrayList;
@@ -58,6 +59,7 @@ public abstract class AsyncResultProgram
private boolean mCanceled;
private OnCancelListener mOnCancelListener;
+ private OnEndListener mOnEndListener;
private StringBuffer mTempBuffer;
@@ -95,6 +97,7 @@ public abstract class AsyncResultProgram
this.mPartialDataType = Collections.synchronizedList(new ArrayList<Byte>());
this.mTempBuffer = new StringBuffer();
this.mOnCancelListener = null;
+ this.mOnEndListener = null;
this.mCanceled = false;
}
@@ -263,6 +266,38 @@ public abstract class AsyncResultProgram
* {@inheritDoc}
*/
@Override
+ public final boolean end() {
+ // Internally this method do the same things that cancel method, but invokes
+ // onEnd instead of onCancel
+
+ //Is't cancelable by definition?
+ if (!isCancelable()) {
+ return false;
+ }
+
+ //Stop the thread
+ synchronized (this.mSync) {
+ this.mWorkerThread.mAlive = false;
+ this.mSync.notify();
+ }
+
+ //Notify ending
+ SIGNAL signal = onRequestEnd();
+ if (this.mOnEndListener != null) {
+ if (signal == null) {
+ this.mCanceled = this.mOnEndListener.onEnd();
+ } else {
+ this.mCanceled = this.mOnEndListener.onSendSignal(signal);
+ }
+ return this.mCanceled;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public final void setOnCancelListener(OnCancelListener onCancelListener) {
this.mOnCancelListener = onCancelListener;
}
@@ -271,12 +306,31 @@ public abstract class AsyncResultProgram
* {@inheritDoc}
*/
@Override
+ public final void setOnEndListener(OnEndListener onEndListener) {
+ this.mOnEndListener = onEndListener;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public boolean isCancelable() {
//By defect an asynchronous command is cancelable
return true;
}
/**
+ * Method that returns if the command is expected to finalize by it self, or needs
+ * a call to end method.
+ *
+ * @return boolean If the command is expected to finalize by it self.
+ */
+ @SuppressWarnings("static-method")
+ public boolean isExpectEnd() {
+ return true;
+ }
+
+ /**
* An internal class for process partial results sequentially in a
* secure way.
*/
diff --git a/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgramListener.java b/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgramListener.java
index 3f986a7e..f91854e4 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgramListener.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/AsyncResultProgramListener.java
@@ -16,12 +16,21 @@
package com.cyanogenmod.explorer.commands.shell;
+import com.cyanogenmod.explorer.commands.SIGNAL;
+
/**
* An interface for communicate shell results in a asynchronous way.
*/
public interface AsyncResultProgramListener {
/**
+ * Method invoked when an end of the program start.
+ *
+ * @return SIGNAL The signal to send to the process
+ */
+ SIGNAL onRequestEnd();
+
+ /**
* Method invoked when the parse of results will start.
*/
void onStartParsePartialResult();
diff --git a/src/com/cyanogenmod/explorer/commands/shell/Command.java b/src/com/cyanogenmod/explorer/commands/shell/Command.java
index 7bbc2f38..70270028 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/Command.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/Command.java
@@ -25,6 +25,8 @@ import com.cyanogenmod.explorer.R;
import com.cyanogenmod.explorer.console.CommandNotFoundException;
import com.cyanogenmod.explorer.console.ExecutionException;
import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+import com.cyanogenmod.explorer.preferences.ExplorerSettings;
+import com.cyanogenmod.explorer.preferences.Preferences;
import com.cyanogenmod.explorer.util.ShellHelper;
import org.xmlpull.v1.XmlPullParserException;
@@ -53,6 +55,8 @@ public abstract class Command {
private static String sStartCodeCmd;
private static String sExitCodeCmd;
+ private boolean mTrace;
+
/**
* @Constructor of <code>Command</code>
*
@@ -95,6 +99,27 @@ public abstract class Command {
//Load the command info
getCommandInfo(ExplorerApplication.getInstance().getResources());
+
+ // Get the current trace value
+ reloadTrace();
+ }
+
+ /**
+ * Method that return id the command had to trace his operations
+ *
+ * @return boolean If the command had to trace
+ */
+ public boolean isTrace() {
+ return this.mTrace;
+ }
+
+ /**
+ * Method that reload the status of trace setting
+ */
+ public final void reloadTrace() {
+ this.mTrace = Preferences.getSharedPreferences().getBoolean(
+ ExplorerSettings.SETTINGS_SHOW_TRACES.getId(),
+ ((Boolean)ExplorerSettings.SETTINGS_SHOW_TRACES.getDefaultValue()).booleanValue());
}
/**
diff --git a/src/com/cyanogenmod/explorer/commands/shell/ExecCommand.java b/src/com/cyanogenmod/explorer/commands/shell/ExecCommand.java
index 91130f33..384b8b27 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/ExecCommand.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/ExecCommand.java
@@ -18,6 +18,7 @@ package com.cyanogenmod.explorer.commands.shell;
import com.cyanogenmod.explorer.commands.AsyncResultListener;
import com.cyanogenmod.explorer.commands.ExecExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
import com.cyanogenmod.explorer.console.CommandNotFoundException;
import com.cyanogenmod.explorer.console.ExecutionException;
import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
@@ -90,6 +91,14 @@ public class ExecCommand extends AsyncResultProgram implements ExecExecutable {
* {@inheritDoc}
*/
@Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public int getExitCode() {
return this.mExitCode;
}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/FindCommand.java b/src/com/cyanogenmod/explorer/commands/shell/FindCommand.java
index 76088999..2381bbce 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/FindCommand.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/FindCommand.java
@@ -20,6 +20,7 @@ import android.util.Log;
import com.cyanogenmod.explorer.commands.AsyncResultListener;
import com.cyanogenmod.explorer.commands.FindExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
import com.cyanogenmod.explorer.console.CommandNotFoundException;
import com.cyanogenmod.explorer.console.ExecutionException;
import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
@@ -53,7 +54,7 @@ public class FindCommand extends AsyncResultProgram implements FindExecutable {
private static final String TAG = "FindCommand"; //$NON-NLS-1$
- private static final String ID_FIND_DIRECTORY = "find"; //$NON-NLS-1$
+ private static final String ID = "find"; //$NON-NLS-1$
private final String mDirectory;
private final List<FileSystemObject> mFiles;
@@ -70,7 +71,7 @@ public class FindCommand extends AsyncResultProgram implements FindExecutable {
public FindCommand(
String directory, Query query, AsyncResultListener asyncResultListener)
throws InvalidCommandDefinitionException {
- super(ID_FIND_DIRECTORY, asyncResultListener, createArgs(directory, query));
+ super(ID, asyncResultListener, createArgs(directory, query));
this.mFiles = new ArrayList<FileSystemObject>();
this.mPartial = ""; //$NON-NLS-1$
this.mDirectory = directory;
@@ -199,6 +200,14 @@ public class FindCommand extends AsyncResultProgram implements FindExecutable {
* {@inheritDoc}
*/
@Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public boolean isIgnoreShellStdErrCheck() {
return true;
}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java b/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java
index 0ef83971..fa8e8b9c 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java
@@ -20,6 +20,7 @@ import android.util.Log;
import com.cyanogenmod.explorer.commands.AsyncResultListener;
import com.cyanogenmod.explorer.commands.FolderUsageExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
import com.cyanogenmod.explorer.console.CommandNotFoundException;
import com.cyanogenmod.explorer.console.ExecutionException;
import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
@@ -191,6 +192,14 @@ public class FolderUsageCommand extends AsyncResultProgram implements FolderUsag
* {@inheritDoc}
*/
@Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public boolean isIgnoreShellStdErrCheck() {
return true;
}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/Program.java b/src/com/cyanogenmod/explorer/commands/shell/Program.java
index d2d4a838..fbba9695 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/Program.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/Program.java
@@ -22,6 +22,8 @@ import com.cyanogenmod.explorer.console.ExecutionException;
import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
import com.cyanogenmod.explorer.console.NoSuchFileOrDirectory;
+import java.io.OutputStream;
+
/**
* An abstract class that represents a command that need a shell
@@ -34,14 +36,22 @@ public abstract class Program extends Command implements Executable {
*/
public interface ProgramListener {
/**
- * Invoked when a request the program need to write in the shell program. This
- * method only can be invoked when {@link #onProgramReady()} method was invoked.
+ * Invoked when a request the program need to write in the shell program.
*
* @param data The data to write to the shell console
+ * @param offset The initial position in buffer to store the bytes read from this stream
+ * @param byteCount The maximum number of bytes to store in b
* @return boolean If the write was transmitted successfully
* @throws ExecutionException If the console is not ready
*/
- boolean onRequestWrite(byte[] data) throws ExecutionException;
+ boolean onRequestWrite(byte[] data, int offset, int byteCount) throws ExecutionException;
+
+ /**
+ * Method that returns the output stream of the console.
+ *
+ * @return OutputStream The output stream of the console
+ */
+ OutputStream getOutputStream();
}
// The listener for the program
@@ -75,7 +85,7 @@ public abstract class Program extends Command implements Executable {
/**
* Method that returns the program listener
- *
+ *
* @return ProgramListener The program listener
*/
protected ProgramListener getProgramListener() {
@@ -84,7 +94,7 @@ public abstract class Program extends Command implements Executable {
/**
* Method that sets the program listener
- *
+ *
* @param programListener The program listener
*/
public void setProgramListener(ProgramListener programListener) {
@@ -123,12 +133,4 @@ public abstract class Program extends Command implements Executable {
/**NON BLOCK**/
}
- /**
- * Method invoked when the program is ready. At this point the program allow
- * write back to the stdout.
- */
- public void onProgramReady() {
- /**NON BLOCK**/
- }
-
}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/ReadCommand.java b/src/com/cyanogenmod/explorer/commands/shell/ReadCommand.java
new file mode 100644
index 00000000..69ed894f
--- /dev/null
+++ b/src/com/cyanogenmod/explorer/commands/shell/ReadCommand.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands.shell;
+
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.ReadExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
+import com.cyanogenmod.explorer.console.CommandNotFoundException;
+import com.cyanogenmod.explorer.console.ExecutionException;
+import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+
+/**
+ * A class for read a file
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?cat"}
+ */
+public class ReadCommand extends AsyncResultProgram implements ReadExecutable {
+
+ private static final String ID = "read"; //$NON-NLS-1$
+
+ /**
+ * Constructor of <code>ExecCommand</code>.
+ *
+ * @param file The file to read
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ReadCommand(
+ String file, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(ID, asyncResultListener, new String[]{file});
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {/** NON BLOCK **/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean canceled) {/** NON BLOCK **/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {
+ //If a listener is defined, then send the partial result
+ if (partialIn != null && partialIn.length() > 0) {
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onPartialResult(partialIn.getBytes());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParseErrorPartialResult(String partialErr) {/** NON BLOCK **/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIgnoreShellStdErrCheck() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ //Ignore exit code 143 (canceled)
+ //Ignore exit code 137 (kill -9)
+ if (exitCode != 0 && exitCode != 143 && exitCode != 137) {
+ throw new ExecutionException(
+ "exitcode != 0 && != 143 && != 137"); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/ResolveLinkCommand.java b/src/com/cyanogenmod/explorer/commands/shell/ResolveLinkCommand.java
index e2bdb3e7..dfeb908a 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/ResolveLinkCommand.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/ResolveLinkCommand.java
@@ -21,6 +21,7 @@ import com.cyanogenmod.explorer.console.CommandNotFoundException;
import com.cyanogenmod.explorer.console.ExecutionException;
import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
import com.cyanogenmod.explorer.model.FileSystemObject;
+import com.cyanogenmod.explorer.util.FileHelper;
import com.cyanogenmod.explorer.util.ParseHelper;
import java.io.BufferedReader;
@@ -47,7 +48,10 @@ public class ResolveLinkCommand extends SyncResultProgram implements ResolveLink
* @throws InvalidCommandDefinitionException If the command has an invalid definition
*/
public ResolveLinkCommand(String src) throws InvalidCommandDefinitionException {
- super(ID, src, new File(src).getParentFile().getAbsolutePath());
+ super(ID, src,
+ (src.compareTo(FileHelper.ROOT_DIRECTORY) == 0) ?
+ FileHelper.ROOT_DIRECTORY :
+ new File(src).getParentFile().getAbsolutePath());
}
/**
diff --git a/src/com/cyanogenmod/explorer/commands/shell/SendSignalCommand.java b/src/com/cyanogenmod/explorer/commands/shell/SendSignalCommand.java
new file mode 100644
index 00000000..0ba879b2
--- /dev/null
+++ b/src/com/cyanogenmod/explorer/commands/shell/SendSignalCommand.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands.shell;
+
+import com.cyanogenmod.explorer.commands.SIGNAL;
+import com.cyanogenmod.explorer.commands.SendSignalExecutable;
+import com.cyanogenmod.explorer.console.CommandNotFoundException;
+import com.cyanogenmod.explorer.console.ExecutionException;
+import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+
+import java.text.ParseException;
+
+
+/**
+ * A class that represents a command for send signal to processes
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?kill"}
+ */
+public class SendSignalCommand extends SyncResultProgram implements SendSignalExecutable {
+
+ private static final String ID_SIGNAL = "sendsignal"; //$NON-NLS-1$
+ private static final String ID_TERMINATE = "terminate"; //$NON-NLS-1$
+
+ /**
+ * Constructor of <code>SendSignalCommand</code>.
+ *
+ * @param process The process which to send the signal
+ * @param signal The signal to send
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public SendSignalCommand(int process, SIGNAL signal) throws InvalidCommandDefinitionException {
+ super(ID_SIGNAL, String.valueOf(signal.getSignal()), String.valueOf(process));
+ }
+
+ /**
+ * Constructor of <code>SendSignalCommand</code>. This method sends a kill (terminate)
+ *
+ * @param process The process which to send the signal
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public SendSignalCommand(int process) throws InvalidCommandDefinitionException {
+ super(ID_TERMINATE, String.valueOf(process));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ // Always true. If fails, the checkExitCode will raise an exception
+ return Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/Shell.java b/src/com/cyanogenmod/explorer/commands/shell/Shell.java
index bb311233..0cf288e7 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/Shell.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/Shell.java
@@ -98,28 +98,28 @@ public abstract class Shell extends Command {
throws InsufficientPermissionsException, NoSuchFileOrDirectory,
CommandNotFoundException, ExecutionException, ReadOnlyFilesystemException {
//Check problems in the standard error
- if (exitCode != 0 && err.indexOf("No such file or directory") != -1) { //$NON-NLS-1$
+ if (err.indexOf("No such file or directory") != -1) { //$NON-NLS-1$
throw new NoSuchFileOrDirectory();
}
//Normally usage code is generated for invalid commands, but let's assume
//that the invalid command is generated for and error caused for non
//existing directory
- if (exitCode != 0 && err.indexOf("Usage:") != -1) { //$NON-NLS-1$
+ if (err.indexOf("Usage:") != -1) { //$NON-NLS-1$
throw new NoSuchFileOrDirectory();
}
- if (exitCode != 0 && err.indexOf("Permission denied") != -1) { //$NON-NLS-1$
+ if (err.indexOf("Permission denied") != -1) { //$NON-NLS-1$
if (program instanceof SyncResultExecutable) {
throw new InsufficientPermissionsException((SyncResultExecutable)program);
}
throw new InsufficientPermissionsException();
}
- if (exitCode != 0 && err.indexOf("Operation not permitted") != -1) { //$NON-NLS-1$
+ if (err.indexOf("Operation not permitted") != -1) { //$NON-NLS-1$
if (program instanceof SyncResultExecutable) {
throw new InsufficientPermissionsException((SyncResultExecutable)program);
}
throw new InsufficientPermissionsException();
}
- if (exitCode != 0 && err.indexOf("Read-only file system") != -1) { //$NON-NLS-1$
+ if (err.indexOf("Read-only file system") != -1) { //$NON-NLS-1$
if (program instanceof WritableExecutable) {
throw new ReadOnlyFilesystemException(
((WritableExecutable)program).getWritableMountPoint());
diff --git a/src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java b/src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java
index 909f4bcc..08ddbfb6 100644
--- a/src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java
+++ b/src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java
@@ -42,7 +42,11 @@ import com.cyanogenmod.explorer.commands.MoveExecutable;
import com.cyanogenmod.explorer.commands.ParentDirExecutable;
import com.cyanogenmod.explorer.commands.ProcessIdExecutable;
import com.cyanogenmod.explorer.commands.QuickFolderSearchExecutable;
+import com.cyanogenmod.explorer.commands.ReadExecutable;
import com.cyanogenmod.explorer.commands.ResolveLinkExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
+import com.cyanogenmod.explorer.commands.SendSignalExecutable;
+import com.cyanogenmod.explorer.commands.WriteExecutable;
import com.cyanogenmod.explorer.console.CommandNotFoundException;
import com.cyanogenmod.explorer.console.shell.ShellConsole;
import com.cyanogenmod.explorer.model.Group;
@@ -420,6 +424,20 @@ public class ShellExecutableCreator implements ExecutableCreator {
* {@inheritDoc}
*/
@Override
+ public ReadExecutable createReadExecutable(
+ String file, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException {
+ try {
+ return new ReadCommand(file, asyncResultListener);
+ } catch (InvalidCommandDefinitionException icdEx) {
+ throw new CommandNotFoundException("ReadCommand", icdEx); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public ResolveLinkExecutable createResolveLinkExecutable(String fso)
throws CommandNotFoundException {
try {
@@ -429,4 +447,44 @@ public class ShellExecutableCreator implements ExecutableCreator {
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SendSignalExecutable createSendSignalExecutable(int process, SIGNAL signal)
+ throws CommandNotFoundException {
+ try {
+ return new SendSignalCommand(process, signal);
+ } catch (InvalidCommandDefinitionException icdEx) {
+ throw new CommandNotFoundException("SendSignalCommand", icdEx); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SendSignalExecutable createKillExecutable(int process)
+ throws CommandNotFoundException {
+ try {
+ return new SendSignalCommand(process);
+ } catch (InvalidCommandDefinitionException icdEx) {
+ throw new CommandNotFoundException("SendSignalCommand", icdEx); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public WriteExecutable createWriteExecutable(
+ String file, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException {
+ try {
+ return new WriteCommand(file, asyncResultListener);
+ } catch (InvalidCommandDefinitionException icdEx) {
+ throw new CommandNotFoundException("WriteCommand", icdEx); //$NON-NLS-1$
+ }
+ }
+
}
diff --git a/src/com/cyanogenmod/explorer/commands/shell/WriteCommand.java b/src/com/cyanogenmod/explorer/commands/shell/WriteCommand.java
new file mode 100644
index 00000000..fbd3a6b0
--- /dev/null
+++ b/src/com/cyanogenmod/explorer/commands/shell/WriteCommand.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands.shell;
+
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.SIGNAL;
+import com.cyanogenmod.explorer.commands.WriteExecutable;
+import com.cyanogenmod.explorer.console.CommandNotFoundException;
+import com.cyanogenmod.explorer.console.ExecutionException;
+import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A class for write data to disk.<br/>
+ * <br/>
+ * User MUST call the {@link #createOutputStream()} to get the output stream where
+ * write the data.<br/>. When no more exist then user MUST call the onEnd method
+ * of the asynchronous listener.<br/>
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?dd"}
+ */
+public class WriteCommand extends AsyncResultProgram implements WriteExecutable {
+
+ private static final String ID = "write"; //$NON-NLS-1$
+
+ private static final long TIMEOUT = 1000L;
+
+ /**
+ * @hide
+ */
+ final Object mSync = new Object();
+ private boolean mReady;
+ /**
+ * @hide
+ */
+ boolean mError;
+
+ /**
+ * Constructor of <code>WriteCommand</code>.
+ *
+ * @param file The file where to write the data
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public WriteCommand(
+ String file, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(ID, asyncResultListener, file);
+ this.mReady = false;
+ this.mError = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isExpectEnd() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public OutputStream createOutputStream() throws IOException {
+
+ // Wait until command is ready
+ synchronized (this.mSync) {
+ if (!this.mReady) {
+ try {
+ this.mSync.wait(TIMEOUT);
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+ return getProgramListener().getOutputStream();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {
+ synchronized (this.mSync) {
+ this.mReady = true;
+ this.mSync.notify();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean canceled) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ try {
+ if (this.getProgramListener().getOutputStream() != null) {
+ this.getProgramListener().getOutputStream().flush();
+ }
+ } catch (Exception ex) {/**NON BLOCK**/}
+ try {
+ Thread.yield();
+ } catch (Exception ex) {/**NON BLOCK**/}
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIgnoreShellStdErrCheck() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ //Ignore exit code 143 (canceled)
+ //Ignore exit code 137 (kill -9)
+ if (exitCode != 0 && exitCode != 143 && exitCode != 137) {
+ throw new ExecutionException(
+ "exitcode != 0 && != 143 && != 137"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/src/com/cyanogenmod/explorer/console/Console.java b/src/com/cyanogenmod/explorer/console/Console.java
index e9cf63c0..3ffcecd7 100644
--- a/src/com/cyanogenmod/explorer/console/Console.java
+++ b/src/com/cyanogenmod/explorer/console/Console.java
@@ -26,7 +26,8 @@ import com.cyanogenmod.explorer.preferences.Preferences;
* This class represents a class for executing commands in the operating system layer,
* being the base for all type of consoles (shell, java, ...).
*/
-public abstract class Console implements AsyncResultExecutable.OnCancelListener {
+public abstract class Console
+ implements AsyncResultExecutable.OnEndListener, AsyncResultExecutable.OnCancelListener {
private boolean mTrace;
diff --git a/src/com/cyanogenmod/explorer/console/ConsoleBuilder.java b/src/com/cyanogenmod/explorer/console/ConsoleBuilder.java
index 511e564f..ac9c8f25 100644
--- a/src/com/cyanogenmod/explorer/console/ConsoleBuilder.java
+++ b/src/com/cyanogenmod/explorer/console/ConsoleBuilder.java
@@ -21,6 +21,7 @@ import android.content.SharedPreferences.Editor;
import android.util.Log;
import android.widget.Toast;
+import com.cyanogenmod.explorer.ExplorerApplication;
import com.cyanogenmod.explorer.R;
import com.cyanogenmod.explorer.commands.shell.InvalidCommandDefinitionException;
import com.cyanogenmod.explorer.console.shell.NonPriviledgeConsole;
@@ -147,9 +148,14 @@ public final class ConsoleBuilder {
ConsoleHolder holder = null;
try {
//Create the console, destroy the current console, and marks as current
- holder = new ConsoleHolder(createPrivilegedConsole(context, FileHelper.ROOT_DIRECTORY));
+ holder = new ConsoleHolder(
+ createAndCheckPrivilegedConsole(context, FileHelper.ROOT_DIRECTORY));
destroyConsole();
sHolder = holder;
+
+ // Change also the background console to privileged
+ ExplorerApplication.changeBackgroundConsoleToPriviligedConsole();
+
return sHolder.getConsole() instanceof PrivilegedConsole;
} catch (Throwable e) {
@@ -216,9 +222,14 @@ public final class ConsoleBuilder {
if (sHolder == null) {
sHolder = (requiredSuConsole)
? new ConsoleHolder(
- createPrivilegedConsole(context, FileHelper.ROOT_DIRECTORY))
+ createAndCheckPrivilegedConsole(
+ context, FileHelper.ROOT_DIRECTORY))
: new ConsoleHolder(
createNonPrivilegedConsole(context, FileHelper.ROOT_DIRECTORY));
+ if (requiredSuConsole) {
+ // Change also the background console to privileged
+ ExplorerApplication.changeBackgroundConsoleToPriviligedConsole();
+ }
}
return sHolder.getConsole();
}
@@ -276,7 +287,39 @@ public final class ConsoleBuilder {
public static Console createPrivilegedConsole(Context context, String initialDirectory)
throws FileNotFoundException, IOException, InvalidCommandDefinitionException,
ConsoleAllocException, InsufficientPermissionsException {
- return createPrivilegedConsole(context, initialDirectory, true);
+ PrivilegedConsole console = new PrivilegedConsole(initialDirectory);
+ console.setBufferSize(context.getResources().getInteger(R.integer.buffer_size));
+ console.alloc();
+ if (console.getIdentity().getUser().getId() != ROOT_UID) {
+ //The console is not a privileged console
+ try {
+ console.dealloc();
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ throw new InsufficientPermissionsException(null);
+ }
+ return console;
+ }
+
+ /**
+ * Method that creates a new privileged console. If the allocation of the
+ * privileged console fails, the a non privileged console
+ *
+ * @param context The current context
+ * @param initialDirectory The initial directory of the console
+ * @return Console The privileged console
+ * @throws FileNotFoundException If the initial directory not exists
+ * @throws IOException If initial directory can't not be checked
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ * @throws ConsoleAllocException If the console can't be allocated
+ * @throws InsufficientPermissionsException If the console created is not a privileged console
+ * @see PrivilegedConsole
+ */
+ public static Console createAndCheckPrivilegedConsole(Context context, String initialDirectory)
+ throws FileNotFoundException, IOException, InvalidCommandDefinitionException,
+ ConsoleAllocException, InsufficientPermissionsException {
+ return createAndCheckPrivilegedConsole(context, initialDirectory, true);
}
/**
@@ -294,7 +337,7 @@ public final class ConsoleBuilder {
* @throws InsufficientPermissionsException If the console created is not a privileged console
* @see PrivilegedConsole
*/
- public static Console createPrivilegedConsole(
+ public static Console createAndCheckPrivilegedConsole(
Context context, String initialDirectory, boolean silent)
throws FileNotFoundException, IOException, InvalidCommandDefinitionException,
ConsoleAllocException, InsufficientPermissionsException {
@@ -350,7 +393,6 @@ public final class ConsoleBuilder {
// Rethrow the exception
throw caEx;
}
-
}
}
diff --git a/src/com/cyanogenmod/explorer/console/shell/ShellConsole.java b/src/com/cyanogenmod/explorer/console/shell/ShellConsole.java
index 7c8b4b64..c445d1bb 100644
--- a/src/com/cyanogenmod/explorer/console/shell/ShellConsole.java
+++ b/src/com/cyanogenmod/explorer/console/shell/ShellConsole.java
@@ -25,6 +25,7 @@ import com.cyanogenmod.explorer.commands.ExecutableFactory;
import com.cyanogenmod.explorer.commands.GroupsExecutable;
import com.cyanogenmod.explorer.commands.IdentityExecutable;
import com.cyanogenmod.explorer.commands.ProcessIdExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
import com.cyanogenmod.explorer.commands.shell.AsyncResultProgram;
import com.cyanogenmod.explorer.commands.shell.Command;
import com.cyanogenmod.explorer.commands.shell.InvalidCommandDefinitionException;
@@ -80,15 +81,14 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
//Process References
private final Object mSync = new Object();
- private final Object mPartialSync = new Object();
/**
* @hide
*/
- boolean mActive = false;
+ final Object mPartialSync = new Object();
/**
* @hide
*/
- boolean mReady = false;
+ boolean mActive = false;
private boolean mFinished = true;
private Process mProc = null;
/**
@@ -317,7 +317,11 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
public final void dealloc() {
synchronized (this.mSync) {
if (this.mActive) {
- this.mProc.destroy();
+// Call destroy doesn't ensure that the process can be destroyed. Delegate in close
+// his buffers to do kill the process.
+// try {
+// this.mProc.destroy();
+// } catch (Throwable e) {/**NON BLOCK**/}
this.mActive = false;
this.mFinished = true;
@@ -441,7 +445,6 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
this.mActiveCommand = program;
//Reset the buffers
- this.mReady = false;
this.mStarted = false;
this.mCanceled = false;
this.mSbIn = new StringBuffer();
@@ -475,9 +478,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
program.setProgramListener(this);
if (program instanceof AsyncResultProgram) {
((AsyncResultProgram)program).setOnCancelListener(this);
- synchronized (this.mPartialSync) {
- ((AsyncResultProgram)program).startParsePartialResult();
- }
+ ((AsyncResultProgram)program).setOnEndListener(this);
}
//Send the command + a control code with exit code
@@ -485,6 +486,10 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
//This control code is unique in every invocation and is secure random
//generated (control code 1 + exit code + control code 2)
try {
+ boolean hasEndControl = (!(program instanceof AsyncResultProgram) ||
+ (program instanceof AsyncResultProgram &&
+ ((AsyncResultProgram)program).isExpectEnd()));
+
this.mStartControlPattern = startId1 + "\\d{1,3}" + startId2 + "\\n"; //$NON-NLS-1$ //$NON-NLS-2$
this.mEndControlPattern = endId1 + "\\d{1,3}" + endId2; //$NON-NLS-1$
String startCmd =
@@ -504,10 +509,12 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
.append(" ") //$NON-NLS-1$
.append(cmd)
.append(" ") //$NON-NLS-1$
- .append(args)
- .append(" ") //$NON-NLS-1$
- .append(endCmd)
- .append(FileHelper.NEWLINE);
+ .append(args);
+ if (hasEndControl) {
+ sb = sb.append(" ") //$NON-NLS-1$
+ .append(endCmd);
+ }
+ sb.append(FileHelper.NEWLINE);
this.mOut.write(sb.toString().getBytes());
} catch (InvalidCommandDefinitionException icdEx) {
throw new CommandNotFoundException(
@@ -544,9 +551,6 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
String.valueOf(exitCode)));
}
- // Process is not ready
- this.mReady = false;
-
//Check if invocation was successfully or not
if (!program.isIgnoreShellStdErrCheck()) {
this.mShell.checkStdErr(this.mActiveCommand, exitCode, this.mSbErr.toString());
@@ -616,28 +620,26 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
if (!ShellConsole.this.mStarted) {
ShellConsole.this.mStarted =
isCommandStarted(ShellConsole.this.mSbIn);
- sb.append(ShellConsole.this.mSbIn.toString());
+ sb = new StringBuffer(ShellConsole.this.mSbIn.toString());
+ if (ShellConsole.this.mActiveCommand instanceof AsyncResultProgram
+ && ShellConsole.this.mStarted) {
+ synchronized (ShellConsole.this.mPartialSync) {
+ ((AsyncResultProgram)ShellConsole.
+ this.mActiveCommand).
+ startParsePartialResult();
+ }
+ }
} else {
sb.append((char)r);
}
- //
- if (ShellConsole.this.mStarted &&
- ShellConsole.this.mActiveCommand != null) {
- if (!ShellConsole.this.mReady) {
- ShellConsole.this.mReady = true;
- ShellConsole.this.mActiveCommand.onProgramReady();
- }
-
- }
-
//Notify asynchronous partial data
if (ShellConsole.this.mStarted &&
ShellConsole.this.mActiveCommand != null &&
ShellConsole.this.mActiveCommand instanceof AsyncResultProgram) {
AsyncResultProgram program =
((AsyncResultProgram)ShellConsole.this.mActiveCommand);
- program.parsePartialResult(new String(new char[]{(char)r}));
+ program.parsePartialResult(sb.toString());
}
}
@@ -659,17 +661,33 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
if (!ShellConsole.this.mStarted) {
ShellConsole.this.mStarted =
isCommandStarted(ShellConsole.this.mSbIn);
- sb.append(ShellConsole.this.mSbIn.toString());
+ sb = new StringBuffer(ShellConsole.this.mSbIn.toString());
+ if (ShellConsole.this.mActiveCommand instanceof AsyncResultProgram
+ && ShellConsole.this.mStarted) {
+ synchronized (ShellConsole.this.mPartialSync) {
+ ((AsyncResultProgram)ShellConsole.
+ this.mActiveCommand).
+ startParsePartialResult();
+ }
+ }
} else {
sb.append(s);
}
- //Notify asynchronous partial data
- if (ShellConsole.this.mActiveCommand != null &&
- ShellConsole.this.mActiveCommand instanceof AsyncResultProgram) {
- AsyncResultProgram program =
- ((AsyncResultProgram)ShellConsole.this.mActiveCommand);
- program.parsePartialResult(s);
+ //Check if the command has finished
+ if (isCommandFinished(ShellConsole.this.mSbIn, sb)) {
+ //Notify asynchronous partial data
+ if (ShellConsole.this.mActiveCommand != null &&
+ ShellConsole.this.mActiveCommand
+ instanceof AsyncResultProgram) {
+ AsyncResultProgram program =
+ ((AsyncResultProgram)ShellConsole.this.mActiveCommand);
+ program.parsePartialResult(sb.toString());
+ }
+
+ //Notify the end
+ notifyProcessFinished();
+ break;
}
//Wait for buffer to be filled
@@ -678,12 +696,6 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
} catch (Throwable ex) {
/**NON BLOCK**/
}
-
- //Check if the command has finished
- if (isCommandFinished(ShellConsole.this.mSbIn)) {
- //Notify the end
- notifyProcessFinished();
- }
}
//Audit (if not canceled)
@@ -869,7 +881,6 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
void notifyProcessFinished() {
synchronized (ShellConsole.this.mSync) {
if (this.mActive) {
- this.mReady = false;
this.mSync.notify();
this.mFinished = true;
}
@@ -904,11 +915,19 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
* @return boolean If the command has finished
* @hide
*/
- boolean isCommandFinished(StringBuffer stdin) {
+ boolean isCommandFinished(StringBuffer stdin, StringBuffer partial) {
Pattern pattern = Pattern.compile(this.mEndControlPattern);
if (stdin == null) return false;
Matcher matcher = pattern.matcher(stdin.toString());
- return matcher.find();
+ boolean ret = matcher.find();
+ // Remove partial
+ if (ret && partial != null) {
+ matcher = pattern.matcher(partial.toString());
+ if (matcher.find()) {
+ partial.replace(matcher.start(), matcher.end(), ""); //$NON-NLS-1$
+ }
+ }
+ return ret;
}
/**
@@ -965,6 +984,13 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
if (!(this.mActiveCommand instanceof AsyncResultProgram)) {
return false;
}
+ // Check background console
+ try {
+ ExplorerApplication.getBackgroundConsole();
+ } catch (Exception e) {
+ Log.w(TAG, "There is not background console. Not allowed.", e); //$NON-NLS-1$
+ return false;
+ }
final AsyncResultProgram program = (AsyncResultProgram)this.mActiveCommand;
if (program.getCommand() != null) {
@@ -978,7 +1004,10 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
program.getCommand(),
ExplorerApplication.getBackgroundConsole());
if (pid != null) {
- android.os.Process.killProcess(pid.intValue());
+ CommandHelper.sendSignal(
+ null,
+ pid.intValue(),
+ ExplorerApplication.getBackgroundConsole());
try {
//Wait for process kill
Thread.sleep(100L);
@@ -1002,6 +1031,88 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
}
/**
+ * Method that send a signal to the current command.
+ *
+ * @param SIGNAL The signal to send
+ * @return boolean If the signal was sent
+ * @hide
+ */
+ private boolean sendSignalToCurrentCommand(SIGNAL signal) {
+ synchronized (this.mSync) {
+ //Is synchronous program? Otherwise it can't be canceled
+ if (!(this.mActiveCommand instanceof AsyncResultProgram)) {
+ return false;
+ }
+ // Check background console
+ try {
+ ExplorerApplication.getBackgroundConsole();
+ } catch (Exception e) {
+ Log.w(TAG, "There is not background console. Not allowed.", e); //$NON-NLS-1$
+ return false;
+ }
+
+ final AsyncResultProgram program = (AsyncResultProgram)this.mActiveCommand;
+ if (program.getCommand() != null) {
+ try {
+ if (program.isCancelable()) {
+ try {
+ //Get the PID in background
+ Integer pid =
+ CommandHelper.getProcessId(
+ null,
+ this.mShell.getPid(),
+ program.getCommand(),
+ ExplorerApplication.getBackgroundConsole());
+ if (pid != null) {
+ CommandHelper.sendSignal(
+ null,
+ pid.intValue(),
+ signal,
+ ExplorerApplication.getBackgroundConsole());
+ try {
+ //Wait for process kill
+ Thread.sleep(100L);
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ return true;
+ }
+ } finally {
+ // It's finished
+ this.mCanceled = true;
+ notifyProcessFinished();
+ this.mSync.notify();
+ }
+ }
+ } catch (Throwable ex) {
+ Log.w(TAG,
+ String.format("Unable to send signal to current program: %s", //$NON-NLS-1$
+ program.getCommand()), ex);
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onEnd() {
+ //Kill the current command on end request
+ return killCurrentCommand();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onSendSignal(SIGNAL signal) {
+ //Send a signal to the current command on end request
+ return sendSignalToCurrentCommand(signal);
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -1014,19 +1125,31 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
* {@inheritDoc}
*/
@Override
- public boolean onRequestWrite(byte[] data) throws ExecutionException {
+ public boolean onRequestWrite(
+ byte[] data, int offset, int byteCount) throws ExecutionException {
try {
// Method that write to the stdin the data requested by the program
- if (this.mReady && this.mOut != null) {
- this.mOut.write(data);
+ if (this.mOut != null) {
+ this.mOut.write(data, offset, byteCount);
+ this.mOut.flush();
+ Thread.yield();
return true;
}
} catch (Exception ex) {
- Log.w(TAG,
- String.format("Unable to write data to program: %s", //$NON-NLS-1$
- this.mActiveCommand.getCommand(), ex));
+ String msg = String.format("Unable to write data to program: %s", //$NON-NLS-1$
+ this.mActiveCommand.getCommand());
+ Log.e(TAG, msg, ex);
+ throw new ExecutionException(msg, ex);
}
return false;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public OutputStream getOutputStream() {
+ return this.mOut;
+ }
+
}
diff --git a/src/com/cyanogenmod/explorer/model/Permissions.java b/src/com/cyanogenmod/explorer/model/Permissions.java
index 72af8f98..3d285398 100644
--- a/src/com/cyanogenmod/explorer/model/Permissions.java
+++ b/src/com/cyanogenmod/explorer/model/Permissions.java
@@ -16,7 +16,10 @@
package com.cyanogenmod.explorer.model;
+import com.cyanogenmod.explorer.util.ParseHelper;
+
import java.io.Serializable;
+import java.text.ParseException;
/**
* Permissions of a filesystem object.
@@ -26,7 +29,7 @@ import java.io.Serializable;
*/
public class Permissions implements Serializable, Comparable<Permissions> {
- private static final long serialVersionUID = 297676577053698527L;
+ private static final long serialVersionUID = -8268598363293965341L;
private UserPermission mUser;
private GroupPermission mGroup;
@@ -242,4 +245,66 @@ public class Permissions implements Serializable, Comparable<Permissions> {
return String.format("%d%d%d%d", b, u, g, o); //$NON-NLS-1$
}
+ /**
+ * Method that parses and extracts the permissions from a unix string format.
+ *
+ * @param rawPermissions The raw permissions
+ * @return Permissions An object with all the permissions
+ * @throws ParseException If the permissions can't be parsed
+ * @see ParseHelper#parsePermission(String)
+ */
+ public static Permissions fromRawString(String rawPermissions) throws ParseException {
+ return ParseHelper.parsePermission(rawPermissions);
+ }
+
+ /**
+ * Method that converts the unix style octal number into a Permissions reference
+ *
+ * @param octalPermissions The octal permissions
+ * @return Permissions An object with all the permissions
+ * @throws ParseException If the permissions can't be parsed
+ */
+ public static Permissions fromOctalString(String octalPermissions) throws ParseException {
+ int size = octalPermissions.length();
+ if (size != 3 && size != 4) {
+ throw new ParseException(
+ "Invalid permissions string length: !=3 or != 4", 0); //$NON-NLS-1$
+ }
+
+ // Extract the data into char
+ int cc = 0;
+ char b = 0;
+ if (size == 4) {
+ b = octalPermissions.charAt(cc);
+ cc++;
+ }
+ char u = octalPermissions.charAt(cc);
+ cc++;
+ char g = octalPermissions.charAt(cc);
+ cc++;
+ char o = octalPermissions.charAt(cc);
+ cc++;
+
+ //Get permissions
+ UserPermission user =
+ new UserPermission(
+ (u & 0x04) == 0x04,
+ (u & 0x02) == 0x02,
+ (u & 0x01) == 0x01,
+ (b & 0x01) == 0x01);
+ GroupPermission group =
+ new GroupPermission(
+ (g & 0x04) == 0x04,
+ (g & 0x02) == 0x02,
+ (g & 0x01) == 0x01,
+ (b & 0x02) == 0x02);
+ OthersPermission other =
+ new OthersPermission(
+ (o & 0x04) == 0x04,
+ (o & 0x02) == 0x02,
+ (o & 0x01) == 0x01,
+ (b & 0x04) == 0x04);
+ return new Permissions(user, group, other);
+ }
+
}
diff --git a/src/com/cyanogenmod/explorer/tasks/FilesystemAsyncTask.java b/src/com/cyanogenmod/explorer/tasks/FilesystemAsyncTask.java
index e98162dd..43b50213 100644
--- a/src/com/cyanogenmod/explorer/tasks/FilesystemAsyncTask.java
+++ b/src/com/cyanogenmod/explorer/tasks/FilesystemAsyncTask.java
@@ -21,6 +21,7 @@ import android.content.res.Resources;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.os.AsyncTask;
+import android.util.Log;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -34,6 +35,8 @@ import com.cyanogenmod.explorer.util.MountPointHelper;
*/
public class FilesystemAsyncTask extends AsyncTask<String, Integer, Boolean> {
+ private static final String TAG = "FilesystemAsyncTask"; //$NON-NLS-1$
+
/**
* @hide
*/
@@ -143,7 +146,14 @@ public class FilesystemAsyncTask extends AsyncTask<String, Integer, Boolean> {
this.mDiskUsageInfo.post(new Runnable() {
@Override
public void run() {
- final DiskUsage du = MountPointHelper.getMountPointDiskUsage(mp);
+ DiskUsage du = null;
+ try {
+ du = MountPointHelper.getMountPointDiskUsage(mp);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve disk usage information", e); //$NON-NLS-1$
+ du = new DiskUsage(
+ mp.getMountPoint(), 0, 0, 0);
+ }
int usage = 0;
if (du != null && du.getTotal() != 0) {
usage = (int)(du.getUsed() * 100 / du.getTotal());
diff --git a/src/com/cyanogenmod/explorer/util/CommandHelper.java b/src/com/cyanogenmod/explorer/util/CommandHelper.java
index 4ea9dfac..29b26d4a 100644
--- a/src/com/cyanogenmod/explorer/util/CommandHelper.java
+++ b/src/com/cyanogenmod/explorer/util/CommandHelper.java
@@ -45,9 +45,13 @@ import com.cyanogenmod.explorer.commands.MoveExecutable;
import com.cyanogenmod.explorer.commands.ParentDirExecutable;
import com.cyanogenmod.explorer.commands.ProcessIdExecutable;
import com.cyanogenmod.explorer.commands.QuickFolderSearchExecutable;
+import com.cyanogenmod.explorer.commands.ReadExecutable;
import com.cyanogenmod.explorer.commands.ResolveLinkExecutable;
+import com.cyanogenmod.explorer.commands.SIGNAL;
+import com.cyanogenmod.explorer.commands.SendSignalExecutable;
import com.cyanogenmod.explorer.commands.SyncResultExecutable;
import com.cyanogenmod.explorer.commands.WritableExecutable;
+import com.cyanogenmod.explorer.commands.WriteExecutable;
import com.cyanogenmod.explorer.commands.shell.InvalidCommandDefinitionException;
import com.cyanogenmod.explorer.console.CommandNotFoundException;
import com.cyanogenmod.explorer.console.Console;
@@ -60,11 +64,13 @@ import com.cyanogenmod.explorer.console.OperationTimeoutException;
import com.cyanogenmod.explorer.console.ReadOnlyFilesystemException;
import com.cyanogenmod.explorer.model.DiskUsage;
import com.cyanogenmod.explorer.model.FileSystemObject;
+import com.cyanogenmod.explorer.model.FolderUsage;
import com.cyanogenmod.explorer.model.Group;
import com.cyanogenmod.explorer.model.Identity;
import com.cyanogenmod.explorer.model.MountPoint;
import com.cyanogenmod.explorer.model.Permissions;
import com.cyanogenmod.explorer.model.Query;
+import com.cyanogenmod.explorer.model.SearchResult;
import com.cyanogenmod.explorer.model.User;
import java.io.FileNotFoundException;
@@ -753,6 +759,7 @@ public final class CommandHelper {
* @throws OperationTimeoutException If the operation exceeded the maximum time of wait
* @throws ExecutionException If the operation returns a invalid exit code
* @see AsyncResultExecutable
+ * @see ExecExecutable
*/
public static AsyncResultExecutable exec(
Context context, String cmd, AsyncResultListener asyncResultListener, Console console)
@@ -787,8 +794,9 @@ public final class CommandHelper {
* @throws CommandNotFoundException If the command was not found
* @throws OperationTimeoutException If the operation exceeded the maximum time of wait
* @throws ExecutionException If the operation returns a invalid exit code
- * @see "SearchResult"
+ * @see SearchResult
* @see AsyncResultExecutable
+ * @see FindExecutable
*/
public static AsyncResultExecutable findFiles(
Context context, String directory, Query search,
@@ -823,8 +831,9 @@ public final class CommandHelper {
* @throws CommandNotFoundException If the command was not found
* @throws OperationTimeoutException If the operation exceeded the maximum time of wait
* @throws ExecutionException If the operation returns a invalid exit code
- * @see "SearchResult"
+ * @see FolderUsage
* @see AsyncResultExecutable
+ * @see FolderUsageExecutable
*/
public static AsyncResultExecutable getFolderUsage(
Context context, String directory,
@@ -1034,6 +1043,149 @@ public final class CommandHelper {
}
/**
+ * Method that send a signal to a process.
+ *
+ * @param context The current context (needed if console == null)
+ * @param process The process which to send the signal
+ * @param signal The signal to send
+ * @param console The console in which execute the program. <code>null</code>
+ * to attach to the default console
+ * @throws FileNotFoundException If the initial directory not exists
+ * @throws IOException If initial directory can't not be checked
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws ConsoleAllocException If the console can't be allocated
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ * @throws CommandNotFoundException If the command was not found
+ * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
+ * @throws ExecutionException If the operation returns a invalid exit code
+ * @see ProcessIdExecutable
+ */
+ public static void sendSignal(
+ Context context, int process, SIGNAL signal, Console console)
+ throws FileNotFoundException, IOException, ConsoleAllocException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException,
+ CommandNotFoundException, OperationTimeoutException,
+ ExecutionException, InvalidCommandDefinitionException {
+ Console c = ensureConsole(context, console);
+ SendSignalExecutable executable =
+ c.getExecutableFactory().newCreator().createSendSignalExecutable(process, signal);
+ execute(context, executable, c);
+ }
+
+ /**
+ * Method that send a kill signal to a process.
+ *
+ * @param context The current context (needed if console == null)
+ * @param process The process which to send the signal
+ * @param console The console in which execute the program. <code>null</code>
+ * to attach to the default console
+ * @throws FileNotFoundException If the initial directory not exists
+ * @throws IOException If initial directory can't not be checked
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws ConsoleAllocException If the console can't be allocated
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ * @throws CommandNotFoundException If the command was not found
+ * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
+ * @throws ExecutionException If the operation returns a invalid exit code
+ * @see ProcessIdExecutable
+ */
+ public static void sendSignal(
+ Context context, int process, Console console)
+ throws FileNotFoundException, IOException, ConsoleAllocException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException,
+ CommandNotFoundException, OperationTimeoutException,
+ ExecutionException, InvalidCommandDefinitionException {
+ Console c = ensureConsole(context, console);
+ SendSignalExecutable executable =
+ c.getExecutableFactory().newCreator().createKillExecutable(process);
+ execute(context, executable, c);
+ }
+
+ /**
+ * Method that read data from disk.
+ *
+ * @param context The current context (needed if console == null)
+ * @param file The file where to read the data
+ * @param asyncResultListener The partial result listener
+ * @param console The console in which execute the program.
+ * <code>null</code> to attach to the default console
+ * @return AsyncResultProgram The command executed in background
+ * @throws FileNotFoundException If the initial directory not exists
+ * @throws IOException If initial directory can't not be checked
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws ConsoleAllocException If the console can't be allocated
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ * @throws CommandNotFoundException If the command was not found
+ * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
+ * @throws ExecutionException If the operation returns a invalid exit code
+ * @see "byte[]"
+ * @see AsyncResultExecutable
+ */
+ public static AsyncResultExecutable read(
+ Context context, String file,
+ AsyncResultListener asyncResultListener, Console console)
+ throws FileNotFoundException, IOException, ConsoleAllocException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException,
+ CommandNotFoundException, OperationTimeoutException,
+ ExecutionException, InvalidCommandDefinitionException {
+ Console c = ensureConsole(context, console);
+ ReadExecutable executable =
+ c.getExecutableFactory().newCreator().
+ createReadExecutable(file, asyncResultListener);
+ execute(context, executable, c);
+ return executable;
+ }
+
+ /**
+ * Method that writes data to disk.
+ *
+ * @param context The current context (needed if console == null)
+ * @param file The file where to write the data
+ * @param asyncResultListener The partial result listener
+ * @param console The console in which execute the program.
+ * <code>null</code> to attach to the default console
+ * @return AsyncResultProgram The command executed in background
+ * @throws FileNotFoundException If the initial directory not exists
+ * @throws IOException If initial directory can't not be checked
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws ConsoleAllocException If the console can't be allocated
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ * @throws CommandNotFoundException If the command was not found
+ * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
+ * @throws ExecutionException If the operation returns a invalid exit code
+ * @throws ReadOnlyFilesystemException If the operation writes in a read-only filesystem
+ * @see WriteExecutable
+ */
+ public static WriteExecutable write(
+ Context context, String file,
+ AsyncResultListener asyncResultListener, Console console)
+ throws FileNotFoundException, IOException, ConsoleAllocException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException,
+ CommandNotFoundException, OperationTimeoutException,
+ ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
+ Console c = ensureConsole(context, console);
+ // Prior to write to disk the data, ensure that can write to the disk using
+ // createFile method
+ //- Create
+ CreateFileExecutable executable1 =
+ c.getExecutableFactory().newCreator().createCreateFileExecutable(file);
+ writableExecute(context, executable1, c);
+ if (executable1.getResult().booleanValue()) {
+ //- Write
+ WriteExecutable executable2 =
+ c.getExecutableFactory().newCreator().
+ createWriteExecutable(file, asyncResultListener);
+ execute(context, executable2, c);
+ return executable2;
+ }
+ throw new ExecutionException(String.format("Fail to create file %s", file)); //$NON-NLS-1$
+ }
+
+ /**
* Method that re-execute the command.
*
* @param context The current context (needed if console == null)
diff --git a/src/com/cyanogenmod/explorer/util/FileHelper.java b/src/com/cyanogenmod/explorer/util/FileHelper.java
index c624e20b..49acb31a 100644
--- a/src/com/cyanogenmod/explorer/util/FileHelper.java
+++ b/src/com/cyanogenmod/explorer/util/FileHelper.java
@@ -59,8 +59,7 @@ public final class FileHelper {
* The root directory.
* @hide
*/
- public static final String ROOT_DIRECTORY =
- ExplorerApplication.getInstance().getApplicationContext().getString(R.string.root_dir);
+ public static String ROOT_DIRECTORY = "/"; //$NON-NLS-1$
/**
* The parent directory string.
diff --git a/src/com/cyanogenmod/explorer/util/MountPointHelper.java b/src/com/cyanogenmod/explorer/util/MountPointHelper.java
index 7ba81660..6dd1872d 100644
--- a/src/com/cyanogenmod/explorer/util/MountPointHelper.java
+++ b/src/com/cyanogenmod/explorer/util/MountPointHelper.java
@@ -69,7 +69,12 @@ public final class MountPointHelper {
* @return MountPoint The mount point information
*/
public static MountPoint getMountPointFromDirectory(String dir) {
- return getMountPointFromDirectory(ExplorerApplication.getBackgroundConsole(), dir);
+ try {
+ return getMountPointFromDirectory(ExplorerApplication.getBackgroundConsole(), dir);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve mount point information.", e); //$NON-NLS-1$
+ }
+ return null;
}
/**
diff --git a/src/com/cyanogenmod/explorer/util/StorageHelper.java b/src/com/cyanogenmod/explorer/util/StorageHelper.java
index 5c3aa753..8ec7ff35 100644
--- a/src/com/cyanogenmod/explorer/util/StorageHelper.java
+++ b/src/com/cyanogenmod/explorer/util/StorageHelper.java
@@ -106,7 +106,7 @@ public final class StorageHelper {
public static boolean isPathInStorageVolume(String path) {
StorageVolume[] volumes =
getStorageVolumes(ExplorerApplication.getInstance().getApplicationContext());
- for (int i=0; i < volumes.length; i++) {
+ for (int i = 0; i < volumes.length; i++) {
StorageVolume vol = volumes[i];
if (path.startsWith(vol.getPath())) {
return true;
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
index c49ef9ce..a5237bcc 100644
--- a/tests/res/values/strings.xml
+++ b/tests/res/values/strings.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="app_name">ExplorerTest</string>
+ <string name="app_name">CMExplorerTest</string>
</resources> \ No newline at end of file
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/AbstractConsoleTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/AbstractConsoleTest.java
index 21575091..d5ead37e 100644
--- a/tests/src/com/cyanogenmod/explorer/commands/shell/AbstractConsoleTest.java
+++ b/tests/src/com/cyanogenmod/explorer/commands/shell/AbstractConsoleTest.java
@@ -16,6 +16,7 @@
package com.cyanogenmod.explorer.commands.shell;
+import com.cyanogenmod.explorer.ExplorerApplication;
import com.cyanogenmod.explorer.console.Console;
import com.cyanogenmod.explorer.console.ConsoleBuilder;
import com.cyanogenmod.explorer.console.shell.ShellConsole;
@@ -44,6 +45,7 @@ public abstract class AbstractConsoleTest extends android.test.AndroidTestCase {
protected void setUp() throws Exception {
//Setup the console
if (isRootConsoleNeeded()) {
+ ExplorerApplication.changeBackgroundConsoleToPriviligedConsole();
this.mConsole = ConsoleBuilder.createPrivilegedConsole(getContext(), INITIAL_DIR);
} else {
this.mConsole = ConsoleBuilder.createNonPrivilegedConsole(getContext(), INITIAL_DIR);
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/ExecCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/ExecCommandTest.java
index d6754236..580288f4 100644
--- a/tests/src/com/cyanogenmod/explorer/commands/shell/ExecCommandTest.java
+++ b/tests/src/com/cyanogenmod/explorer/commands/shell/ExecCommandTest.java
@@ -16,13 +16,14 @@
package com.cyanogenmod.explorer.commands.shell;
-import java.io.File;
-import java.io.FileWriter;
+import java.io.OutputStream;
import android.os.Environment;
import android.test.suitebuilder.annotation.MediumTest;
import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.WriteExecutable;
+import com.cyanogenmod.explorer.model.Permissions;
import com.cyanogenmod.explorer.util.CommandHelper;
/**
@@ -36,6 +37,7 @@ public class ExecCommandTest extends AbstractConsoleTest {
Environment.getDataDirectory().getAbsolutePath() + "/source.sh"; //$NON-NLS-1$
private static final String EXEC_PROGRAM =
"#!/system/bin/sh\necho \"List of files:\"\nls -la\n"; //$NON-NLS-1$
+ private static final String EXEC_CMD_PERMISSIONS = "0755"; //$NON-NLS-1$
/**
* @hide
@@ -63,9 +65,18 @@ public class ExecCommandTest extends AbstractConsoleTest {
public void testExecWithPartialResult() throws Exception {
try {
// Create the test program
- FileWriter fw = new FileWriter(new File(EXEC_CMD));
- fw.write(EXEC_PROGRAM);
- fw.close();
+ WriteExecutable writeCmd =
+ CommandHelper.write(getContext(), EXEC_CMD, null, getConsole());
+ OutputStream os = writeCmd.createOutputStream();
+ os.write(EXEC_PROGRAM.getBytes());
+ writeCmd.end();
+
+ // Enable execute permission
+ CommandHelper.changePermissions(
+ getContext(),
+ EXEC_CMD,
+ Permissions.fromOctalString(EXEC_CMD_PERMISSIONS),
+ getConsole());
// Execute the test program
this.mNewPartialData = false;
@@ -75,11 +86,11 @@ public class ExecCommandTest extends AbstractConsoleTest {
}
public void onAsyncEnd(boolean canceled) {
synchronized (ExecCommandTest.this.mSync) {
- ExecCommandTest.this.mSync.notifyAll();
+ ExecCommandTest.this.mSync.notify();
}
}
public void onException(Exception cause) {
- fail(cause.toString());
+ fail(String.valueOf(cause));
}
public void onPartialResult(Object results) {
ExecCommandTest.this.mNewPartialData = true;
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/FindCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/FindCommandTest.java
index 978772c6..ec24076f 100644
--- a/tests/src/com/cyanogenmod/explorer/commands/shell/FindCommandTest.java
+++ b/tests/src/com/cyanogenmod/explorer/commands/shell/FindCommandTest.java
@@ -79,11 +79,11 @@ public class FindCommandTest extends AbstractConsoleTest {
public void onAsyncEnd(boolean canceled) {
synchronized (FindCommandTest.this.mSync) {
FindCommandTest.this.mNormalEnd = true;
- FindCommandTest.this.mSync.notifyAll();
+ FindCommandTest.this.mSync.notify();
}
}
public void onException(Exception cause) {
- fail(cause.toString());
+ fail(String.valueOf(cause));
}
@SuppressWarnings("unchecked")
public void onPartialResult(Object results) {
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java
index c028aac8..018fa537 100644
--- a/tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java
+++ b/tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java
@@ -81,11 +81,11 @@ public class FolderUsageCommandTest extends AbstractConsoleTest {
public void onAsyncEnd(boolean canceled) {
synchronized (FolderUsageCommandTest.this.mSync) {
FolderUsageCommandTest.this.mNormalEnd = true;
- FolderUsageCommandTest.this.mSync.notifyAll();
+ FolderUsageCommandTest.this.mSync.notify();
}
}
public void onException(Exception cause) {
- fail(cause.toString());
+ fail(String.valueOf(cause));
}
public void onPartialResult(Object result) {
FolderUsageCommandTest.this.mNewPartialData = true;
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/ReadCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/ReadCommandTest.java
new file mode 100644
index 00000000..bb54aa0f
--- /dev/null
+++ b/tests/src/com/cyanogenmod/explorer/commands/shell/ReadCommandTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands.shell;
+
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import com.cyanogenmod.explorer.commands.AsyncResultExecutable;
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.util.CommandHelper;
+
+/**
+ * A class for testing read command.
+ *
+ * @see ReadCommand
+ */
+public class ReadCommandTest extends AbstractConsoleTest {
+
+ private static final String TAG = "ReadCommandTest"; //$NON-NLS-1$
+
+ private static final String READ_FILE = "/boot.txt"; //$NON-NLS-1$
+
+ /**
+ * @hide
+ */
+ final Object mSync = new Object();
+ /**
+ * @hide
+ */
+ boolean mNewPartialData;
+ /**
+ * @hide
+ */
+ boolean mNormalEnd;
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRootConsoleNeeded() {
+ return true;
+ }
+
+ /**
+ * Method that performs a read of a file
+ *
+ * @throws Exception If an exception occurs while executing the test
+ */
+ @MediumTest
+ public void testReadWithPartialResult() throws Exception {
+ this.mNewPartialData = false;
+ this.mNormalEnd = false;
+ final StringBuffer sb = new StringBuffer();
+ AsyncResultExecutable cmd =
+ CommandHelper.read(getContext(), READ_FILE, new AsyncResultListener() {
+ public void onAsyncStart() {
+ /**NON BLOCK**/
+ }
+ public void onAsyncEnd(boolean canceled) {
+ synchronized (ReadCommandTest.this.mSync) {
+ ReadCommandTest.this.mNormalEnd = true;
+ ReadCommandTest.this.mSync.notify();
+ }
+ }
+ public void onException(Exception cause) {
+ fail(String.valueOf(cause));
+ }
+ public void onPartialResult(Object results) {
+ ReadCommandTest.this.mNewPartialData = true;
+ sb.append(new String((byte[])results));
+ }
+ }, getConsole());
+ synchronized (ReadCommandTest.this.mSync) {
+ ReadCommandTest.this.mSync.wait(15000L);
+ }
+ try {
+ if (!this.mNormalEnd && cmd != null && cmd.isCancelable() && !cmd.isCanceled()) {
+ cmd.cancel();
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ assertTrue("no new partial data", this.mNewPartialData); //$NON-NLS-1$
+ assertNotNull("sb==null", sb); //$NON-NLS-1$
+ Log.v(TAG, String.format("read data: %s", sb.toString())); //$NON-NLS-1$
+ assertTrue("read.size > 0", sb.length() > 0); //$NON-NLS-1$
+ }
+
+}
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/SendSignalCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/SendSignalCommandTest.java
new file mode 100644
index 00000000..83a8ccfc
--- /dev/null
+++ b/tests/src/com/cyanogenmod/explorer/commands/shell/SendSignalCommandTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands.shell;
+/**
+ * A class for testing the {@link SendSignalCommand} command.
+ *
+ * @see SendSignalCommand
+ */
+public class SendSignalCommandTest extends AbstractConsoleTest {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRootConsoleNeeded() {
+ return false;
+ }
+
+ // Can't perform any test, because a running program in a shell is needed.
+}
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/WriteCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/WriteCommandTest.java
new file mode 100644
index 00000000..a0626ab9
--- /dev/null
+++ b/tests/src/com/cyanogenmod/explorer/commands/shell/WriteCommandTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * 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 com.cyanogenmod.explorer.commands.shell;
+
+import java.io.OutputStream;
+import java.util.Random;
+
+import android.os.Environment;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.WriteExecutable;
+import com.cyanogenmod.explorer.util.CommandHelper;
+
+/**
+ * A class for testing write command.
+ *
+ * @see WriteCommand
+ */
+public class WriteCommandTest extends AbstractConsoleTest {
+
+ private static final String WRITE_FILE_SMALL =
+ Environment.getDataDirectory().getAbsolutePath() + "/write-test-s.txt"; //$NON-NLS-1$
+ private static final String WRITE_FILE_LARGE =
+ Environment.getDataDirectory().getAbsolutePath() + "/write-test-l.txt"; //$NON-NLS-1$
+ private static final byte[] TEST_DATA = new byte[]{(byte)33, (byte)36, '\n'};
+
+ private static final int DATA_SIZE = 4096;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRootConsoleNeeded() {
+ return true;
+ }
+
+ /**
+ * Method that performs a small write test.
+ *
+ * @throws Exception If an exception occurs while executing the test
+ */
+ @SmallTest
+ public void testSmallWriteWithPartialResult() throws Exception {
+ try {
+ WriteExecutable cmd =
+ CommandHelper.write(getContext(),
+ WRITE_FILE_SMALL, new AsyncResultListener() {
+ public void onAsyncStart() {/**NON BLOCK**/}
+ public void onAsyncEnd(boolean canceled) {/**NON BLOCK**/}
+ public void onException(Exception cause) {
+ fail(String.valueOf(cause));
+ }
+ public void onPartialResult(Object results) {/**NON BLOCK**/}
+ }, getConsole());
+ OutputStream os = cmd.createOutputStream();
+ os.write(TEST_DATA, 0, TEST_DATA.length);
+ cmd.end();
+
+ // Wait for allow close all instrumentation data
+ Thread.sleep(2500L);
+ } finally {
+ try {
+ CommandHelper.deleteFile(getContext(), WRITE_FILE_SMALL, getConsole());
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+ /**
+ * Method that performs a large write test.
+ *
+ * @throws Exception If an exception occurs while executing the test
+ */
+ @LargeTest
+ public void testLargeWriteWithPartialResult() throws Exception {
+ try {
+ WriteExecutable cmd =
+ CommandHelper.write(getContext(),
+ WRITE_FILE_LARGE, new AsyncResultListener() {
+ public void onAsyncStart() {/**NON BLOCK**/}
+ public void onAsyncEnd(boolean canceled) {/**NON BLOCK**/}
+ public void onException(Exception cause) {
+ fail(String.valueOf(cause));
+ }
+ public void onPartialResult(Object results) {/**NON BLOCK**/}
+ }, getConsole());
+ OutputStream os = cmd.createOutputStream();
+ Random random = new Random();
+ for (int i = 0; i < 50; i++) {
+ byte[] data = new byte[DATA_SIZE];
+ random.nextBytes(data);
+ os.write(data, 0, data.length);
+ }
+ cmd.end();
+
+ // Wait for allow close all instrumentation data
+ Thread.sleep(2500L);
+ } finally {
+ try {
+ CommandHelper.deleteFile(getContext(), WRITE_FILE_LARGE, getConsole());
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+}