Skip to content

Repository files navigation

Cellular Honeypot Detector

Android Kotlin UI Framework Database Developer License

An Android app that watches the cell your phone is attached to and tells you when something about it changes. Everything stays on the device: it holds no INTERNET permission, so it cannot send your data anywhere even if a future change tried to.

What this app is not. It cannot read baseband signalling, it cannot see your encryption state, it cannot detect silent SMS, and it cannot tell you that you are being targeted by an IMSI catcher. Android gives no application access to any of that. Earlier versions of this app claimed all four. See Limitations and docs/LESSONS.md.


What it actually does

Every five seconds while the app is open, it reads the registered cell from Android's TelephonyManager and compares that reading against everything it has seen this session.

  • Cell readings — identity, tracking/location area, PLMN, signal level and neighbour count, each shown only when the platform reports it. Anything unreported shows as .
  • Comparative anomaly detection — five rules, all of which look at change rather than at a single reading. Radio access dropping from LTE/NR to GSM/UMTS; a neighbour list that was populated and is now empty; a cell that kept its identity but changed its area code; a signal far above that cell's own median for the session. Each finding shows the readings it came from, so you can disagree with it.
  • Integrity score — 100 minus the weights of the rules that fired. It reads , not 100%, until the session has enough readings to compare against.
  • Observation log — one row per scan, including scans that produced nothing, with the reason. Searchable, copyable, exportable via the share sheet.
  • Cell registry — every identified cell, how often it has been seen, and where the device was standing at the time.
  • Network link facts — interface, addresses, DNS servers, private-DNS state, VPN transport, SIM country versus network country. Read from LinkProperties; nothing is measured or traced.

What each anomaly rule means

Signal Fires when Severity
Legacy radio access in use Serving cell is 2G/3G Informational
Radio access downgraded Now 2G/3G, after LTE/NR earlier in the session Warning
Neighbour list emptied Zero neighbours, having reported ≥2 earlier Warning
Area code changed on a stable cell Same cell ID, different TAC/LAC Warning
Signal far above baseline ≥20 dB above this cell's own session median Elevated

Every one of these has an innocent explanation that is more likely than interception — leaving LTE coverage, moving between cells, a carrier reconfiguring a site. The app says so on the screen. Treat findings as things to look at, not as conclusions.


Limitations

Stated here rather than left to be discovered.

  • Foreground only. Monitoring runs in a ViewModel, so it stops when you leave the app or the screen turns off. Gaps in the log are expected. There is no background service.
  • No baseband access. No Android app can read RRC or NAS signalling on an unmodified device, with any permission. The observation log is a log of what TelephonyManager returned.
  • No encryption visibility. Android exposes no ciphering indicator. The app cannot tell you whether your traffic is encrypted over the air.
  • No silent SMS detection. Type-0 and class-0 messages are handled below the application layer.
  • No traceroute or latency measurement. An unrooted app has no raw sockets and no control over IP TTL.
  • Cell positions are device positions. A handset can locate itself, not a transmitter.
  • Per-session baselines only. A cell seen yesterday is not compared against today's reading.
  • A real IMSI catcher may produce no signal here at all. Modern interception does not have to force a downgrade or empty a neighbour list. Absence of findings is not evidence of absence.
  • play-services-location is a proprietary Google dependency — the only one. It provides the fused location fix. There is no Firebase, no analytics and no other Google component.

Privacy

  • No INTERNET permission. The app cannot open a network connection. This is enforced by Android, not promised in this file, and it is checkable in one line of AndroidManifest.xml.
  • android:allowBackup="false", with cloud backup and device-transfer rules that exclude the app's storage. Earlier versions had backup enabled with empty rules, so the log was copied to the user's Google Drive.
  • No analytics, no ads, no backend, no account.
  • The database is not encrypted. It is a plain Room/SQLite file in app-private storage. Earlier versions of the privacy policy called it encrypted; it never was. If your threat model includes someone with access to your unlocked device, use the in-app clear function.

Permissions and why: READ_PHONE_STATE and ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION are both required by Android before it will report cell identity at all; ACCESS_NETWORK_STATE for the link facts; POST_NOTIFICATIONS to raise an alert. Full detail in PRIVACY_POLICY.md.


Architecture

  • Language: Kotlin
  • UI: Jetpack Compose, Material 3
  • Pattern: MVVM, with the detection rules isolated in a pure-Kotlin detector package
  • Storage: Room (KSP), schema exported to app/schemas/, real migrations
  • Async: Coroutines, StateFlow, collectAsStateWithLifecycle
app/src/main/java/com/techluddite/cellhoneypot/
  scanner/     TelephonyManager, ConnectivityManager and location readers
  detector/    AnomalyDetector — pure Kotlin, no Android imports, unit-tested
  data/        Room entities, DAO, migrations, repository
  viewmodel/   Watchdog loop and monitor state
  ui/          Compose screens and components

See docs/ARCHITECTURE.md for the invariants and the reasoning behind the detection rules and their weights.


Building

Requires JDK 17+ and an Android SDK with API 36. The Gradle wrapper is checked in; earlier versions of this repository had none, and could not be built from a clean checkout at all.

git clone https://github.com/TechLuddite/Cellular-Honeypot-Detector.git
cd Cellular-Honeypot-Detector

./gradlew :app:testDebugUnitTest    # detector rules + Room migration test
./gradlew :app:lintDebug            # runs with abortOnError = true
./gradlew :app:assembleDebug

assembleRelease requires a real upload keystore (KEYSTORE_PATH, STORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD, or my-upload-key.jks in the project root) and fails without one. It used to fall back to the checked-in debug key.


History

This project was generated in Google AI Studio, and versions 1–9 shipped detection logic that invented its own inputs: a Random cell ID, sixteen random bytes displayed as a baseband hex dump, a hardcoded ciphering algorithm, hardcoded network latencies, a nine-tenths-constant signal chart and a default GPS coordinate in San Francisco. Version 10 is the result of an audit that removed all of it.

docs/LESSONS.md is the postmortem — ten recurring failure shapes, each with the instance that revealed it and a tell for spotting the same shape elsewhere. It is the most useful file in this repository.


Support


License

MIT. Copyright (c) 2026 TechLuddite.

About

Privacy-first Android tool detecting IMSI Catchers, fake cell towers, and baseband anomalies.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages