An Android network-signal inspector built with Flutter and Kotlin. SIM and carrier info, LTE / 5G NR cellular signal, Wi-Fi and nearby access points, GNSS satellites, and a TCP / DNS latency test.
简体中文 | English
Core principle: only show values the system actually reports. There is no random or hard-coded data in real mode. A field the system does not provide is
nulland shown as--, never as0. No hidden APIs, no reflection, no root.
- Features
- Screenshots
- Tech Stack
- Android Compatibility
- Permissions
- Install and Run
- Build from Source
- Project Structure
- Development Status
- Contributing
- License
- Acknowledgements
| Page | What it shows |
|---|---|
| Overview | Per-SIM carrier, network type, primary signal and IPv4 / IPv6; Wi-Fi state with IPv4 / IPv6 / MAC; GNSS state; cellular and Wi-Fi trend charts. Tap a card for details. The speed icon at the top right opens the latency test. |
| Cellular | Independent state for each SIM (SIM1 / SIM2 switch). SIM, carrier, MCC/MNC, roaming, data state. LTE: RSRP / RSRQ / RSSI / SINR / CQI / TA, PCI, TAC, CI, eNodeB ID, EARFCN, band, bandwidth. NR: SS / CSI RSRP / RSRQ / SINR, PCI, TAC, NCI, NRARFCN, band. NSA / SA detection, neighbor cells, 60 s / 5 min trends. |
| Wi-Fi | Current connection (SSID, router MAC, RSSI, frequency, channel, width, link speed, IPv4 / IPv6, device MAC, gateway, DNS, security, standard), nearby access points (band filter, sorting), 2.4 / 5 / 6 GHz channel graph (tap for details), RSSI trend. |
| GNSS | Fix status, latitude / longitude, altitude, accuracy, speed, TTFF; visible / used-in-fix / tracked satellite counts; constellation breakdown with country / organization flags; sky plot; C/N0 bar chart grouped by constellation; satellite list; C/N0 and satellite-count trends. While there is no fresh GNSS fix, the position card shows the system's fused / network position (Wi-Fi, cell towers) instead, clearly labelled "not GNSS", and switches back to the GNSS fix automatically once one exists. |
| Latency test | DNS time plus TCP-handshake timing (min / avg / max, jitter, loss) against built-in site lists (Mainland China, US / international) and up to 20 custom targets. |
| Settings | Theme (dark-first / light / system), language (Chinese / English / system), refresh rate, history window, CSV / JSON export, clear history, permission manager, demo mode, app update, about. App update: checks the latest GitHub Release (automatically at launch at most once every 24 hours, switch-off in Settings, or by tapping "Check for updates"), downloads the APK for the device's ABI, verifies its SHA-256 against the release's SHA256SUMS.txt and its signing key against the installed app, then opens the system installer. Stable releases only; drafts and pre-releases are ignored. |
Also: multi-SIM support with per-subscription isolation, event-driven sensing (no polling), and a session-only demo mode with a visible banner and clearly fictional data.
Notable limits (all explained in the app):
- Normal apps cannot send ICMP ping, so the latency test measures a TCP handshake (close to, but not the same as, ping).
- Android 10+ hides the real device MAC from normal apps; the app shows
--instead of the02:00:00:00:00:00placeholder. - Wi-Fi scanning is throttled by Android; the app does not try to bypass it.
- Availability of NR / NSA fields, neighbor cells, bands and bandwidth depends on the device's modem and vendor.
The screenshot was taken in the Android Emulator. The carrier, addresses, location and signal values are simulated by the emulator, not measured on a real device. Screenshots of the other pages are not published yet.
| Area | Choice |
|---|---|
| Framework | Flutter 3.47.x, Dart ^3.13.5 |
| UI | Material 3, custom CustomPainter charts, fl_chart |
| State management | Riverpod 3.x (flutter_riverpod) |
| Localization | flutter_localizations, intl, built-in zh / en string table |
| Native layer | Kotlin (Android): TelephonyManager, WifiManager, LocationManager / GnssStatus, ConnectivityManager |
| Bridge | One MethodChannel and four EventChannels. No Flutter plugins are used. |
| Build | Android Gradle Plugin 9.1.0, Kotlin 2.4.0, Gradle 9.3.1 |
| Tests | flutter_test, 99 unit / widget tests |
Architecture: feature-first folders with a Repository layer; a SignalPlatform abstraction (AndroidPlatform, UnsupportedPlatform, DemoPlatform) is the only boundary to the device. See AGENTS.md for details.
- Minimum: Android 10 (API 29).
compileSdk/targetSdkfollow the Flutter default (API 36). - Some data depends on the OS version:
| Capability | Requirement |
|---|---|
TelephonyCallback listeners |
API 31+ (API 29 / 30 fall back to PhoneStateListener) |
LTE bands from CellIdentityLte.getBands() |
API 30+ (NR: API 29+); otherwise only a unique EARFCN lookup, labeled as computed |
| Per-SIM mobile IP address matching | API 31+ (NetworkCapabilities.getSubscriptionIds()); older versions only identify the current data SIM |
NEARBY_WIFI_DEVICES runtime permission |
API 33+ |
| Baseband C/N0 for satellites | API 30+ |
- Windows builds are a UI shell only: the pages run and settings / export work, but cellular, Wi-Fi and GNSS collection is not implemented there (the app shows "not supported", never fake data).
- iOS, macOS, Linux and Web are not supported.
| Permission | Purpose |
|---|---|
READ_PHONE_STATE |
SIM, carrier, cellular signal |
ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION |
Cell identity, Wi-Fi names and scan results, GNSS |
NEARBY_WIFI_DEVICES (Android 13+) |
Nearby Wi-Fi scan results |
ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_NETWORK_STATE |
Wi-Fi state; CHANGE_WIFI_STATE is used only when you tap "Scan" |
INTERNET |
(1) Latency test: connects only after you tap start. (2) Update check / download: one anonymous GET to GitHub Releases (the app version is only in the User-Agent), automatic at most once every 24 hours unless you switch it off in Settings. No data is uploaded |
REQUEST_INSTALL_PACKAGES |
Lets you hand a downloaded SignalScope update to the system installer; Android asks you to confirm and only accepts an APK signed with the installed app's key |
The app does not place calls, read call logs, or upload any data. Telephony, Wi-Fi and GPS hardware features are declared required=false, so devices without them can still install the app and the matching pages show "not supported".
Download the APK that matches your device from the Releases page (arm64-v8a for most current phones; SHA256SUMS.txt lets you verify the download) and install it on an Android 10+ device. Builds that contain the updater (anything after v1.0.0) can then update themselves from Settings → App update.
Or build the APK yourself (see below) and install it:
adb install build/app/outputs/flutter-apk/app-arm64-v8a-release.apkOr run directly on a connected device or emulator:
flutter run -d <device-id>Release builds are signed with the maintainer's release key, which is not in this repository. Building a release yourself requires your own signing key (see "Release signing" below); debug builds need no setup.
Prerequisites: Flutter 3.47.x (Dart 3.13), JDK 17+ (the Android Studio JBR works), Android SDK.
git clone https://github.com/lwh0630/SignalScope.git
cd SignalScope
flutter pub get
flutter analyze
flutter test
# Debug
flutter run -d <android-device>
# Release APKs, one per ABI
flutter build apk --release --split-per-abi
# -> build/app/outputs/flutter-apk/app-<abi>-release.apkA release build never falls back to the debug key: without signing material, flutter build apk --release fails with an explanatory message. Provide your own key either through a git-ignored android/key.properties:
storeFile=C:/path/outside/the/repo/release.jks
storePassword=...
keyPassword=...
keyAlias=...or through the environment variables ANDROID_KEYSTORE_PATH, ANDROID_STORE_PASSWORD, ANDROID_KEY_PASSWORD and ANDROID_KEY_ALIAS (environment variables take precedence). Never commit the keystore or these values.
The Android Release workflow runs when a v*.*.* tag is pushed or when started manually. It reads the keystore from the production environment secrets, builds one APK per ABI, verifies the signing certificate with apksigner, and creates a draft GitHub Release; nothing is published automatically.
The Windows UI shell needs Visual Studio with "Desktop development with C++":
flutter run -d windowsAndroid Studio creates android/local.properties with your SDK paths. It is local only and git-ignored.
lib/
core/ theme, i18n (zh / en), history ring buffer, lifecycle, throttling, utils
data/
models/ SimInfo, CellularInfo, WifiConnectionInfo, GnssSnapshot, ...
platform/ SignalPlatform + Android / Unsupported / Demo implementations
features/ overview, cellular, wifi, gnss, latency, settings, permissions,
export, update, shell, monitoring (data / application / presentation)
shared/widgets/ cards, gauges, trend charts, monitor bar, address widgets
android/app/src/main/kotlin/com/signalscope/app/
MainActivity, NativeChannelHandler, PermissionService, SimInfoService,
CellularService, WifiSignalService, GnssService, NetworkAddresses,
ExportService, UpdateInstaller, NativeUtils
assets/branding/ app icon sources and generator script
test/ unit and widget tests
- Implemented and covered by automated tests (
flutter test): data parsing ("missing meansnull"), controllers and lifecycle, charts' geometry, latency statistics, i18n key parity, export formats, demo data. - Not yet verified on a wide range of real hardware. The app has not been fully validated on real multi-SIM 5G devices; NR / NSA field availability depends strongly on the vendor modem.
- The Windows target is a UI shell only.
- GitHub Actions CI checks formatting, runs
flutter analyzeandflutter test, and builds a debug APK on every push and pull request tomain. Releases are listed on the Releases page; the separate Android Release workflow builds the signed APKs and creates a draft release on demand (see "Release signing"), which the maintainer publishes by hand.
Issues and pull requests are welcome, within the license terms below.
- Read
AGENTS.mdfor architecture and conventions (also useful for human contributors). - Keep changes small and focused; do not add dependencies without a reason.
- Use only public Android APIs and never fake signal data.
- Run
dart format lib test,flutter analyzeandflutter testbefore opening a pull request, and say which checks you ran. - Update both
README.mdandREADME_zh.mdwhen you change features, permissions or build steps.
By contributing you agree that your contribution is licensed under the same terms as this project (CC BY-NC-SA 4.0).
This project is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0).
- You may study, copy and modify the project for non-commercial purposes.
- You must give appropriate credit, indicate changes, and share adaptations under the same license.
- Commercial use beyond what the license allows requires separate permission from the copyright holder.
See LICENSE (full legal text), NOTICE and ATTRIBUTION.md (attribution policy, including an optional, non-mandatory request for derivative projects).
Note: Creative Commons itself does not recommend using its licenses for software source code. This project uses CC BY-NC-SA 4.0 by the maintainer's explicit choice. Dependencies (Flutter, Dart, and the packages in
pubspec.yaml) keep their own licenses.
