Skip to content

Commit

Permalink
Changed the approach to drawing the wifi spectrum a bit and other imp…
Browse files Browse the repository at this point in the history
…rovements to the wifi spectrum chart
  • Loading branch information
christianrowlands committed Jan 29, 2024
1 parent 6759ff0 commit c489948
Show file tree
Hide file tree
Showing 17 changed files with 357 additions and 198 deletions.
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,37 @@ scanning and displaying a list of the nearby Bluetooth devices.

## CDR Log Files

### Call Detail Record (CDR) Logging in Network Survey

**Introduction:**
The Network Survey app now includes a feature for logging Call Detail Records (CDR). This functionality provides insight into various interactions your phone has with the cellular network.
The Network Survey app now includes a feature for logging Call Detail Records (CDR). This
functionality provides insight into various interactions your phone has with the cellular network.

**What is a CDR?**
A Call Detail Record (CDR) is a data record that documents specific interactions a phone has with the cellular network. These interactions include phone calls, SMS messages, and changes in cell tower connections.
A Call Detail Record (CDR) is a data record that documents specific interactions a phone has with
the cellular network. These interactions include phone calls, SMS messages, and changes in cell
tower connections.

**Use Cases:**

- **Monitoring Network Activity:** Record and review your phone's cellular network interactions.
- **Educational Insight:** Gain an understanding of how cellular networks operate.
- **Privacy Monitoring:** Check if your phone is connecting to unexpected cell towers.

**Note on Feature Limitations:**
- The regular version of Network Survey does not log SMS events in the CDR due to Google Play's policy against apps requesting full SMS permissions.
- Logging the "other" phone number associated with call events is also not supported in the regular version.

- The regular version of Network Survey does not log SMS events in the CDR due to Google Play's
policy against apps requesting full SMS permissions.
- Logging the "other" phone number associated with call events is also not supported in the regular
version.

