/*
* Copyright (C) 2012, Katy Hilgenberg.
* Special acknowledgments to: Knowledge & Data Engineering Group, University of Kassel (http://www.kde.cs.uni-kassel.de).
* Contact: sdcf@cs.uni-kassel.de
*
* This file is part of the SDCFramework (Sensor Data Collection Framework) project.
*
* The SDCFramework is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The SDCFramework 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 the SDCFramework. If not, see <http://www.gnu.org/licenses/>.
*/
package de.unikassel.android.sdcframework.app;
import java.io.PrintWriter;
import java.io.StringWriter;
import de.unikassel.android.sdcframework.R;
import de.unikassel.android.sdcframework.app.facade.ISDCService;
import de.unikassel.android.sdcframework.app.facade.SDCService;
import de.unikassel.android.sdcframework.service.ServiceManagerImpl;
import de.unikassel.android.sdcframework.service.facade.ServiceManager;
import de.unikassel.android.sdcframework.util.DefaultUncaughtExceptionHandler;
import de.unikassel.android.sdcframework.util.LogfileManager;
import de.unikassel.android.sdcframework.util.Logger;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.widget.Toast;
/**
* The sensor data collection framework service component. Internally it is
* delegating to a {@linkplain ServiceManager management component}, which is
* supervising all the other service components.
*
* @author Katy Hilgenberg
*
*/
@SuppressLint( "Registered" )
public final class SDCServiceImpl extends Service implements SDCService
{
/**
* the service binder
*/
/**
*
*/
private final ISDCService.Stub binder = new ISDCService.Stub()
{
/*
* (non-Javadoc)
*
* @see de.unikassel.android.sdcframework.app.facade.ISDCService#
* doEnableSampleBroadCasting(boolean)
*/
@Override
public void doEnableSampleBroadCasting( boolean doEnable )
throws RemoteException
{
getServiceManager().doEnableSampleBroadCasting( doEnable );
}
};
/**
* the notification identifier
*/
private int NOTIFICATION = R.id.ServiceNotification;
/**
* the service manager maintaining the framework components
*/
private ServiceManager servicManager;
/**
* Getter for the service manager
*
* @return the service manager
*/
public ServiceManager getServiceManager()
{
if ( servicManager == null )
{
setServiceManager( new ServiceManagerImpl() );
}
return servicManager;
}
/**
* Setter for the service manager
*
* @param serviceManager
* the service manager to set
*/
public void setServiceManager( ServiceManager serviceManager )
{
// assure referential integrity
ServiceManager oldServiceManager = this.servicManager;
if ( oldServiceManager != serviceManager )
{
if ( oldServiceManager != null )
{
this.servicManager = null;
oldServiceManager.setSDCService( null );
}
this.servicManager = serviceManager;
if ( this.servicManager != null )
{
this.servicManager.setSDCService( this );
}
}
}
/*
* (non-Javadoc)
*
* @see android.app.Service#onBind(android.content.Intent)
*/
@Override
public IBinder onBind( Intent intent )
{
return binder;
}
/*
* (non-Javadoc)
*
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate()
{
initializeService();
super.onCreate();
}
/*
* (non-Javadoc)
*
* @see android.app.Service#onDestroy()
*/
@Override
public void onDestroy()
{
shutDownService();
// release instance references
setServiceManager( null );
}
/*
* (non-Javadoc)
*
* @see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
@Override
public int onStartCommand( Intent intent, int flags, int startId )
{
// signal service shall run until stopService is called explicitly
return START_STICKY;
}
/**
* main initialization method
*/
private void initializeService()
{
// create and start service manager
try
{
// first install default uncaught exception handler
Thread.setDefaultUncaughtExceptionHandler( new DefaultUncaughtExceptionHandler(
Thread.getDefaultUncaughtExceptionHandler() ) );
Context applicationContext = getApplicationContext();
getServiceManager().onCreate( applicationContext );
getServiceManager().onResume( applicationContext );
// sending a broadcast signaling running state changed to true
broadcastRunningState( true );
// create notification for controller activity access
// and signal the user that the service was started.
String text = "started";
startForeground( getText( R.string.sdc_service_name ).toString() + " "
+ text );
Logger.getInstance().info( this, text );
}
catch ( Exception ex )
{
Throwable e = ex.fillInStackTrace();
StringWriter traceOut = new StringWriter();
e.printStackTrace( new PrintWriter( traceOut, true ) );
Logger.getInstance().error( this,
"Fatal error on service initialization!\n" + traceOut.toString() );
e.printStackTrace();
}
}
/**
* shutdown and clean up method for the service
*/
private void shutDownService()
{
try
{
// stop and destroy service manager
Context applicationContext = getApplicationContext();
getServiceManager().onPause( applicationContext );
// sending a broadcast signaling running state changed to false
broadcastRunningState( false );
// cancel the persistent notification
stopForeground( true );
// notify about state change
String text = "stopped";
Toast.makeText( this,
getText( R.string.sdc_service_name ).toString() + " " + text,
Toast.LENGTH_SHORT ).show();
// destroy the service manager
//( will destroy maintained instances as well )
getServiceManager().onDestroy( applicationContext );
Logger.getInstance().info( this, text );
setServiceManager( null );
// finally destroy the global logger instance
Logger.releaseInstance();
LogfileManager.prepareReleaseInstance();
}
catch ( Exception ex )
{
StringWriter traceOut = new StringWriter();
ex.printStackTrace( new PrintWriter( traceOut, true ) );
Logger.getInstance().error( this,
"Error on service shutdown!\n" + traceOut.toString() );
}
}
/**
* Does start the process in foreground and create and display a notification
* for the user for quick access to the controlling activity.
*/
private void startForeground( String text )
{
// create and send the persistent notification ( use local pho9ne time here
// )
Notification notification = new Notification( R.drawable.serviceicon, text,
System.currentTimeMillis() );
// the PendingIntent to launch the controller activity if the user selects
// this notification
PendingIntent contentIntent =
PendingIntent.getActivity( this, 0, new Intent( this,
SDCServiceController.class ), 0 );
// set the info for the views that show in the notification panel.
notification.setLatestEventInfo( this,
getText( R.string.sdc_service_label ), text, contentIntent );
// send the notification.
// getNotificationManager().notify( NOTIFICATION, notification );
startForeground( NOTIFICATION, notification );
}
/*
* (non-Javadoc)
*
* @see
* android.app.Service#onConfigurationChanged(android.content.res.Configuration
* )
*/
@Override
public void onConfigurationChanged( Configuration newConfig )
{
super.onConfigurationChanged( newConfig );
}
/**
* Getter for the service context power manager
*
* @return the power manager in this context
*/
public PowerManager getPowerManager()
{
return (PowerManager) getSystemService( Context.POWER_SERVICE );
}
/**
* Method to broadcast the service running state change
*
* @param isRunning
* flag for the service running state to broadcast
*/
private void broadcastRunningState( boolean isRunning )
{
Intent intent = new Intent();
intent.setAction( ACTION );
intent.putExtra( INTENT_NAME_RUNNING_FLAG, isRunning );
sendBroadcast( intent );
}
}