Skip to content

Commit

Permalink
Adds CSV logging for Wi-Fi
Browse files Browse the repository at this point in the history
  • Loading branch information
christianrowlands committed Nov 6, 2023
1 parent 15237cd commit 37c2c44
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private NetworkSurveyConstants()
public static final int LOGGING_NOTIFICATION_ID = 1;

public static final String LOG_DIRECTORY_NAME = "NetworkSurveyData";
public static final String CSV_LOG_DIRECTORY_NAME = "NetworkSurveyData/csv";
public static final String CSV_LOG_DIRECTORY_NAME = "NetworkSurveyData";

public static final String GSM_FILE_NAME_PREFIX = "craxiom-gsm-";
public static final String CDMA_FILE_NAME_PREFIX = "craxiom-cdma-";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ public abstract class CsvConstants
public static final String LATITUDE = "latitude";
public static final String LONGITUDE = "longitude";
public static final String ALTITUDE = "altitude";
public static final String SPEED = "speed";
public static final String ACCURACY = "accuracy";
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* <p>
* The constants in this class are intended to match the constants defined in the
* <a href="https://messaging.networksurvey.app/">Network Survey Messaging API</a>.
*
* @since 1.13
*/
public class LteCsvConstants extends CellularCsvConstants
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.craxiom.networksurvey.constants.csv;

/**
* The constants associated with the Wi-Fi CSV file headers.
* <p>
* The constants in this class are intended to match the constants defined in the
* <a href="https://messaging.networksurvey.app/">Network Survey Messaging API</a>.
*/
public class WifiCsvConstants extends CellularCsvConstants
{
private WifiCsvConstants()
{
}

public static final String SOURCE_ADDRESS = "sourceAddress";
public static final String DESTINATION_ADDRESS = "destinationAddress";
public static final String BSSID = "bssid";
public static final String BEACON_INTERVAL = "beaconInterval";
public static final String SERVICE_SET_TYPE = "serviceSetType";
public static final String SSID = "ssid";
public static final String SUPPORTED_RATES = "supportedRates";
public static final String EXT_SUPPORTED_RATES = "extendedSupportedRates";
public static final String CIPHER_SUITES = "cipherSuites";
public static final String AKM_SUITES = "akmSuites";
public static final String ENCRYPTION_TYPE = "encryptionType";
public static final String WPA = "wps";
public static final String CHANNEL = "channel";
public static final String FREQ_MHZ = "frequencyMhz";
public static final String SIGNAL_STRENGTH = "signalStrength";
public static final String SNR = "snr";
public static final String NODE_TYPE = "nodeType";
public static final String STANDARD = "standard";
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ String[] getHeaders()
return CdrEvent.getHeaders();
}

@Override
String[] getHeaderComments()
{
return new String[]{"CSV Version=0.1.0"};
}

// Needs to be synchronized so we don't write two records at the same time... which I saw happen
@Override
public synchronized void onCdrEvent(CdrEvent record)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.craxiom.networksurvey.logging;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
Expand Down Expand Up @@ -74,6 +76,11 @@ public abstract class CsvRecordLogger

abstract String[] getHeaders();

/**
* @return A String array of comments and other information that should be written to the top of the CSV file.
*/
abstract String[] getHeaderComments();

