/*
* This file is part of the RootTools Project: http://code.google.com/p/roottools/
*
* Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Dominik Schuermann, Adam Shanks
*
* This code is dual-licensed under the terms of the Apache License Version 2.0 and
* the terms of the General Public License (GPL) Version 2.
* You may use this code according to either of these licenses as is most appropriate
* for your project on a case-by-case basis.
*
* The terms of each license can be found in the root directory of this project's repository as well as at:
*
* * http://www.apache.org/licenses/LICENSE-2.0
* * http://www.gnu.org/licenses/gpl-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under these Licenses is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See each License for the specific language governing permissions and
* limitations under that License.
*/
package com.stericson.RootTools.execution;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import java.io.IOException;
import com.stericson.RootTools.RootTools;
public abstract class Command {
ExecutionMonitor executionMonitor = null;
Handler mHandler = null;
boolean executing = false;
String[] command = {};
boolean javaCommand = false;
Context context = null;
boolean finished = false;
boolean terminated = false;
boolean handlerEnabled = true;
int exitCode = -1;
int id = 0;
int timeout = RootTools.default_Command_Timeout;
public abstract void commandOutput(int id, String line);
public abstract void commandTerminated(int id, String reason);
public abstract void commandCompleted(int id, int exitCode);
/**
* Constructor for executing a normal shell command
* @param id the id of the command being executed
* @param command the command, or commands, to be executed.
*/
public Command(int id, String... command) {
this.command = command;
this.id = id;
createHandler(RootTools.handlerEnabled);
}
/**
* Constructor for executing a normal shell command
* @param id the id of the command being executed
* @param handlerEnabled when true the handler will be used to call the
* callback methods if possible.
* @param command the command, or commands, to be executed.
*/
public Command(int id, boolean handlerEnabled, String... command) {
this.command = command;
this.id = id;
createHandler(handlerEnabled);
}
/**
* Constructor for executing a normal shell command
* @param id the id of the command being executed
* @param timeout the time allowed before the shell will give up executing the command
* and throw a TimeoutException.
* @param command the command, or commands, to be executed.
*/
public Command(int id, int timeout, String... command) {
this.command = command;
this.id = id;
this.timeout = timeout;
createHandler(RootTools.handlerEnabled);
}
/**
* Constructor for executing Java commands rather than binaries
* @param javaCommand when True, it is a java command.
* @param context needed to execute java command.
*/
public Command(int id, boolean javaCommand, Context context, String... command) {
this(id, command);
this.javaCommand = javaCommand;
this.context = context;
}
/**
* Constructor for executing Java commands rather than binaries
* @param javaCommand when True, it is a java command.
* @param context needed to execute java command.
*/
public Command(int id, boolean handlerEnabled, boolean javaCommand, Context context, String... command) {
this(id, handlerEnabled, command);
this.javaCommand = javaCommand;
this.context = context;
}
/**
* Constructor for executing Java commands rather than binaries
* @param javaCommand when True, it is a java command.
* @param context needed to execute java command.
*/
public Command(int id, int timeout, boolean javaCommand, Context context, String... command) {
this(id, timeout, command);
this.javaCommand = javaCommand;
this.context = context;
}
protected void finishCommand() {
executing = false;
finished = true;
this.notifyAll();
}
protected void commandFinished() {
if (!terminated) {
synchronized (this) {
if (mHandler != null && handlerEnabled) {
Message msg = mHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt(CommandHandler.ACTION, CommandHandler.COMMAND_COMPLETED);
msg.setData(bundle);
mHandler.sendMessage(msg);
}
else {
commandCompleted(id, exitCode);
}
RootTools.log("Command " + id + " finished.");
finishCommand();
}
}
}
private void createHandler(boolean handlerEnabled) {
this.handlerEnabled = handlerEnabled;
if (Looper.myLooper() != null && handlerEnabled) {
RootTools.log("CommandHandler created");
mHandler = new CommandHandler();
}
else {
RootTools.log("CommandHandler not created");
}
}
public String getCommand() {
StringBuilder sb = new StringBuilder();
if(javaCommand) {
String filePath = context.getFilesDir().getPath();
for (int i = 0; i < command.length; i++) {
/*
* TODO Make withFramework optional for applications
* that do not require access to the fw. -CFR
*/
sb.append(
"dalvikvm -cp " + filePath + "/anbuild.dex"
+ " com.android.internal.util.WithFramework"
+ " com.stericson.RootTools.containers.RootClass "
+ command[i]);
sb.append('\n');
}
}
else {
for (int i = 0; i < command.length; i++) {
sb.append(command[i]);
sb.append('\n');
}
}
return sb.toString();
}
public boolean isExecuting() {
return executing;
}
public boolean isHandlerEnabled() {
return handlerEnabled;
}
public boolean isFinished() {
return finished;
}
public int getExitCode() {
return this.exitCode;
}
protected void setExitCode(int code) {
synchronized (this) {
exitCode = code;
}
}
protected void startExecution() {
executionMonitor = new ExecutionMonitor();
executionMonitor.setPriority(Thread.MIN_PRIORITY);
executionMonitor.start();
executing = true;
}
public void terminate(String reason) {
try {
Shell.closeAll();
RootTools.log("Terminating all shells.");
terminated(reason);
} catch (IOException e) {}
}
protected void terminated(String reason) {
synchronized (Command.this) {
if (mHandler != null && handlerEnabled) {
Message msg = mHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt(CommandHandler.ACTION, CommandHandler.COMMAND_TERMINATED);
bundle.putString(CommandHandler.TEXT, reason);
msg.setData(bundle);
mHandler.sendMessage(msg);
}
else {
commandTerminated(id, reason);
}
RootTools.log("Command " + id + " did not finish because it was terminated. Termination reason: " + reason);
setExitCode(-1);
terminated = true;
finishCommand();
}
}
protected void output(int id, String line) {
if (mHandler != null && handlerEnabled) {
Message msg = mHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt(CommandHandler.ACTION, CommandHandler.COMMAND_OUTPUT);
bundle.putString(CommandHandler.TEXT, line);
msg.setData(bundle);
mHandler.sendMessage(msg);
}
else {
commandOutput(id, line);
}
}
private class ExecutionMonitor extends Thread {
public void run() {
while (!finished) {
synchronized (Command.this) {
try {
Command.this.wait(timeout);
} catch (InterruptedException e) {}
}
if (!finished) {
RootTools.log("Timeout Exception has occurred.");
terminate("Timeout Exception");
}
}
}
}
private class CommandHandler extends Handler {
static final public String ACTION = "action";
static final public String TEXT = "text";
static final public int COMMAND_OUTPUT = 0x01;
static final public int COMMAND_COMPLETED = 0x02;
static final public int COMMAND_TERMINATED = 0x03;
public void handleMessage(Message msg) {
int action = msg.getData().getInt(ACTION);
String text = msg.getData().getString(TEXT);
switch (action) {
case COMMAND_OUTPUT:
commandOutput(id, text);
break;
case COMMAND_COMPLETED:
commandCompleted(id, exitCode);
break;
case COMMAND_TERMINATED:
commandTerminated(id, text);
break;
}
}
}
}