/* * 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.devices; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import android.content.Context; import android.telephony.CellLocation; import android.telephony.NeighboringCellInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.widget.Toast; import de.unikassel.android.sdcframework.R; import de.unikassel.android.sdcframework.data.Sample; import de.unikassel.android.sdcframework.data.independent.GSMNeighborCell; import de.unikassel.android.sdcframework.data.independent.GSMSampleData; import de.unikassel.android.sdcframework.devices.facade.SampleProvidingSensorDevice; import de.unikassel.android.sdcframework.devices.facade.SensorDeviceIdentifier; import de.unikassel.android.sdcframework.util.Logger; import de.unikassel.android.sdcframework.util.TimeInformation; import de.unikassel.android.sdcframework.util.TimeProvider; /** * Implementation of the GSM sensor device as active device providing sensor * data for the scanner. * * @author Katy Hilgenberg * */ public final class GSMDevice extends ScannerStateAwareSensorDevice implements SampleProvidingSensorDevice { /** * The events to listen for in enabled state */ private final static int eventsEnabled = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_SERVICE_STATE; /** * The events to listen for in enabled state */ private final static int eventsDisabled = PhoneStateListener.LISTEN_SERVICE_STATE; /** * Private class implementing a phone state listener for GSM devices. * * @author Katy Hilgenberg * */ private class GSMPhoneStateListener extends PhoneStateListener { /** * Constructor */ public GSMPhoneStateListener() { super(); } /* * (non-Javadoc) * * @see * android.telephony.PhoneStateListener#onServiceStateChanged(android.telephony * .ServiceState) */ @Override public final void onServiceStateChanged( ServiceState serviceState ) { try { // handle state changes doReactOnStateChange( serviceState ); } catch ( Exception e ) { Logger.getInstance().error( this, "Exception in onServiceStateChanged" ); } } /* * (non-Javadoc) * * @see * android.telephony.PhoneStateListener#onCellLocationChanged(android.telephony * .CellLocation) */ @Override public final void onCellLocationChanged( CellLocation location ) { try { if ( location instanceof GsmCellLocation ) { // update stored cell location information GsmCellLocation cellLocation = (GsmCellLocation) location; int cellId = cellLocation.getCid(); int locationAreaCode = cellLocation.getLac(); updateCellLocation( cellId, locationAreaCode ); } } catch ( Exception e ) { Logger.getInstance().error( this, "Exception in onCellLocationChanged" ); } } /* * (non-Javadoc) * * @see * android.telephony.PhoneStateListener#onSignalStrengthsChanged(android * .telephony.SignalStrength) */ @Override public final void onSignalStrengthsChanged( SignalStrength signalStrength ) { try { if ( telephoyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM ) { // update stored signal strength updateSignalStrength( signalStrength.getGsmSignalStrength() ); } } catch ( Exception e ) { Logger.getInstance().error( this, "Exception in onSignalStrengthsChanged" ); } } } /** * The context */ private Context context; /** * The last signaled service state */ private final AtomicInteger lastServiceState; /** * The telephony manager reference */ private final TelephonyManager telephoyManager; /** * The phone state listener to tack cell or signal strength changes */ private final PhoneStateListener phoneStateListener; /** * The GSM sample holding the current device state */ private final GSMSampleData currentSampleData; /** * Constructor * * @param context * the context */ public GSMDevice( Context context ) { super( SensorDeviceIdentifier.GSM ); this.telephoyManager = (TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE ); this.phoneStateListener = new GSMPhoneStateListener(); this.lastServiceState = new AtomicInteger(); this.currentSampleData = new GSMSampleData(); setContext( context ); setLastServiceState( ServiceState.STATE_IN_SERVICE ); } /** * Getter for the last service state * * @return the last service state */ public final int getLastServiceState() { return lastServiceState.get(); } /** * Setter for the last service state * * @param lastServiceState * the last service state to set */ private synchronized final void setLastServiceState( int lastServiceState ) { this.lastServiceState.set( lastServiceState ); } /** * Setter for the context * * @param context * the context to set */ private synchronized final void setContext( Context context ) { this.context = context; } /** * Getter for the context * * @return the context */ private synchronized final Context getContext() { return context; } /** * Registration of listener for the given events * * @return true if successful, false otherwise */ private synchronized final boolean registerListener( int events ) { if ( telephoyManager != null ) { telephoyManager.listen( phoneStateListener, events ); return true; } return false; } /* * (non-Javadoc) * * @see de.unikassel.android.sdcframework.devices.AbstractSensorDevice# * doSignalDeviceNotEnabledInSystem(android.content.Context) */ @Override public final void doSignalDeviceNotEnabledInSystem( Context applicationContext ) { String appName = applicationContext.getText( R.string.sdc_service_name ).toString(); String message = applicationContext.getText( R.string.msg_gsm_unavailable ).toString(); // signal to user that gsm is not available Toast.makeText( applicationContext, appName + ": " + message, Toast.LENGTH_LONG ).show(); Logger.getInstance().warning( this, message + " (" + serviceStateToString( getLastServiceState() ) + ")" ); } /* * (non-Javadoc) * * @see de.unikassel.android.sdcframework.devices.facade.SensorDevice# * isDeviceInSystemEnabled(android.content.Context) */ @Override public final boolean isDeviceInSystemEnabled( Context context ) { int state = getLastServiceState(); return state == ServiceState.STATE_IN_SERVICE || state == ServiceState.STATE_EMERGENCY_ONLY; } /** * Updater for the current signal strength * * @param signalStrength * the current signal strengt */ public synchronized final void updateSignalStrength( int signalStrength ) { currentSampleData.setSignalStrength( signalStrength ); updateNeighborCells(); } /** * Updater for the current cell location * * @param cellId * the cell id * @param locationAreaCode * the location area code */ public synchronized final void updateCellLocation( int cellId, int locationAreaCode ) { currentSampleData.setCellId( cellId ); currentSampleData.setLocationAreaCode( locationAreaCode ); updateNeighborCells(); } /** * Method to update neighbor cell information */ private final void updateNeighborCells() { List< NeighboringCellInfo > listNeigborCells = telephoyManager.getNeighboringCellInfo(); currentSampleData.getNeighbors().clear(); if ( listNeigborCells != null ) { for ( NeighboringCellInfo cellinfo : listNeigborCells ) { int networkType = cellinfo.getNetworkType(); // we do only sample GSM neighbors in a GSM sensor :) if ( networkType == TelephonyManager.NETWORK_TYPE_GPRS || networkType == TelephonyManager.NETWORK_TYPE_EDGE ) { int cellId = cellinfo.getCid(); int rssi = cellinfo.getRssi(); if ( cellId != NeighboringCellInfo.UNKNOWN_CID && rssi != NeighboringCellInfo.UNKNOWN_RSSI ) { currentSampleData.getNeighbors().add( new GSMNeighborCell( cellId, rssi ) ); } } } } } /** * Updater for the operator name * * @param operatorName * the operator name */ public synchronized final void updateOperatorName( String operatorName ) { currentSampleData.setOperator( operatorName ); } /** * Handler for service state changes * * @param serviceState * the current service state */ private final void doReactOnStateChange( ServiceState serviceState ) { updateOperatorName( serviceState.getOperatorAlphaLong() ); int state = serviceState.getState(); // determine state change flags boolean oldStateWasEnabled = isDeviceInSystemEnabled( null ); setLastServiceState( state ); boolean newStateIsEnabled = isDeviceInSystemEnabled( null ); if ( oldStateWasEnabled != newStateIsEnabled ) { if ( newStateIsEnabled ) { // device state changed to enabled doHandleDeviceEnabledBySystem( getContext() ); } else { // device state changed to disabled doHandleDeviceDisabledBySystem( getContext() ); } } } /* * (non-Javadoc) * * @see * de.unikassel.android.sdcframework.devices.ScannerStateAwareSensorDevice * #onScannerRunningStateChange(boolean, android.content.Context) */ @Override protected final void onScannerRunningStateChange( boolean isRunning, Context context ) { if ( isRunning ) registerListener( eventsEnabled ); else registerListener( eventsDisabled ); } /** * Does create a human readable string representation of the service state * * @param serviceState * the service state * @return the string representation of the service state */ private final static String serviceStateToString( int serviceState ) { String state = "unknown state"; switch ( serviceState ) { case ServiceState.STATE_POWER_OFF: state = "power off"; break; case ServiceState.STATE_IN_SERVICE: state = "in service"; break; case ServiceState.STATE_EMERGENCY_ONLY: state = "emergency only"; break; case ServiceState.STATE_OUT_OF_SERVICE: state = "out of service"; break; } return state; } /* * (non-Javadoc) * * @see * de.unikassel.android.sdcframework.devices.AbstractSensorDevice#onDestroy * (android.content.Context) */ @Override public final void onDestroy( Context context ) { registerListener( PhoneStateListener.LISTEN_NONE ); setContext( null ); super.onDestroy( context ); } /* * (non-Javadoc) * * @see * de.unikassel.android.sdcframework.devices.facade.SampleProvidingSensorDevice * #getSample() */ @Override public synchronized final Sample getSample() { TimeInformation ti = TimeProvider.getInstance().getAccurateTimeInformation(); Sample sample = SampleFactory.getInstance().createSample( ti, getDeviceIdentifier(), getConfiguration().getSamplePriority().ordinal(), currentSampleData.doClone() ); return sample; } /* * (non-Javadoc) * * @see * de.unikassel.android.sdcframework.devices.AbstractSensorDevice#onCreate * (android.content.Context) */ @Override public final void onCreate( Context context ) { // do here that initialization stuff what would trigger an escape from // constructor if ( telephoyManager != null ) { CellLocation cellLocation = telephoyManager.getCellLocation(); if ( cellLocation instanceof GsmCellLocation ) { GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation; updateCellLocation( gsmCellLocation.getCid(), gsmCellLocation.getLac() ); updateOperatorName( telephoyManager.getNetworkOperatorName() ); } } registerListener( eventsEnabled ); } /* * (non-Javadoc) * * @see * de.unikassel.android.sdcframework.devices.facade.SampleProvidingSensorDevice * #hasSample() */ @Override public boolean hasSample() { return currentSampleData.getCellId() > 0; } }