转载:https://www.cnblogs.com/jevan/p/3169617.html
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
");
os.writeBytes("busybox cp /system/bin/superuser /system/bin/su
");
os.writeBytes("busybox chown 0:0 /system/bin/su
");
os.writeBytes("chmod 4755 /system/bin/su
");
os.writeBytes("rm /system/bin/superuser
");
os.writeBytes("/system/bin/monkey -v 100
");
os.writeBytes("exit
");
os.flush();
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第一种(exec)方式二:
- public static synchronized void runshell3()
- {
- String cmd = "sendkey 3 2 ";
- 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 ".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 ", 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<Integer, ProcessReference> processReferences
- = new HashMap<Integer, ProcessReference>();
- /**
- * 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<String, String> 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
- }
殊路同归!!!哈。