Skip to content

About

Android app that masks PIN input during USSD sessions (M-Pesa, Safaricom) to prevent shoulder-surfing. Uses AccessibilityService to overlay a secure input on the system USSD dialog.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

USSD Shield

Android app that protects sensitive PIN input during USSD sessions from shoulder-surfing.

Problem

Interactive USSD sessions (M-Pesa *334#, Safaricom *444#, etc.) display PIN prompts in plain text through the system's MMIDialogActivity. Anyone looking at your screen can see your PIN as you type it.

Solution

USSD Shield uses an AccessibilityService to monitor the system USSD dialog. When a PIN prompt is detected (e.g. "Enter M-PESA PIN"), a masked overlay appears on top of the system dialog. The user enters their PIN into the overlay (shown as dots), and the app fills the system dialog's input field and submits automatically.

Non-sensitive prompts (menus, confirmations) pass through to the system dialog normally.

How It Works

User dials *334# → System MMIDialogActivity opens
  → AccessibilityService detects the USSD dialog
  → Traverses accessibility node tree, extracts prompt text
  → HeuristicClassifier determines if prompt is sensitive
  → If sensitive: masked PIN overlay appears
  → User enters PIN (shown as •••)
  → Service fills system EditText + clicks Send
  → Session continues / completes

Features

  • PIN masking — overlay with immediate dot masking (no brief character reveal)
  • Heuristic classification — regex-based detection of PIN/password/OTP prompts in English, Swahili, and French
  • Session-scoped — only intercepts USSD sessions started by the app, never interferes with other apps
  • Fail-secure — aggressive fallback regex masks anything requesting digit/code input
  • Termination detection — detects "transaction complete", "balance is", etc. to end sessions
  • FLAG_SECURE — prevents screenshots and screen recording of the app

Permissions

Permission Purpose
CALL_PHONE Initiate USSD requests
READ_PHONE_STATE Access telephony for USSD
SYSTEM_ALERT_WINDOW Overlay permission (fallback)
BIND_ACCESSIBILITY_SERVICE Monitor system USSD dialog

Setup

  1. Build and install the app
  2. Grant phone permissions when prompted
  3. Enable "USSD Shield" in Settings → Accessibility
  4. Grant overlay permission if prompted
  5. Dial a USSD code through the app

Building

./gradlew assembleDebug
./gradlew installDebug

Requires Android SDK with compileSdk 36 and minSdk 26.

Architecture

app/src/main/java/com/example/ussdshield/
├── MainActivity.kt                          # Permission checks, navigation
├── accessibility/
│   ├── UssdAccessibilityService.kt          # Monitors com.android.phone, classifies prompts
│   ├── UssdOverlayManager.kt               # WindowManager overlay for masked PIN input
│   └── UssdInterceptBus.kt                 # LiveData event bus (Service ↔ ViewModel)
├── ml/
│   └── HeuristicClassifier.kt              # Regex-based prompt classification
└── ui/
    ├── dialer/DialerFragment.kt            # USSD code entry keypad
    ├── session/SessionFragment.kt          # Session state display
    └── ussd/UssdSessionViewModel.kt        # USSD state machine, TelecomManager fallback

License

MIT

About

Android app that masks PIN input during USSD sessions (M-Pesa, Safaricom) to prevent shoulder-surfing. Uses AccessibilityService to overlay a secure input on the system USSD dialog.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages