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 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 30-minute hot list updates. Community-maintained, no accounts, no tracking.
- STIR/SHAKEN Trusted-Caller Allow β new detection layer that short-circuits heuristic / ML / campaign-burst when the carrier signs a
PASSEDattestation. Paired with the existingFAILED-blocks layer for a clean PASSED / FAILED / NOT_VERIFIED decision table. Priority slot sits below every explicit user rule β your blocklist always wins over the carrier. - Auto-Mute Low-Confidence Blocks β opt-in setting that silences blocks scoring below 60% confidence to voicemail instead of hard-rejecting them, so you can review uncertain calls after the fact. High-confidence hits (database, blocklist, STIR fail, heuristic β₯ 60) still hard-reject even with auto-mute on.
- Centralized block-response decision table β
buildBlockResponse()helper inCallShieldScreeningServicewith a pureshouldSilence()companion. Three silence/reject branches (silent-voicemail / auto-mute / hard-reject) now share one reviewable code path. Behavior-preserving refactor β existing response shapes emit identicalCallResponse.Builder()calls. - 14 new JVM unit tests β full priority-ladder regression sweep in
StirShakenTrustCheckerTestand every branch of the silence/reject table inCallShieldScreeningServiceAutoMuteTest. - 597 total unit tests + GitHub Actions CI β automated test pipeline on every push.
- 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.
- 32,933 confirmed spam numbers β sourced from 1.75M FCC consumer complaints (2+ reports each), FTC Do Not Call, ToastedSpam, and community reports
- 15+ layer detection + ML β database, heuristics, campaign burst detection, on-device gradient-boosted tree, SMS content analysis, RCS filter, STIR/SHAKEN, and more
- Real-time caller ID overlay β parallel lookups against SkipCalls, PhoneBlock, WhoCalledMe + OpenCNAM caller name, with SIT tone anti-autodialer
- 30-minute hot list β trending spam numbers and campaign ranges refresh every 30 minutes via GitHub Actions
- Callback-aware β won't block callbacks from numbers you recently called, or urgent repeated callers
- Community-driven β one-tap anonymous contribution via Cloudflare Worker, daily merge into database
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 |
| 7000 | User Blocklist + Database | Block | Personal blocklist + 32,933 confirmed spam numbers + hot list (refreshed every 30 min) |
| 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 |
| 5000 | Recently Dialed | Allow | Numbers you called in the last 24h β they're probably calling back |
| 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 Trust β SMS Keyword Rules (with schedule) β SMS Content Analysis (30+ regex patterns, URL shorteners, suspicious TLDs, spam domain blocklist).
- 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 β URLhaus (abuse.ch) checks for phishing/malware URLs in SMS/RCS (post-decision, notification only)
- STIR/SHAKEN β blocks calls failing carrier caller ID verification (Android 11+)
- After-Call Feedback β "Was this spam?" notification after suspicious calls for community reporting
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.
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)
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).
- 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 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
- Blocklist, Wildcards, Keywords, Whitelist, Database
- Export/import blocklists as JSON, per-rule enable/disable toggles
- Regex validation before adding wildcard rules
- Weekly bar chart with daily breakdown
- Detection source donut chart
- Monthly trend line
- Top offenders, area code heatmap, hourly heatmap
- 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
- Today vs yesterday blocked count with trend indicator
- Last blocked number and time
- Quick-access to lookup and protection status
- One-tap anonymous contribution via Cloudflare Worker
- False positive reporting subtracts votes
- Share spam warnings to any app
- Full backup/restore, CSV log export, auto-cleanup (7/14/30/90 days)
- Weekly full sync + 30-minute 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
| 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 |
| 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 |
| 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 |
| Source | What It Checks |
|---|---|
| URLhaus (abuse.ch) | Phishing/malware URLs in SMS/RCS bodies |
- 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
- Database-only backup β
android:fullBackupContentexcludes preferences containing API keys
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 (URL only)
No API keys required. No accounts. No analytics. No ads.
- 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
./gradlew assembleReleaseRequires JDK 17+. Signed APK at app/build/outputs/apk/release/app-release.apk.
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=..../gradlew testDebugUnitTest # 210 testsCI runs automatically via GitHub Actions on every push and pull request.
| Component | Technology |
|---|---|
| Language | Kotlin |
| UI | Jetpack Compose + Material 3 |
| Theme | Premium AMOLED black + Catppuccin Mocha |
| Database | Room (SQLite) β 6 entities |
| Networking | OkHttp |
| JSON | Moshi |
| ML | Pure Kotlin gradient-boosted tree (20 features) |
| Settings | DataStore Preferences |
| Background | WorkManager |
| Community API | Cloudflare Workers |
| URL Safety | URLhaus (abuse.ch) |
| CI | GitHub Actions |
| Tests | 210 unit tests (JUnit) |
| Strings | 544+ resources (translation-ready) |
| Accessibility | 100+ content descriptions, 48dp touch targets |
| Min SDK | 29 (Android 10) |
| Target SDK | 35 |
For deep technical details, see CLAUDE.md.
MIT