**Alternative Version with Extended Features:**
If you're interested in CDR logging that includes SMS events and call details:
1. **Install from Source:** You can build and install the app using the source code available in this repository.
2. **Download Pre-Built APK:** Access the latest "*cdr-release.apk" under the latest release on our [GitHub Releases page](https://github.com/YourRepo/NetworkSurvey/releases).
3. **IzzyOnDroid F-Droid:** Install from [IzzyOnDroid F-Droid](https://apt.izzysoft.de/fdroid/index/apk/com.craxiom.networksurvey).

1. **Install from Source:** You can build and install the app using the source code available in
this repository.
2. **Download Pre-Built APK:** Access the latest "*cdr-release.apk" under the latest release on
our [GitHub Releases page](https://github.com/YourRepo/NetworkSurvey/releases).
3. **IzzyOnDroid F-Droid:** Install
from [IzzyOnDroid F-Droid](https://apt.izzysoft.de/fdroid/index/apk/com.craxiom.networksurvey).

## Tracking And Privacy

Expand Down
2 changes: 1 addition & 1 deletion networksurvey/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ apply plugin: 'kotlin-android'

ext.androidVersion = '28.0.0'
ext.grpcVersion = '1.58.0'
ext.networkSurveyMessagingVersion = '0.15.0'
ext.networkSurveyMessagingVersion = '1.0.0'
ext.navigationVersion = '2.7.6'

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,24 @@ public void onReceive(Context context, Intent intent)

Timber.i("SIM State Change Detected. Restarting the cellular fragment");

// Restart this fragment (yes, it seems overly complicated but it works)
FragmentManager fragmentManager = getParentFragmentManager();
Fragment currentFragment = fragmentManager.findFragmentById(R.id.main_cellular_fragment);
if (currentFragment != null)
try
{
FragmentTransaction detachTransaction = fragmentManager.beginTransaction();
detachTransaction.detach(currentFragment);
detachTransaction.commit();

FragmentTransaction attachTransaction = fragmentManager.beginTransaction();
attachTransaction.attach(currentFragment);
attachTransaction.commit();
// Restart this fragment (yes, it seems overly complicated but it works)
FragmentManager fragmentManager = getParentFragmentManager();
Fragment currentFragment = fragmentManager.findFragmentById(R.id.main_cellular_fragment);
if (currentFragment != null)
{
FragmentTransaction detachTransaction = fragmentManager.beginTransaction();
detachTransaction.detach(currentFragment);
detachTransaction.commit();

FragmentTransaction attachTransaction = fragmentManager.beginTransaction();
attachTransaction.attach(currentFragment);
attachTransaction.commit();
}
} catch (Exception e)
{
Timber.w(e, "Could not restart the cellular fragment after a SIM event.");
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@

import com.craxiom.messaging.WifiBeaconRecord;
import com.craxiom.messaging.WifiBeaconRecordData;
import com.craxiom.messaging.wifi.WifiBandwidth;
import com.craxiom.networksurvey.R;
import com.craxiom.networksurvey.constants.WifiBeaconMessageConstants;
import com.craxiom.networksurvey.model.WifiNetwork;
import com.craxiom.networksurvey.model.WifiRecordWrapper;
import com.craxiom.networksurvey.util.ColorUtils;
import com.craxiom.networksurvey.util.WifiUtils;

import timber.log.Timber;

Expand Down Expand Up @@ -82,6 +84,7 @@ public void onBindViewHolder(final ViewHolder holder, int position)
holder.encryptionType.setText(WifiBeaconMessageConstants.getEncryptionTypeString(data.getEncryptionType()));
holder.frequency.setText(data.hasFrequencyMhz() ? context.getString(R.string.wifi_frequency_value, data.getFrequencyMhz().getValue()) : "");
holder.channel.setText(data.hasChannel() ? context.getString(R.string.wifi_channel_value, data.getChannel().getValue()) : "");
holder.bandwidth.setText(data.getBandwidth() != WifiBandwidth.UNKNOWN ? context.getString(R.string.wifi_bandwidth_value, WifiUtils.formatBandwidth(data.getBandwidth())) : "");
boolean passpoint = data.getPasspoint().getValue();
holder.passpoint.setText((data.hasPasspoint() && passpoint) ? "Passpoint" : "");
holder.capabilities.setText(wifiRecordWrapper.getCapabilitiesString());
Expand Down Expand Up @@ -114,6 +117,7 @@ class ViewHolder extends RecyclerView.ViewHolder
final TextView encryptionType;
final TextView frequency;
final TextView channel;
final TextView bandwidth;
final TextView passpoint;
final TextView capabilities;
WifiBeaconRecord wifiRecord;
Expand All @@ -128,6 +132,7 @@ class ViewHolder extends RecyclerView.ViewHolder
encryptionType = view.findViewById(R.id.encryption_type);
frequency = view.findViewById(R.id.wifi_frequency);
channel = view.findViewById(R.id.wifi_channel);
bandwidth = view.findViewById(R.id.wifi_bandwidth);
passpoint = view.findViewById(R.id.wifi_passpoint);
capabilities = view.findViewById(R.id.wifi_capabilities);

Expand All @@ -151,6 +156,7 @@ class ViewHolder extends RecyclerView.ViewHolder
data.getSsid(),
data.hasFrequencyMhz() ? data.getFrequencyMhz().getValue() : null,
data.hasChannel() ? data.getChannel().getValue() : null,
data.getBandwidth(),
WifiBeaconMessageConstants.getEncryptionTypeString(data.getEncryptionType()),
data.hasPasspoint() ? data.getPasspoint().getValue() : null,
capabilities.getText().toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import com.craxiom.messaging.wifi.WifiBandwidth
import com.craxiom.networksurvey.constants.NetworkSurveyConstants
import com.craxiom.networksurvey.listeners.IWifiSurveyRecordListener
import com.craxiom.networksurvey.model.WifiRecordWrapper
Expand Down Expand Up @@ -103,7 +104,8 @@ class WifiSpectrumFragment : AServiceDataFragment(), IWifiSurveyRecordListener {
WifiNetworkInfo(
it.wifiBeaconRecord.data.ssid!!,
it.wifiBeaconRecord.data.signalStrength.value.toInt(),
it.wifiBeaconRecord.data.channel.value
it.wifiBeaconRecord.data.channel.value,
it.wifiBeaconRecord.data.bandwidth
)
}
?: emptyList()
Expand All @@ -123,5 +125,6 @@ class WifiSpectrumFragment : AServiceDataFragment(), IWifiSurveyRecordListener {
data class WifiNetworkInfo(
val ssid: String,
val signalStrength: Int,
val channel: Int
val channel: Int,
val bandwidth: WifiBandwidth
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.craxiom.networksurvey.model

import com.craxiom.messaging.wifi.WifiBandwidth
import java.io.Serializable

data class WifiNetwork(
Expand All @@ -8,6 +9,7 @@ data class WifiNetwork(
val ssid: String,
val frequency: Int?,
val channel: Int?,
val bandwidth: WifiBandwidth?,
val encryptionType: String,
val passpoint: Boolean?,
val capabilities: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
import com.craxiom.messaging.phonestate.NetworkType;
import com.craxiom.messaging.phonestate.SimState;
import com.craxiom.messaging.wifi.EncryptionType;
import com.craxiom.messaging.wifi.Standard;
import com.craxiom.messaging.wifi.WifiBandwidth;
import com.craxiom.networksurvey.BuildConfig;
import com.craxiom.networksurvey.GpsListener;
import com.craxiom.networksurvey.NetworkSurveyActivity;
Expand Down Expand Up @@ -99,7 +101,7 @@
import com.craxiom.networksurvey.util.MathUtils;
import com.craxiom.networksurvey.util.ParserUtils;
import com.craxiom.networksurvey.util.PreferenceUtils;
import com.craxiom.networksurvey.util.WifiCapabilitiesUtils;
import com.craxiom.networksurvey.util.WifiUtils;
import com.google.protobuf.BoolValue;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
Expand Down Expand Up @@ -1500,20 +1502,27 @@ private WifiRecordWrapper generateWiFiBeaconSurveyRecord(ScanResult apScanResult
// TODO At some point it would be nice to add the Cipher Suites and AKM Suites, but I can't seem to get
// enough information for that.

final EncryptionType encryptionType = WifiCapabilitiesUtils.getEncryptionType(capabilities);
final EncryptionType encryptionType = WifiUtils.getEncryptionType(capabilities);
if (encryptionType != EncryptionType.UNKNOWN)
{
dataBuilder.setEncryptionType(encryptionType);
}

dataBuilder.setWps(BoolValue.newBuilder().setValue(WifiCapabilitiesUtils.supportsWps(capabilities)).build());
dataBuilder.setWps(BoolValue.newBuilder().setValue(WifiUtils.supportsWps(capabilities)).build());
}

if (apScanResult.isPasspointNetwork())
{
dataBuilder.setPasspoint(BoolValue.newBuilder().setValue(true).build());
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
setWifiStandard(dataBuilder, apScanResult.getWifiStandard());
}

setWifiBandwidth(dataBuilder, apScanResult.channelWidth);

final WifiBeaconRecord.Builder recordBuilder = WifiBeaconRecord.newBuilder();
recordBuilder.setMessageType(WifiBeaconMessageConstants.WIFI_BEACON_RECORD_MESSAGE_TYPE);
recordBuilder.setVersion(BuildConfig.MESSAGING_API_VERSION);
Expand Down Expand Up @@ -1795,6 +1804,44 @@ private void setBandwidth(LteRecordData.Builder lteRecordBuilder, CellIdentityLt
}
}

/**
* Sets the Wi-Fi standard on the record if it is valid.
*/
private void setWifiStandard(WifiBeaconRecordData.Builder wifiBeaconBuilder, int androidWifiStandard)
{
Standard wifiStandard = switch (androidWifiStandard)
{
case ScanResult.WIFI_STANDARD_UNKNOWN, ScanResult.WIFI_STANDARD_LEGACY, ScanResult.WIFI_STANDARD_11AD ->
Standard.UNKNOWN;
case ScanResult.WIFI_STANDARD_11N -> Standard.IEEE80211N;
case ScanResult.WIFI_STANDARD_11AC -> Standard.IEEE80211AC;
case ScanResult.WIFI_STANDARD_11AX -> Standard.IEEE80211AX;
case ScanResult.WIFI_STANDARD_11BE -> Standard.IEEE80211BE;
default -> Standard.UNKNOWN;
};

if (wifiStandard != Standard.UNKNOWN) wifiBeaconBuilder.setStandard(wifiStandard);
}

/**
* Sets the Wi-Fi Bandwidth on the record.
*/
private void setWifiBandwidth(WifiBeaconRecordData.Builder wifiBeaconBuilder, int androidWifiBandwidth)
{
WifiBandwidth wifiBandwidth = switch (androidWifiBandwidth)
{
case ScanResult.CHANNEL_WIDTH_20MHZ -> WifiBandwidth.MHZ_20;
case ScanResult.CHANNEL_WIDTH_40MHZ -> WifiBandwidth.MHZ_40;
case ScanResult.CHANNEL_WIDTH_80MHZ -> WifiBandwidth.MHZ_80;
case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ -> WifiBandwidth.MHZ_80_PLUS;
case ScanResult.CHANNEL_WIDTH_160MHZ -> WifiBandwidth.MHZ_160;
case ScanResult.CHANNEL_WIDTH_320MHZ -> WifiBandwidth.MHZ_320;
default -> WifiBandwidth.UNKNOWN;
};

if (wifiBandwidth != WifiBandwidth.UNKNOWN) wifiBeaconBuilder.setBandwidth(wifiBandwidth);
}

/**
* @param telephonyManager The manager to use to get the voice network type.
* @return The Current Network type for voice calls. This method checks the Android permissions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,34 @@ package com.craxiom.networksurvey.ui.wifi
import com.patrykandpatrick.vico.core.axis.AxisItemPlacer
import com.patrykandpatrick.vico.core.chart.dimensions.HorizontalDimensions
import com.patrykandpatrick.vico.core.chart.draw.ChartDrawContext
import com.patrykandpatrick.vico.core.chart.layout.HorizontalLayout
import com.patrykandpatrick.vico.core.context.MeasureContext

class ChannelAxisItemPlacer(
private val spacing: Int,
private val offset: Int,
private val everyOtherLabel: Boolean = false,
private val shiftExtremeTicks: Boolean = false,
private val addExtremeLabelPadding: Boolean = false,
private val customLabelValues: List<Float> // Add your custom label values here
) : AxisItemPlacer.Horizontal {

override fun getShiftExtremeTicks(context: ChartDrawContext): Boolean = shiftExtremeTicks

override fun getAddFirstLabelPadding(context: MeasureContext) =
context.horizontalLayout is HorizontalLayout.FullWidth && addExtremeLabelPadding && offset == 0
override fun getAddFirstLabelPadding(context: MeasureContext) = false

override fun getAddLastLabelPadding(context: MeasureContext): Boolean =
with(context) {
context.horizontalLayout is HorizontalLayout.FullWidth && addExtremeLabelPadding
}
override fun getAddLastLabelPadding(context: MeasureContext): Boolean = false

override fun getLabelValues(
context: ChartDrawContext,
visibleXRange: ClosedFloatingPointRange<Float>,
fullXRange: ClosedFloatingPointRange<Float>,
): List<Float> {
// Return your custom label values here
if (everyOtherLabel) {
val everyOtherLabelValues = mutableListOf<Float>()
for (i in customLabelValues.indices) {
if (i % 2 == 0) {
everyOtherLabelValues.add(customLabelValues[i])
}
}
return everyOtherLabelValues
}
return customLabelValues
}

Expand All @@ -41,12 +42,20 @@ class ChannelAxisItemPlacer(
return listOf(customLabelValues[0], customLabelValues[customLabelValues.size - 1])
}

override fun getLineValues(
context: ChartDrawContext,
visibleXRange: ClosedFloatingPointRange<Float>,
fullXRange: ClosedFloatingPointRange<Float>
): List<Float> {
return customLabelValues
}

override fun getStartHorizontalAxisInset(
context: MeasureContext,
horizontalDimensions: HorizontalDimensions,
tickThickness: Float
): Float {
return 0f
return 0f
}

override fun getEndHorizontalAxisInset(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ class SpectrumPointConnector : LineCartesianLayer.LineSpec.PointConnector {

if (prevY > y) {
// Left side
val controlPoint1X = prevX + (x - prevX) * 0.16f
val controlPoint1X = prevX + (x - prevX) * 0.05f
val controlPoint1Y = prevY + (y - prevY) * 0.5f
val controlPoint2X = prevX + (x - prevX) * 0.60f
val controlPoint2Y = prevY + (y - prevY) * 0.93f
val controlPoint2X = prevX + (x - prevX) * 0.2f
val controlPoint2Y = prevY + (y - prevY) * 1f

path.cubicTo(controlPoint1X, controlPoint1Y, controlPoint2X, controlPoint2Y, x, y)
} else {
// Right side
val controlPoint1X = prevX + (x - prevX) * 0.6f
val controlPoint1Y = prevY + (y - prevY) * 0.16f
val controlPoint2X = prevX + (x - prevX) * 0.89f
val controlPoint2Y = prevY + (y - prevY) * 0.70f
val controlPoint1X = prevX + (x - prevX) * 0.8f
val controlPoint1Y = prevY + (y - prevY) * 0f
val controlPoint2X = prevX + (x - prevX) * 1f
val controlPoint2Y = prevY + (y - prevY) * 0.5f

path.cubicTo(controlPoint1X, controlPoint1Y, controlPoint2X, controlPoint2Y, x, y)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.craxiom.networksurvey.fragments.WifiDetailsFragment
import com.craxiom.networksurvey.ui.SignalChart
import com.craxiom.networksurvey.ui.UNKNOWN_RSSI
import com.craxiom.networksurvey.util.ColorUtils
import com.craxiom.networksurvey.util.WifiUtils

/**
* A Compose screen that shows the details of a single WiFi network. The main purpose for this
Expand Down Expand Up @@ -175,6 +176,19 @@ private fun LazyListScope.chartItems(
)
}

Row(
modifier = Modifier
.padding(start = padding, end = padding, bottom = padding / 2)
.fillMaxWidth(),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.Start
) {
Text(
text = "Bandwidth: ${WifiUtils.formatBandwidth(viewModel.wifiNetwork.bandwidth)}",
style = MaterialTheme.typography.titleMedium
)
}

Row(
modifier = Modifier
.padding(start = padding, end = padding, bottom = padding / 2)
Expand Down
Loading

0 comments on commit c489948

Please sign in to comment.