为Android构建终端仿真器

时间:2011-12-29 20:28:11

标签: android terminal terminal-emulator

我一直在尝试为Android构建终端模拟器。作为一个新手,我的想法是执行每个命令并将输出存储在一个文件中,每次执行后都会显示其内容。 伪代码:

public Boolean execCommands(String command) {
        try {
            rt = Runtime.getRuntime();
            process = rt.exec("su");
            DataOutputStream os = new DataOutputStream(process.getOutputStream());
            os.writeBytes("echo $ \""+command+ "\" >> /sdcard/Android/data/terminalemulatorlog.txt\n\n\n"); 
            /**** Note :  String command = (EditText)findViewById(R.id.command).getText().toString(); ****/
            os.flush();
            os.writeBytes("exit\n");
            os.flush();
            process.waitFor();
            }
        // Error Handling
        displayOutput(); //Loads and displays the Text File (/sdcard/Android/data/terminalemulatorlog.txt)
        return true;
        }

除了一些特殊命令(例如'clear')之外,这段代码可以正常工作。 但我更关心的是以下问题:

  1. 每次执行命令时,我最终都会寻求超级用户权限(第二行代码)。而且我想废除它。
  2. 如果用户输入一个命令后跟另一个命令,则为 如:

    cd /sdcard    
    touch File.txt    
    

    File.txt 在'/'中创建,而不是在'/ sdcard'中创建。到目前为止,为了避免这种情况,我将跟踪所有'cd'命令,以确定当前工作目录是什么。而且我希望有更好的解决方法。

  3. 如果有人能帮助我,我将不胜感激。

3 个答案:

答案 0 :(得分:2)

不确定您是否仍然需要这个,但这里是我一次发出多个命令而不使用“su”让它们运行的​​方式。

try {
    String[] commands = {           
            "dumpstate > /sdcard/LogFiles/dumpstate.txt",
            "dumpsys > /sdcard/LogFiles/dumpsys.txt",
            "logcat -d > /sdcard/LogFiles/log.txt",
            "cat /sdcard/LogFiles/dumpstate.txt /sdcard/LogFiles/dumpsys.txt /sdcard/LogFiles/log.txt > /sdcard/LogFiles/bugreport.rtf" };
    Process p = Runtime.getRuntime().exec("/system/bin/sh -");
    DataOutputStream os = new DataOutputStream(p.getOutputStream());
    for (String tmpCmd : commands) {
        os.writeBytes(tmpCmd + "\n");
    }
} catch (IOException e) {
    e.printStackTrace();
}

答案 1 :(得分:1)

这有点晚了,但这里有几种方法。

1)

而不是使用su作为起点使用/ system / bin / sh。

并在致电后

rt.exec("/system/bin/sh");

您应该抓住输出流和输入流以提供更多命令。

发出命令后,您应该回显“--- EOF ---”之类的魔术线,并在读取该行后停止读取输入。如果你没有这个,你将最终得到InputStream阻塞的读取函数。

2)将数据传输到您编写的本机进程,该进程只是将数据移动到您的Android应用程序,并在终端附加一个终止字符或字符串。

我不完全确定如何做到这一点,但它与以前的方法基本相同,只是依赖于您作为中间人的原生应用程序。

这将使您接近功能正常的“终端模拟器”。

3)如果你不是真正的Ternimal Emulator,那么没有别的方法可以做到:使用打开与psuedoterminal连接的本机应用程序。

以下是有关如何打开pty的一些基本信息:link

Terminal Emulator是一个使用这种技术的开源项目。

看看here

答案 2 :(得分:0)

关于问题1:

每次执行命令时,我最终都会寻求SuperUser权限(第二行代码)。我想消除这个问题。

感谢Xonar从另一个答案中提出的建议:

发出命令后,您应该回显一条神奇的行,例如“ --- EOF ---”,并在读取该行之后停止读取输入。

科特林的解决方案:

private lateinit var suProcess: Process
private lateinit var outputStream: DataOutputStream

private fun getSu(): Boolean {
    return try {
        suProcess = Runtime.getRuntime().exec("su")
        outputStream = DataOutputStream(suProcess.outputStream)
        true
    } catch (e: Exception) {
        e.printStackTrace()
        false
    }
}

private fun sudo(command: String): List<String>? {
    return try {
        outputStream.writeBytes("$command\n")
        outputStream.flush()

        outputStream.writeBytes("echo ---EOF---\n")
        outputStream.flush()

        val reader = suProcess.inputStream.bufferedReader()
        val result = mutableListOf<String>()
        while (true) {
            val line = reader.readLine()
            if (line == "---EOF---") break
            result += line
        }

        result
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

private fun exitTerminal() {
    try {
        outputStream.writeBytes("exit\n")
        outputStream.flush()

        suProcess.waitFor()
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        outputStream.close()
    }
}

//Activity method
override fun onDestroy() {
    super.onDestroy()

    exitTerminal()
}