Welcome to XDA

Search to go directly to your device's forum

Register an account

Unlock full posting privileges

Ask a question

No registration required
Post Reply

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

OP MidnightJava

18th January 2014, 11:55 PM   |  #1  
OP Member
Flag Springfield, VA
Thanks Meter: 48
 
87 posts
Join Date:Joined: Sep 2012
More
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?
19th January 2014, 02:40 PM   |  #2  
SimplicityApks's Avatar
Senior Member
Flag Aachen
Thanks Meter: 337
 
345 posts
Join Date:Joined: May 2013
More
Quote:
Originally Posted by MidnightJava

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.
The Following User Says Thank You to SimplicityApks For This Useful Post: [ View ]
21st January 2014, 05:11 PM   |  #3  
Member
Thanks Meter: 21
 
88 posts
Join Date:Joined: Sep 2013
Can you show how you start the process?
21st January 2014, 08:13 PM   |  #4  
OP Member
Flag Springfield, VA
Thanks Meter: 48
 
87 posts
Join Date:Joined: Sep 2012
More
Quote:
Originally Posted by EmptinessFiller

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 by MidnightJava; 21st January 2014 at 08:21 PM.
Post Reply Subscribe to Thread
Previous Thread Next Thread
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes