af-wall/app/src/main/java/com/stericson/rootshell/execution/Command.java
2025-11-20 14:05:38 +01:00

325 lines
9.3 KiB
Java

/*
* This file is part of the RootShell Project: http://code.google.com/p/RootShell/
*
* Copyright (c) 2014 Stephen Erickson, Chris Ravenscroft
*
* 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.rootshell.execution;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import com.stericson.rootshell.RootShell;
import java.io.IOException;
public class Command {
//directly modified by JavaCommand
protected boolean javaCommand = false;
protected Context context = null;
public int totalOutput = 0;
public int totalOutputProcessed = 0;
ExecutionMonitor executionMonitor = null;
Handler mHandler = null;
//Has this command already been used?
protected boolean used = false;
boolean executing = false;
String[] command = {};
boolean finished = false;
boolean terminated = false;
boolean handlerEnabled = true;
int exitCode = -1;
int id = 0;
int timeout = RootShell.defaultCommandTimeout;
/**
* 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(RootShell.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(RootShell.handlerEnabled);
}
//If you override this you MUST make a final call
//to the super method. The super call should be the last line of this method.
public void commandOutput(int id, String line) {
RootShell.log("Command", "ID: " + id + ", " + line);
totalOutputProcessed++;
}
public void commandTerminated(int id, String reason) {
//pass
}
public void commandCompleted(int id, int exitcode) {
//pass
}
protected final 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);
}
RootShell.log("Command " + id + " finished.");
finishCommand();
}
}
}
private void createHandler(boolean handlerEnabled) {
this.handlerEnabled = handlerEnabled;
if (Looper.myLooper() != null && handlerEnabled) {
RootShell.log("CommandHandler created");
mHandler = new CommandHandler();
} else {
RootShell.log("CommandHandler not created");
}
}
public final void finish()
{
RootShell.log("Command finished at users request!");
commandFinished();
}
protected final void finishCommand() {
this.executing = false;
this.finished = true;
this.notifyAll();
}
public final String getCommand() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < command.length; i++) {
if (i > 0) {
sb.append('\n');
}
sb.append(command[i]);
}
return sb.toString();
}
public final boolean isExecuting() {
return executing;
}
public final boolean isHandlerEnabled() {
return handlerEnabled;
}
public final boolean isFinished() {
return finished;
}
public final int getExitCode() {
return this.exitCode;
}
protected final void setExitCode(int code) {
synchronized (this) {
exitCode = code;
}
}
protected final void startExecution() {
this.used = true;
executionMonitor = new ExecutionMonitor(this);
executionMonitor.setPriority(Thread.MIN_PRIORITY);
executionMonitor.start();
executing = true;
}
public final void terminate()
{
RootShell.log("Terminating command at users request!");
terminated("Terminated at users request!");
}
protected final void terminate(String reason) {
try {
Shell.closeAll();
RootShell.log("Terminating all shells.");
terminated(reason);
} catch (IOException e) {
}
}
protected final 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);
}
RootShell.log("Command " + id + " did not finish because it was terminated. Termination reason: " + reason);
setExitCode(-1);
terminated = true;
finishCommand();
}
}
protected final void output(int id, String line) {
totalOutput++;
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 {
private final Command command;
public ExecutionMonitor(Command command) {
this.command = command;
}
public void run() {
if(command.timeout > 0)
{
synchronized (command) {
try {
RootShell.log("Command " + command.id + " is waiting for: " + command.timeout);
command.wait(command.timeout);
} catch (InterruptedException e) {
RootShell.log("Exception: " + e);
}
if (!command.isFinished()) {
RootShell.log("Timeout Exception has occurred for command: " + command.id + ".");
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 final 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;
}
}
}
}