/*
* 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.filemanager.commands.shell;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import com.android.internal.util.XmlUtils;
import com.cyanogenmod.filemanager.FileManagerApplication;
import com.cyanogenmod.filemanager.R;
import com.cyanogenmod.filemanager.console.CommandNotFoundException;
import com.cyanogenmod.filemanager.console.ExecutionException;
import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
import com.cyanogenmod.filemanager.preferences.Preferences;
import com.cyanogenmod.filemanager.util.ShellHelper;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
/**
* An abstract class that represents a command to be executed
* in the underlying operating system.
*
* @see "command_list.xml"
*/
public abstract class Command {
// Command list XML tags
private static final String TAG_COMMAND_LIST = "CommandList"; //$NON-NLS-1$
private static final String TAG_COMMAND = "command"; //$NON-NLS-1$
private static final String TAG_STARTCODE = "startcode"; //$NON-NLS-1$
private static final String TAG_EXITCODE = "exitcode"; //$NON-NLS-1$
private static final String EXPANDED_ARGS = "[@]"; //$NON-NLS-1$
private final String mId;
private String mCmd;
private String mArgs; // The real arguments
private final Object[] mCmdArgs; //The arguments to be formatted
private static String sStartCodeCmd;
private static String sExitCodeCmd;
private boolean mTrace;
/**
* @Constructor of <code>Command</code>
*
* @param id The resource identifier of the command
* @param args Arguments of the command (will be formatted with the arguments from
* the command definition)
* @throws InvalidCommandDefinitionException If the command has an invalid definition
*/
public Command(String id, String... args) throws InvalidCommandDefinitionException {
this(id, true, args);
}
/**
* @Constructor of <code>Command</code>
*
* @param id The resource identifier of the command
* @param prepare Indicates if the argument must be prepared
* @param args Arguments of the command (will be formatted with the arguments from
* the command definition)
* @throws InvalidCommandDefinitionException If the command has an invalid definition
*/
public Command(String id, boolean prepare, String... args)
throws InvalidCommandDefinitionException {
super();
this.mId = id;
//Convert and quote arguments
this.mCmdArgs = new Object[args.length];
int cc = args.length;
for (int i = 0; i < cc; i++) {
//Quote the arguments?
if (prepare) {
this.mCmdArgs[i] =
"\"" + ShellHelper.prepareArgument(args[i]) //$NON-NLS-1$
+ "\""; //$NON-NLS-1$
} else {
this.mCmdArgs[i] = ShellHelper.prepareArgument(args[i]);
}
}
//Load the command info
getCommandInfo(FileManagerApplication.getInstance().getResources());
// Get the current trace value
reloadTrace();
}
/**
* Method that add expended arguments to the arguments. This is defined with a
* <code>[@]</code> expression in the <code>commandArgs</code> attribute of the
* command xml definition file.
*
* @param args The expanded arguments
* @param prepare Indicates if the argument must be prepared
*/
protected void addExpandedArguments(String[] args, boolean prepare) {
// Don't use of regexp to avoid the need to parse of args to make it compilable.
// Only one expanded argument of well known characters
int pos = this.mArgs.indexOf(EXPANDED_ARGS);
if (pos != -1) {
int cc = args.length;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < cc; i++) {
//Quote the arguments?
if (prepare) {
sb = sb.append("\"" + //$NON-NLS-1$
ShellHelper.prepareArgument(args[i]) + "\""); //$NON-NLS-1$
sb = sb.append(" "); //$NON-NLS-1$
} else {
sb = sb.append(ShellHelper.prepareArgument(args[i]));
sb = sb.append(" "); //$NON-NLS-1$
}
}
// Replace the expanded argument
String start = this.mArgs.substring(0, pos);
String end = this.mArgs.substring(pos+EXPANDED_ARGS.length());
this.mArgs = start + sb.toString() + end;
}
}
/**
* Method that return if the command has to trace his operations
*
* @return boolean If the command has 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(
FileManagerSettings.SETTINGS_SHOW_TRACES.getId(),
((Boolean)FileManagerSettings.SETTINGS_SHOW_TRACES.getDefaultValue()).booleanValue());
}
/**
* Method that checks if the result code of the execution was successfully.
*
* @param exitCode Program exit code
* @throws InsufficientPermissionsException If an operation requires elevated permissions
* @throws CommandNotFoundException If the command was not found
* @throws ExecutionException If the operation returns a invalid exit code
* @hide
*/
public abstract void checkExitCode(int exitCode)
throws InsufficientPermissionsException, CommandNotFoundException,
ExecutionException;
/**
* Method that returns the resource identifier of the command.
*
* @return String The resource identifier of the command
*/
public String getId() {
return this.mId;
}
/**
* This method must returns the name of the full qualified command path.<br />
* <br />
* This method always must returns a full qualified path, and not an
* abbreviation to the command to avoid security problems.<br />
* In the same way, a command not must contains any type of arguments.
* Arguments must be passed through method {@link #getArguments()}
*
* @return String The full qualified command path
* @see #getArguments()
*/
public String getCommand() {
return this.mCmd;
}
/**
* This method can return the list of arguments to be executed along
* with the command.
*
* @return String A list of individual arguments
*/
public String getArguments() {
return this.mArgs;
}
/**
* Method that loads the resource command list xml and
* inflate the internal variables.
*
* @param resources The application resource manager
* @throws InvalidCommandDefinitionException If the command has an invalid definition
*/
private void getCommandInfo(Resources resources) throws InvalidCommandDefinitionException {
//Read the command list xml file
XmlResourceParser parser = resources.getXml(R.xml.command_list);
try {
//Find the root element
XmlUtils.beginDocument(parser, TAG_COMMAND_LIST);
while (true) {
XmlUtils.nextElement(parser);
String element = parser.getName();
if (element == null) {
break;
}
if (TAG_COMMAND.equals(element)) {
CharSequence id = parser.getAttributeValue(R.styleable.Command_commandId);
if (id != null && id.toString().compareTo(this.mId) == 0) {
CharSequence path =
parser.getAttributeValue(R.styleable.Command_commandPath);
CharSequence args =
parser.getAttributeValue(R.styleable.Command_commandArgs);
if (path == null) {
throw new InvalidCommandDefinitionException(
this.mId + ": path is null"); //$NON-NLS-1$
}
if (args == null) {
throw new InvalidCommandDefinitionException(
this.mId + ": args is null"); //$NON-NLS-1$
}
//Save paths
this.mCmd = path.toString();
this.mArgs = args.toString();
//Format the arguments of the process with the command arguments
if (this.mArgs != null && this.mArgs.length() > 0
&& this.mCmdArgs != null && this.mCmdArgs.length > 0) {
this.mArgs = String.format(this.mArgs, this.mCmdArgs);
}
return;
}
}
}
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
parser.close();
}
//Command not found
throw new InvalidCommandDefinitionException(this.mId);
}
/**
* Method that returns the exit code command info.
*
* @param resources The application resource manager
* @return String The exit code command info
* @throws InvalidCommandDefinitionException If the command is not present or has an
* invalid definition
*/
public static synchronized String getStartCodeCommandInfo(
Resources resources) throws InvalidCommandDefinitionException {
//Singleton
if (sStartCodeCmd != null) {
return new String(sStartCodeCmd);
}
//Read the command list xml file
XmlResourceParser parser = resources.getXml(R.xml.command_list);
try {
//Find the root element
XmlUtils.beginDocument(parser, TAG_COMMAND_LIST);
while (true) {
XmlUtils.nextElement(parser);
String element = parser.getName();
if (element == null) {
break;
}
if (TAG_STARTCODE.equals(element)) {
CharSequence path = parser.getAttributeValue(R.styleable.Command_commandPath);
if (path == null) {
throw new InvalidCommandDefinitionException(
TAG_STARTCODE + ": path is null"); //$NON-NLS-1$
}
//Save paths
sStartCodeCmd = path.toString();
return new String(sStartCodeCmd);
}
}
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
parser.close();
}
//Command not found
throw new InvalidCommandDefinitionException(TAG_STARTCODE);
}
/**
* Method that returns the exit code command info.
*
* @param resources The application resource manager
* @return String The exit code command info
* @throws InvalidCommandDefinitionException If the command is not present or has an
* invalid definition
*/
public static synchronized String getExitCodeCommandInfo(
Resources resources) throws InvalidCommandDefinitionException {
//Singleton
if (sExitCodeCmd != null) {
return new String(sExitCodeCmd);
}
//Read the command list xml file
XmlResourceParser parser = resources.getXml(R.xml.command_list);
try {
//Find the root element
XmlUtils.beginDocument(parser, TAG_COMMAND_LIST);
while (true) {
XmlUtils.nextElement(parser);
String element = parser.getName();
if (element == null) {
break;
}
if (TAG_EXITCODE.equals(element)) {
CharSequence path = parser.getAttributeValue(R.styleable.Command_commandPath);
if (path == null) {
throw new InvalidCommandDefinitionException(
TAG_EXITCODE + ": path is null"); //$NON-NLS-1$
}
//Save paths
sExitCodeCmd = path.toString();
return new String(sExitCodeCmd);
}
}
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
parser.close();
}
//Command not found
throw new InvalidCommandDefinitionException(TAG_EXITCODE);
}
}