See what your Android device reveals — and why it matters for fingerprinting.
AndroWatch is an open-source Android privacy audit app inspired by Loupe. It collects on-device fingerprint signals through public Android APIs, groups them by access cost, and explains why each reading can contribute to tracking.
All data stays on your phone unless you explicitly export a JSON report.
- Features
- Signal tiers
- Architecture
- Install from GitHub Releases
- Build it yourself
- API documentation (KDoc)
- Security
- Contributing
- License
| Feature | Description |
|---|---|
| 30 signal categories | Device identity, network, sensors, apps, WebView canvas/WebGL, and more |
| 3 sensitivity tiers | Passive · Permissioned · Advanced |
| Privacy rationales | Every signal includes a short explanation of fingerprinting relevance |
| Runtime permission gates | Dangerous permissions requested only when you open a category |
| Live collectors | Battery, motion, and audio route update in real time |
| JSON export | Share a structured report via Android share sheet (FileProvider) |
| Material 3 UI | Jetpack Compose, dynamic color, edge-to-edge |
| i18n | Extensive values-* string resources |
| Release hardening | R8 minify, resource shrink, privacy-safe logging |
Passive (17) — no dangerous permissions
DeviceIdentity · GoogleAccount · SystemInfo · Display · Locale · Accessibility · DeviceMotion · Battery · Storage · Network · Fonts · InstalledVoices · AppInfo · Pasteboard · Audio · Graphics · Telephony
Permissioned (10) — runtime permission required
Motion · Location · Cameras · Bluetooth · LocalNetwork · Contacts · Photos · Calendar · Reminders · MusicLibrary
Advanced (3) — side-channels / on-demand probes
InstalledAppsProbe · WebViewFingerprint · PreviousInstallsLog
Full API mapping: docs/ANDROID_API_MAP.md
flowchart LR
P[Passive<br/>17 collectors] --> UI[Compose UI]
R[Permissioned<br/>10 collectors] --> UI
A[Advanced<br/>3 collectors] --> UI
UI --> E[JSON Export]
| Tier | Runtime prompt | Examples |
|---|---|---|
| Passive | None | Build.*, Settings, ConnectivityManager |
| Permissioned | Per category | Location, camera, contacts, Bluetooth scan |
| Advanced | On-demand / side-channel | WebView canvas hash, URI scheme app probing |
10-module Gradle project — dependencies flow downward only:
:app
├── :collector:assembly → tier-passive / tier-permissioned / tier-advanced
│ └── :collector:engine → :collector:contract
├── :feature:export
└── :core:model, :core:permission
See docs/ARCHITECTURE.md for the full module graph and data flow.
| Module | Role |
|---|---|
:app |
Compose shell, CategoryViewModel, navigation |
:core:model |
FingerprintSignal, SignalCategory, Sensitivity |
:core:permission |
PermissionCenter — category → permission mapping |
:collector:contract |
SignalCollector / LiveSignalCollector interfaces |
:collector:engine |
Probes, interpreters, WebView/GLES/mDNS helpers |
:collector:tier-* |
30 category collectors |
:collector:assembly |
CollectorRegistry wiring |
:feature:export |
ReportExporter JSON + share intent |
- Open Releases.
- Download the APK matching your device CPU:
| APK suffix | Device |
|---|---|
arm64-v8a |
Most phones & tablets (2017+) |
armeabi-v7a |
Older 32-bit ARM |
x86_64 |
Emulators / some Chromebooks |
x86 |
Older 32-bit emulators |
- Enable Install unknown apps for your browser/files app.
- Install the APK.
# Or via ADB (replace ABI and version)
adb install AndroWatch-0.1.0-arm64-v8a-release.apk| Tool | Version |
|---|---|
| JDK | 17+ (Temurin recommended) |
| Android SDK | API 37 (platforms;android-37, build-tools;36.x) |
adb |
For device install (platform-tools) |
Copy local.properties.example → local.properties and set sdk.dir, or set ANDROID_HOME.
git clone https://github.com/LTechnologies0/AndroWatch.git
cd AndroWatch
cp local.properties.example local.properties # edit sdk.dir
# Debug APK (single ABI — fast)
./gradlew :app:assembleDebug
# Run unit tests
./gradlew test
# Install on connected device
./gradlew installToPhone
# or
adb install -r app/build/outputs/apk/debug/app-arm64-v8a-debug.apk
# Release APKs (all 4 ABIs, signed if keystore present)
./scripts/generate-release-keystore.sh # first time only
./gradlew :app:assembleReleasegit clone https://github.com/LTechnologies0/AndroWatch.git
cd AndroWatch
Copy-Item local.properties.example local.properties # edit sdk.dir
.\gradlew.bat :app:assembleDebug
.\gradlew.bat test
.\gradlew.bat installToPhone# udev rules for ADB (once, requires sudo)
sudo ./scripts/setup-udev.sh 2>/dev/null || true
adb devices
./gradlew installToPhone| Command | Output |
|---|---|
assembleDebug |
1 APK (arm64-v8a by default) |
assembleDebug -Ponionphone.devAbi=x86_64 |
Debug for emulator |
assembleRelease |
4 signed APKs (one per ABI) |
dokkaGenerate |
HTML API docs in build/dokka/html/ |
./scripts/generate-release-keystore.sh
# Creates release.keystore + keystore.properties (both gitignored)
./gradlew :app:assembleReleaseNever commit keystore.properties, *.keystore, or local.properties.
All public APIs are documented with KDoc in source. HTML reference is generated with Dokka:
./gradlew dokkaGenerate
# → build/dokka/html/index.htmlPublished automatically to GitHub Pages on every push to main:
https://ltechnologies0.github.io/AndroWatch/
(workflow: .github/workflows/docs.yml)
- Local-first: no network upload of fingerprint data
- No hardcoded secrets: signing via
keystore.properties(gitignored) or CI secrets - Release stripping: verbose collector logs removed by R8 (
gradle/privacy-logging.pro) - Backup disabled in manifest
See SECURITY.md for vulnerability reporting.
| GitHub Secret | Purpose |
|---|---|
RELEASE_KEYSTORE_BASE64 |
Base64-encoded .keystore file |
RELEASE_KEYSTORE_PASSWORD |
Keystore password |
RELEASE_KEY_ALIAS |
Key alias (androwatch) |
RELEASE_KEY_PASSWORD |
Key password |
Generate values with ./scripts/generate-release-keystore.sh.
Only three workflow files to avoid run spam — each push/PR produces a single
CI run with conditional jobs (cancel-in-progress cancels superseded runs).
| Workflow | Trigger | Jobs / Purpose |
|---|---|---|
| CI | Push main, PR, weekly, manual |
test-and-debug, codeql, dependency-review (PR), docs → Pages (main), scorecard |
| Community | Issues, PRs, weekly | labeler, greetings, auto-assign, triage, stale, release-drafter |
| Release | Tag v*.*.* |
Signed multi-ABI APKs → GitHub Release |
| Dependabot | Weekly | 1 grouped Gradle PR + 1 grouped Actions PR |
- Fork the repository
- Create a feature branch
- Run
./gradlew test :app:assembleDebug - Add KDoc for new public APIs
- Open a pull request
| Topic | Android (AndroWatch) | iOS (Loupe) |
|---|---|---|
| App probing | Package visibility limits (API 30+) | Broader enumeration |
| Device ID | ANDROID_ID (scoped) |
identifierForVendor |
| Install persistence | EncryptedSharedPreferences probe |
Keychain-based |
MIT © 2026 AndroWatch contributors
Inspired by Loupe · Built with Kotlin & Jetpack Compose