Merge "Look up phone numbers associated with a NotificationRecord's contact URI." into sc-v2-dev am: c69d39d72e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17198933

Change-Id: I7a6da08a453893addc99144a02e6e84d8a340168
This commit is contained in:
Yuri Lin
2022-03-16 15:17:44 +00:00
committed by Automerger Merge Worker
6 changed files with 375 additions and 15 deletions

View File

@@ -200,6 +200,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;
@@ -1525,6 +1529,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;

View File

@@ -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() {

View File

@@ -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;
@@ -126,7 +127,7 @@ public class ZenModeFiltering {
}
protected void recordCall(NotificationRecord record) {
REPEAT_CALLERS.recordCall(mContext, extras(record));
REPEAT_CALLERS.recordCall(mContext, extras(record), record.getPhoneNumbers());
}
/**
@@ -325,6 +326,10 @@ public class ZenModeFiltering {
}
}
protected void cleanUpCallersAfter(long timeThreshold) {
REPEAT_CALLERS.cleanUpCallsAfter(timeThreshold);
}
private static class RepeatCallers {
// We keep a separate map per uri scheme to do more generous number-matching
// handling on telephone numbers specifically. For other inputs, we
@@ -333,7 +338,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);
@@ -341,7 +347,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) {
@@ -365,6 +371,23 @@ public class ZenModeFiltering {
}
}
// Clean up all calls that occurred after the given time.
// Used only for tests, to clean up after testing.
private synchronized void cleanUpCallsAfter(long timeThreshold) {
for (int i = mTelCalls.size() - 1; i >= 0; i--) {
final long time = mTelCalls.valueAt(i);
if (time > timeThreshold) {
mTelCalls.removeAt(i);
}
}
for (int j = mOtherCalls.size() - 1; j >= 0; j--) {
final long time = mOtherCalls.valueAt(j);
if (time > timeThreshold) {
mOtherCalls.removeAt(j);
}
}
}
private void setThresholdMinutes(Context context) {
if (mThresholdMinutes <= 0) {
mThresholdMinutes = context.getResources().getInteger(com.android.internal.R.integer
@@ -372,7 +395,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;
@@ -393,6 +417,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) {

View File

@@ -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;
@@ -1328,4 +1329,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"));
}
}

View File

@@ -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);

View File

@@ -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));
}
}