2

First thing my app does is checking for "su" since it's necessary for the app to work. Even though it sometimes work, often after typing "killall packageName" in the terminal. I've done a simple test application and I can't get it to work every time. Code where it happens:

String[] args = new String[] { "su" };
Log.v(TAG, "run(" + Arrays.toString(args) + ")");
FutureTask<Process> task = new FutureTask<Process>(new Callable<Process>() {
    @Override
    public Process call() throws Exception {
        return Runtime.getRuntime().exec(args);
    }
});
try {
    Executors.newSingleThreadExecutor().execute(task);
    return task.get(10, TimeUnit.SECONDS);
} catch (Throwable t) {
    task.cancel(true);
    throw new IOException("failed to start process within 10 seconds", t);
}

Complete project: https://github.com/chrulri/android_testexec

Since this app does nothing more than running exec() in the first place, I cannot close any previously opened file descriptors as mentioned in another stackoverflow question: https://stackoverflow.com/a/11317150/1145705

PS: I run Android 4.0.3 / 4.0.4 on different devices.

Community
  • 1
  • 1
chrulri
  • 1,822
  • 14
  • 16

2 Answers2

2

3c71 was right about open file descriptors. In my case, it was the AdMob SDK which caused the problems since it was sometimes (re-)loading the Ads from the web at the sime time I tried to call exec(..) leaving me hanging in a deadlock.

My solution is to fork a "su" process ONCE and reuse it for all commands and load the Ads AFTER forking that process.

chrulri
  • 1,822
  • 14
  • 16
0

To use Runtime.exec safely you should wait for the process to finish and consume the output and error streams, preferably concurrently (to prevent blocking): http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html

Martin Wilson
  • 3,386
  • 1
  • 24
  • 29
  • Please look at the example code and the stackoverflow link I've posted. The problem is that exec(..) never returns. The FutureTask is simply a wrapper to detect this kind of problem. If it wouldn't hang there, the FutureTask wouldn't be needed at all. – chrulri Jul 09 '12 at 08:37
  • PS: this code just starts the process, handling (e.g. reading streams and doing stuff) is done afterwards. Have a look at the complete test app on github (link is posted) – chrulri Jul 09 '12 at 08:53
  • Had a quick look. Are you sure your FutureTask is actually being executed? Can't remember the exact details but I think you need to execute it with an ExecutorService – Martin Wilson Jul 09 '12 at 09:04
  • Yes, this was a mistake in the test app (bad copy pasta from the real app). I fixed this now but the problem of hanging exec(..) call is still persistent on random occasions. – chrulri Jul 09 '12 at 09:52
  • That is strange indeed. Are you absolutely certain the blocking is actually in the Runtime.exec() method? – Martin Wilson Jul 09 '12 at 10:37
  • Yes, otherwise the task.get(..) wouldn't throw a timeout exception. Same happens with ProcessBuilder. Btw: This is about Android, not Java itself. See also the stackoverflow question from user "3c71" I've linked above. He came to the same conclusion but could solve it by closing all file descriptors before calling exec. But I do not have any open streams or file descriptors yet. – chrulri Jul 09 '12 at 11:26
  • I guess it's the same problem as [here](http://stackoverflow.com/questions/6018126/problem-with-runtime-exec-and-android) which seems fixed by [that commit](https://github.com/android/platform_bionic/commit/177ba8cb42ed6d232e7c8bcad5e6ee21fc51a0e8#libc/bionic) but there has to be a workaround since @3c71 got it sorted out somehow. – chrulri Jul 09 '12 at 11:56
  • no actual sorting out, but I made sure no file descriptors are still open as described in chrulri answer. Though I would have been glad to avoid having to sync admob too! – 3c71 Sep 26 '12 at 18:32