【问题标题】:execute shell command from android从android执行shell命令
【发布时间】:2014-01-22 19:15:28
【问题描述】:

我正在尝试从应用程序模拟器终端执行此命令(您可以在 google play 中找到它)在这个应用程序中我写 su 并按 enter,所以写:

screenrecord --time-limit 10 /sdcard/MyVideo.mp4

再按一次enter,使用android kitkat的新功能开始录屏。

所以,我尝试使用这个从 java 执行相同的代码:

Process su = Runtime.getRuntime().exec("su");
Process execute = Runtime.getRuntime().exec("screenrecord --time-limit 10 /sdcard/MyVideo.mp4");

但不工作,因为文件没有创建。显然我在安装了 android kitkat 的 root 设备上运行。问题出在哪里?我该如何解决?因为终端模拟器可以工作,而 Java 不行?

【问题讨论】:

    标签: java android xml shell android-4.4-kitkat


    【解决方案1】:

    您应该获取刚刚启动的su 进程的标准输入并在那里写下命令,否则您正在使用当前UID 运行命令。

    试试这样的:

    try{
        Process su = Runtime.getRuntime().exec("su");
        DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
    
        outputStream.writeBytes("screenrecord --time-limit 10 /sdcard/MyVideo.mp4\n");
        outputStream.flush();
    
        outputStream.writeBytes("exit\n");
        outputStream.flush();
        su.waitFor();
    }catch(IOException e){
        throw new Exception(e);
    }catch(InterruptedException e){
        throw new Exception(e);
    }
    

    【讨论】:

    • su 的某些版本将在命令行上使用参数,因此您可以例如exec("su -c screenrecord /sdcard/foo.mp4").
    • 是否可以在没有root权限的情况下进行录制?
    • 非常感谢您提供的这个 sn-p,并且只是补充一下我在这篇文章中错过的是 在命令末尾必须有一个 \n字符串
    • 它返回了以下错误。运行 exec() 时出错。命令:Super User 工作目录:null 环境:null
    • 我运行这些代码得到su error=13, permission denied,它需要root权限吗?
    【解决方案2】:

    @CarloCannas 对代码的修改:

    public static void sudo(String...strings) {
        try{
            Process su = Runtime.getRuntime().exec("su");
            DataOutputStream outputStream = new DataOutputStream(su.getOutputStream());
    
            for (String s : strings) {
                outputStream.writeBytes(s+"\n");
                outputStream.flush();
            }
    
            outputStream.writeBytes("exit\n");
            outputStream.flush();
            try {
                su.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            outputStream.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    

    (欢迎大家为outputStream.close()寻找更好的地方)

    使用示例:

    private static void suMkdirs(String path) {
        if (!new File(path).isDirectory()) {
            sudo("mkdir -p "+path);
        }
    }
    

    更新: 要获得结果(输出到标准输出),请使用:

    public static String sudoForResult(String...strings) {
        String res = "";
        DataOutputStream outputStream = null;
        InputStream response = null;
        try{
            Process su = Runtime.getRuntime().exec("su");
            outputStream = new DataOutputStream(su.getOutputStream());
            response = su.getInputStream();
    
            for (String s : strings) {
                outputStream.writeBytes(s+"\n");
                outputStream.flush();
            }
    
            outputStream.writeBytes("exit\n");
            outputStream.flush();
            try {
                su.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            res = readFully(response);
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            Closer.closeSilently(outputStream, response);
        }
        return res;
    }
    public static String readFully(InputStream is) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length = 0;
        while ((length = is.read(buffer)) != -1) {
            baos.write(buffer, 0, length);
        }
        return baos.toString("UTF-8");
    }
    

    静默关闭多个 Closeables (Soсket may be no Closeable) 的实用程序是:

    public class Closer {
    // closeAll()
    public static void closeSilently(Object... xs) {
        // Note: on Android API levels prior to 19 Socket does not implement Closeable
        for (Object x : xs) {
            if (x != null) {
                try {
                    Log.d("closing: "+x);
                    if (x instanceof Closeable) {
                        ((Closeable)x).close();
                    } else if (x instanceof Socket) {
                        ((Socket)x).close();
                    } else if (x instanceof DatagramSocket) {
                        ((DatagramSocket)x).close();
                    } else {
                        Log.d("cannot close: "+x);
                        throw new RuntimeException("cannot close "+x);
                    }
                } catch (Throwable e) {
                    Log.x(e);
                }
            }
        }
    }
    }
    

    【讨论】:

    • 你先生,摇滚。是的。
    【解决方案3】:
    Process p;
    StringBuffer output = new StringBuffer();
    try {
        p = Runtime.getRuntime().exec(params[0]);
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(p.getInputStream()));
        String line = "";
        while ((line = reader.readLine()) != null) {
            output.append(line + "\n");
            p.waitFor();
        }
    } 
    catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    String response = output.toString();
    return response;
    

    【讨论】:

    • 我无法使用上述命令执行器am start -n com.qop.tabletApp.activity.ConfigurationAndSetup启动应用程序