Whether I use this:
process = Runtime.getRuntime().exec("logcat -d time");
or that:
process = new ProcessBuilder()
.command("logcat", "-d", "time")
.redirectErrorStream(true)
.start();
I get the same results: it often hangs within the exec() or start() call, no matter what I tried to do! The thread running this cannot even be interrupted with Thread.interrupt()! The child process is definitely started and if killed the above commands return.
These calls may fail on first attempt, so THERE IS NO WAY TO READ THEIR OUTPUT! I can also use a simple "su -c kill xxx" command line, same result!
EDIT: Started debugging the java_lang_ProcessManager.cpp file in an NDK project with some debugging logs! So here is what I found so far, after the fork() the parent does this:
int result;
int count = read(statusIn, &result, sizeof(int)); <- hangs there
close(statusIn);
Though the child process is not supposed to block on it: That's what the child does (if started at all!):
// Make statusOut automatically close if execvp() succeeds.
fcntl(statusOut, F_SETFD, FD_CLOEXEC); <- make the parent will not block
// Close remaining unwanted open fds.
closeNonStandardFds(statusOut, androidSystemPropertiesFd); <- hangs here sometimes
...
execvp(commands[0], commands);
// If we got here, execvp() failed or the working dir was invalid.
execFailed:
int error = errno;
write(statusOut, &error, sizeof(int));
close(statusOut);
exit(error);
The child can fail for 2 reproducible reasons: 1- child code is not running, but the parent believes it is! 2- child blocks on closeNonStandardFds(statusOut, androidSystemPropertiesFd);
In either case the read(statusIn...) in the parent ends in deadlock! and a child process is left dead (and cannot be accessed, pid unknown, no Process object)!
Runtime features a static method called getRuntime() , which retrieves the current Java Runtime Environment. That is the only way to obtain a reference to the Runtime object. With that reference, you can run external programs by invoking the Runtime class's exec() method.
exec(String command) method executes the specified string command in a separate process. This is a convenience method. An invocation of the form exec(command) behaves in exactly the same way as the invocation exec(command, null, null).
This problem is fixed in Jelly Bean (Android 4.1) but not in ICS (4.0.4) and I guess it will never be fixed in ICS.
Above solution didn't prove to be reliable in any ways, causing more issues on some devices!
So I reverted back to the standard .exec() and kept digging...
Looking at the child code that hangs, I noticed the child process will hang while trying to close all file descriptors inherited from the parent (except the one created within the exec() call) !
So I search the whole app code for any BufferedReader/Writer and similar classes to make sure those would be closed when calling exec()!
The frequency of the issue was considerably reduced, and actually never occured again when I removed the last opened file descriptor before calling exec().
NB: Make sure SU binary is up-to-date, it can actually cause this issue too!
Enjoy your search ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With