How to run an interactive shell on android? (rooted device)
I need the nexts steps:
1 - Execute shell process (onCreate)
Process p = Runtime.getRuntime().exec(String[]{"su","-c","sh"});
2 - Get the output (onCreate)
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream));
//dos is a field (a class attribute)
dos = new DataOutputStream(p.getOutputStream());
new Thread(new Runnable() {
#Override
public void run() {
String l;
//wait the console output and write it
while((l = br.readLine()) != null) {
Log.v("info", l);
}
}
}).start();
3 - Execute a command (Button -> onClick())
cmd("wpa_cli");
The cmd method is:
public void cmd(String cmd) {
dos.writeBytes(cmd + "\n");
dos.flush();
}
The Log never shows the console output.
Next step:
4 - Get the output of a subcommand of wpa_cli (another Button -> onClick())
cmd("help");
Should show the wpa_cli help but it doesn't work.
If i press the button many times appears incomplete output (help)
What is the correct way to initialize a process when the Activity is creating and keep it active to interact?
Thanks.
PostData
I replaced the line Log.v("info", l);
with
fos.write(l);
Message msg = handlerTest.obtainMessage();
msg.obj = l;
handlerTest.sendMessage(msg);
fos -> FileOutputStream object
Handler handlerTest = new Handler() {
#Override
handleMessage(Message msg) {
if (msg != null) {
//alert... (String)msg.obj;
}
}
};
And only displays the alert from the direct commands, as help for example. The command status only work when the stream is closed. (after execute cmd("quit"); and cmd("exit");)
I don't understand. stdout is more that one? I can interact with the output without close the stream?
With adb shell i read the output file (created after closing the stream) and it's complete.
PostData2:
The problem is the buffer. Between the binary file (executed) output and java process i can disable the buffer without using Native I/O?
The Android Terminal Emulator works fine, it's using NIO?
Solution by OP.
Without using the tool stdbuf (of coreutils) in Android I found the next solution:
I'm using two processes called process1 and process2: both -> new ProcessBuilder(new String[]{"su", "-c", "sh"}).start();
In the first process I run wpa_cli
dos1.write("wpa_cli".getBytes());
dos1.flush();
In a thread I wait 5 seconds and run wpa_cli in the second process.
dos2.write("wpa_cli".getBytes());
dos2.flush();
To get the output from wpa_cli is necessary to close it with quit
dos1.write("quit".getBytes());
dos1.flush();
The main thread that work with both processes:
new Thread(new Runnable() {
#Override
public void run() {
boolean firstProcess = true;
while (follow) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (follow) {
if (firstProcess) {
cmd("wpa_cli", dos2);
cmd("quit", dos1);
} else {
cmd("wpa_cli", dos1);
cmd("quit", dos2);
}
firstProcess = !firstProcess;
}
}
}
}).start();
Important: Both processes have this (when the process is created):
new Thread(new Runnable() {
#Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(process1.getInputStream()));
String l;
try {
Message msg;
while ((l = br.readLine()) != null) {
msg = handlerStdout.obtainMessage();
msg.obj = l;
handlerStdout.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
The handler handlerStdout does the desired job. It receives the output of wpa_cli every 5 seconds: the output comes from process1 and process2 alternatively
An alternative solution would be to compile coreutils or stdbuf for Android. Or install compiled coreutils from http://forum.xda-developers.com/showthread.php?t=2613243 (for me not work stdbuf because the libstdbuf.so not found)
With stdbuf -oL wpa_cli the output isn't buffered (buffer one line). Without it the output is buffered (4k).
Related
I have successfully compiled the code and generated the executable file. But when i try to execute commands in android application am getting the error iper[1]:syntax error ')' unexpected.Please suggest any solutions. Thanks in advance.
I have done the code execution in an AsyncTask.
#Override
protected String doInBackground(Void... voids) {
//Iperf command syntax check using a Regular expression to protect the system from user exploitation.
String str = inputCommands.getText().toString();
if (!str.matches("(iperf )?((-[s,-server])|(-[c,-client] ([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5]))|(-[c,-client] \\w{1,63})|(-[h,-help]))(( -[f,-format] [bBkKmMgG])|(\\s)|( -[l,-len] \\d{1,5}[KM])|( -[B,-bind] \\w{1,63})|( -[r,-tradeoff])|( -[v,-version])|( -[N,-nodelay])|( -[T,-ttl] \\d{1,8})|( -[U,-single_udp])|( -[d,-dualtest])|( -[w,-window] \\d{1,5}[KM])|( -[n,-num] \\d{1,10}[KM])|( -[p,-port] \\d{1,5})|( -[L,-listenport] \\d{1,5})|( -[t,-time] \\d{1,8})|( -[i,-interval] \\d{1,4})|( -[u,-udp])|( -[b,-bandwidth] \\d{1,20}[bBkKmMgG])|( -[m,-print_mss])|( -[P,-parallel] d{1,2})|( -[M,-mss] d{1,20}))*"))
{
publishProgress("Error: invalid syntax. Please try again.\n\n");
return null;
}
try {
//The user input for the parameters is parsed into a string list as required from the ProcessBuilder Class.
String[] commands = inputCommands.getText().toString().split(" ");
List<String> commandList = new ArrayList<String>(Arrays.asList(commands));
//If the first parameter is "iperf", it is removed
if (commandList.get(0).equals((String) "iperf")) {
commandList.remove(0);
}
//The execution command is added first in the list for the shell interface.
commandList.add(0,"/data/data/com.example.networkcheck/iperf");
//The process is now being run with the verified parameters.
process = new ProcessBuilder().command(commandList).redirectErrorStream(true).start();
//A buffered output of the stdout is being initialized so the iperf output could be displayed on the screen.
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
int read;
//The output text is accumulated into a string buffer and published to the GUI
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
output.append(buffer, 0, read);
//This is used to pass the output to the thread running the GUI, since this is separate thread.
publishProgress(output.toString());
output.delete(0, output.length());
}
reader.close();
process.destroy();
}
catch (IOException e) {
publishProgress("\nError occurred while accessing system resources, please reboot and try again.");
e.printStackTrace();
}
return null;
}
#Override
public void onProgressUpdate(String... strings) {
tv.append(strings[0]);
//The next command is used to roll the text to the bottom
scroller.post(new Runnable() {
public void run() {
scroller.smoothScrollTo(0, tv.getBottom());
}
});
}
I have an Activity which every second write a counter to the logcat:
Runnable rLog = new Runnable() {
#Override
public void run() {
i++;
Log.d("bbb", "i= " + i);
handler.postDelayed(this, 1000);
}
};
In addition - I have a service which read from "logcat -s bbb" and log it:
Runnable rGetLog = new Runnable() {
#Override
public void run() {
Runtime rt = Runtime.getRuntime();
Process process = null;
try {
process = rt.exec("logcat -s bbb");
} catch (IOException e1) {
e1.printStackTrace();
}
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
try {
while ((line = bufferedReader.readLine()) != null)
{
Log.d("aaa", "get line = " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
This code works well. The problem starts when I change the tag "bbb" to a real tag such as "AndroidRuntime" or another tag... I got an empty response from logcat
(if I run at the same time "logcat -s AndroidRuntime" from adb I got lots of lines...)
Who knows what the problem is? what can be different?
Thanks!
From Android Jelly Bean, applications cannot read log entries from other applications, unless your device is rooted and you read the logs as superuser.
try using sudo to get permissions:
process = rt.exec("su && logcat -s YOUR_TAG");
Hi everyone I recently wrote some code to read LogCat output continuously within a background Thread started by a background Service in my app. I do not think there are errors in my code but it's not working.
In my Thread, I have:
#Override
public void run() {
Process process = null;
String cmd = "logcat -v time | grep 'AudioFocus requestAudioFocus()'";
try {
process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("logcat -c" + "\n");
os.writeBytes(cmd + "\n");
os.flush();
} catch(Exception e) {
}
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
try {
while ( (line = reader.readLine()) != null ) {
Log.e(StaticVal.TAG, line);
// code for write line to my own log file
}
} catch(Exception e) {
}
}
I also added permission to manifest:
<uses-permission android:name="android.permission.READ_LOGS" />
However, when I plug my device with PC and test it with Android Studio, Log.e(StaticVal.TAG, line); did not print out the results and nothing wrote into my own log file.
Any help is appreciated!
(Directly run logcat -v time | grep 'AudioFocus requestAudioFocus()' on my device through adb terminal get the results as expected)
UPDATE There are a number of other posts asking how to get a Screenshot in android but none seemed to have a full answer of how to do so. Originally I posted this as a question due to a particular issue I was running into while attempting to open a stream to the Frame Buffer. Now I've swapped over to dumping the Frame Buffer to a file so I've updated my post to show how I got there. For reference (and acknowledgement), I found the command to send the FrameBuffer to a file from this post (unfortunately he didn't provide how he got to that point). I'm just missing how to turn the raw data I pulled from the Frame Buffer into an actual image file.
My intention was to take a full dump of the actual screen on an Android Device. The only way I could find to do so without using the adb bridge was to directly access the Frame Buffer of the system. Obviously this approach will require root privileges on the device and for the app running it! Fortunately for my purposes I have control over how the Device is set up and having the device rooted with root privileges provided to my application is feasible. My testing is currently being done on an old Droid running 2.2.3.
I found my first hints of how to approach it from https://stackoverflow.com/a/6970338/1446554. After a bit more research I found another article that describes how to properly run shell commands as root. They were using it to execute a reboot, I use it to send the current frame buffer to an actual file. My current testing has only gotten as far as doing this via ADB and in a basic Activity (each being provided root). I will be doing further testing from a Service running in the background, updates to come! Here is my entire test activity that can export the current screen to a file:
public class ScreenshotterActivity extends Activity {
public static final String TAG = "ScreenShotter";
private Button _SSButton;
private PullScreenAsyncTask _Puller;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_SSButton = (Button)findViewById(R.id.main_screenshotButton);
_SSButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (_Puller != null)
return;
//TODO: Verify that external storage is available! Could always use internal instead...
_Puller = new PullScreenAsyncTask();
_Puller.execute((Void[])null);
}
});
}
private void runSuShellCommand(String cmd) {
Runtime runtime = Runtime.getRuntime();
Process proc = null;
OutputStreamWriter osw = null;
StringBuilder sbstdOut = new StringBuilder();
StringBuilder sbstdErr = new StringBuilder();
try { // Run Script
proc = runtime.exec("su");
osw = new OutputStreamWriter(proc.getOutputStream());
osw.write(cmd);
osw.flush();
osw.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (osw != null) {
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
if (proc != null)
proc.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
sbstdOut.append(readBufferedReader(new InputStreamReader(proc.getInputStream())));
sbstdErr.append(readBufferedReader(new InputStreamReader(proc.getErrorStream())));
}
private String readBufferedReader(InputStreamReader input) {
BufferedReader reader = new BufferedReader(input);
StringBuilder found = new StringBuilder();
String currLine = null;
String sep = System.getProperty("line.separator");
try {
// Read it all in, line by line.
while ((currLine = reader.readLine()) != null) {
found.append(currLine);
found.append(sep);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
class PullScreenAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
File ssDir = new File(Environment.getExternalStorageDirectory(), "/screenshots");
if (ssDir.exists() == false) {
Log.i(TAG, "Screenshot directory doesn't already exist, creating...");
if (ssDir.mkdirs() == false) {
//TODO: We're kinda screwed... what can be done?
Log.w(TAG, "Failed to create directory structure necessary to work with screenshots!");
return null;
}
}
File ss = new File(ssDir, "ss.raw");
if (ss.exists() == true) {
ss.delete();
Log.i(TAG, "Deleted old Screenshot file.");
}
String cmd = "/system/bin/cat /dev/graphics/fb0 > "+ ss.getAbsolutePath();
runSuShellCommand(cmd);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
_Puller = null;
}
}
}
This also requires adding the android.permission.WRITE_EXTERNAL_STORAGE permission to the Manifest. As suggested in this post. Otherwise it runs, doesn't complain, doesn't create the directories nor the file.
Originally I couldn't get usable data from the Frame Buffer due to not understanding how to properly run shell commands. Now that I've swapped to using the streams for executing commands I can use '>' to send the Frame Buffer's current data to an actual file...
Programmatically you can run "adb shell /system/bin/screencap -p /sdcard/img.png" as below :
Process sh = Runtime.getRuntime().exec("su", null,null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + "/sdcard/img.png").getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
An easy solution for ICS devices is to use the following from the command line
adb shell /system/bin/screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png screenshot.png
This'll save the screenshot.png file in the current directory.
Tested on a Samsung Galaxy SII running 4.0.3.
That would be different for different phones. It depends on the underlying graphics format of your device. You can poll what the graphics format is using system calls. If you are only going to run this on devices that you know the graphics format of you can write a converter that turns it into a known format.
You can have a look at the following project: http://code.google.com/p/android-fb2png/
If you look at the source code for fb2png.c you can see that they poll FBIOGET_VSCREENINFO which contains info about how the device stores the screen image in memory. Once you know that, you should be able to convert it into a format you can use.
I hope this helps.
This question has been asked here before but the solutions provided are not working..I am trying to display the contents of /data/dalvik-cache folder. I know that to do this we need to become su. I even did that but still i am unable to execute a shell command..
package org.linuxconfidg.Example2;
import android.app.Activity;
import android.widget.*;
import android.os.Bundle;
import java.io.*;
public class Example2Activity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String lsreturn=myFunLs();
TextView tv=new TextView(this);
tv.setText("Hello Sindhu !! Try to get it \n"+lsreturn);
setContentView(tv);
}
public String myFunLs()
{
try {
// Executes the command.
Process process;
process = Runtime.getRuntime().exec("/system/bin/su");
process = Runtime.getRuntime().exec("/system/bin/ls /data/dalvik-cache > /data/local");
pr
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
output.append(buffer, 0, read);
}
reader.close();
// Waits for the command to finish.
process.waitFor();
return output.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Can anyone please help me out in finding out how to run linux commands in android application. I am testing this app in my emulator which is defaultly rooted
You can't simply run 'su' on the emulator, there's no root access by default. You'll need to install the 'su' program as well as the SuperUser.apk, and you'll have to do this each time you start the emulator unless using snapshots.
More information and links to the files you need can be found here on SO as well as this blog post by Russell Davis
I think the problem comes from the fact that you are using TWO different process instances.
You have to be on the su process to carry on sending commands:
You can check the question "Read command output inside su process"
for an answer.
Then I tried & managed to make working code (I'm sure it works!)
public void runAsRoot(String[] cmds) throws Exception {
Process p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
InputStream is = p.getInputStream();
for (String tmpCmd : cmds) {
os.writeBytes(tmpCmd+"\n");
int readed = 0;
byte[] buff = new byte[4096];
// if cmd requires an output
// due to the blocking behaviour of read(...)
boolean cmdRequiresAnOutput = true;
if (cmdRequiresAnOutput) {
while( is.available() <= 0) {
try { Thread.sleep(200); } catch(Exception ex) {}
}
while( is.available() > 0) {
readed = is.read(buff);
if ( readed <= 0 ) break;
String seg = new String(buff,0,readed);
console.println("#> "+seg);
}
}
}
os.writeBytes("exit\n");
os.flush();
}
In the below example, I try to execute "/system/bin/screencap" to capture android screen.
via adb:
> adb shell
# /system/bin/screencap -p /sdcard/myscreenshot.png
via Android app:
sh = Runtime.getRuntime().exec("su", null,null);
OutputStream os = sh.getOutputStream();
os.write(("/system/bin/screencap -p " + path).getBytes("ASCII"));
os.flush();
os.close();
sh.waitFor();
Hope this helps.