KT

CallShield

Spam call and text blocker — GitHub-hosted spam database, no API keys, no subscriptions

KotlinAndroid
Delivery
Source-first
Browse code, README, and release notes on GitHub.
Primary lane
Kotlin / Android lane
The clearest adjacent context for this project inside the portfolio.
Freshness
Updated Jul 9, 2026
Latest release
No tag yet
README is the clearest project overview right now.

Preview

Using the generated project card as a clean fallback until a live capture is available.

CallShield card

Source at github.com/SysAdminDoc/CallShield.

README

Cached at build time, cleaned up for in-site reading, and linked back to the canonical GitHub source.

12 min read 2,468 words 14 sections
Contents
  1. v1.7.12 Highlights
  2. How It Works
  3. Detection Pipeline (v1.7.12)
  4. Live Caller ID Overlay
  5. ML Spam Scorer
  6. Features
  7. Data Sources
  8. Security
  9. Privacy
  10. Requirements
  11. Building
  12. Testing
  13. Tech Stack
  14. License

CallShield Logo

CallShield

Open-source spam call and text blocker for Android
15+ layer detection + Gradient-Boosted Tree ML | 32,933 spam numbers | Real-time caller ID | RCS filter | No required API keys

Release 32,933 Numbers 711 Tests Android 10+ MIT License No required API keys


CallShield blocks spam calls and texts using a 15+ layer on-device detection engine with a gradient-boosted tree ML scorer, campaign burst detection, RCS notification filter, and real-time caller ID overlay. Powered by a 32,933-number database with scheduled hot-list updates. Community-maintained, no accounts, no tracking.

v1.7.12 Highlights

  • Durable blocked-call logging - blocked-call decisions now enqueue an idempotent pending Room row before responding to Android, then a Hilt-backed retry worker flushes the row into the final call log without duplicating notifications or widget refreshes.

  • Room v10 migration - call_log now carries a nullable unique logKey, pending_blocked_call_logs persists retry state, and migration/DAO tests cover duplicate suppression and retry windows.

  • Community-report abuse backoff - the Cloudflare Worker rate-limit and dedup helpers now have local tests, and the Android client surfaces HTTP 429 with the server-provided retry delay.

  • ASCII-only digit utility - security-sensitive phone-number extraction and UI entry/review paths now share named ASCII-only helpers instead of Unicode digit matching.

  • External blocklist subscription guardrails - custom HTTP(S) CSV/TXT/JSON feeds validate byte and row limits, preview add/remove/source impact before commit, and can be disabled or removed with feed-owned rows rolled back.

  • Compose BOM 2026.05.00 refresh — UI dependencies now resolve on the current Compose 1.11.1 release train, with Material 3 1.4.0 and refreshed lifecycle/core/savedstate transitive locks across debug, release, and unit test configurations.

  • Configuration-aware Compose copy — snackbar, toast, semantic, validation, and search-result count text now use Compose resource APIs instead of stale LocalContext.getString / resources reads inside composables.

  • WorkManager 2.11.2 refresh — background sync, hot-list refresh, and daily digest jobs now run on the current WorkManager line with Android 15+ network-constraint fixes.

  • Worker schedule contract tests — sync, manual refresh, hot-list, and digest WorkRequests now have JVM coverage for repeat intervals, network constraints, initial delay, and retry backoff.

  • DataStore privacy hardening — settings now run on DataStore 1.2.1, and the optional AbstractAPI enrichment key is stored in a private no-backup DataStore instead of the backed-up public settings file.

  • Safer backup boundary — database and public preferences remain restorable for device transfers, while optional local credentials migrate out of Android Auto Backup scope on first read or save.

  • Reproducible-build groundwork — Gradle dependency locking is enabled, the resolved dependency graph is checked in, AGP VCS metadata is disabled for release APKs, and local release guards block wall-clock build metadata from being embedded into APK inputs.

  • Release hash sidecars — release builds now produce SHA256 sidecars for APK artifact integrity, with Windows helpers for signed local releases and content-level APK rebuild comparisons.

  • Network hardening — OkHttp is upgraded to 5.3.2 and all direct data, community-report, URL-safety, and caller-ID enrichment hosts are protected by centralized certificate pinning.

  • Modern Android build stack — AGP is upgraded to 8.10.1, Kotlin/KSP are aligned on 2.2.21, and Room is upgraded to 2.8.4, keeping codegen and R8 compatible with the current Kotlin metadata used by the networking stack.

  • Stats and scan feedback polish — weekly activity labels now respect locale weekday names, Statistics detection-source labels are resource-backed, and scan permission/failure copy is consistent across call-log and SMS flows.

  • Settings credential polish — the optional AbstractAPI key is masked by default, has explicit show/hide control, reports "Not configured", "Saved locally", and "Unsaved changes" states before saving, and is now kept out of backed-up public preferences.

  • Premium-polish UX refresh — shared premium action, compact-action, icon-tile, and state-card treatments now unify dashboard, lookup, details, recent-call, blocklist, diagnostics, onboarding, and settings flows.

  • Clearer recovery states — Blocked Log empty and filtered states now explain what happened and provide a direct "Show all activity" recovery action when filters hide records.

  • Trust-focused settings feedback — the trusted push-alert source picker now shows installed-source coverage and skeleton loading while package labels resolve.

  • Hardening foundation from v1.7.2 — spoof-proof ASCII phone normalization, SMS size caps, regex ReDoS validation, LRU notice gates, separated PendingIntent IDs, and atomic crash-log writes.

  • STIR/SHAKEN Authenticated Allow — carrier-authenticated caller ID can short-circuit weak heuristic / ML blocks while still yielding to every explicit user and system rule.

  • Answered-caller trust — numbers answered repeatedly inside the configured lookback window can ring through lower-confidence heuristic/ML suspicion while explicit block rules still win first.

  • Emergency callback grace — unknown callbacks can ring through after a local emergency call for a configurable window while explicit block rules still win first.

  • SMS burst protection - repeated unknown SMS senders or same-prefix floods can be blocked as sms_burst, with blocked-SMS notification actions to mark safe or report.

  • 711 total JVM unit tests - the local unit suite covers detection, workers, utilities, repository contracts, and permission readiness before release.

  • Gradient-Boosted Tree ML model — 20 features, pure Kotlin, no TFLite dependency.

  • Campaign burst detection — NPA-NXX prefix clustering identifies coordinated spam waves.

  • Full accessibility — content descriptions across Compose UI, 48dp minimum touch targets.

