在java中执行shell有好几种方式:第一种(exec)方式一public
static
synchronized
void runshell2()
{
File superuser = new File("/system/bin/superuser");
if (superuser.exists())
{
// return device to original state
Process process;
try
{
process = Runtime.getRuntime().exec("superuser");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n");
os.writeBytes("busybox cp /system/bin/superuser /system/bin/su\n");
os.writeBytes("busybox chown 0:0 /system/bin/su\n");
os.writeBytes("chmod 4755 /system/bin/su\n");
os.writeBytes("rm /system/bin/superuser\n");
os.writeBytes("/system/bin/monkey -v 100\n");
os.writeBytes("exit\n");
os.flush();
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第一种(exec)方式二:public
static
synchronized
void runshell3()
{
String cmd = "sendkey 3 2\n";
try
{
Process exeEcho = Runtime.getRuntime().exec("su");
exeEcho.getOutputStream().write(cmd.getBytes());
exeEcho.getOutputStream().flush();
} catch (IOException e)
{
//showMessage("Excute exception: " + e.getMessage());
e.printStackTrace();
}
}
第二种方式一:public
static
synchronized
void runShell() {
ProcessBuilder pb = new ProcessBuilder("/system/bin/sh");
// java.lang.ProcessBuilder: Creates operating system processes.
pb.directory(new File("/system/bin"));// 设置shell的当前目录。
try {
Process proc = pb.start();
// 获取输入流,可以通过它获取SHELL的输出。
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
// 获取输出流,可以通过它向SHELL发送命令。
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
out.println("pwd");
out.println("su root");// 执行这一句时会弹出对话框(以下程序要求授予最高权限...),要求用户确认。
// out.println("cat /proc/version");
// out.println("monkey -v 500");
// out.println("cd /data/data");//这个目录在系统中要求有root权限才可以访问的。
// out.println("ls -l");//这个命令如果能列出当前安装的APK的数据文件存放目录,就说明我们有了ROOT权限。
out.println("exit");
// proc.waitFor();
String line;
while ((line = in.readLine()) != null) {
System.out.println(line); // 打印输出结果
}
while ((line = err.readLine()) != null) {
System.out.println(line); // 打印错误输出结果
}
in.close();
out.close();
proc.destroy();
} catch (Exception e) {
System.out.println("exception:" + e);
}
}
第二种方式二:/**
* 执行一个shell命令,并返回字符串值
*
* @param cmd
* 命令名称&参数组成的数组(例如:{"/system/bin/cat", "/proc/version"})
* @param workdirectory
* 命令执行路径(例如:"system/bin/")
* @return 执行结果组成的字符串
* @throws IOException
*/
public
static
synchronized String run(String[] cmd, String workdirectory) {
StringBuffer result = new StringBuffer();
try {
ProcessBuilder builder = new ProcessBuilder(cmd);
InputStream in = null;
// 设置一个路径(绝对路径了就不一定需要)
if (workdirectory != null) {
// 设置工作目录(同上)
builder.directory(new File(workdirectory));
// 合并标准错误和标准输出
builder.redirectErrorStream(true);
// 启动一个新进程
Process process = builder.start();
// 读取进程标准输出流
in = process.getInputStream();
byte[] re = new
byte[1024];
while (in.read(re) != -1) {
result = result.append(new String(re));
}
}
// 关闭输入流
if (in != null) {
in.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return result.toString();
}
笔记:
今天使用第一种,发现了以下问题:/**
* 执行shell
*
* @return 0:成功,其它为失败。
*/
public
static
synchronized
int runShellCmd() {
BufferedReader input = null;
PrintWriter output = null;
Process pro = null;
try {
pro = Runtime.getRuntime().exec("adb shell");
input = new BufferedReader(new InputStreamReader(pro.getInputStream()));
pro.getOutputStream().write("pidof mediaserver\r\n".getBytes());
pro.getOutputStream().flush();
String line = input.readLine();
int pid = 0;
/**
* 按道理说直接执行命令打印是这样的:
* root@android:/ # adb shell
* root@android:/ # pidof mediaserver
* 7114
* 也就是说第三行就应该是我取到的pid值,但是实际上却是5行?
*/
for (int i = 0; i < 6; i++) {
Log.e(TAG , i + "line is" + line);
pid = toInt(line, 0);
if (pid > 0)
break;
line = input.readLine();
}
Log.e(TAG, "pid:" + pid);
/**
* 实际打印如下:
* E/MainActivity( 7036): 0 line is pidof mediaserver
* E/MainActivity( 7036): 1 line is
* E/MainActivity( 7036): 2 line is root@android:/ # pidof mediaserver
* E/MainActivity( 7036): 3 line is
* E/MainActivity( 7036): 4 line is 6946
* E/MainActivity( 7036): pid:6946
* 为什么会多出2个空行??
*/
if (pid == 0) {
throw
new IOException("not find mediaserver process!");
}
String killCmd = String.format("kill -9 %d\r\n", pid);
/**
* 直接这么使用不行的,不知道什么原因,执行结果死活不对。
*/
pro.getOutputStream().write(killCmd.getBytes());
pro.getOutputStream().flush();
/**
* 再一次这么重开就ok了,谁能告诉我原因?
*/
pro.destroy();
pro = null;
pro = Runtime.getRuntime().exec("adb shell");
pro.getOutputStream().write(killCmd.getBytes());
pro.getOutputStream().flush();
} catch (IOException ex) {
ex.printStackTrace();
return -1;
} finally {
try {
if (input != null) {
input.close();
}
if (output != null) {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (pro != null) {
pro.destroy();
pro = null;
}
}
return 0;
}
我去看看源码是怎么样的!
Runtime的exec最终调用的是ProcessManager,代码如下所示:/**
* Executes the specified command and its arguments in a separate native
* process. The new process uses the environment provided in {@code envp}
* and the working directory specified by {@code directory}.
*
* @param progArray
* the array containing the program to execute as well as any
* arguments to the program.
* @param envp
* the array containing the environment to start the new process
* in.
* @param directory
* the directory in which to execute the program. If {@code null},
* execute if in the same directory as the parent process.
* @return the new {@code Process} object that represents the native
* process.
* @throws IOException
* if the requested program can not be executed.
* @throws SecurityException
* if the current {@code SecurityManager} disallows program
* execution.
* @see SecurityManager#checkExec
* @since Android 1.0
*/
public Process exec(String[] progArray, String[] envp, File directory) throws IOException {
// BEGIN android-changed: push responsibility for argument checking into ProcessManager
return ProcessManager.getInstance().exec(progArray, envp, directory, false);
// END android-changed
}
ProcessManager的exec代码如下:/**
* Map from pid to Process. We keep weak references to the Process objects
* and clean up the entries when no more external references are left. The
* process objects themselves don't require much memory, but file
* descriptors (associated with stdin/out/err in this case) can be
* a scarce resource.
*/
private
final Map processReferences
= new HashMap();
/**
* Executes a process and returns an object representing it.
*/
Process exec(String[] taintedCommand, String[] taintedEnvironment, File workingDirectory,
boolean redirectErrorStream) throws IOException {
// Make sure we throw the same exceptions as the RI.
if (taintedCommand == null) {
throw
new NullPointerException();
}
if (taintedCommand.length == 0) {
throw
new IndexOutOfBoundsException();
}
// Handle security and safety by copying mutable inputs and checking them.
String[] command = taintedCommand.clone();
String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null;
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkExec(command[0]);//权限检查
}
// Check we're not passing null Strings to the native exec.
for (String arg : command) {
if (arg == null) {
throw
new NullPointerException();
}
}
// The environment is allowed to be null or empty, but no element may be null.
if (environment != null) {
for (String env : environment) {
if (env == null) {
throw
new NullPointerException();
}
}
}
FileDescriptor in = new FileDescriptor();
FileDescriptor out = new FileDescriptor();
FileDescriptor err = new FileDescriptor();
String workingPath = (workingDirectory == null)
? null
: workingDirectory.getPath();
// Ensure onExit() doesn't access the process map before we add our
// entry.
synchronized (processReferences) {
int pid;
try {
/**
* 调用exec函数
*/
pid = exec(command, environment, workingPath, in, out, err, redirectErrorStream);
} catch (IOException e) {
IOException wrapper = new IOException("Error running exec()."
+ "Command:" + Arrays.toString(command)
+ "Working Directory:" + workingDirectory
+ "Environment:" + Arrays.toString(environment));
wrapper.initCause(e);
throw wrapper;
}
/**
* 新建一个进程实现。
*/
ProcessImpl process = new ProcessImpl(pid, in, out, err);
/**
* 创建一个进程引用。
*/
ProcessReference processReference
= new ProcessReference(process, referenceQueue);
/**
* 加入到全局进程引用map中。
*/
processReferences.put(pid, processReference);
/*
* This will wake up the child monitor thread in case there
* weren't previously any children to wait on.
*/
processReferences.notifyAll();
return process;
}
}
本地exec的原型:/**
* Executes a native process. Fills in in, out, and err and returns the
* new process ID upon success.
*/
static
native
int exec(String[] command, String[] environment,
String workingDirectory, FileDescriptor in, FileDescriptor out,
FileDescriptor err, boolean redirectErrorStream) throws IOException;
对应的native文件为://Android 4.0.3在
./libcore/luni/src/main/native/java_lang_ProcessManager.cpp
//Android 2.2在
./dalvik/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.cpp
//源码为:
/**
* Converts Java String[] to char** and delegates to executeProcess().
*/
static pid_t java_lang_ProcessManager_exec(
JNIEnv* env, jclass clazz, jobjectArray javaCommands,
jobjectArray javaEnvironment, jstring javaWorkingDirectory,
jobject inDescriptor, jobject outDescriptor, jobject errDescriptor,
jboolean redirectErrorStream) {
// Copy commands into char*[].
char** commands = convertStrings(env, javaCommands);
// Extract working directory string.
const
char* workingDirectory = NULL;
if (javaWorkingDirectory != NULL) {
workingDirectory = env->GetStringUTFChars(javaWorkingDirectory, NULL);
}
// Convert environment array.
char** environment = convertStrings(env, javaEnvironment);
//关键就这一行.
pid_t result = executeProcess(
env, commands, environment, workingDirectory,
inDescriptor, outDescriptor, errDescriptor, redirectErrorStream);
// Temporarily clear exception so we can clean up.
jthrowable exception = env->ExceptionOccurred();
env->ExceptionClear();
freeStrings(env, javaEnvironment, environment);
// Clean up working directory string.
if (javaWorkingDirectory != NULL) {
env->ReleaseStringUTFChars(javaWorkingDirectory, workingDirectory);
}
freeStrings(env, javaCommands, commands);
// Re-throw exception if present.
if (exception != NULL) {
if (env->Throw(exception) < 0) {
LOGE("Error rethrowing exception!");
}
}
return result;
}
看看executeProcess接口,其实源码注释写的很清楚。/** Executes a command in a child process. */
static pid_t executeProcess(JNIEnv* env, char** commands, char** environment,
const
char* workingDirectory, jobject inDescriptor,
jobject outDescriptor, jobject errDescriptor,
jboolean redirectErrorStream) {
int i, result, error;
// Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe.
int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
for (i = 0; i < PIPE_COUNT; i++) {
if (pipe(pipes + i * 2) == -1) {
jniThrowIOException(env, errno);
closePipes(pipes, -1);
return -1;
}
}
int stdinIn = pipes[0];
int stdinOut = pipes[1];
int stdoutIn = pipes[2];
int stdoutOut = pipes[3];
int stderrIn = pipes[4];
int stderrOut = pipes[5];
int statusIn = pipes[6];
int statusOut = pipes[7];
pid_t childPid = fork();
// If fork() failed...
if (childPid == -1) {
jniThrowIOException(env, errno);
closePipes(pipes, -1);
return -1;
}
// If this is the child process...
if (childPid == 0) {
/*
* Note: We cannot malloc() or free() after this point!
* A no-longer-running thread may be holding on to the heap lock, and
* an attempt to malloc() or free() would result in deadlock.
*/
// Replace stdin, out, and err with pipes.
dup2(stdinIn, 0);
dup2(stdoutOut, 1);
if (redirectErrorStream) {
dup2(stdoutOut, 2);
} else {
dup2(stderrOut, 2);
}
// Close all but statusOut. This saves some work in the next step.
closePipes(pipes, statusOut);
// Make statusOut automatically close if execvp() succeeds.
fcntl(statusOut, F_SETFD, FD_CLOEXEC);
// Close remaining open fds with the exception of statusOut.
closeNonStandardFds(statusOut);
// Switch to working directory.
if (workingDirectory != NULL) {
if (chdir(workingDirectory) == -1) {
goto execFailed;
}
}
// Set up environment.
if (environment != NULL) {
environ = environment;
}
// Execute process. By convention, the first argument in the arg array
// should be the command itself. In fact, I get segfaults when this
// isn't the case.
execvp(commands[0], commands);
// If we got here, execvp() failed or the working dir was invalid.
execFailed:
error = errno;
write(statusOut, &error, sizeof(int));
close(statusOut);
exit(error);
}
// This is the parent process.
// Close child's pipe ends.
close(stdinIn);
close(stdoutOut);
close(stderrOut);
close(statusOut);
// Check status pipe for an error code. If execvp() succeeds, the other
// end of the pipe should automatically close, in which case, we'll read
// nothing.
int count = read(statusIn, &result, sizeof(int));
close(statusIn);
if (count > 0) {
jniThrowIOException(env, result);
close(stdoutIn);
close(stdinOut);
close(stderrIn);
return -1;
}
// Fill in file descriptor wrappers.
jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn);
jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut);
jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn);
return childPid;
}
至此,Runtime的exec就全部结束了。如果对下面的fork,execvp这2个函数不了解。建议看看APU。
最后来看看ProcessBuilder类的实现:/**
* Starts a new process based on the current state of this process builder.
*
* @return the new {@code Process} instance.
* @throws NullPointerException
* if any of the elements of {@link #command()} is {@code null}.
* @throws IndexOutOfBoundsException
* if {@link #command()} is empty.
* @throws SecurityException
* if {@link SecurityManager#checkExec(String)} doesn't allow
* process creation.
* @throws IOException
* if an I/O error happens.
*/
public Process start() throws IOException {
// BEGIN android-changed: push responsibility for argument checking into ProcessManager
String[] cmdArray = command.toArray(new String[command.size()]);
String[] envArray = new String[environment.size()];
int i = 0;
for (Map.Entry entry : environment.entrySet()) {
envArray[i++] = entry.getKey() + "=" + entry.getValue(); //$NON-NLS-1$
}
//和Runtime.exec的一样。
return ProcessManager.getInstance().exec(cmdArray, envArray, directory, redirectErrorStream);
// END android-changed
}
殊路同归!!!哈。