I'm trying to Execute an interactive binary file on my android tablet.
I can execute the server binary just fine and its working perfectly.
But I need to interact with it on a CLI at a specified port where I need to send it a command and receive the response, process the response and send another command as reply.
eg:
$nc 192.168.1.1 8111
>Connected to CLI
$Request Server Status
>Server Sending File to Client X
$Stop Server
>Server Stopped
Where $command represents commands I sent and >command is the reply from server.
So to test this I tried the below function :-
private String TryExecuteCommand(String command) {
try {
proc = Runtime.getRuntime().exec("nc 127.0.0.1 8888");
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
int read;
char[] buffer1 = new char[4096];
StringBuffer output = new StringBuffer();
DataOutputStream writer = new DataOutputStream(proc.getOutputStream());
writer.writeBytes("continuous responses" + "\n");
Thread.sleep(2000);
writer.writeBytes("quit" + "\n");
while ((read = reader.read(buffer1)) > 0)
output.append(buffer1, 0, read);
proc.waitFor();
reader.close();
if (dataLines.length > 0)
return dataLines.toString();
else
return "";
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "Error occured";
}
With this code I'm able to get the output after the "quit" command is issued. But if i try to read the response before quit is issued I get the EPIPE broken error.
Any help is appreciated :)
Related
I am implementing an app which can ping remote hosts, get ping measurements and use them further for different purposes.
My question is, is it okay to use android system ping command? Does it need any special permissions except android.permission.INTERNET?
Is there any chance Google will ask any questions about system command execution or even refuse to publish it?
My code:
private static String ping(String url, int testAmount) {
String str = null;
try {
Process process = Runtime.getRuntime().exec(
"/system/bin/ping -W 1 -c " + testAmount + " " + url);
BufferedReader reader = new BufferedReader(new InputStreamReader(
process.getInputStream()));
int i;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((i = reader.read(buffer)) > 0)
output.append(buffer, 0, i);
reader.close();
str = output.toString();
} catch (IOException e) {
Log.e(TAG, "Ping Exception: ", e);
}
return str;
}
It returns a string containing ping command output
I have a wireless device communicating over port 22 connected to my network. Once the device starts up, it immediately starts sending log data. Once this device is sent a command, it stops logging and responds accordingly. This all works, I have tested it using a telnet client.
My problem is that I can't seem to send it a command properly in my app. I am reading the log data as planned, but when I send it a command, in this case the command "r", it continues outputting log data instead what it should be showing me for that particular command. This has to mean that I am not properly sending the command. This is my code for the task that sends it the command and logs the output in the android logcat:
public class ReceiveVarTask extends AsyncTask<Void, Void, Void>{
String dstAddress;
int Port;
ReceiveVarTask(String addr, int port) {
dstAddress = addr;
Port = port;
}
protected Void doInBackground(Void... vars){
Socket socket = null;
String command = "r";
try {
Log.i(TAG, "Connecting to port 22");
socket = new Socket(dstAddress, Port);
Log.i(TAG, "Connected to port 22");
PrintWriter writer = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
writer.println(command);
String line = reader.readLine();
Log.i(TAG, line);
while(line!=null && !isCancelled()){
line = reader.readLine();
Log.i(TAG, line);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
protected void onPostExecute(Void arg) {
taskRunning = false;
}
}
What am I doing wrong? Why is it not registering the command?
UPDATE:
I have used several telnet clients for testing, and the server is reading the 'r' command as expected on some. It works using a windows telnet client, and it works using the vSSH app by Velestar. Interestingly enough, when testing this with the android telnet client by ClockworkMod, the server is not registering the command either.
Could it be something to do with encoding?
Do I need any additional permissions to work with sockets? I have android.permission.INTERNET declared in the manifest.
UPDATE 2:
The developer of of the hardware just told me that the hardware is expecting the command to be ASCII encoded and CR terminated. So I will try changing PrintWrite initialization to:
PrintWriter writer = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream(), "ASCII")), true);
and the print command to:
writer.print(command+"\r");
Changing the the encoding type to "US-ASCII" and adding a carriage return instead of a new line did the trick. I'm able to read and write data as expected.
From my understanding of your problem description, you have a telnet server, and when a telnet client connects to that server, the server start sending the client log data. The client may send an "r" to the server to stop the log stream.
I suggest you first confirm using another telnet client that sending an "r" does stop the server from generating more log data.
You could use Wireshark to check if the "r" command is indeed sent.
I'm trying to send some commands to Android (client) from VB.NET (server) using sockets. I can connect the client to the server, but I don't know how to receive the commands sent by the server.
Here's a part of my Android code:
public void connect(View v){ //called on button click
SERVER_IP = ip.getText().toString(); //it gets the server's ip from an editText
SERVER_PORT = Integer.parseInt(port.getText().toString()); //and the port too
Toast.makeText(this, "Trying to connect to\n" + SERVER_IP + ":" + SERVER_PORT + "...", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
public void run() {
InetAddress serverAddr;
try {
serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVER_PORT); //It establishes the connection with the server
if(socket != null && socket.isConnected()){ //not sure if it is correct
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//Here comes the problem, I don't know what to add...
}
} catch (Exception e) {
}
}
}).start();
}
And here's a part of my VB.NET send code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
send(TextBox1.text)
End Sub
Private Sub Send(ByVal command)
Dim temp() As Byte = UTF8.GetBytes(command) 'Is UTF8 right to use for that?
stream.Write(temp, 0, temp.Length)
stream.Flush()
End Sub
Question1: is it right to us UTF8 instead of for example ASCII encoding?
Question2: what would I change in the Android code if it wanted to use a timer that sends a command every second?
Thanks.
To read input from a BufferedReader you need to do something similiar to this:
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while((line = input.readLine()) != null){
// do something with the input here
}
A nice tutorial on sockets is available from oracle in the docs: http://docs.oracle.com/javase/tutorial/networking/sockets/readingWriting.html
The default charset on Android is UTF-8 http://developer.android.com/reference/java/nio/charset/Charset.html, so no worries there but you can always send a byte stream from the server onto the client and decode it however you want.
To receive a byte stream you need to do this:
BufferedInputStream input = new BufferedInputStream(socket.getInputStream());
byte[] buffer = new byte[byteCount];
while(input.read(buffer, 0, byteCount) != -1 ){
// do something with the bytes
// for example decode it to string
String decoded = new String(buffer, Charset.forName("UTF-8"));
// keep in mind this string might not be a complete command it's just a decoded byteCount number of bytes
}
As you see it's much easier if you send strings instead of bytes.
If you want to receive input from the server periodically, one of the solutions would be to create a loop which opens a socket, receives input, process it, closes the socket, and then repeats, our you could just keep the loop running endlessly until some command like "STOP" is received.
I am trying to use SuperUser commands to create a list of files that are in a certain location. I am using the method laid out in this post:
Android using Super User Permissions ? allowing access
My specific code looks like this:
try {
Process process = Runtime.getRuntime().exec("su");
DataOutputStream outputStream = new DataOutputStream(process.getOutputStream());
DataInputStream inputStream = new DataInputStream(process.getInputStream());
outputStream.writeBytes("cd " + baseDirectory + "/system/app" + "\n");
outputStream.flush();
outputStream.writeBytes("ls" + "\n");
outputStream.flush();
outputStream.writeBytes("exit\n");
outputStream.flush();
process.waitFor();
} catch (IOException e) {
} catch (InterruptedException e) {
}
and it runs without any errors. My problem is that I can't figure out how to produce any output.
Please note that in the code I am trying to get a list of Apps (I know I can do this in different ways) but I need it to work in a general case...
After you flush the exit command try reading your DataInputStream:
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);
}
String result = output.toString();
You don't need to use su to get a list of installed packages
How to get a list of installed android applications and pick one to run
However in your code, you say it doesn't output anything. But what exactly would you like it to output (and where)? You never told it to display anything.
firstly I will present my situation.
I need to execute "su" command in my android app and it works well. Then I need to execute "ls" command and read the output. I'm doing it by getting the output stream from the "su" process and writing my command into it.
And here goes the question. How to read the output of the "ls" process? All I have is the "su" Process object. Getting the input stream from it gives nothing, because "su" doesn't write anything. But "ls" does and I don't know how to access its output messages.
I have searched many sites but I didn't find any solution. Maybe someone will help me:)
Regards
Ok, I've found a solution. It should look like this:
Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
DataOutputStream stdin = new DataOutputStream(p.getOutputStream());
//from here all commands are executed with su permissions
stdin.writeBytes("ls /data\n"); // \n executes the command
InputStream stdout = p.getInputStream();
byte[] buffer = new byte[BUFF_LEN];
int read;
String out = new String();
//read method will wait forever if there is nothing in the stream
//so we need to read it in another way than while((read=stdout.read(buffer))>0)
while(true){
read = stdout.read(buffer);
out += new String(buffer, 0, read);
if(read<BUFF_LEN){
//we have read everything
break;
}
}
//do something with the output
Hope it will be helpful for someone
public String ls () {
Class<?> execClass = Class.forName("android.os.Exec");
Method createSubprocess = execClass.getMethod("createSubprocess", String.class, String.class, String.class, int[].class);
int[] pid = new int[1];
FileDescriptor fd = (FileDescriptor)createSubprocess.invoke(null, "/system/bin/ls", "/", null, pid);
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fd)));
String output = "";
try {
String line;
while ((line = reader.readLine()) != null) {
output += line + "\n";
}
}
catch (IOException e) {}
return output;
}
Check this code mentioned here:
How to run terminal command in Android application?
try {
// Executes the command.
Process process = Runtime.getRuntime().exec("/system/bin/ls /sdcard");
// Reads stdout.
// NOTE: You can write to stdin of the command using
// process.getOutputStream().
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);
}
References
this code
GScript
I modified accepted answer by #glodos for following problems:
the streams are closed, otherwise the exec process hangs forever, on the opened stream. If you execute ps in shell (ie adb shell)
after several executions then you'll see several su processes
alive. They needs to be properly terminated.
added waitFor() to make sure the process is terminated.
Added handling for read=-1, now commands with empty stdout can be executed. Previously they crashed on new String(buffer, 0, read)
Using StringBuffer for more efficient strings handling.
private String execCommand(String cmd) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
DataOutputStream stdout = new DataOutputStream(p.getOutputStream());
stdout.writeBytes(cmd);
stdout.writeByte('\n');
stdout.flush();
stdout.close();
BufferedReader stdin = new BufferedReader(new InputStreamReader(p.getInputStream()));
char[] buffer = new char[1024];
int read;
StringBuffer out = new StringBuffer();
while((read = stdin.read(buffer)) > 0) {
out.append(buffer, 0, read);
}
stdin.close();
p.waitFor();
return out.toString();
}
Some credits go to #Sherif elKhatib ))