|
在java中执行shell有好几种方式:第一种(exec)方式一
publicstaticsynchronizedvoid 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)方式二:
publicstaticsynchronizedvoid 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();
???}
}
第二种方式一:
publicstaticsynchronizedvoid 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
????*/
???publicstaticsynchronized 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 = newbyte[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:成功,其它为失败。
?*/
publicstaticsynchronizedint 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) {
?????????thrownew 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.
?*/
privatefinal 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) {
????????thrownew NullPointerException();
????}
????if (taintedCommand.length == 0) {
????????thrownew 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) {
????????????thrownew 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) {
????????????????thrownew 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.
?*/
staticnativeint 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.
????constchar* 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,
????????constchar* 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
}
殊路同归!!!哈。
|
|