[Q] Can I register a listener on process state?

Search This thread

MidnightJava

Senior Member
Sep 4, 2012
115
51
Reston, VA
I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.

I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?
 

SimplicityApks

Senior Member
May 26, 2013
354
344
Aachen
I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.

I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?

Afaik there's no way to accomplish that other than your way or being system/root app. See this similar question here for reference.
 
  • Like
Reactions: MidnightJava

MidnightJava

Senior Member
Sep 4, 2012
115
51
Reston, VA
Can you show how you start the process?

Sure. Here's the class that manages starting, stopping, and statusing (running or not) the binary executable. In this case, it's the omniNames service of the omni ORB (CORBA broker).

Code:
public class RHManager {

	private TimerTask task = new TimerTask() {

		@Override
		public void run() {
			if (RHManager.this.listener != null) {
				listener.running(isOmniNamesRunning());
			}
		}

	};

	private IStatusListener listener;

	public RHManager() {

	}

	public void startOmniNames() {
		final Exec exec = new Exec();
		final String[] args = new String[]
				{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
				"start"};
		final String[] env = new String[] {"LD_LIBRARY_PATH=/sdcard/data/com.axiosengineering.rhmanager/omniORB/lib"};
		Thread t = new Thread() {
			public void run() {
				try {
					int res = exec.doExec(args, env);
					logMsg("omniNames start return code " + res);
				} catch (IOException e) {
					logMsg("Failed to start omniNames");
					e.printStackTrace();
				}
				String std = exec.getOutResult();
				logMsg("omniNames start: std out==> " + std );
				String err = exec.getErrResult();
				logMsg("omniNames start: err out==> " + err );
			};
		};
		t.start();
		logMsg("omniNames started");

	}

	private boolean isOmniNamesRunning() {
		String pid_s = getOmniNamesPid();
		Integer pid = null;
		if (pid_s != null) {
			try {
				pid = Integer.parseInt(pid_s);
			} catch (NumberFormatException e) {
				return false;
			}
		}

		if (pid != null) {
			RunningAppProcessInfo activityMgr = new ActivityManager.RunningAppProcessInfo("omniNames", pid, null);
			return activityMgr.processName != null ;
		}
		return false;
	}

	public void stopOmniNames() {
		String pid = getOmniNamesPid();
		android.os.Process.killProcess(Integer.parseInt(pid));
		android.os.Process.sendSignal(Integer.parseInt(pid), android.os.Process.SIGNAL_KILL);
	}

	private String getOmniNamesPid() {
		Exec exec = new Exec();
		final String[] args = new String[]
				{RhMgrConstants.INSTALL_LOCATION_OMNI_NAMES_SCRIPTS + "/" + RhMgrConstants.OMNI_NAMES_SCRIPT_FILE,
				"pid"};
		String pid = "";
		try {
			int res = exec.doExec(args, null);
			logMsg("oniNames pid return code: " + res);
		} catch (IOException e) {
			logMsg("Failed to start omniNames");
			e.printStackTrace();
			return pid;
		}
		String std = exec.getOutResult();
		logMsg("omniNames pid: std out ==> " + std);
		String err = exec.getErrResult();
		logMsg("omniNames pid: err out ==> " + err);

		String[] parts = std.split("\\s+");
		if (parts.length >= 2) {
			pid = parts[1];
		}
		return pid;
	}

        //monitor omniNames status and report status periodically to an IStatusListener
	public void startMonitorProcess(IStatusListener listener, String string) {
		this.listener = listener;
		Timer t = new Timer();
		t.schedule(task, 0, 1000);
	}
	
	private void logMsg(String msg) {
		if (RhMgrConstants.DEBUG) {
			System.err.println(msg);
		}
	}

}

Here's the Exec class that handles invocation of Runtime#exec(), consumes std and err out, and reports those and process return status to the caller.

Code:
public class Exec {

	private String outResult;
	private String errResult;
	private Process process;
	private boolean failed = false;
	StreamReader outReader;
	StreamReader errReader;

	public int doExec(String[] cmd, String[] envp) throws IOException{
		Timer t = null;
		try {
			process = Runtime.getRuntime().exec(cmd, envp);
			outReader = new StreamReader(process.getInputStream());
			outReader.setPriority(10);
			errReader = new StreamReader(process.getErrorStream());
			outReader.start();
			errReader.start();
			t = new Timer();
			t.schedule(task, 10000);
			int status = process.waitFor();
			outReader.join();
			errReader.join();
			StringWriter outWriter = outReader.getResult();
			outResult = outWriter.toString();
			outWriter.close();
			StringWriter errWriter = errReader.getResult();
			errResult = errWriter.toString();
			errWriter.close();
			return (failed ? -1: status);
		} catch (InterruptedException e) {
			return -1;
		} finally {
			if (t != null) {
				t.cancel();
			}
		}
	}

	public int doExec(String[] cmd) throws IOException{
		return doExec(cmd, null);
	}

	public String getOutResult(){
		return outResult;
	}

	public String getErrResult(){
		return errResult;
	}

	private static class StreamReader extends Thread {
		private InputStream is;
		private StringWriter sw;

		StreamReader(InputStream is) {
			this.is = is;
			sw = new StringWriter(30000);
		}
		public void run() {
			try {
				int c;
				while ((c = is.read()) != -1){
					sw.write(c);
				}
			}
			catch (IOException e) { ; }
		}

		StringWriter getResult() {
			try {
				is.close();
			} catch (IOException e) {
				System.err.println("Unable to close input stream in StreamReader");
			}
			return sw;
		}
	}

	private TimerTask task = new TimerTask() {

		@Override
		public void run() {
			failed = true;
			process.destroy();
		}

	};
}

Here's the script that startOminNames() invokes. It's the shell script installed with omniORB with functions other than start and get_pid removed, since those are handled by Android classes. You can invoke any executable in place of the script, or wrap your executable in a script.

Code:
#
# omniNames      init file for starting up the OMNI Naming service
#
# chkconfig:   - 20 80
# description: Starts and stops the OMNI Naming service
#

exec="/sdcard/data/com.axiosengineering.rhmanager/omniORB/bin/omniNames"
prog="omniNames"
logdir="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs"
logfile="/sdcard/data/com.axiosengineering.rhmanager/omniORB/logs/omninames-localhost.err.log"
options=" -start -always -logdir $logdir -errlog $logfile"

start() {
    #[ -x $exec ] || exit 5
    echo -n $"Starting $prog: "
    $exec  $options
}

get_pid() {
   	ps | grep omniNames
}

case "$1" in
    start)
        start && exit 0
        $1
        ;;
    pid)
        get_pid
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
        exit 2
esac
exit $?

And here's the IStatusListener interface

Code:
public interface IStatusListener {
	
	public void running(boolean running);

}

Runtime.exec() has some pitfalls. See this helpful Runtime.exec tutorial for a nice explanation.

And you may also want to check out this post on loading native binaries in Android.
 
Last edited:

Top Liked Posts

  • There are no posts matching your filters.
  • 1
    I'm an experienced developer but new to Android development. I have an app that runs some native binaries, and I provide a status indicator to show when the native process is running and when it's not. Currently I poll the device to figure this out, using the ActivityManager API to determine if specific processes are running or not.

    I'm hoping there is some way to register a listener on process state changes, so I can get notified when my process starts or stops. I looked through the API, and there doesn't seem to be such a thing. Does anyone know how I can keep track of process start and stop other than polling via ActivityManager?

    Afaik there's no way to accomplish that other than your way or being system/root app. See this similar question here for reference.