From c7dab0feab6623711124ed24051b732715ca0bb5 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Thu, 24 Feb 2022 15:42:37 -0500 Subject: [PATCH] Check separate phone numbers in RepeatCallers.isRepeat In the case where a new call notification comes in with just a contact uri, and the telephone number associated with the call was previously received, we need to also try to check any looked up phone numbers. Bug: 219562304 Test: ZenModeFilteringTest, NotificationManagerTest Change-Id: I81b7bf63b7cfd36b8765dc39c225103d4cd48587 Merged-In: I81b7bf63b7cfd36b8765dc39c225103d4cd48587 (cherry picked from commit 49633a3cef3cbd0ce03171c7973a07ce4ebcb0d6) --- .../server/notification/ZenModeFiltering.java | 69 ++++++++++++++----- .../notification/ZenModeFilteringTest.java | 42 +++++++++-- 2 files changed, 88 insertions(+), 23 deletions(-) diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java index 45e0080c69fb1..458bef3a2567e 100644 --- a/services/core/java/com/android/server/notification/ZenModeFiltering.java +++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java @@ -106,7 +106,7 @@ public class ZenModeFiltering { if (zen == Global.ZEN_MODE_ALARMS) return false; // not an alarm if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { if (consolidatedPolicy.allowRepeatCallers() - && REPEAT_CALLERS.isRepeat(context, extras)) { + && REPEAT_CALLERS.isRepeat(context, extras, null)) { return true; } if (!consolidatedPolicy.allowCalls()) return false; // no other calls get through @@ -215,7 +215,8 @@ public class ZenModeFiltering { } if (isCall(record)) { if (policy.allowRepeatCallers() - && REPEAT_CALLERS.isRepeat(mContext, extras(record))) { + && REPEAT_CALLERS.isRepeat( + mContext, extras(record), record.getPhoneNumbers())) { ZenLog.traceNotIntercepted(record, "repeatCaller"); return false; } @@ -336,6 +337,9 @@ public class ZenModeFiltering { private final ArrayMap mOtherCalls = new ArrayMap<>(); private int mThresholdMinutes; + // Record all people URIs in the extras bundle as well as the provided phoneNumbers set + // as callers. The phoneNumbers set is used to pass in any additional phone numbers + // associated with the people URIs as separately retrieved from contacts. private synchronized void recordCall(Context context, Bundle extras, ArraySet phoneNumbers) { setThresholdMinutes(context); @@ -348,7 +352,13 @@ public class ZenModeFiltering { recordCallers(extraPeople, phoneNumbers, now); } - private synchronized boolean isRepeat(Context context, Bundle extras) { + // Determine whether any people in the provided extras bundle or phone number set is + // a repeat caller. The extras bundle contains the people associated with a specific + // notification, and will suffice for most callers; the phoneNumbers array may be used + // to additionally check any specific phone numbers previously retrieved from contacts + // associated with the people in the extras bundle. + private synchronized boolean isRepeat(Context context, Bundle extras, + ArraySet phoneNumbers) { setThresholdMinutes(context); if (mThresholdMinutes <= 0 || extras == null) return false; final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); @@ -356,7 +366,7 @@ public class ZenModeFiltering { final long now = System.currentTimeMillis(); cleanUp(mTelCalls, now); cleanUp(mOtherCalls, now); - return checkCallers(context, extraPeople); + return checkCallers(context, extraPeople, phoneNumbers); } private synchronized void cleanUp(ArrayMap calls, long now) { @@ -419,7 +429,31 @@ public class ZenModeFiltering { } } - private synchronized boolean checkCallers(Context context, String[] people) { + // helper function to check mTelCalls array for a number, and also check its decoded + // version + private synchronized boolean checkForNumber(String number, String defaultCountryCode) { + if (mTelCalls.containsKey(number)) { + // check directly via map first + return true; + } else { + // see if a number that matches via areSameNumber exists + String numberToCheck = Uri.decode(number); + if (numberToCheck != null) { + for (String prev : mTelCalls.keySet()) { + if (PhoneNumberUtils.areSamePhoneNumber( + numberToCheck, prev, defaultCountryCode)) { + return true; + } + } + } + } + return false; + } + + // Check whether anyone in the provided array of people URIs or phone number set matches a + // previously recorded phone call. + private synchronized boolean checkCallers(Context context, String[] people, + ArraySet phoneNumbers) { // get the default country code for checking telephone numbers final String defaultCountryCode = context.getSystemService(TelephonyManager.class).getNetworkCountryIso(); @@ -429,20 +463,8 @@ public class ZenModeFiltering { final Uri uri = Uri.parse(person); if ("tel".equals(uri.getScheme())) { String number = uri.getSchemeSpecificPart(); - if (mTelCalls.containsKey(number)) { - // check directly via map first + if (checkForNumber(number, defaultCountryCode)) { return true; - } else { - // see if a number that matches via areSameNumber exists - String numberToCheck = Uri.decode(number); - if (numberToCheck != null) { - for (String prev : mTelCalls.keySet()) { - if (PhoneNumberUtils.areSamePhoneNumber( - numberToCheck, prev, defaultCountryCode)) { - return true; - } - } - } } } else { if (mOtherCalls.containsKey(person)) { @@ -450,6 +472,17 @@ public class ZenModeFiltering { } } } + + // also check any passed-in phone numbers + if (phoneNumbers != null) { + for (String num : phoneNumbers) { + if (checkForNumber(num, defaultCountryCode)) { + return true; + } + } + } + + // no matches return false; } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java index abcc8c1e99cb9..8ac729e294243 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java @@ -121,7 +121,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { // Create a notification record with the people String array as the // bundled extras, and the numbers ArraySet as additional phone numbers. - private NotificationRecord getRecordWithPeopleInfo(String[] people, + private NotificationRecord getCallRecordWithPeopleInfo(String[] people, ArraySet numbers) { // set up notification record NotificationRecord r = mock(NotificationRecord.class); @@ -131,6 +131,8 @@ public class ZenModeFilteringTest extends UiServiceTestCase { when(sbn.getNotification()).thenReturn(notification); when(r.getSbn()).thenReturn(sbn); when(r.getPhoneNumbers()).thenReturn(numbers); + when(r.getCriticality()).thenReturn(CriticalNotificationExtractor.NORMAL); + when(r.isCategory(CATEGORY_CALL)).thenReturn(true); return r; } @@ -349,12 +351,42 @@ public class ZenModeFilteringTest extends UiServiceTestCase { assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r)); } + @Test + public void testRepeatCallers_checksPhoneNumbers() { + // set up telephony manager behavior + when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); + + // first, record a phone call from a telephone number + String[] callNumber = new String[]{"tel:12345678910"}; + mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(callNumber, null)); + + // set up policy to only allow repeat callers + Policy policy = new Policy( + PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); + + // make sure that a record with the phone number in extras is correctly allowed through + NotificationRecord r = getCallRecordWithPeopleInfo(callNumber, null); + assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r)); + + // make sure that a record with the phone number in the phone numbers array is also + // allowed through + NotificationRecord r2 = getCallRecordWithPeopleInfo(new String[]{"some_contact_uri"}, + new ArraySet<>(new String[]{"12345678910"})); + assertFalse(mZenModeFiltering.shouldIntercept( + ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r2)); + + // A record with the phone number in neither of the above should be intercepted + NotificationRecord r3 = getCallRecordWithPeopleInfo(new String[]{"tel:10987654321"}, + new ArraySet<>(new String[]{"15555555555"})); + assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r3)); + } + @Test public void testMatchesCallFilter_repeatCallers_directMatch() { // after calls given an email with an exact string match, make sure that // matchesCallFilter returns the right thing String[] mailSource = new String[]{"mailto:hello.world"}; - mZenModeFiltering.recordCall(getRecordWithPeopleInfo(mailSource, null)); + mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(mailSource, null)); // set up policy to only allow repeat callers Policy policy = new Policy( @@ -377,7 +409,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:+1-617-555-1212"}; - mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null)); + mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(telSource, null)); // set up policy to only allow repeat callers Policy policy = new Policy( @@ -421,7 +453,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:%2B16175551212"}; - mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null)); + mZenModeFiltering.recordCall(getCallRecordWithPeopleInfo(telSource, null)); // set up policy to only allow repeat callers Policy policy = new Policy( @@ -468,7 +500,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { String[] contactSource = new String[]{"content://contacts/lookup/uri-here"}; ArraySet contactNumbers = new ArraySet<>( new String[]{"1-617-555-1212", "1-617-555-3434"}); - NotificationRecord record = getRecordWithPeopleInfo(contactSource, contactNumbers); + NotificationRecord record = getCallRecordWithPeopleInfo(contactSource, contactNumbers); record.mergePhoneNumbers(contactNumbers); mZenModeFiltering.recordCall(record);