This document explains exactly how Phase 2 was implemented with specific line numbers and code snippets.
Phase 2 adds real-time notification monitoring to detect phishing URLs in notifications from any app (Gmail, WhatsApp, SMS, etc.).
Location: NotificationMonitorService.kt
Purpose: Core service that monitors all system notifications
// Line 6: Import PhishingRepository interface
import com.droid.cybershield.domain.repository.PhishingRepository
// Line 7: Enable Hilt dependency injection
import dagger.hilt.android.AndroidEntryPoint
// Line 28: Mark service for Hilt injection
@AndroidEntryPoint
class NotificationMonitorService : NotificationListenerService() {
// Lines 44-45: Inject PhishingRepository from Phase 1
@Inject
lateinit var phishingRepository: PhishingRepository
// Line 47: Create coroutine scope for async operations
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
}What this does: Sets up the service with dependency injection to access Phase 1's phishing detection system.
// Lines 57-58: Called when ANY notification arrives
override fun onNotificationPosted(sbn: StatusBarNotification) {
super.onNotificationPosted(sbn)
// Lines 69-75: Ignore CyberShield's own notifications
val packageName = sbn.packageName
if (packageName == this.packageName) {
Log.d(TAG, "⏭️ Skipping: Self notification (CyberShield)")
return
}
// Lines 77-83: Extract text from notification
val notificationText = extractNotificationText(sbn)
if (notificationText.isNullOrEmpty()) {
Log.d(TAG, "⏭️ Skipping: No text content in notification")
return
}
// Lines 87-93: Find URLs in the text
val urls = extractUrls(notificationText)
if (urls.isEmpty()) {
Log.d(TAG, "⏭️ Skipping: No URLs found in notification")
return
}
// Lines 100-105: Check each URL for phishing
scope.launch {
urls.forEach { url ->
checkUrlAndAlert(url, packageName, sbn.id)
}
}
}What this does: Listens for notifications, extracts text, finds URLs, and triggers phishing checks.
// Lines 132-151: Extract text from notification
private fun extractNotificationText(sbn: StatusBarNotification): String? {
return try {
val extras = sbn.notification.extras
// Line 135: Get notification title
val title = extras.getCharSequence("android.title")?.toString() ?: ""
// Line 136: Get notification text
val text = extras.getCharSequence("android.text")?.toString() ?: ""
// Line 137: Get expanded text
val bigText = extras.getCharSequence("android.bigText")?.toString() ?: ""
// Line 139: Combine all text fields
val combined = "$title $text $bigText".trim()
if (combined.isNotEmpty()) combined else null
} catch (e: Exception) {
Log.e(TAG, "Error extracting notification text", e)
null
}
}What this does: Extracts all text content from notification (title, body, expanded text).
// Lines 38-41: Regex pattern to find URLs
private val URL_PATTERN = Regex(
"""https?://[^\s<>"']+""",
RegexOption.IGNORE_CASE
)
// Lines 157-170: Extract URLs from text
private fun extractUrls(text: String): List<String> {
return try {
URL_PATTERN.findAll(text)
.map { matchResult ->
// Line 162: Clean trailing punctuation
matchResult.value.trimEnd('.', ',', ')', ']', '}', '!', '?', ';')
}
.distinct() // Line 164: Remove duplicates
.toList()
} catch (e: Exception) {
Log.e(TAG, "Error extracting URLs", e)
emptyList()
}
}What this does: Uses regex to find all HTTP/HTTPS URLs and cleans them.
// Lines 179-226: Check URL and alert if phishing
private suspend fun checkUrlAndAlert(
url: String,
packageName: String,
notificationId: Int
) {
try {
// Lines 192-195: Call Phase 1 phishing detection
val result = phishingRepository.checkUrl(
url = url,
source = "notification:$packageName"
)
// Lines 206-216: Alert if phishing with high confidence
if (result.isPhishing && result.confidence > ALERT_THRESHOLD) {
Log.w(TAG, "⚠️ PHISHING DETECTED IN NOTIFICATION!")
Log.w(TAG, "URL: $url")
Log.w(TAG, "Source: $packageName")
Log.w(TAG, "Confidence: ${(result.confidence * 100).toInt()}%")
// Line 216: Show alert notification
showPhishingAlert(url, result.confidence, packageName, notificationId)
}
} catch (e: Exception) {
Log.e(TAG, "❌ Error checking URL: $url", e)
}
}What this does:
- Line 192-195: Calls
PhishingRepository.checkUrl()from Phase 1 - Line 206: Checks if confidence > 70% (ALERT_THRESHOLD)
- Line 216: Shows alert notification to user
// Lines 236-263: Show phishing alert notification
private fun showPhishingAlert(
url: String,
confidence: Float,
sourceApp: String,
notificationId: Int
) {
try {
// Lines 244-249: Get human-readable app name
val appName = try {
val appInfo = packageManager.getApplicationInfo(sourceApp, 0)
packageManager.getApplicationLabel(appInfo).toString()
} catch (e: Exception) {
sourceApp
}
// Lines 252-257: Create and show alert notification
val notificationHelper = com.droid.cybershield.presentation.notification.NotificationHelper(this)
notificationHelper.showPhishingAlertNotification(
url = url,
confidence = confidence,
sourceApp = appName
)
} catch (e: Exception) {
Log.e(TAG, "Error showing phishing alert", e)
}
}What this does: Gets app name and delegates to NotificationHelper to show alert.
Location: NotificationHelper.kt
Purpose: Creates and displays alert notifications
// Lines 30-33: Define phishing alert channel
private const val PHISHING_CHANNEL_ID = "cybershield_phishing_alerts"
private const val PHISHING_CHANNEL_NAME = "Phishing Alerts"
private const val PHISHING_CHANNEL_DESCRIPTION = "Critical alerts for phishing URLs detected in notifications"
// Lines 214-227: Create high-priority channel
val phishingAlertsChannel = NotificationChannel(
PHISHING_CHANNEL_ID,
PHISHING_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH // Line 218: High priority
).apply {
description = PHISHING_CHANNEL_DESCRIPTION
enableLights(true)
lightColor = 0xFFD32F2F.toInt() // Line 222: Red color
enableVibration(true)
setShowBadge(true)
setBypassDnd(true) // Line 225: Bypass Do Not Disturb
}What this does: Creates a high-priority notification channel for phishing alerts.
// Lines 122-191: Show phishing alert
fun showPhishingAlertNotification(url: String, confidence: Float, sourceApp: String) {
// Lines 130-139: Check notification permission (Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val hasPermission = context.checkSelfPermission(
android.Manifest.permission.POST_NOTIFICATIONS
) == android.content.pm.PackageManager.PERMISSION_GRANTED
if (!hasPermission) {
Log.w(TAG, "POST_NOTIFICATIONS permission not granted!")
return
}
}
// Lines 141-153: Create intent to open Phishing Prevention page
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
putExtra("navigate_to", "phishing")
putExtra("detected_url", url)
}
// Lines 159-162: Format display data
val confidencePercent = (confidence * 100).toInt()
val displayUrl = if (url.length > 50) url.take(47) + "..." else url
// Lines 165-178: Build notification
val notification = NotificationCompat.Builder(context, PHISHING_CHANNEL_ID)
.setSmallIcon(R.drawable.logo)
.setContentTitle("⚠️ Phishing Alert")
.setContentText("Suspicious link detected in $sourceApp")
.setStyle(NotificationCompat.BigTextStyle()
.bigText("⚠️ Phishing link detected in $sourceApp\n\nURL: $displayUrl\nConfidence: $confidencePercent%\n\nDo not click this link!"))
.setPriority(NotificationCompat.PRIORITY_HIGH) // Line 171: High priority
.setCategory(NotificationCompat.CATEGORY_ALARM) // Line 172: Alarm category
.setColor(0xFFD32F2F.toInt()) // Line 177: Red color
.setVibrate(longArrayOf(0, 500, 200, 500)) // Line 176: Aggressive vibration
.build()
// Lines 180-183: Show notification
val notificationId = generateNotificationId()
NotificationManagerCompat.from(context).notify(notificationId, notification)
}What this does: Creates and displays a high-priority red alert notification with vibration.
Location: AndroidManifest.xml
Purpose: Register the notification listener service
<!-- Lines 97-111: Register NotificationListenerService -->
<service
android:name=".service.NotificationMonitorService"
android:label="CyberShield Notification Monitor"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>What this does:
- Line 104: Sets service name
- Line 106: Requires system permission to bind notification listener
- Line 107: Allows system to bind to service
- Line 109: Declares service as NotificationListenerService
NotificationMonitorService (Phase 2)
↓ Line 192-195
phishingRepository.checkUrl(url, source)
↓
PhishingRepositoryImpl (Phase 1)
↓ Lines 25-168
├── Whitelist check (Lines 31-54)
├── Google Safe Browsing (Lines 56-83)
├── ONNX ML Model (Line 86)
├── Adaptive threshold (Line 91)
└── Smart decision (Lines 94-112)
Phase 2 does NOT modify Phase 1 code - it only consumes the existing checkUrl() API.
| File | Lines | Purpose |
|---|---|---|
| NotificationMonitorService.kt | 44-45 | Inject PhishingRepository |
| 57-109 | Detect notifications and extract URLs | |
| 132-151 | Extract notification text | |
| 157-170 | Extract URLs with regex | |
| 192-195 | Call Phase 1 phishing check | |
| 206-216 | Alert if phishing detected | |
| 236-263 | Show alert notification | |
| NotificationHelper.kt | 122-191 | Create phishing alert notification |
| 165-178 | Build high-priority notification | |
| 214-227 | Create notification channel | |
| AndroidManifest.xml | 103-111 | Register NotificationListenerService |
1. Notification arrives → onNotificationPosted() [Line 57]
2. Extract text → extractNotificationText() [Line 78]
3. Find URLs → extractUrls() [Line 88]
4. For each URL → checkUrlAndAlert() [Line 103]
5. Check phishing → phishingRepository.checkUrl() [Line 192]
6. If phishing → showPhishingAlert() [Line 216]
7. Display alert → NotificationHelper.showPhishingAlertNotification() [Line 253]
- Reuses existing PhishingRepository singleton from Phase 1
- No need to recreate dependencies
- Clean separation of concerns
- Non-blocking async processing
- Prevents ANR (Application Not Responding)
- Can check multiple URLs in parallel
private const val ALERT_THRESHOLD = 0.7f- Balances security vs. user annoyance
- Prevents alert fatigue from low-confidence detections
- High priority (IMPORTANCE_HIGH)
- Can bypass Do Not Disturb
- User can customize per channel
Total Implementation: 3 files, ~500 lines of code
Core Logic:
- NotificationMonitorService listens for notifications
- Extracts URLs using regex
- Calls Phase 1's
PhishingRepository.checkUrl() - Shows high-priority alert if phishing detected
Integration: Phase 2 is a pure extension that consumes Phase 1's API without modifying it.