/* * CCNx Android Helper Library. * * Copyright (C) 2010-2012 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 org.ccnx.android.ccnlib.CCNxServiceStatus.SERVICE_STATUS; import org.ccnx.android.ccnlib.CcndWrapper.CCND_OPTIONS; import org.ccnx.android.ccnlib.RepoWrapper.REPO_OPTIONS; import org.ccnx.android.ccnlib.RepoWrapper.CCNS_OPTIONS; import org.ccnx.android.ccnlib.RepoWrapper.CCNR_OPTIONS; import android.content.Context; import android.util.Log; import android.os.Environment; import java.io.File; /** * This is a helper class to access the ccnd and repo services. It provides * abstractions so that the programs can start and stop the services as well * as interact with them for configuration and monitoring. */ public final class CCNxServiceControl { public final static Long MINIMUM_SECONDS_SINCE_EPOCH = 946684800L; private final static String TAG = "CCNxServiceControl"; private static String mErrorMessage = ""; CcndWrapper ccndInterface; RepoWrapper repoInterface; Context _ctx; CCNxServiceCallback _cb = null; SERVICE_STATUS ccndStatus = SERVICE_STATUS.SERVICE_OFF; SERVICE_STATUS repoStatus = SERVICE_STATUS.SERVICE_OFF; CCNxServiceCallback ccndCallback = new CCNxServiceCallback(){ @Override public void newCCNxStatus(SERVICE_STATUS st) { Log.i(TAG,"ccndCallback ccndStatus = " + st.toString()); ccndStatus = st; switch(ccndStatus){ case SERVICE_OFF: newCCNxAPIStatus(SERVICE_STATUS.CCND_OFF); break; case SERVICE_INITIALIZING: newCCNxAPIStatus(SERVICE_STATUS.CCND_INITIALIZING); break; case SERVICE_TEARING_DOWN: newCCNxAPIStatus(SERVICE_STATUS.CCND_TEARING_DOWN); break; case SERVICE_RUNNING: newCCNxAPIStatus(SERVICE_STATUS.CCND_RUNNING); break; case SERVICE_ERROR: newCCNxAPIStatus(SERVICE_STATUS.SERVICE_ERROR); break; default: Log.d(TAG, "ccndCallback, ignoring status = " + st.toString()); } } }; CCNxServiceCallback repoCallback = new CCNxServiceCallback(){ @Override public void newCCNxStatus(SERVICE_STATUS st) { Log.i(TAG,"repoCallback repoStatus = " + st.toString()); repoStatus = st; switch(repoStatus){ case SERVICE_OFF: newCCNxAPIStatus(SERVICE_STATUS.REPO_OFF); break; case SERVICE_INITIALIZING: newCCNxAPIStatus(SERVICE_STATUS.REPO_INITIALIZING); break; case SERVICE_TEARING_DOWN: newCCNxAPIStatus(SERVICE_STATUS.REPO_TEARING_DOWN); break; case SERVICE_RUNNING: newCCNxAPIStatus(SERVICE_STATUS.REPO_RUNNING); break; case SERVICE_ERROR: newCCNxAPIStatus(SERVICE_STATUS.SERVICE_ERROR); break; default: Log.d(TAG, "repoCallback, ignoring status = " + st.toString()); } } }; public CCNxServiceControl(Context ctx) { _ctx = ctx; ccndInterface = new CcndWrapper(_ctx); ccndInterface.setCallback(ccndCallback); ccndStatus = ccndInterface.getStatus(); repoInterface = new RepoWrapper(_ctx); repoInterface.setCallback(repoCallback); repoStatus = repoInterface.getStatus(); } public void registerCallback(CCNxServiceCallback cb){ _cb = cb; } public void unregisterCallback(){ _cb = null; } /** * Start the CCN daemon and Repo * If configuration parameters have been set these will be used * This is a BLOCKING call * * @return true if everything started correctly, false otherwise */ public boolean startAll(){ if (checkSystemOK()) { newCCNxAPIStatus(SERVICE_STATUS.START_ALL_INITIALIZING); Log.i(TAG,"startAll waiting for CCND startService"); ccndInterface.startService(); Log.i(TAG,"startAll waiting for CCND waitForReady"); ccndInterface.waitForReady(); newCCNxAPIStatus(SERVICE_STATUS.START_ALL_CCND_DONE); if(!ccndInterface.isReady()){ mErrorMessage = mErrorMessage.concat("Unable to start ccnd service."); newCCNxAPIStatus(SERVICE_STATUS.START_ALL_ERROR); return false; } Log.i(TAG,"startAll waiting for REPO startService"); repoInterface.startService(); Log.i(TAG,"startAll waiting for REPO waitForReady"); repoInterface.waitForReady(); newCCNxAPIStatus(SERVICE_STATUS.START_ALL_REPO_DONE); if(!repoInterface.isReady()){ mErrorMessage = mErrorMessage.concat("Unable to start repo service."); newCCNxAPIStatus(SERVICE_STATUS.START_ALL_ERROR); return false; } newCCNxAPIStatus(SERVICE_STATUS.START_ALL_DONE); return true; } else { newCCNxAPIStatus(SERVICE_STATUS.START_ALL_ERROR); return false; } } /** * Start the CCN daemon and Repo * If configuration parameters have been set these will be used * This is a non-blocking call. If you want to be notified when everything * has started then you should register a callback before issuing this call. */ public void startAllInBackground(){ Runnable r = new Runnable(){ public void run() { startAll(); } }; Thread thd = new Thread(r); thd.start(); } public void connect(){ ccndInterface.bindIfRunning(); repoInterface.bindIfRunning(); } /** * Disconnect from the services. This is needed for a clean exit from an application. It leaves the services running. */ public void disconnect(){ ccndInterface.unbindService(); repoInterface.unbindService(); } /** * Stop the CCN daemon and Repo * This call will unbind from the service and stop it. There is no need to issue a disconnect(). */ public void stopAll(){ repoInterface.stopService(); ccndInterface.stopService(); newCCNxAPIStatus(SERVICE_STATUS.STOP_ALL_DONE); } public boolean checkSystemOK() { // // Do a quick check of things before we start. If we can't properly initialize the following, don't pass go. // We fail right away rather than checking everything. // 1) system time // 2) check external storage writable // 3) other checks - TBD ... In the future we may want to verify that we have at least one usable *face // Log.d(TAG, "Checking current time in millis: " + System.currentTimeMillis() + " and date today = " + new java.util.Date()); if (System.currentTimeMillis()/1000 < MINIMUM_SECONDS_SINCE_EPOCH) { // Realistically no modern device will be shipping from the factory without a reasonable default // near or close to the current date at manufacture, nor will it lack the ability to get time // from the network. However, in dealing with Android "open source", some devices still seem to // ship with time set to the beginning of the epoch, i.e., 0. Log.e(TAG,"Error in checkSystemOK(), please set OS System Time to valid, non-default date."); mErrorMessage = mErrorMessage.concat("Please set OS System Time before running this service."); return false; } if (!Environment.getExternalStorageDirectory().canWrite()) { // Again, not a likely scenario, but it's been seen before that some Android devices have either // low quality media or problems in the design of the SDCARD reader that prevent the external storage // from build a valid write target. Since we'll need both access to this storage and write // access to it, we should not proceed if we fail to get writable external storage. // Future, more robust versions of this service should look for alternatives (app data space) // before failing completely. Log.e(TAG,"Error in checkSystemOK(), please fix permissions to access external storage for write, or insert writable media."); mErrorMessage = mErrorMessage.concat("Please check external SDCARD is available and writable."); return false; } return true; } public boolean isCcndRunning(){ return ccndInterface.isRunning(); } public boolean isRepoRunning(){ return repoInterface.isRunning(); } public void startCcnd(){ ccndInterface.startService(); } public void stopCcnd(){ ccndInterface.stopService(); } public void startRepo(){ repoInterface.startService(); } public void stopRepo(){ repoInterface.stopService(); } public void newCCNxAPIStatus(SERVICE_STATUS s){ Log.d(TAG,"newCCNxAPIStatus sending " + s.toString()); try { if(_cb != null) { _cb.newCCNxStatus(s); } } catch(Exception e){ // Did the callback just throw an exception?? // We're going to ignore it, it's not our problem (right?) Log.e(TAG,"The client callback has thrown an exception"); e.printStackTrace(); } } public void setCcndOption(CCND_OPTIONS option, String value) { ccndInterface.setOption(option, value); } public void setRepoOption(REPO_OPTIONS option, String value) { repoInterface.setOption(option, value); } public void setSyncOption(CCNS_OPTIONS option, String value) { repoInterface.setOption(option, value); } public void setCcnrOption(CCNR_OPTIONS option, String value) { repoInterface.setOption(option, value); } public String getErrorMessage() { return mErrorMessage; } public void clearErrorMessage() { mErrorMessage = ""; } /** * Are ccnd and the repo running and ready? * @return true if BOTH ccnd and the repo are in state Running */ public boolean isAllRunning(){ return(SERVICE_STATUS.SERVICE_RUNNING.equals(ccndStatus) && SERVICE_STATUS.SERVICE_RUNNING.equals(repoStatus)); } public SERVICE_STATUS getCcndStatus(){ return ccndStatus; } public SERVICE_STATUS getRepoStatus(){ return repoStatus; } }