How It Works

  1. 32,933 confirmed spam numbers — sourced from 1.75M FCC consumer complaints (2+ reports each), FTC Do Not Call, ToastedSpam, and community reports
  2. 15+ layer detection + ML — database, heuristics, campaign burst detection, on-device gradient-boosted tree, SMS content/burst analysis, RCS filter, STIR/SHAKEN, and more
  3. Real-time caller ID overlay — parallel lookups against SkipCalls, PhoneBlock, WhoCalledMe + OpenCNAM caller name, with SIT tone anti-autodialer
  4. Scheduled hot list — trending spam numbers and campaign ranges refresh through the repository data pipeline
  5. Callback-aware — won't block callbacks from numbers you recently called, answered repeatedly, after a local emergency call, or urgent repeated callers
  6. Community-driven — one-tap anonymous contribution via Cloudflare Worker, daily merge into database

Detection Pipeline (v1.7.12)

All detection layers implement a shared IChecker interface and run in priority order via CheckerPipeline.run — first non-null result wins, every layer is testable in isolation. Priorities are stable numbers; the ladder below is the live order.

Priority Layer Verdict How It Works
10000 Manual Whitelist Allow Numbers you've explicitly marked as always-allow
9000 Contact Whitelist Allow Numbers in your phone's contacts always pass through
8500 STIR/SHAKEN Failed Block Carrier-authenticated caller ID failure gets blocked before heuristic layers
7000 User Blocklist + Database Block Personal blocklist + 32,933 confirmed spam numbers + scheduled hot-list data
6900 System Block List (A4) Block Read-only bridge to Android's BlockedNumberContract — respects stock Phone/Messages blocks
6000 Prefix Rules Block Wangiri country codes, US premium rate (+1900), international premium
5500 Wildcard / Regex Block Custom patterns like +1832555* or full regex, now with optional schedule
5400 Range Patterns (A5) Block Length-locked # patterns like +33162######, with schedule + coverage safety rail
5300 STIR/SHAKEN Authenticated Allow Carrier-authenticated caller ID can allow through lower-confidence heuristic/ML suspicion while explicit blocks still win first
5000 Recently Dialed Allow Numbers you called in the last 24h — they're probably calling back
4980 Emergency Callback Allow Unknown callbacks can ring through after a local emergency call during the configured grace window
4950 Answered Caller Allow Numbers answered repeatedly inside the configured lookback window
4900 Repeated Urgent Allow Same number calls 2x in 5 min → allowed through
4700 Push-Alert Bridge (A3) Allow Uber/DoorDash/Amazon/Gmail notification about an arriving call? Let it through
4500 Campaign Recorder Side-effect only; feeds burst detection below
4000 Quiet Hours Block Block all non-contact calls during configurable hours
3500 Frequency Auto-Block Block Numbers that call 3+ times in 7 days get auto-blocked
3000 Heuristic Engine Block VoIP ranges, neighbor spoofing, rapid-fire detection, 30+ rules
2500 Campaign Burst Block NPA-NXX prefix clustering detects coordinated spam waves
2000 ML Spam Scorer Block 20-feature on-device gradient-boosted tree model

