At the exact moment of ANR, Thread-176 was executing a shell command with Runtime.getRuntime.exec(), which entered the lock section in ProcessManager.exec().

publicProcessexec(String[]taintedCommand,String[]taintedEnvironment,FileworkingDirectory,booleanredirectErrorStream){// Ensure onExit() doesn't access the process map before we add our entry.synchronized(processReferences){processReferences.put(pid,processReference);/*
* This will wake up the child monitor thread in case there
* weren't previously any children to wait on.
*/processReferences.notifyAll();returnprocess;}}

So when our Main thread tried to execute a command later, it's blocked for a long time which caused the ANR. The "processReferences" is a HashMap which records the exit value of each child process. When ProcessManager is initialized, it creates a Thread which checks the status of every child in an infinite loop. When the status of one child process is exited or signaled, it will set the exit value accordingly.

privateProcessManager(){// Spawn a thread to listen for signals from child processes.ThreadreaperThread=newThread(ProcessManager.class.getName()){@Overridepublicvoidrun(){watchChildren();}};}

The case is clear. We should prevent calling any method which takes a lock. With lock involved, you won't know how long your UI thread will wait before other threads release the lock. Runtime.exec() should be invoked asynchronously instead. If your UI relies on the result of exec(), place it at a earlier stage and cache it for later reference. In some extreme case, use NDK to fork the process.