/*
* CCNx Android Helper Library.
*
* Copyright (C) 2010, 2011 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.android.ccnlib;
import java.util.List;
import java.util.Properties;
import java.util.Map.Entry;
import org.ccnx.android.ccnlib.CCNxServiceStatus.SERVICE_STATUS;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/**
* Wrap up the AIDL calls to a CCNxService. Hide all the ugly RPC from
* the user of the services.
*/
public abstract class CCNxWrapper {
public String TAG = "CCNxWrapper";
private static final int DEFAULT_WAIT_FOR_READY = 60000;
private Object _lock = new Object();
// Timeout to wait for ready in ms
private int waitForReadyTimeout = DEFAULT_WAIT_FOR_READY;
protected String serviceClassName = "";
protected String serviceName = "";
protected String serviceType = TAG;
protected ServiceConnection sConn;
protected Object iServiceLock = new Object();
protected ICCNxService iService = null;
protected String OPTION_LOG_LEVEL = "0";
protected Properties options = new Properties();
CCNxServiceCallback _scb;
SERVICE_STATUS status = SERVICE_STATUS.SERVICE_OFF;
Context _ctx;
private IStatusCallback _cb = new IStatusCallback.Stub() {
public void status(int st){
Log.d(TAG,"Received a status update.. " + status.name());
synchronized(_lock) {
setStatus(SERVICE_STATUS.fromOrdinal(st));
if(SERVICE_STATUS.SERVICE_FINISHED.equals(status)){
setStatus(SERVICE_STATUS.SERVICE_OFF);
}
}
issueCallback();
}
};
public CCNxWrapper(Context ctx){
_ctx = ctx;
}
public void setCallback(CCNxServiceCallback scb){
_scb = scb;
}
protected void issueCallback(){
_scb.newCCNxStatus(status);
}
/**
* Start the service. If the service is running we will not try to start it
* we will only try to bind to it
* @return true if we are bound, false if we are not, some error occurred
*/
public boolean startService(){
Log.d(TAG,"startService()");
if(!isRunning()){
_ctx.startService(getStartIntent());
} else {
setStatus(SERVICE_STATUS.SERVICE_RUNNING);
issueCallback();
}
bindService();
return isBound();
}
public void bindIfRunning(){
Log.d(TAG,"If Running, Bind");
if(isRunning()){
bindService();
}
}
/**
* Check whether we are bound to the running service
* @return true if we are bound, false if we are not
*/
public boolean isBound(){
synchronized(iServiceLock) {
return(iService != null);
}
}
protected void bindService(){
sConn = new ServiceConnection(){
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, " Service Connected");
synchronized(iServiceLock) {
iService = ICCNxService.Stub.asInterface(binder);
try {
iService.registerStatusCallback(_cb);
SERVICE_STATUS st = SERVICE_STATUS.fromOrdinal(iService.getStatus());
Log.i(TAG,"bindService sets status: " + st);
setStatus(st);
} catch (RemoteException e) {
// Did the service crash?
e.printStackTrace();
}
}
issueCallback();
}
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, " Service Disconnected");
synchronized(iServiceLock) {
iService = null;
}
}
};
_ctx.bindService(getBindIntent(), sConn, Context.BIND_AUTO_CREATE);
}
protected void unbindService(){
synchronized(iServiceLock) {
if(isBound()){
_ctx.unbindService(sConn);
}
iService = null;
}
}
public void stopService(){
synchronized(iServiceLock) {
try {
if( null != iService)
iService.stop();
} catch (RemoteException e) {
e.printStackTrace();
}
unbindService();
}
_ctx.stopService(new Intent(serviceName));
}
public boolean isRunning(){
ActivityManager am = (ActivityManager) _ctx.getSystemService(Activity.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(50);
for(ActivityManager.RunningServiceInfo s : services){
if(s.service.getClassName().equalsIgnoreCase(serviceClassName)){
Log.d(TAG,"Found " + serviceClassName + " Service Running");
return true;
}
}
Log.d(TAG,serviceClassName + " Not Running");
return false;
}
/**
* Sets the default timeout to be used for the waitForReady() call.
* Set it to 0 to not use a timeout.
*
* @param timeout timeout to be used in waitForReady()
*/
public void setWaitForReadyTimeout(int timeout){
waitForReadyTimeout = timeout;
}
/**
* waits until this service is in state RUNNING. (blocking call)
* There is a timeout associated with this wait. It can be changed
* using the setWaitForReadyTimeout() function. If set to 0 it will wait forever.
*
* Alternatively, you can call waitForReady() and give it the timeout parameter as the argument.
*
* @return true if we are ready, false if we timed out
*/
public boolean waitForReady(){
return waitForReady(waitForReadyTimeout);
}
/**
* waits until the service is in state RUNNING. (blocking call)
* This function takes a timeout as a parameter. If the timeout is triggered it will
* return false. If timeout is set to 0 it won't be used and wait will block indefinitely.
*
* This function does not use the default value ser by setWaitForReadyTimeout().
*
* @param timeout timeout passed to the wait() call.
* @return true if we are ready, false otherwise
*/
public boolean waitForReady(int timeout){
synchronized(_lock){
while(!SERVICE_STATUS.SERVICE_RUNNING.equals(status)){
try {
if(timeout == 0){
_lock.wait();
} else {
_lock.wait(waitForReadyTimeout);
}
} catch (InterruptedException e) {
// Our timeout expired
return false;
}
}
}
return true;
}
public boolean isReady(){
return (SERVICE_STATUS.SERVICE_RUNNING.equals(status));
}
public void setOption(String key, String value) {
options.setProperty(key, value);
}
public void clearOptions() {
options.clear();
}
protected void fillIntentOptions(Intent i) {
for(Entry<Object, Object> entry : options.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
i.putExtra(key, value);
Log.i(TAG, "fillIntentOptions - Adding option " + key + " = " + value);
}
}
/**
* Create an Intent to be issued to bind to the Service. It will be used as the parameter to bindService()
* This function is useful for adding "extras" to the intent
*
* @return Intent to start this service
*/
protected abstract Intent getBindIntent();
/**
* Create an Intent to be issued to start the Service. It will be used as the parameter to startService()
* This function is useful for adding "extras" to the intent
*
* @return Intent to start this service
*/
protected abstract Intent getStartIntent();
public SERVICE_STATUS getStatus(){
return status;
}
protected void setStatus(SERVICE_STATUS st) {
Log.i(TAG,"setStatus = " + st);
synchronized(_lock) {
status = st;
_lock.notifyAll();
}
}
}