/**
* Sets up all the GeoPackage stuff so that the survey records can be written to a log file.
* <p>
Expand Down Expand Up @@ -135,11 +142,11 @@ public boolean enableLogging(boolean enable)
}
}

void writeCsvRecord(Object[] row) throws IOException
void writeCsvRecord(Object[] row, boolean flush) throws IOException
{
if (lazyFileCreation) lazyCreateFileIfNecessary();
printer.printRecord(row);
printer.flush(); // TODO could we get away with not flushing on every record?
if (flush) printer.flush(); // TODO could we get away with not flushing on every record?
checkIfRolloverNeeded();
}

Expand Down Expand Up @@ -169,7 +176,13 @@ private boolean prepareCsvForLogging()

Timber.i("Creating the log file: %s", loggingFileName);

CSVFormat csvFormat = CSVFormat.Builder.create().setHeader(getHeaders()).build();
final String versionName = getVersionName();

CSVFormat csvFormat = CSVFormat.Builder.create()
.setHeaderComments("Created by Network Survey version=" + versionName,
getHeaderComments())
.setHeader(getHeaders())
.build();
try
{
final FileWriter out = new FileWriter(loggingFileName);
Expand Down Expand Up @@ -389,4 +402,18 @@ public void reset()
recordCount.set(0);
}
}

/**
* @return The NS App version number, or an empty string if it could not be determined.
*/
private String getVersionName() {
try
{
PackageInfo info = applicationContext.getPackageManager().getPackageInfo(applicationContext.getPackageName(), 0);
return info.versionName;
} catch (PackageManager.NameNotFoundException e)
{
return "";
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.craxiom.networksurvey.logging;

import static com.craxiom.networksurvey.constants.csv.CellularCsvConstants.SERVING_CELL;
import static com.craxiom.networksurvey.constants.csv.CsvConstants.SPEED;
import static com.craxiom.networksurvey.constants.csv.LteCsvConstants.ACCURACY;
import static com.craxiom.networksurvey.constants.csv.LteCsvConstants.ALTITUDE;
import static com.craxiom.networksurvey.constants.csv.LteCsvConstants.DEVICE_TIME;
Expand Down Expand Up @@ -34,7 +36,7 @@
import timber.log.Timber;

/**
* Responsible for taking in CDR Events and logging them to a CSV file.
* Responsible for taking in LTE survey records and logging them to a CSV file.
*/
public class LteCsvLogger extends CsvRecordLogger implements ICellularSurveyRecordListener
{
Expand All @@ -47,17 +49,23 @@ public LteCsvLogger(NetworkSurveyService networkSurveyService, Looper serviceLoo
@Override
String[] getHeaders()
{ // TODO Figure out how to add other information to the header such as version number
return new String[]{DEVICE_TIME, LATITUDE, LONGITUDE, ALTITUDE, ACCURACY,
return new String[]{DEVICE_TIME, LATITUDE, LONGITUDE, ALTITUDE, SPEED, ACCURACY,
MISSION_ID, RECORD_NUMBER, GROUP_NUMBER,
MCC, MNC, TAC, ECI, EARFCN, PCI, RSRP, RSRQ, TA, LTE_BANDWIDTH, PROVIDER};
MCC, MNC, TAC, ECI, EARFCN, PCI, RSRP, RSRQ, TA, SERVING_CELL, LTE_BANDWIDTH, PROVIDER};
}

@Override
String[] getHeaderComments()
{
return new String[]{"CSV Version=0.1.0"};
}

@Override
public synchronized void onLteSurveyRecord(LteRecord record)
{
try
{
writeCsvRecord(convertToObjectArray(record));
writeCsvRecord(convertToObjectArray(record), true);
} catch (IOException e)
{
Timber.e(e, "Could not log the LTE record to the CSV file");
Expand All @@ -72,13 +80,15 @@ private String[] convertToObjectArray(LteRecord record)
{
LteRecordData data = record.getData();

// TODO Should we use the full enum toString?
final String lteBandwidth = LteMessageConstants.getLteBandwidth(data.getLteBandwidth());

return new String[]{
data.getDeviceTime(),
String.valueOf(data.getLatitude()),
String.valueOf(data.getLongitude()),
String.valueOf(data.getAltitude()),
String.valueOf(data.getSpeed()),
String.valueOf(data.getAccuracy()),
data.getMissionId(),
String.valueOf(data.getRecordNumber()),
Expand All @@ -92,6 +102,7 @@ private String[] convertToObjectArray(LteRecord record)
data.hasRsrp() ? String.valueOf(data.getRsrp().getValue()) : "",
data.hasRsrq() ? String.valueOf(data.getRsrq().getValue()) : "",
data.hasTa() ? String.valueOf(data.getTa().getValue()) : "",
data.hasServingCell() ? String.valueOf(data.getServingCell().getValue()) : "",
lteBandwidth,
data.getProvider()};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.craxiom.networksurvey.logging;

import static com.craxiom.networksurvey.constants.csv.CsvConstants.SPEED;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.ACCURACY;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.AKM_SUITES;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.ALTITUDE;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.BEACON_INTERVAL;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.BSSID;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.CHANNEL;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.CIPHER_SUITES;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.DESTINATION_ADDRESS;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.DEVICE_TIME;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.ENCRYPTION_TYPE;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.EXT_SUPPORTED_RATES;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.FREQ_MHZ;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.LATITUDE;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.LONGITUDE;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.MISSION_ID;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.NODE_TYPE;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.RECORD_NUMBER;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.SERVICE_SET_TYPE;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.SIGNAL_STRENGTH;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.SNR;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.SOURCE_ADDRESS;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.SSID;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.STANDARD;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.SUPPORTED_RATES;
import static com.craxiom.networksurvey.constants.csv.WifiCsvConstants.WPA;

import android.os.Looper;

import com.craxiom.messaging.WifiBeaconRecordData;
import com.craxiom.messaging.wifi.CipherSuite;
import com.craxiom.networksurvey.constants.NetworkSurveyConstants;
import com.craxiom.networksurvey.listeners.IWifiSurveyRecordListener;
import com.craxiom.networksurvey.model.WifiRecordWrapper;
import com.craxiom.networksurvey.services.NetworkSurveyService;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

import timber.log.Timber;

/**
* Responsible for taking in Wi-Fi survey records and logging them to a CSV file.
*/
public class WifiCsvLogger extends CsvRecordLogger implements IWifiSurveyRecordListener
{
public WifiCsvLogger(NetworkSurveyService networkSurveyService, Looper serviceLooper)
{
super(networkSurveyService, serviceLooper, NetworkSurveyConstants.CSV_LOG_DIRECTORY_NAME,
NetworkSurveyConstants.WIFI_FILE_NAME_PREFIX, true);
}

@Override
String[] getHeaders()
{
return new String[]{DEVICE_TIME, LATITUDE, LONGITUDE, ALTITUDE, SPEED, ACCURACY,
MISSION_ID, RECORD_NUMBER,
SOURCE_ADDRESS, DESTINATION_ADDRESS, BSSID, BEACON_INTERVAL, SERVICE_SET_TYPE, SSID,
SUPPORTED_RATES, EXT_SUPPORTED_RATES, CIPHER_SUITES, AKM_SUITES, ENCRYPTION_TYPE,
WPA, CHANNEL, FREQ_MHZ, SIGNAL_STRENGTH, SNR, NODE_TYPE, STANDARD};
}

@Override
String[] getHeaderComments()
{
return new String[]{"CSV Version=0.1.0"};
}

@Override
public synchronized void onWifiBeaconSurveyRecords(List<WifiRecordWrapper> wifiBeaconRecords)
{
wifiBeaconRecords.forEach(wrapper -> {
try
{
writeCsvRecord(convertToObjectArray(wrapper), false);
} catch (IOException e)
{
Timber.e(e, "Could not log the Wi-Fi record to the CSV file");
}
});

try
{
printer.flush();
} catch (IOException e)
{
Timber.e(e, "Could not flush the Wi-Fi records to the CSV file");
}
}

/**
* @return A String array that contains the LTE record values that can be written out as a CSV
* row.
*/
private String[] convertToObjectArray(WifiRecordWrapper wrapper)
{
WifiBeaconRecordData data = wrapper.getWifiBeaconRecord().getData();

final List<CipherSuite> cipherSuitesList = data.getCipherSuitesList();
String cipherSuites = "";
if (!cipherSuitesList.isEmpty())
{
cipherSuites = cipherSuitesList.stream().map(Enum::toString)
.collect(Collectors.joining(";"));
}

return new String[]{
data.getDeviceTime(),
String.valueOf(data.getLatitude()),
String.valueOf(data.getLongitude()),
String.valueOf(data.getAltitude()),
String.valueOf(data.getSpeed()),
String.valueOf(data.getAccuracy()),
data.getMissionId(),
String.valueOf(data.getRecordNumber()),
data.getSourceAddress(),
data.getDestinationAddress(),
data.getBssid(),
data.hasBeaconInterval() ? String.valueOf(data.getBeaconInterval().getValue()) : "",
"", // Service Set Type, not supported by NS
data.getSsid(),
"", // Supported Rates, not supported by NS
"", // Extended Supported Rates, not supported by NS
cipherSuites,
"", // AKM Suites, not supported by NS
data.getEncryptionType().toString(),
data.hasWps() ? String.valueOf(data.getWps().getValue()) : "",
data.hasChannel() ? String.valueOf(data.getChannel().getValue()) : "",
data.hasFrequencyMhz() ? String.valueOf(data.getFrequencyMhz().getValue()) : "",
data.hasSignalStrength() ? String.valueOf(data.getSignalStrength().getValue()) : "",
data.hasSnr() ? String.valueOf(data.getSnr().getValue()) : "",
"", // Node Type, not supported by NS
"" // Standard, not supported by NS
};
}
}
Loading

0 comments on commit 37c2c44

Please sign in to comment.