diff options
6 files changed, 354 insertions, 15 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 96d7521439cd..70e968fa8bdc 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -201,6 +201,10 @@ public final class NotificationRecord { private boolean mIsAppImportanceLocked; private ArraySet<Uri> mGrantableUris; + // Storage for phone numbers that were found to be associated with + // contacts in this notification. + private ArraySet<String> mPhoneNumbers; + // Whether this notification record should have an update logged the next time notifications // are sorted. private boolean mPendingLogUpdate = false; @@ -1547,6 +1551,26 @@ public final class NotificationRecord { return mPendingLogUpdate; } + /** + * Merge the given set of phone numbers into the list of phone numbers that + * are cached on this notification record. + */ + public void mergePhoneNumbers(ArraySet<String> phoneNumbers) { + // if the given phone numbers are null or empty then don't do anything + if (phoneNumbers == null || phoneNumbers.size() == 0) { + return; + } + // initialize if not already + if (mPhoneNumbers == null) { + mPhoneNumbers = new ArraySet<>(); + } + mPhoneNumbers.addAll(phoneNumbers); + } + + public ArraySet<String> getPhoneNumbers() { + return mPhoneNumbers; + } + @VisibleForTesting static final class Light { public final int color; diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index d7bc3bb8af28..dc4d04feab72 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -68,7 +68,10 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private static final boolean ENABLE_PEOPLE_VALIDATOR = true; private static final String SETTING_ENABLE_PEOPLE_VALIDATOR = "validate_notification_people_enabled"; - private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.STARRED }; + private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.LOOKUP_KEY, + Contacts.STARRED, Contacts.HAS_PHONE_NUMBER }; + private static final String[] PHONE_LOOKUP_PROJECTION = + { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }; private static final int MAX_PEOPLE = 10; private static final int PEOPLE_CACHE_SIZE = 200; @@ -409,6 +412,35 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return lookupResult; } + @VisibleForTesting + // Performs a contacts search using searchContacts, and then follows up by looking up + // any phone numbers associated with the resulting contact information and merge those + // into the lookup result as well. Will have no additional effect if the contact does + // not have any phone numbers. + LookupResult searchContactsAndLookupNumbers(Context context, Uri lookupUri) { + LookupResult lookupResult = searchContacts(context, lookupUri); + String phoneLookupKey = lookupResult.getPhoneLookupKey(); + if (phoneLookupKey != null) { + String selection = Contacts.LOOKUP_KEY + " = ?"; + String[] selectionArgs = new String[] { phoneLookupKey }; + try (Cursor cursor = context.getContentResolver().query( + ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONE_LOOKUP_PROJECTION, + selection, selectionArgs, /* sortOrder= */ null)) { + if (cursor == null) { + Slog.w(TAG, "Cursor is null when querying contact phone number."); + return lookupResult; + } + + while (cursor.moveToNext()) { + lookupResult.mergePhoneNumber(cursor); + } + } catch (Throwable t) { + Slog.w(TAG, "Problem getting content resolver or querying phone numbers.", t); + } + } + return lookupResult; + } + private void addWorkContacts(LookupResult lookupResult, Context context, Uri corpLookupUri) { final int workUserId = findWorkUserId(context); if (workUserId == -1) { @@ -454,6 +486,9 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private final long mExpireMillis; private float mAffinity = NONE; + private boolean mHasPhone = false; + private String mPhoneLookupKey = null; + private ArraySet<String> mPhoneNumbers = new ArraySet<>(); public LookupResult() { mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS; @@ -473,6 +508,15 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { Slog.i(TAG, "invalid cursor: no _ID"); } + // Lookup key for potentially looking up contact phone number later + final int lookupKeyIdx = cursor.getColumnIndex(Contacts.LOOKUP_KEY); + if (lookupKeyIdx >= 0) { + mPhoneLookupKey = cursor.getString(lookupKeyIdx); + if (DEBUG) Slog.d(TAG, "contact LOOKUP_KEY is: " + mPhoneLookupKey); + } else { + if (DEBUG) Slog.d(TAG, "invalid cursor: no LOOKUP_KEY"); + } + // Starred final int starIdx = cursor.getColumnIndex(Contacts.STARRED); if (starIdx >= 0) { @@ -484,6 +528,39 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } else { if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED"); } + + // whether a phone number is present + final int hasPhoneIdx = cursor.getColumnIndex(Contacts.HAS_PHONE_NUMBER); + if (hasPhoneIdx >= 0) { + mHasPhone = cursor.getInt(hasPhoneIdx) != 0; + if (DEBUG) Slog.d(TAG, "contact HAS_PHONE_NUMBER is: " + mHasPhone); + } else { + if (DEBUG) Slog.d(TAG, "invalid cursor: no HAS_PHONE_NUMBER"); + } + } + + // Returns the phone lookup key that is cached in this result, or null + // if the contact has no known phone info. + public String getPhoneLookupKey() { + if (!mHasPhone) { + return null; + } + return mPhoneLookupKey; + } + + // Merge phone number found in this lookup and store it in mPhoneNumbers. + public void mergePhoneNumber(Cursor cursor) { + final int phoneNumIdx = cursor.getColumnIndex( + ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER); + if (phoneNumIdx >= 0) { + mPhoneNumbers.add(cursor.getString(phoneNumIdx)); + } else { + if (DEBUG) Slog.d(TAG, "invalid cursor: no NORMALIZED_NUMBER"); + } + } + + public ArraySet<String> getPhoneNumbers() { + return mPhoneNumbers; } private boolean isExpired() { @@ -509,6 +586,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { // Amount of time to wait for a result from the contacts db before rechecking affinity. private static final long LOOKUP_TIME = 1000; private float mContactAffinity = NONE; + private ArraySet<String> mPhoneNumbers = null; private NotificationRecord mRecord; private PeopleRankingReconsideration(Context context, String key, @@ -543,7 +621,9 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart()); } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle); - lookupResult = searchContacts(mContext, uri); + // only look up phone number if this is a contact lookup uri and thus isn't + // already directly a phone number. + lookupResult = searchContactsAndLookupNumbers(mContext, uri); } else { lookupResult = new LookupResult(); // invalid person for the cache if (!"name".equals(uri.getScheme())) { @@ -561,6 +641,13 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity()); } mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity()); + // merge any phone numbers found in this lookup result + if (lookupResult.getPhoneNumbers() != null) { + if (mPhoneNumbers == null) { + mPhoneNumbers = new ArraySet<>(); + } + mPhoneNumbers.addAll(lookupResult.getPhoneNumbers()); + } } else { if (DEBUG) Slog.d(TAG, "lookupResult is null"); } @@ -581,6 +668,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { float affinityBound = operand.getContactAffinity(); operand.setContactAffinity(Math.max(mContactAffinity, affinityBound)); if (VERBOSE) Slog.i(TAG, "final affinity: " + operand.getContactAffinity()); + operand.mergePhoneNumbers(mPhoneNumbers); } public float getContactAffinity() { diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java index 29aad63a1f4b..d04b3315fcec 100644 --- a/services/core/java/com/android/server/notification/ZenModeFiltering.java +++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java @@ -33,6 +33,7 @@ import android.telecom.TelecomManager; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.messages.nano.SystemMessageProto; @@ -140,7 +141,7 @@ public class ZenModeFiltering { } protected void recordCall(NotificationRecord record) { - REPEAT_CALLERS.recordCall(mContext, extras(record)); + REPEAT_CALLERS.recordCall(mContext, extras(record), record.getPhoneNumbers()); } /** @@ -351,7 +352,8 @@ public class ZenModeFiltering { private final ArrayMap<String, Long> mOtherCalls = new ArrayMap<>(); private int mThresholdMinutes; - private synchronized void recordCall(Context context, Bundle extras) { + private synchronized void recordCall(Context context, Bundle extras, + ArraySet<String> phoneNumbers) { setThresholdMinutes(context); if (mThresholdMinutes <= 0 || extras == null) return; final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras); @@ -359,7 +361,7 @@ public class ZenModeFiltering { final long now = System.currentTimeMillis(); cleanUp(mTelCalls, now); cleanUp(mOtherCalls, now); - recordCallers(extraPeople, now); + recordCallers(extraPeople, phoneNumbers, now); } private synchronized boolean isRepeat(Context context, Bundle extras) { @@ -407,7 +409,8 @@ public class ZenModeFiltering { } } - private synchronized void recordCallers(String[] people, long now) { + private synchronized void recordCallers(String[] people, ArraySet<String> phoneNumbers, + long now) { for (int i = 0; i < people.length; i++) { String person = people[i]; if (person == null) continue; @@ -428,6 +431,14 @@ public class ZenModeFiltering { mOtherCalls.put(person, now); } } + + // record any additional numbers from the notification record if + // provided; these are in the format of just a phone number string + if (phoneNumbers != null) { + for (String num : phoneNumbers) { + mTelCalls.put(num, now); + } + } } private synchronized boolean checkCallers(Context context, String[] people) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 75420337a63e..7e27e5438a0c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -66,6 +66,7 @@ import android.os.Vibrator; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.StatusBarNotification; +import android.util.ArraySet; import android.widget.RemoteViews; import androidx.test.filters.SmallTest; @@ -1304,4 +1305,45 @@ public class NotificationRecordTest extends UiServiceTestCase { assertFalse(record.isConversation()); } + + @Test + public void mergePhoneNumbers_nulls() { + // make sure nothing dies if we just don't have any phone numbers + StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */, + true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, + false /* lights */, false /* defaultLights */, null /* group */); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + + // by default, no phone numbers + assertNull(record.getPhoneNumbers()); + + // nothing happens if we attempt to merge phone numbers but there aren't any + record.mergePhoneNumbers(null); + assertNull(record.getPhoneNumbers()); + } + + @Test + public void mergePhoneNumbers_addNumbers() { + StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */, + true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, + false /* lights */, false /* defaultLights */, null /* group */); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel); + + // by default, no phone numbers + assertNull(record.getPhoneNumbers()); + + // make sure it behaves properly when we merge in some real content + record.mergePhoneNumbers(new ArraySet<>( + new String[]{"16175551212", "16175552121"})); + assertTrue(record.getPhoneNumbers().contains("16175551212")); + assertTrue(record.getPhoneNumbers().contains("16175552121")); + assertFalse(record.getPhoneNumbers().contains("16175553434")); + + // now merge in a new number, make sure old ones are still there and the new one + // is also there + record.mergePhoneNumbers(new ArraySet<>(new String[]{"16175553434"})); + assertTrue(record.getPhoneNumbers().contains("16175551212")); + assertTrue(record.getPhoneNumbers().contains("16175552121")); + assertTrue(record.getPhoneNumbers().contains("16175553434")); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java index 0bf105d62053..0552a8350329 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java @@ -19,8 +19,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -29,6 +34,7 @@ import android.app.Person; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; +import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.UserManager; @@ -43,6 +49,8 @@ import com.android.server.UiServiceTestCase; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; @@ -240,6 +248,118 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { assertFalse(ContentProvider.uriHasUserId(queryUri.getValue())); } + @Test + public void testMergePhoneNumbers_noPhoneNumber() { + // If merge phone number is called but the contacts lookup turned up no available + // phone number (HAS_PHONE_NUMBER is false), then no query should happen. + + // setup of various bits required for querying + final Context mockContext = mock(Context.class); + final ContentResolver mockContentResolver = mock(ContentResolver.class); + when(mockContext.getContentResolver()).thenReturn(mockContentResolver); + final int contactId = 12345; + final Uri lookupUri = Uri.withAppendedPath( + ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId)); + + // when the contact is looked up, we return a cursor that has one entry whose info is: + // _ID: 1 + // LOOKUP_KEY: "testlookupkey" + // STARRED: 0 + // HAS_PHONE_NUMBER: 0 + Cursor cursor = makeMockCursor(1, "testlookupkey", 0, 0); + when(mockContentResolver.query(any(), any(), any(), any(), any())).thenReturn(cursor); + + // call searchContacts and then mergePhoneNumbers, make sure we never actually + // query the content resolver for a phone number + new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); + verify(mockContentResolver, never()).query( + eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), + eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }), + contains(ContactsContract.Contacts.LOOKUP_KEY), + any(), // selection args + isNull()); // sort order + } + + @Test + public void testMergePhoneNumbers_hasNumber() { + // If merge phone number is called and the contact lookup has a phone number, + // make sure there's then a subsequent query for the phone number. + + // setup of various bits required for querying + final Context mockContext = mock(Context.class); + final ContentResolver mockContentResolver = mock(ContentResolver.class); + when(mockContext.getContentResolver()).thenReturn(mockContentResolver); + final int contactId = 12345; + final Uri lookupUri = Uri.withAppendedPath( + ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId)); + + // when the contact is looked up, we return a cursor that has one entry whose info is: + // _ID: 1 + // LOOKUP_KEY: "testlookupkey" + // STARRED: 0 + // HAS_PHONE_NUMBER: 1 + Cursor cursor = makeMockCursor(1, "testlookupkey", 0, 1); + + // make sure to add some specifics so this cursor is only returned for the + // contacts database lookup. + when(mockContentResolver.query(eq(lookupUri), any(), + isNull(), isNull(), isNull())).thenReturn(cursor); + + // in the case of a phone lookup, return null cursor; that's not an error case + // and we're not checking the actual storing of the phone data here. + when(mockContentResolver.query(eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), + eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }), + contains(ContactsContract.Contacts.LOOKUP_KEY), + any(), isNull())).thenReturn(null); + + // call searchContacts and then mergePhoneNumbers, and check that we query + // once for the + new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); + verify(mockContentResolver, times(1)).query( + eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), + eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }), + contains(ContactsContract.Contacts.LOOKUP_KEY), + eq(new String[] { "testlookupkey" }), // selection args + isNull()); // sort order + } + + // Creates a cursor that points to one item of Contacts data with the specified + // columns. + private Cursor makeMockCursor(int id, String lookupKey, int starred, int hasPhone) { + Cursor mockCursor = mock(Cursor.class); + when(mockCursor.moveToFirst()).thenReturn(true); + doAnswer(new Answer<Boolean>() { + boolean mAccessed = false; + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + if (!mAccessed) { + mAccessed = true; + return true; + } + return false; + } + + }).when(mockCursor).moveToNext(); + + // id + when(mockCursor.getColumnIndex(ContactsContract.Contacts._ID)).thenReturn(0); + when(mockCursor.getInt(0)).thenReturn(id); + + // lookup key + when(mockCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)).thenReturn(1); + when(mockCursor.getString(1)).thenReturn(lookupKey); + + // starred + when(mockCursor.getColumnIndex(ContactsContract.Contacts.STARRED)).thenReturn(2); + when(mockCursor.getInt(2)).thenReturn(starred); + + // has phone number + when(mockCursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)).thenReturn(3); + when(mockCursor.getInt(3)).thenReturn(hasPhone); + + return mockCursor; + } + private void assertStringArrayEquals(String message, String[] expected, String[] result) { String expectedString = Arrays.toString(expected); String resultString = Arrays.toString(result); 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 0f18cc61a95a..abcc8c1e99cb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java @@ -50,11 +50,13 @@ import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.ArraySet; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.util.NotificationMessagingUtil; import com.android.server.UiServiceTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -72,6 +74,8 @@ public class ZenModeFilteringTest extends UiServiceTestCase { @Mock private TelephonyManager mTelephonyManager; + private long mTestStartTime; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -79,6 +83,13 @@ public class ZenModeFilteringTest extends UiServiceTestCase { // for repeat callers / matchesCallFilter mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); + mTestStartTime = System.currentTimeMillis(); + } + + @After + public void tearDown() { + // make sure to get rid of any data stored in repeat callers + mZenModeFiltering.cleanUpCallersAfter(mTestStartTime); } private NotificationRecord getNotificationRecord() { @@ -108,7 +119,10 @@ public class ZenModeFilteringTest extends UiServiceTestCase { return extras; } - private NotificationRecord getNotificationRecordWithPeople(String[] people) { + // 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, + ArraySet<String> numbers) { // set up notification record NotificationRecord r = mock(NotificationRecord.class); StatusBarNotification sbn = mock(StatusBarNotification.class); @@ -116,6 +130,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { notification.extras = makeExtrasBundleWithPeople(people); when(sbn.getNotification()).thenReturn(notification); when(r.getSbn()).thenReturn(sbn); + when(r.getPhoneNumbers()).thenReturn(numbers); return r; } @@ -339,7 +354,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { // 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(getNotificationRecordWithPeople(mailSource)); + mZenModeFiltering.recordCall(getRecordWithPeopleInfo(mailSource, null)); // set up policy to only allow repeat callers Policy policy = new Policy( @@ -362,7 +377,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:+1-617-555-1212"}; - mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); + mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null)); // set up policy to only allow repeat callers Policy policy = new Policy( @@ -406,7 +421,7 @@ public class ZenModeFilteringTest extends UiServiceTestCase { when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); String[] telSource = new String[]{"tel:%2B16175551212"}; - mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource)); + mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null)); // set up policy to only allow repeat callers Policy policy = new Policy( @@ -419,25 +434,64 @@ public class ZenModeFilteringTest extends UiServiceTestCase { Bundle different1 = makeExtrasBundleWithPeople(new String[]{"tel:%2B16175553434"}); Bundle different2 = makeExtrasBundleWithPeople(new String[]{"tel:+16175553434"}); - assertTrue("same number should match", + assertTrue("same number 1 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same1, null, 0, 0)); - assertTrue("same number should match", + assertTrue("same number 2 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same2, null, 0, 0)); - assertTrue("same number should match", + assertTrue("same number 3 should match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, same3, null, 0, 0)); - assertFalse("different number should not match", + assertFalse("different number 1 should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different1, null, 0, 0)); - assertFalse("different number should not match", + assertFalse("different number 2 should not match", ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, UserHandle.SYSTEM, different2, null, 0, 0)); } + + @Test + public void testMatchesCallFilter_repeatCallers_viaRecordPhoneNumbers() { + // make sure that phone numbers that are passed in via the NotificationRecord's + // cached phone numbers field (from a contact lookup if the record is provided a contact + // uri) also get recorded in the repeat callers list. + + // set up telephony manager behavior + when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us"); + + String[] contactSource = new String[]{"content://contacts/lookup/uri-here"}; + ArraySet<String> contactNumbers = new ArraySet<>( + new String[]{"1-617-555-1212", "1-617-555-3434"}); + NotificationRecord record = getRecordWithPeopleInfo(contactSource, contactNumbers); + record.mergePhoneNumbers(contactNumbers); + mZenModeFiltering.recordCall(record); + + // set up policy to only allow repeat callers + Policy policy = new Policy( + PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE); + + // both phone numbers should register here + Bundle tel1 = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"}); + Bundle tel2 = makeExtrasBundleWithPeople(new String[]{"tel:16175553434"}); + Bundle different = makeExtrasBundleWithPeople(new String[]{"tel:16175555656"}); + + assertTrue("contact number 1 should match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + tel1, null, 0, 0)); + assertTrue("contact number 2 should match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + tel2, null, 0, 0)); + assertFalse("different number should not match", + ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS, + policy, UserHandle.SYSTEM, + different, null, 0, 0)); + } } |