diff --git a/apps/android/app/src/main/java/ai/openclaw/app/NotificationForwardingPolicy.kt b/apps/android/app/src/main/java/ai/openclaw/app/NotificationForwardingPolicy.kt index 21ff2e2a6f32..4081c0d7fc77 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/NotificationForwardingPolicy.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/NotificationForwardingPolicy.kt @@ -27,6 +27,7 @@ internal data class NotificationForwardingPolicy( val quietEnd: String, val maxEventsPerMinute: Int, val sessionKey: String?, + val selfPackageName: String = "", ) /** Applies the operator-configured package allow/block list after trimming input. */ @@ -35,6 +36,10 @@ internal fun NotificationForwardingPolicy.allowsPackage(packageName: String): Bo if (normalized.isEmpty()) { return false } + val self = selfPackageName.trim() + if (self.isNotEmpty() && normalized == self) { + return false + } return when (mode) { NotificationPackageFilterMode.Allowlist -> packages.contains(normalized) NotificationPackageFilterMode.Blocklist -> !packages.contains(normalized) diff --git a/apps/android/app/src/main/java/ai/openclaw/app/SecurePrefs.kt b/apps/android/app/src/main/java/ai/openclaw/app/SecurePrefs.kt index fa3b35c11a14..2157fca76b4d 100644 --- a/apps/android/app/src/main/java/ai/openclaw/app/SecurePrefs.kt +++ b/apps/android/app/src/main/java/ai/openclaw/app/SecurePrefs.kt @@ -313,6 +313,7 @@ class SecurePrefs( quietEnd = quietEnd, maxEventsPerMinute = maxEvents.coerceAtLeast(1), sessionKey = sessionKey, + selfPackageName = normalizedAppPackage, ) } diff --git a/apps/android/app/src/test/java/ai/openclaw/app/NotificationForwardingPolicyTest.kt b/apps/android/app/src/test/java/ai/openclaw/app/NotificationForwardingPolicyTest.kt index 0388064d7ea1..e36995b11550 100644 --- a/apps/android/app/src/test/java/ai/openclaw/app/NotificationForwardingPolicyTest.kt +++ b/apps/android/app/src/test/java/ai/openclaw/app/NotificationForwardingPolicyTest.kt @@ -77,6 +77,25 @@ class NotificationForwardingPolicyTest { assertFalse(policy.allowsPackage("com.other.app")) } + @Test + fun allowsPackage_neverForwardsSelfPackageEvenInAllowlist() { + val policy = + NotificationForwardingPolicy( + enabled = true, + mode = NotificationPackageFilterMode.Allowlist, + packages = setOf("ai.openclaw.app", "com.other.app"), + quietHoursEnabled = false, + quietStart = "22:00", + quietEnd = "07:00", + maxEventsPerMinute = 20, + sessionKey = null, + selfPackageName = "ai.openclaw.app", + ) + + assertFalse(policy.allowsPackage("ai.openclaw.app")) + assertTrue(policy.allowsPackage("com.other.app")) + } + @Test fun isWithinQuietHours_handlesWindowCrossingMidnight() { val policy = diff --git a/apps/android/app/src/test/java/ai/openclaw/app/SecurePrefsNotificationForwardingTest.kt b/apps/android/app/src/test/java/ai/openclaw/app/SecurePrefsNotificationForwardingTest.kt index 891d0171bc7f..7a47c33c6b83 100644 --- a/apps/android/app/src/test/java/ai/openclaw/app/SecurePrefsNotificationForwardingTest.kt +++ b/apps/android/app/src/test/java/ai/openclaw/app/SecurePrefsNotificationForwardingTest.kt @@ -128,4 +128,20 @@ class SecurePrefsNotificationForwardingTest { assertFalse(policy.enabled) assertEquals(NotificationPackageFilterMode.Blocklist, policy.mode) } + + @Test + fun getNotificationForwardingPolicy_blocksSelfPackageInAllowlistMode() { + val context = RuntimeEnvironment.getApplication() + val plainPrefs = context.getSharedPreferences("openclaw.node", Context.MODE_PRIVATE) + plainPrefs.edit().clear().commit() + + val prefs = SecurePrefs(context) + prefs.setNotificationForwardingMode(NotificationPackageFilterMode.Allowlist) + prefs.setNotificationForwardingPackages(listOf("ai.openclaw.app", "com.other.app")) + + val policy = prefs.getNotificationForwardingPolicy(appPackageName = "ai.openclaw.app") + + assertFalse(policy.allowsPackage("ai.openclaw.app")) + assertTrue(policy.allowsPackage("com.other.app")) + } }