I have Android 7 device with root (it works)
I want to execute root commands via OutputStream and get messages about results via InputStream.
private void getSu() {
if (mProcess != null) return;
try {
mProcess = Runtime.getRuntime().exec("su");
} catch (IOException e) {
e.printStackTrace();
}
}
private void getStdOut() {
if (mStdOut != null) return;
getSu();
mStdOut = new DataOutputStream(mProcess.getOutputStream());
}
private void getStdIn() {
if (mStdIn != null) return;
getSu();
mStdIn = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
}
After that I'm trying to execute "mount" command and read results of this command.
getSu();
getStdOut();
getStdIn();
String[] mountLine = getMount(mStdOut, mStdIn, "/system");
private String[] getMount(#NonNull DataOutputStream stdOut, #NonNull BufferedReader stdIn, String string) {
String[] res=null;
try {
stdOut.writeBytes("mount");
stdOut.writeBytes("\n");
String str;
while ((str=stdIn.readLine()) != null) {
if (str.contains(string)) {
res = str.split(" ");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
This code was working on Android 6 before. But on Android 7 I get InputSream full of \u0000
I tried to execute this command via adb shell and I got nice results. Why do I get InputSream full of \u0000?
Android 7+ needs:
stdOut.flush();
After writeBytes()
Related
I'm trying to execute julia.exe in Java.
Here is the code:
Process pTest = Runtime.getRuntime().exec("C:/Program Files/Julia-0.4.1/bin/julia.exe");
When I run it, nothing happens.
However, if I try another executable file, it works well. For example:
Process pTest = Runtime.getRuntime().exec("C:/Program Files/anotherProgram/program.exe");
program.exe will run just as expected.
julia.exe is a little special.
If I run it on command prompt, it will execute on the command prompt. In other words, it won't pop up its own window.
I've done a test:
#julia script, it's path: C:/Users/Thomas/Julia/test.jl
function test1()
println("it's test1")
end
test1()
I execute this command on the command prompt:
C:\>C:/Program Files/Julia-0.4.1/bin/julia.exe C:/Users/Thomas/Julia/test.jl
then I will get it's test1 on the command prompt.
What I need is to execute C:/Program Files/Julia-0.4.1/bin/julia.exe C:/Users/Thomas/Julia/test.jl in my java project and get it's test1 on the console of eclipse.
Here is my java project:
public class Main {
public static void main(String[] args){
try {
String[] params = {"C:/Program Files/Julia-0.4.1/bin/julia.exe", "C:/Users/Thomas/Julia/test.jl"};
Process pTest = Runtime.getRuntime().exec(params);
try {
if (pTest.waitFor() != 0) {
System.err.println("exit value = " + pTest.exitValue());
}
BufferedReader in = new BufferedReader(new InputStreamReader(pTest.getInputStream()));
StringBuffer stringBuffer = new StringBuffer();
String line = null;
while ((line = in.readLine()) != null) {
stringBuffer.append(line+"-");
}
System.out.println(stringBuffer.toString());
} catch (InterruptedException e) {
System.err.println(e);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Consider this changed (and working) implementation removing the too-early invocation of waitFor and exitValue:
public class Main {
public static void main(String[] args) {
try {
String[] params = {"C:/Program Files/Julia-0.4.1/bin/julia.exe",
"C:/Users/Thomas/Julia/test.jl"};
Process pTest = Runtime.getRuntime().exec(params);
BufferedReader in = new BufferedReader(new InputStreamReader(pTest.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
System.out.println("===");
System.out.println("Julia exit value = " + pTest.exitValue());
} catch (IOException e) {
e.printStackTrace();
}
}
}
This produced the following output with your test-script:
it's test1
===
Julia exit value = 0
I got it finally.
As julia.exe execute on the command prompt immediately, we must give admin privileges to the users of cmd.exe.
Here is my code:
private static model connectRemoteSession(String accountName,String password) throws IOException{
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
Runtime runtime = Runtime.getRuntime();
String com = // some command
proc = runtime.exec(com);
Worker worker = new Worker(proc);
reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
stderr = new BufferedReader(new InputStreamReader(
proc.getErrorStream()));
String outLine;
String errLine;
worker.start();
try {
worker.join(300000);
if (worker.exit != null){
//read the outout and error stream and take actions accordingly
}
else{
proc.destroy();
while ((outLine = reader.readLine()) != null)
{
CloudBackup.logger.info("online exchange output line ="+outLine);
output.append(outLine);
}
while((errLine = stderr.readLine()) != null){
CloudBackup.logger.info("online exchange error line ="+errLine);
error.append(errLine);
}
throw new TimeoutException();
}
} catch(InterruptedException ex) {
worker.interrupt();
Thread.currentThread().interrupt();
throw ex;
} finally {
proc.destroy();
}
}catch(Exception e){
CloudBackup.logger.severe(e.getMessage());
}finally{
reader.close();
proc.getOutputStream().close();
stderr.close();
}
return model;
}
class Worker extends Thread {
private final Process process;
Integer exit;
Worker(Process process) {
this.process = process;
}
public void run() {
try {
exit = process.waitFor();
} catch (InterruptedException ignore) {
return;
}
}
This issue I am facing is one one machine the code is working fine but on another machine the worker.exit is always null, although I put the logs in the worker.exit == null and saw that the process is getting over but somehow process.waitFor is not capturing it.
I know that process.waitFor() is available in Java 8 so I checked the version on both the machined and they have same version Java 8. Also there is no other thread running.
Try this, it can be a caching issue.
volatile Integer exit;
Recently I added "adb devices" in the nano ./bash_profile so that I can run it from any directory.
I used one java application to run
public static void main(String [] args) {
executeCmd("adb devices");
}
private static void executeCmd(String string) {
InputStream pipedOut = null;
try {
Process aProcess = Runtime.getRuntime().exec(string);
// These two thread shall stop by themself when the process end
Thread pipeThread = new Thread(new StreamGobber(aProcess.getInputStream()));
Thread errorThread = new Thread(new StreamGobber(aProcess.getErrorStream()));
pipeThread.start();
errorThread.start();
aProcess.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
class StreamGobber implements Runnable {
private InputStream Pipe;
public StreamGobber(InputStream pipe) {
if(pipe == null) {
throw new NullPointerException("bad pipe");
}
Pipe = pipe;
}
public void run() {
try {
byte buffer[] = new byte[2048];
int read = Pipe.read(buffer);
while(read >= 0) {
System.out.write(buffer, 0, read);
read = Pipe.read(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(Pipe != null) {
try {
Pipe.close();
} catch (IOException e) {
}
}
}
when I run any other commands such as "ls" it's working fine!!
I'm using mac ..
thanks :)
Maybe global path problem on mac. You can try run with absolute adb program path as command.
I'm running a device with busybox.
Folder or files with no whitespaces moved correctly, but seems that folders with whitespaces don't move correctly
public static boolean mv(File source, File target) {
if (!source.exists() || !target.exists()) {
return false;
}
try {
StringBuilder command = new StringBuilder("mv -v ");
command.append('\"');
command.append(source.getCanonicalPath());
command.append('\"');
command.append(' ');
command.append('\"');
command.append(target.getCanonicalPath());
command.append('\"');
System.out.println(command.toString());
Process process = Runtime.getRuntime().exec(command.toString());
StringBuilder output = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
output.append(line);
}
System.out.println(output.toString());
return process.waitFor() == 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
The output is
mv -v "/storage/sdcard0/media/music/Progressive Death Metal" "/storage/sdcard0/Music"
No mv output, method just returns "false" (non-zero exit code).
And must I use canonical path, or is it okay to use absolute path and leave it to shell?
EDIT
I also came up that if the filename had quotes, the argument will be wrong, so I made a method adding escape characters
private static String getCommandLineString(String input) {
return input.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\'", "\\\'")
.replace("`", "\\`")
.replace(" ", "\\ ");
}
And now mv looks like this
public static boolean mv(File source, File target) {
if (!source.exists() || !target.exists()) {
return false;
}
try {
StringBuilder command = new StringBuilder("mv -v ");
command.append(getCommandLineString(source.getAbsolutePath()));
command.append(' ');
command.append(getCommandLineString(target.getAbsolutePath()));
System.out.println(command.toString());
Process process = Runtime.getRuntime().exec(command.toString());
StringBuilder output = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
output.append(line);
}
System.out.println(output.toString());
return process.waitFor() == 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
what I get is
mv -v /sdcard/media/music/Progressive\ Death\ Metal /sdcard/Music
But still I get silent non-zero exit code.
Finally got it working. Exec should ask for shell, while OutputStream should write commands.
private static boolean execute(boolean superuser, String command) {
DataOutputStream os = null;
try {
Process process = Runtime.getRuntime().exec(superuser ? "su" : "sh");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(command + "\n");
os.flush();
os.writeBytes("exit\n");
os.flush();
return process.waitFor() == 0;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) { try { os.close(); } catch (Exception e) {} }
}
return false;
}
public static boolean mv(File source, File target) {
if (!source.exists() || !target.exists()) {
return false;
}
try {
StringBuilder command = new StringBuilder("mv ");
command.append(getCommandLineString(source.getAbsolutePath()));
command.append(' ');
command.append(getCommandLineString(target.getAbsolutePath()));
return execute(false, command.toString());
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
I've a batch file that needs to be invoked from a java program. The batch file in-turn invokes an EXE. The EXE program will return data which I want to handle. If the EXE prints data to console am able to capture it as follows. But when the EXE is returning data after its completion, am not able to capture it.
ProcessBuilder pb = new ProcessBuilder("foo.bat");
Process p = pb.start();
int exitValue = p.waitFor();
BufferedReader reader;
// System.out.println("Exit Value" + exitValue);
if (exitValue == 0) {
reader = new BufferedReader(new InputStreamReader(p
.getInputStream()));
} else {
reader = new BufferedReader(new InputStreamReader(p
.getErrorStream()));
}
StringBuffer sb = new StringBuffer();
String temp = reader.readLine();
while (temp != null) {
sb.append(temp);
temp = reader.readLine();
}
reader.close();
System.out.println(sb.toString());
How do i need to capture the data returned by the EXE executed from a batch file?
The EXE is basically a C program. When I invoke the C program, the main method returns me the data, which I want to handle it.
Do you have control over the script? I'd try storing the return value (that's what you want?) from the executable to an environment variable. You may have to export it. Here's a tutorial on how to handle environment variables with Java.
Thanks to jitters comment - no, it doesn't work. We can't change values of the environment variable in a 'global' way (now I know..)
But, the idea works with a little adaptation: I'd still try to store the return value in a global accessible resource: Simply send the return value to a file (exec myapp > result.txt) and read the value from that file in your java application.
I think i had the same problem some time ago. One problem with the previous strategy is that you're waiting for the process to finish (waitFor) to capture the data returned from it. You may have problems if the process fails or hang ups. A better aproach would be something like this:
You should create two threads to consume the input stream and the error stream of the process, independently of the waitFor call. Something like these should work:
1.- Create a class that wraps the execution of the process:
public class ExecutionWrapper {
private int exitStatus;
private String[] command;
private String[] environment;
private String directory;
private boolean running;
private Process process;
private ExecutionWrapperOutput error;
private ExecutionWrapperOutput output;
public ExecutionWrapper(String command, String[] environment, String directory) {
this.command = new String[] { command };
this.environment = environment;
this.directory = directory;
this.exitStatus = -1;
}
public ExecutionWrapper(List<String> command, List<String> environment, String directory) {
if (command != null)
this.command = command.toArray(new String[command.size()]);
if (environment != null)
this.environment = environment.toArray(new String[environment.size()]);
this.directory = directory;
this.exitStatus = -1;
}
public void start() {
try {
this.process = Runtime.getRuntime().exec(this.command, this.environment, new File(this.directory));
this.running = true;
// Error and information messages
this.error = new ExecutionWrapperOutput(this.process.getErrorStream());
this.output = new ExecutionWrapperOutput(this.process.getInputStream());
// Start the messaging threads
this.error.start();
this.output.start();
// Final status
Runnable runner = new Runnable() {
public void run() {
try {
ExecutionWrapper.this.exitStatus = ExecutionWrapper.this.process.waitFor();
ExecutionWrapper.this.running = false;
ExecutionWrapper.this.process.destroy();
} catch (Exception ex) {
LoggingUtiles.exception(ex);
ExecutionWrapper.this.exitStatus = -1;
}
}
};
new Thread(runner).start();
} catch (Throwable t) {
LoggingUtiles.exception(t);
}
}
public void stop() {
this.running = false;
this.process.destroy();
}
public boolean isRunning() {
return running;
}
public int getExitStatus() {
return exitStatus;
}
public String[] getError(boolean clear) {
return this.error.getLines(clear);
}
public String[] getOutput(boolean clear) {
return this.output.getLines(clear);
}
public String[] getCommand() {
return command;
}
public String getDirectory() {
return directory;
}
public void waitFor() {
try {
process.waitFor();
} catch (Throwable t) {
LoggingUtiles.exception(t);
}
}
}
2.- Then, create the ExecutionWrapperOutput class, that processes the output of the process streams:
public class ExecutionWrapperOutput extends Thread {
private InputStream is;
private List<String> output;
private Object mutex = new Object();
ExecutionWrapperOutput(InputStream is) {
this.is = is;
this.output = new ArrayList<String>();
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
synchronized (mutex) {
output.add(line);
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public String[] getLines(boolean clear) {
String[] lines = null;
synchronized (mutex) {
lines = output.toArray(new String[] {});
if (clear)
output.clear();
}
return lines;
}
}
Maybe all this works for you. Let me now if it works...