A native Android app for passive surveillance detection. Open it, hit START, and a circle turns green / yellow / orange / red depending on how confident the engine is that there's a Flock Safety ALPR, an Axon body camera, or active police presence near you.
Five independent sensors. Each one feeds the same scoring engine, and cross-source corroboration pushes the global tier upward — a BLE OUI hit and a DeFlock map match in the same area produce a higher tier than either alone.
Vendor MAC OUIs (Axon, Flock Penguin / Raven, XUNTONG mfg id
0x09C8, "TN" serial pattern), Raven service UUIDs,
device-name patterns. Iterates every manufacturer-specific data
entry, not just the first.
31-prefix Flock OUI superset and Flock-XXXX name
patterns. WifiManager.getScanResults() polled every
35 s — just under the Android 11+ 4-scans/2-min throttle.
Direct Overpass POSTs (overpass.deflock.org →
fallback overpass-api.de) for
man_made=surveillance · surveillance:type=ALPR in a
5 km bbox. 24 h on-disk cache by 0.05° grid cell; refetches when
the user moves > 1.5 km from the last fetch center.
citizen.com/api/incident/trending bbox poll every
60 s, then per-incident detail via /api/incident/{id}
with an in-memory cache. Fire- and medical-only events filtered
out. First poll fires immediately on the first location fix.
Every observation scored 0–100 by ConfidenceEngine.
The on-screen tier is the maximum live score across all sources,
with an RSSI rise-peak-fall detector for stationary signals.
live-map/api/georss in 2025/2026. Mobile clients
receive HTTP 403 and the only known workarounds (Selenium proxy on a
home server, Waze-for-Cities partner program) aren't viable for a
phone-deployed app. Citizen replaces it as the police-presence source.
Score 0–100. The user-facing circle uses the 4-tier mapping. When
idle, the circle shows muted gray with IDLE so it's
distinguishable at a glance from "scanning, all clear."
geo: intent).OVERWATCH • RED (or whatever
tier); text shows the top detection's score + label. Priority bumps
to HIGH on RED so the system surfaces it as a heads-up.One foreground service owns all scanners, the notification, and the
vibrator. No detection-history database — all state is in-memory and
clears on stop, by design. Service uses START_NOT_STICKY
so a system kill doesn't auto-restart into a stuck state.
ui/MainScreen.kt circle + START/STOP + tap-to-open bottom sheet ui/SettingsScreen.kt source toggles, distance sliders, vibrate, theme ui/theme/Theme.kt Material 3 dark/light + threat colors service/DetectionService.kt foreground service — owns scanners, notif, vibration scan/BleScanner.kt BLE callback scanner scan/WifiScanner.kt WifiManager poller + SCAN_RESULTS receiver scan/DeflockClient.kt Overpass POST + 24 h on-disk cache scan/DeflockScanner.kt location-driven proximity check + backoff scan/CitizenClient.kt GET /trending + /incident/{id} scan/CitizenScanner.kt 60 s poller, fire/medical filter, per-id cache fusion/ConfidenceEngine.kt scoring (one place — BLE / WiFi / DeFlock / Citizen) fusion/RssiTracker.kt rise-peak-fall stationary-signal detector fusion/DetectionStore.kt in-memory dedup, 5-min retention, max-tier flow fusion/SourceHealth.kt per-source OK/FAILED registry for drill-down fusion/ThreatLevel.kt 4-tier enum + DetectionSource enum data/location/LocationProvider.kt FusedLocationProviderClient wrapper data/settings/Settings.kt SharedPreferences-backed StateFlow settings data/targets/ BleOuis, WifiOuis, RavenUuids, Patterns, Manufacturers
Requested at runtime when you press START for the first time. If you permanently deny a required permission ("don't ask again"), the START button swaps to Open app settings which fires the per-app system-settings page so you can grant manually.
| Permission | Why |
|---|---|
BLUETOOTH_SCAN, BLUETOOTH_CONNECT (API 31+) | BLE scanning |
BLUETOOTH, BLUETOOTH_ADMIN (≤ API 30) | BLE scanning, legacy |
ACCESS_FINE_LOCATION | Required for BLE pre-S, WiFi pre-T, and DeFlock/Citizen proximity |
NEARBY_WIFI_DEVICES (API 33+) | WiFi scan results without using location |
ACCESS_WIFI_STATE, CHANGE_WIFI_STATE | Trigger and read scan results |
INTERNET, ACCESS_NETWORK_STATE | DeFlock Overpass + Citizen API |
FOREGROUND_SERVICE_* | Keep scanning with the screen off |
POST_NOTIFICATIONS (API 33+) | Foreground-service notification |
VIBRATE | Haptic alert on threat-tier escalation |
Requires JDK 21 (Android Gradle Plugin 8.7.x rejects JDK 26), Android Studio with SDK Platform 34 + Build-Tools 34.x + Platform-Tools.
# 1) Copy the example local.properties and point sdk.dir at your install $ cp local.properties.example local.properties # edit local.properties → sdk.dir=/Users/<you>/Library/Android/sdk # 2) Make sure JAVA_HOME is JDK 21 $ export JAVA_HOME=/usr/local/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home # 3) Build & install on a connected device with USB debugging $ ./gradlew :app:installDebug
Or download the latest debug-signed APK from Releases and sideload.
stop() + start()
in one tap.Phases 1–5 (skeleton, BLE, WiFi, DEFLOCK, CITIZEN, polish) complete and field-tested. Current release v0.1.7 addresses two full audit passes.
Tool for situational awareness about deployed surveillance
infrastructure in public spaces. Local laws regarding electronic
surveillance, RF monitoring, and police-tracking apps vary — your
responsibility to know what's legal where you are. Personal-use
license; reference repos under REFERENCES/ retain their
own licenses and are not redistributed as part of this project.