SMS-specific layers (append after the shared chain): SMS Context TrustSMS Keyword Rules (with schedule) → SMS Burst ProtectionSMS Content Analysis (30+ regex patterns, URL shorteners, suspicious TLDs, spam domain blocklist).

Additional Layers
  • Caller ID Overlay — suspicious calls (heuristic score 30-59) trigger a live multi-source lookup overlay with SkipCalls, PhoneBlock, WhoCalledMe + OpenCNAM caller name
  • RCS Filter — NotificationListenerService monitors Google/Samsung Messages for RCS spam
  • URL Safety — local spam-domain checks run before URLhaus (abuse.ch), with query-string stripping enabled by default for remote SMS/RCS URL checks
  • STIR/SHAKEN — blocks calls failing carrier caller ID verification (Android 11+)
  • After-Call Feedback — "Was this spam?" notification after suspicious calls for community reporting
Per-Rule Schedules (A7)

Any wildcard, range, or SMS keyword rule can be time-gated to specific days of the week and an hour window. The hour picker supports overnight wrap; daysMask = 0 is the "no gating" sentinel so rules created before v1.6 behave identically.

Live Caller ID Overlay

When a call comes in, CallShield shows a real-time overlay that queries 4 sources simultaneously:

┌──────────────────────────────────┐
│ LIKELY SPAM                      │
│ (212) 555-1234                   │
│ New York, NY                     │
│ Spam Score: 80% (17 reports)     │
│ JOHN DOE (OpenCNAM)             │
│ ⚠ SkipCalls: Flagged            │
│ ⚠ PhoneBlock: 5 reports         │
│ ⚠ WhoCalledMe: 12 reports       │
│ All sources checked              │
│ [Search] [Block] [Dismiss]       │
│ 🔈 Play SIT Tone (anti-dialer)  │
└──────────────────────────────────┘
  • Shows instantly with area code, then updates live as each source responds
  • OpenCNAM caller name lookup (free, 60 req/hr)
  • SIT Tone — ITU-T E.180 three-tone sequence tricks autodialers into removing your number
  • Color-coded: green (safe) → yellow → orange → red (spam)

ML Spam Scorer

On-device 20-feature gradient-boosted tree model — pure Kotlin, no TFLite, no heavy ML libraries. Runs in microseconds.

Feature Description
toll_free 800/888/877/etc. prefix
high_spam_npa Area code in high FTC/FCC complaint set
voip_range NPA-NXX in known VoIP spam carrier range
repeated_digits_ratio Fraction of most-common digit
sequential_asc/desc_ratio Sequential digit pairs
all_same_digit All 10 digits identical
nxx_555 Exchange is 555 (test numbers)
last4_zero Subscriber is 0000
invalid_nxx NXX starts with 0 or 1 (NANP-invalid)
subscriber_all_same Last 4 digits all same (9999)
alternating_pattern Even/odd positions uniform (5050505050)
nxx_below_200 Often unassigned ranges
low_digit_entropy Fewer than 4 distinct digits
subscriber_sequential Last 4 form ascending/descending run
+ 6 additional Campaign proximity, time-of-day, call frequency, area code density, prefix heat, neighbor spoof score

Trained weekly from the CallShield database (50K positive + 50K negative samples). Threshold: 0.7 (conservative).

Features

Number Lookup
  • Instant spam check through all 15+ detection layers with animated score gauge (0-100)
  • Auto-paste from clipboard, area code lookup (330+ US/CA), haptic feedback
  • Multi-source reverse lookup: SkipCalls + PhoneBlock + WhoCalledMe + OpenCNAM
Recent Calls & Blocked Log
  • Recent calls with contact names, risk indicators, call type icons, filter chips (All/Missed/Spam)
  • Blocked log with swipe-to-dismiss + undo, grouping with severity-scaled accent bars, filter chips
  • Staggered entrance animations, shimmer loading skeletons
Rules Management (5 tabs)
  • Blocklist, Wildcards, Keywords, Whitelist, Database
  • Export/import blocklists as JSON, per-rule enable/disable toggles
  • Regex validation before adding wildcard rules
Statistics
  • Weekly bar chart with daily breakdown
  • Detection source donut chart
  • Monthly trend line
  • Top offenders, area code heatmap, hourly heatmap
Smart Features
  • Smart suggestions — detects area code spam patterns, one-tap block entire area code
  • Weekly trend indicator — shows if spam is increasing or decreasing vs last week
  • Last blocked preview card on dashboard with tap-to-inspect
  • Blocking profiles: Work / Personal / Sleep / Maximum / Off
  • Callback detection + repeated urgent caller allow-through
  • FTC Do Not Call complaint filing
  • After-call "Was this spam?" feedback notification
