From 526f328e82edb3ec522b43cb3c6670c8c15e5626 Mon Sep 17 00:00:00 2001 From: navneetsingh-dev Date: Fri, 26 Jun 2026 23:01:00 +0530 Subject: [PATCH 1/2] Fix #82: Filter blocked contacts from notification query The getNotificationQuerySql method was previously fetching all unread incoming messages, including those from blocked contacts. This commit injects a ParticipantColumns.BLOCKED = 0 check to ensure blocked messages do not trigger system notifications or audio alerts. --- .../messaging/datamodel/data/ConversationMessageData.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/messaging/datamodel/data/ConversationMessageData.java b/src/com/android/messaging/datamodel/data/ConversationMessageData.java index 77e821f64..4303c2056 100644 --- a/src/com/android/messaging/datamodel/data/ConversationMessageData.java +++ b/src/com/android/messaging/datamodel/data/ConversationMessageData.java @@ -651,7 +651,9 @@ public static final String getNotificationQuerySql() { + MessageData.BUGLE_STATUS_INCOMING_COMPLETE + ", " + MessageData.BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD + ")" + " AND " - + DatabaseHelper.MessageColumns.SEEN + " = 0)" + + DatabaseHelper.MessageColumns.SEEN + " = 0" + + " AND " + + DatabaseHelper.PARTICIPANTS_TABLE + "." + ParticipantColumns.BLOCKED + " = 0)" + ")" + NOTIFICATION_QUERY_SQL_GROUP_BY; } From 248d62995d6a7992a7832e1caeeb75bf960b8114 Mon Sep 17 00:00:00 2001 From: navneetsingh-dev Date: Wed, 15 Jul 2026 12:56:24 +0530 Subject: [PATCH 2/2] feat: Gate audio, secondary users, and MMS notifications for blocked senders --- gradle/verification-keyring.gpg | 0 gradle/verification-metadata.xml | 16 +++++++ settings.gradle.kts | 3 ++ .../datamodel/BugleNotifications.java | 46 ++++++++++++++++--- .../action/ProcessDownloadedMmsAction.java | 17 +++---- .../messaging/receiver/SmsReceiver.java | 17 ++++++- 6 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 gradle/verification-keyring.gpg diff --git a/gradle/verification-keyring.gpg b/gradle/verification-keyring.gpg new file mode 100644 index 000000000..e69de29bb diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 38abfa399..1a2acbe77 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -12874,6 +12874,22 @@ + + + + + + + + + + + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 3d8f30d48..004a112c1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,6 +5,9 @@ pluginManagement { gradlePluginPortal() } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { diff --git a/src/com/android/messaging/datamodel/BugleNotifications.java b/src/com/android/messaging/datamodel/BugleNotifications.java index 23c532fce..ea62c1b3c 100644 --- a/src/com/android/messaging/datamodel/BugleNotifications.java +++ b/src/com/android/messaging/datamodel/BugleNotifications.java @@ -134,7 +134,7 @@ public static void update(final String conversationId, final int coverage) { LogUtil.v(TAG, "Update: conversationId = " + conversationId + " coverage = " + coverage); } - Assert.isNotMainThread(); + Assert.isNotMainThread(); if (!PhoneUtils.getDefault().isDefaultSmsApp()) { LogUtil.d(TAG, "Skipping notification: not the default SMS app"); @@ -157,10 +157,42 @@ public static void update(final String conversationId, final int coverage) { private static void createMessageNotification(final String conversationId) { final MessageNotificationState state = MessageNotificationState.getNotificationState(); final boolean softSound = DataModel.get().isNewMessageObservable(conversationId); + if (state == null) { if (softSound && !TextUtils.isEmpty(conversationId)) { - final Uri ringtoneUri = getNotificationRingtoneUriForConversationId(conversationId); - playObservableConversationNotificationSound(ringtoneUri); + + boolean isBlocked = false; + + // 1. Access the database to check the participant's blocked status + final DatabaseWrapper db = DataModel.get().getDatabase(); + + // 2. Query the participant table for this specific conversation ID + final String query = "SELECT " + DatabaseHelper.ParticipantColumns.BLOCKED + + " FROM " + DatabaseHelper.PARTICIPANTS_TABLE + + " INNER JOIN " + DatabaseHelper.CONVERSATION_PARTICIPANTS_TABLE + + " ON " + DatabaseHelper.PARTICIPANTS_TABLE + "." + DatabaseHelper.ParticipantColumns._ID + + " = " + DatabaseHelper.CONVERSATION_PARTICIPANTS_TABLE + "." + DatabaseHelper.ConversationParticipantsColumns.PARTICIPANT_ID + + " WHERE " + DatabaseHelper.CONVERSATION_PARTICIPANTS_TABLE + "." + DatabaseHelper.ConversationParticipantsColumns.CONVERSATION_ID + " = ?"; + + try (android.database.Cursor cursor = db.rawQuery(query, new String[] { conversationId })) { + if (cursor != null) { + while (cursor.moveToNext()) { + // If any participant in this conversation has BLOCKED = 1, flag it + if (cursor.getInt(0) == 1) { + isBlocked = true; + break; + } + } + } + } + + // 3. The Gatekeeper: Only play the sound if the conversation is NOT blocked + if (!isBlocked) { + final Uri ringtoneUri = getNotificationRingtoneUriForConversationId(conversationId); + playObservableConversationNotificationSound(ringtoneUri); + } else { + LogUtil.v(TAG, "Suppressed audio for blocked observable conversation."); + } } return; } @@ -232,7 +264,7 @@ private static Uri getNotificationRingtoneUriForConversationId(final String conv * @param conversationId The conversation id (optional) */ private static String buildNotificationTag(final String name, - final String conversationId) { + final String conversationId) { final Context context = Factory.get().getApplicationContext(); if (conversationId != null) { return context.getPackageName() + name + ":" + conversationId; @@ -489,7 +521,7 @@ private static void playObservableConversationNotificationSound(final Uri ringto final boolean silenced = audioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; if (silenced) { - return; + return; } final NotificationPlayer player = new NotificationPlayer(LogUtil.BUGLE_TAG); @@ -522,12 +554,12 @@ public static void markMessagesAsRead(final String conversationId, final boolean } public static void notifyEmergencySmsFailed(final String emergencyNumber, - final String conversationId) { + final String conversationId) { final Context context = Factory.get().getApplicationContext(); final CharSequence line1 = MessageNotificationState.applyWarningTextColor(context, context.getString(R.string.notification_emergency_send_failure_line1, - emergencyNumber)); + emergencyNumber)); final String line2 = context.getString(R.string.notification_emergency_send_failure_line2, emergencyNumber); final PendingIntent destinationIntent = UIIntents.get() diff --git a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java index 21fdef56b..2ba2764af 100644 --- a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java +++ b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java @@ -136,10 +136,10 @@ public static void processMessageDownloaded(final int resultCode, final Bundle e // This is called for fast failing downloading (due to airplane mode or mobile data ) public static void processMessageDownloadFastFailed(final String messageId, - final Uri notificationUri, final String conversationId, final String participantId, - final String contentLocation, final int subId, final String subPhoneNumber, - final int statusIfFailed, final boolean autoDownload, final String transactionId, - final int resultCode) { + final Uri notificationUri, final String conversationId, final String participantId, + final String contentLocation, final int subId, final String subPhoneNumber, + final int statusIfFailed, final boolean autoDownload, final String transactionId, + final int resultCode) { Assert.notNull(messageId); Assert.notNull(notificationUri); Assert.notNull(conversationId); @@ -163,8 +163,8 @@ public static void processMessageDownloadFastFailed(final String messageId, } public static void processDownloadActionFailure(final String messageId, final int status, - final int rawStatus, final String conversationId, final String participantId, - final int statusIfFailed, final int subId, final String transactionId) { + final int rawStatus, final String conversationId, final String participantId, + final int statusIfFailed, final int subId, final String transactionId) { Assert.notNull(messageId); Assert.notNull(conversationId); Assert.notNull(participantId); @@ -184,7 +184,7 @@ public static void processDownloadActionFailure(final String messageId, final in } public static void sendDeferredRespStatus(final String messageId, final String transactionId, - final String contentLocation, final int subId) { + final String contentLocation, final int subId) { final ProcessDownloadedMmsAction action = new ProcessDownloadedMmsAction(); final Bundle params = action.actionParameters; params.putString(KEY_MESSAGE_ID, messageId); @@ -473,7 +473,8 @@ private MessageData processResult(final int status, final int rawStatus, final U // TODO: Also write these values to the telephony provider mms.mRead = messageInFocusedConversation; - mms.mSeen = messageInObservableConversation; + // Align with standard SMS: seen = read || messageInObservableConversation || blocked + mms.mSeen = mms.mRead || messageInObservableConversation || blockedSender; // Translate to our format message = MmsUtils.createMmsMessage(mms, conversationId, senderParticipantId, diff --git a/src/com/android/messaging/receiver/SmsReceiver.java b/src/com/android/messaging/receiver/SmsReceiver.java index d96407d9b..3ae511b6f 100644 --- a/src/com/android/messaging/receiver/SmsReceiver.java +++ b/src/com/android/messaging/receiver/SmsReceiver.java @@ -153,8 +153,23 @@ public void onReceive(final Context context, final Intent intent) { (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action) || // TODO: update this with the actual constant from Telephony "android.provider.Telephony.MMS_DOWNLOADED".equals(action))) { - postNewMessageSecondaryUserNotification(); + boolean isSenderBlocked = false; + final android.telephony.SmsMessage[] messages = getMessagesFromIntent(intent); + if (messages != null && messages.length > 0) { + final String senderAddress = messages[0].getOriginatingAddress(); + if (senderAddress != null) { + final com.android.messaging.datamodel.DatabaseWrapper db = com.android.messaging.datamodel.DataModel.get().getDatabase(); + isSenderBlocked = com.android.messaging.datamodel.BugleDatabaseOperations.isBlockedDestination(db, senderAddress); + + } + } + if (isSenderBlocked) { + postNewMessageSecondaryUserNotification(); + }else{ + LogUtil.v(TAG,"SmsReceviver:Suppressed secondary user notification for blocked sender"); + } } + } }