/* * CCNx Android Services * * Copyright (C) 2010, 2011 Palo Alto Research Center, Inc. * * This work is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2 as published by the * Free Software Foundation. * This work 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 General Public License * for more details. You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ package org.ccnx.android.services; import java.util.HashMap; import org.ccnx.android.ccnlib.ICCNxService; import org.ccnx.android.ccnlib.IStatusCallback; import org.ccnx.android.ccnlib.CCNxServiceStatus.SERVICE_STATUS; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; import android.content.SharedPreferences; import android.app.NotificationManager; import android.app.Notification; import android.app.PendingIntent; /** * Generic service wrapper for Android. Provides the basic control * structure and service abstraction. */ public abstract class CCNxService extends Service implements Runnable { protected String TAG = "CCNxService"; protected static final int NOTIFICATION = 2008; // The status of the current service private SERVICE_STATUS status; // Keep a record of all the callbacks we need to issue final RemoteCallbackList<IStatusCallback> mCallbacks = new RemoteCallbackList<IStatusCallback>(); // Where to map all the options that we need for the service protected HashMap<String, String> options = new HashMap<String, String>(); // Thread to run the actual service code protected Thread thd = null; // Is the service thread running public boolean running; protected NotificationManager mNM; protected SharedPreferences mCCNxServicePrefs; private final ICCNxService.Stub serviceBinder = new ICCNxService.Stub() { public int getStatus() { return status.ordinal(); } public void stop() { stopService(); } public void registerStatusCallback(IStatusCallback cb){ if (cb != null) { Log.d(TAG,String.format("Registering callback %08X", cb.hashCode())); mCallbacks.register(cb); } } public void unregisterStatusCallback(IStatusCallback cb){ if (cb != null) { Log.d(TAG,String.format("Unregistering callback %08X", cb.hashCode())); mCallbacks.unregister(cb); } } }; @Override public void onCreate(){ Log.d(TAG, "Creating"); // Some emulators have problems with ipv6 System.setProperty("java.net.preferIPv6Addresses", "false"); // Init Preferences mCCNxServicePrefs = this.getSharedPreferences("ccnxserviceprefs", MODE_WORLD_READABLE); setStatus(SERVICE_STATUS.SERVICE_SETUP); mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); showNotification(); } @Override public void onDestroy(){ Log.d(TAG, "Destroying"); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "Starting: " + startId + ": " + intent); // Get all the options from the intent onStartService(intent); // If service failed to start for any reason, should we keep trying to start? // We want this service to be sticky to ensure execution for arbitrary duration // however, problems in startup usually don't go away, creating repeated user alerts return START_STICKY; } @Override public IBinder onBind(Intent arg0) { Log.d(TAG, "Binding"); return serviceBinder; } @Override public boolean onUnbind(Intent i){ Log.d(TAG, "Unbinding"); return false; } public void Load(){ if (! running){ Log.d(TAG,"Starting new thread"); running = true; thd = new Thread(this,TAG); thd.start(); } Log.d(TAG,"Thread started"); } public void run(){ runService(); } protected void serviceStopped(){ // If there is a service error, don't clear the status to finished // Otherwise we'll never see that the service is in an error state // if (status != SERVICE_STATUS.SERVICE_ERROR) { setStatus(SERVICE_STATUS.SERVICE_FINISHED); } running = false; } public void setStatus(SERVICE_STATUS st) { status = st; // Broadcast to all clients the new value. Log.d(TAG, "Starting broacast"); final int N = mCallbacks.beginBroadcast(); try { for (int i=0; i<N; i++) { try { Log.d(TAG, "Broadcasting to client " + i); mCallbacks.getBroadcastItem(i).status(status.ordinal()); } catch (RemoteException e) { // The RemoteCallbackList will take care of removing // the dead object for us. // Do we really want to bury RemoteException? e.printStackTrace(); } } } finally { mCallbacks.finishBroadcast(); } Log.d(TAG, "Finished broacast"); } protected void dumpOptions(){ for(String opt : options.keySet()){ Log.d(TAG,opt + " = " + options.get(opt)); } } private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.service_started1_msg); // Set the icon, scrolling text and timestamp Notification notification = new Notification(R.drawable.ccnxlogo48px, text, System.currentTimeMillis()); // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Controller.class), 0); // Set the info for the views that show in the notification panel. notification.setLatestEventInfo(this, getText(R.string.app_name), text, contentIntent); // Send the notification. mNM.notify(NOTIFICATION, notification); } protected abstract void onStartService(Intent i); protected abstract void runService(); protected abstract void stopService(); }