Home Screen Widget
  • Today vs yesterday blocked count with trend indicator
  • Last blocked number and time
  • Quick-access to lookup and protection status
Community
  • One-tap anonymous contribution via Cloudflare Worker
  • False positive reporting subtracts votes
  • Share spam warnings to any app
Data & System
  • Selective backup/restore for rules, non-secret settings, and opt-in logs; CSV log export; auto-cleanup (7/14/30/90 days)
  • Weekly full sync + scheduled hot list refresh, daily digest notification
  • Quick Settings tile, app shortcuts, home screen widget
  • Protection test validates all layers and permissions
  • Onboarding wizard with permission requests

Data Sources

Database (32,933 numbers, weekly CI)
Source Method
FCC Consumer Complaints Socrata API, 500K records, min 2 reports
FTC Do Not Call api.ftc.gov (DEMO_KEY)
ToastedSpam Community curated list
Community Reports Anonymous via Cloudflare Worker
Hot List (scheduled refresh)
File Contents
hot_numbers.json Top 500 trending numbers (last 24h)
hot_ranges.json NPA-NXX prefixes with 3+ active campaign numbers
spam_domains.json Phishing/spam domains from community SMS reports
Real-Time Lookup (overlay only)
Source What It Returns Auth
SkipCalls spam flag, 1M+ numbers None
PhoneBlock.net Votes, rating, blacklist None
WhoCalledMe Report count, notes None
OpenCNAM Caller name (CNAM) None (60/hr)
AbstractAPI Carrier, line type Optional key
URL Safety (post-decision)
Source What It Checks
URLhaus (abuse.ch) Phishing/malware URLs in SMS/RCS bodies after local spam-domain matching

Security

  • Network security config — cleartext traffic disabled in production
  • Signing credentials — stored in local.properties, not hardcoded in build files
  • Restricted FileProvider paths — scoped to export directory only
  • Scoped backup — database and public settings are backed up for transfer/restore, while the optional AbstractAPI key is migrated to a private no-backup DataStore

Privacy

All detection runs on-device. No personal data is collected. Network requests:

  • Syncing spam database from GitHub (public)
  • Real-time lookups against free public APIs (number queried, not stored)
  • Community reports to Cloudflare Worker (phone number only, no identity)
  • URLhaus checks for SMS URL safety after local spam-domain matching; fragments and query strings are stripped by default

No required API keys. The optional AbstractAPI key stays on-device in no-backup storage. No accounts. No analytics. No ads.

Requirements

  • Android 10+ (API 29)
  • STIR/SHAKEN requires Android 11+ (API 30)
  • Caller ID overlay requires "Display over other apps" permission
  • RCS filter requires Notification Access permission

Building

./gradlew verifyReproducibleBuildInputs verifyReleaseApkReproducibleMetadata

Requires JDK 17+. Signed APK at app/build/outputs/apk/release/app-release.apk. Generate the release hash sidecar with:

.\scripts\write-release-sha256.ps1

See docs/reproducible-builds.md for the dependency-lock and hash-comparison runbook.

F-Droid submission prep lives in fastlane/metadata/android/en-US/, docs/fdroid/com.sysadmindoc.callshield.yml, and docs/fdroid-submission.md. The actual F-Droid merge request and signature-copy verification still require an fdroiddata/GitLab environment.

Signing: Create local.properties in the project root with your keystore credentials:

RELEASE_STORE_FILE=path/to/keystore.jks
RELEASE_STORE_PASSWORD=...
RELEASE_KEY_ALIAS=...
RELEASE_KEY_PASSWORD=...

Testing

./gradlew testDebugUnitTest   # 711 tests
./gradlew connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.sysadmindoc.callshield.platform.TargetSdkBehaviorSmokeTest

Run tests, lint, release metadata checks, and artifact builds locally before publishing.

Tech Stack

Component Technology
Language Kotlin 2.2.21
UI Jetpack Compose BOM 2026.05.00 + Material 3
Theme Premium AMOLED black + Catppuccin Mocha
Database Room 2.8.4 (SQLite) — 7 entities
Networking OkHttp 5.3.2 + certificate pinning
JSON Moshi
ML Pure Kotlin gradient-boosted tree (20 features)
Settings DataStore Preferences 1.2.1
Background WorkManager 2.11.2
Community API Cloudflare Workers
URL Safety URLhaus (abuse.ch)
Verification Local Gradle, lint, and release-artifact checks
Tests 711 JVM unit tests (JUnit)
Strings 1018 string resources and 28 plural groups (translation-ready)
Accessibility 100+ content descriptions, 48dp touch targets
Min SDK 29 (Android 10)
Target SDK 36

For deep technical details, see CLAUDE.md.

License

MIT

Read on GitHub → github.com/SysAdminDoc/CallShield

Start with the strongest work, a key route, or the full archive.