[英]Building a Terminal Emulator for Android
我一直在嘗試為 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;
}
除了一些特殊命令(例如“清除”)之外,這段代碼可以正常工作。 但我更關心的是以下問題:
如果用戶輸入一個命令,然后輸入另一個命令,
如 :
cd /sdcard touch File.txt
File.txt是在“/”中創建的,而不是在“/sdcard”中。 到目前為止,為了避免這種情況,我正在跟蹤所有 'cd' 命令以找出當前的工作目錄是什么。 我希望有更好的方法來解決這個問題。
如果有人能在這里幫助我,我將不勝感激。
這有點晚了,但這里有幾種方法可以做到這一點。
1)
不要使用 su 作為起點,而是使用 /system/bin/sh。
並在調用后
rt.exec("/system/bin/sh");
您應該按住輸出流和輸入流以提供進一步的命令。
發出命令后,您應該回顯像“---EOF---”這樣的魔術行,並在閱讀該行后停止讀取輸入。 如果你沒有這個,你最終會得到來自 InputStream 阻塞的 read 函數。
2) 將數據通過管道傳輸到您編寫的本機進程,該進程只需將數據移動到您的 Android 應用程序,並在末尾附加終止字符或字符串。
我不完全確定如何執行此操作,但它與之前的方法本質上相同,只是依賴於您作為中間人的本機應用程序。
這將使您接近正常運行的“終端模擬器”。
3)如果您不需要真正的終端模擬器,那么除了使用本機應用程序打開與偽終端的連接之外,別無他法。
以下是有關如何打開 pty 的一些基本信息: 鏈接
Terminal Emulator 是一個使用這種技術的開源項目。
看看這里
不確定您是否仍然需要這個,但這是我一次發出多個命令而不使用“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:
每次執行命令時,我最終都會尋求超級用戶權限(第二行代碼)。 我想取消這個。
感謝 Xonar 從另一個答案中提出的建議:
發出命令后,您應該回顯像“---EOF---”這樣的魔術行,並在閱讀該行后停止讀取輸入。
Kotlin 中的解決方案:
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()
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.