package com.samknows.measurement.environment; import java.util.List; import com.samknows.libcore.SKPorting; import com.samknows.measurement.util.OtherUtils; import android.content.Context; import android.os.Build; import android.telephony.CellInfo; import android.telephony.CellLocation; import android.telephony.NeighboringCellInfo; import android.telephony.PhoneStateListener; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; public class CellTowersDataCollector extends EnvBaseDataCollector { public CellTowersDataCollector(Context context) { super(context); } /* * performs a synchronous read of the signal strength */ static TelephonyManager mTelManager = null; static final CellTowersData mData = new CellTowersData(); static AndroidPhoneStateListener phoneStateListener = null; static final CellTowersData neighbours = new CellTowersData(); @Override synchronized public CellTowersData collect() { //final TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); final CellTowersData data = new CellTowersData(); data.setTime(System.currentTimeMillis()); try { data.setCellLocation(mTelManager.getCellLocation()); List<CellInfo> cellInfo = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // Note: the following call might return NULL cellInfo = mTelManager.getAllCellInfo(); } if (cellInfo != null) { data.setCellInfo(cellInfo); } // Note: the following call might return NULL //data.setNeighbors(mTelManager.getAllCellInfo()); data.setNeighbors(mTelManager.getNeighboringCellInfo()); } catch (SecurityException e) { // Seen - rarely - on some Android devices. // Neither user 99999 nor current process has android.permission.ACCESS_COARSE_LOCATION. SKPorting.sAssert(CellTowersDataCollector.class, false); } // We might be passed null, if the signal is not available currently. // This is perfectly valid! //SKLogger.sAssert(CellTowersDataCollector.class, mData.getSignal() != null); // This following line is actually essential! data.setSignal(mData.getSignal()); addData(mData); addData(neighbours); return data; } synchronized static void sOnSignalStrengthsChanged(SignalStrength signalStrength) { mData.setTime(System.currentTimeMillis()); mData.setSignal(signalStrength); if (OtherUtils.isThisDeviceAnEmulator() == true) { // The signal will usuaully be null on the Emulator... } else { // On a real device... there should generally be a signal... SKPorting.sAssert(CellTowersDataCollector.class, mData.getSignal() != null); } try { mData.setCellLocation(mTelManager.getCellLocation()); } catch (SecurityException e) { // Seen - rarely - on some Android devices. // Neither user 99999 nor current process has android.permission.ACCESS_COARSE_LOCATION. SKPorting.sAssert(CellTowersDataCollector.class, false); } } synchronized static void sOnCellLocationChanged(CellLocation location){ mData.setTime(System.currentTimeMillis()); mData.setCellLocation(location); } synchronized static void sOnCellInfoChanged(List<CellInfo> cellInfo){ List<NeighboringCellInfo> n = mTelManager.getNeighboringCellInfo(); if( n != null && n.size() > 0){ neighbours.setTime(System.currentTimeMillis()); neighbours.setNeighbors(n); } } static class AndroidPhoneStateListener extends PhoneStateListener { @Override public void onSignalStrengthsChanged(SignalStrength signalStrength){ super.onSignalStrengthsChanged(signalStrength); CellTowersDataCollector.sOnSignalStrengthsChanged(signalStrength); } @Override public void onCellLocationChanged(CellLocation location){ super.onCellLocationChanged(location); CellTowersDataCollector.sOnCellLocationChanged(location); } @Override public void onCellInfoChanged(List<CellInfo> cellInfo){ //super.onCellInfoChanged(cellInfo); CellTowersDataCollector.sOnCellInfoChanged(cellInfo); } } // This is called just once, to start monitoring for cell tower signal strength... // We need to do this, as Android does not allow us to query this information synchronously! static public void sStartToCaptureCellTowersData(Context context) { phoneStateListener = new AndroidPhoneStateListener (); try { mTelManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); int mask = PhoneStateListener.LISTEN_CELL_INFO | PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS; //int mask = PhoneStateListener.LISTEN_SIGNAL_STRENGTHS; mTelManager.listen(phoneStateListener, mask); } catch (java.lang.SecurityException e) { // This has been seen on a small number of Android 2.3.3-2.3.7 devices, with the following: // "com.samknows.fcc.FCCApplication: java.lang.SecurityException: Neither user ... nor current process has android.permission.READ_PHONE_STATE." SKPorting.sAssert(CellTowersData.class, false); } catch (Exception e) { SKPorting.sAssert(CellTowersData.class, false); } } @Override public void start() { addData(collect()); } @Override public void stop() { } }