Loading services/core/java/com/android/server/notification/ValidateNotificationPeople.java +105 −106 Original line number Diff line number Diff line Loading @@ -62,7 +62,7 @@ import java.util.concurrent.TimeUnit; public class ValidateNotificationPeople implements NotificationSignalExtractor { // Using a shorter log tag since setprop has a limit of 32chars on variable name. private static final String TAG = "ValidateNoPeople"; private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean ENABLE_PEOPLE_VALIDATOR = true; Loading Loading @@ -105,13 +105,12 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private int mEvictionCount; private NotificationUsageStats mUsageStats; @Override public void initialize(Context context, NotificationUsageStats usageStats) { if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); mUserToContextMap = new ArrayMap<>(); mBaseContext = context; mUsageStats = usageStats; mPeopleCache = new LruCache<>(PEOPLE_CACHE_SIZE); mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt( mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1); if (mEnabled) { Loading @@ -135,7 +134,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { // For tests: just do the setting of various local variables without actually doing work @VisibleForTesting protected void initForTests(Context context, NotificationUsageStats usageStats, LruCache<String, LookupResult> peopleCache) { LruCache peopleCache) { mUserToContextMap = new ArrayMap<>(); mBaseContext = context; mUsageStats = usageStats; Loading @@ -143,7 +142,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { mEnabled = true; } @Override public RankingReconsideration process(NotificationRecord record) { if (!mEnabled) { if (VERBOSE) Slog.i(TAG, "disabled"); Loading Loading @@ -274,7 +272,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId()); final LinkedList<String> pendingLookups = new LinkedList<>(); final LinkedList<String> pendingLookups = new LinkedList<String>(); int personIdx = 0; for (String handle : people) { if (TextUtils.isEmpty(handle)) continue; Loading Loading @@ -322,6 +320,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return Integer.toString(userId) + ":" + handle; } // VisibleForTesting public static String[] getExtraPeople(Bundle extras) { String[] peopleList = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE_LIST); String[] legacyPeople = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE); Loading Loading @@ -418,6 +417,101 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return null; } private LookupResult resolvePhoneContact(Context context, final String number) { Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); return searchContacts(context, phoneUri); } private LookupResult resolveEmailContact(Context context, final String email) { Uri numberUri = Uri.withAppendedPath( ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, Uri.encode(email)); return searchContacts(context, numberUri); } @VisibleForTesting LookupResult searchContacts(Context context, Uri lookupUri) { LookupResult lookupResult = new LookupResult(); final Uri corpLookupUri = ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri); if (corpLookupUri == null) { addContacts(lookupResult, context, lookupUri); } else { addWorkContacts(lookupResult, context, corpLookupUri); } 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) { Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri); return; } final Uri corpLookupUriWithUserId = ContentProvider.maybeAddUserId(corpLookupUri, workUserId); addContacts(lookupResult, context, corpLookupUriWithUserId); } /** Returns the user ID of the managed profile or -1 if none is found. */ private int findWorkUserId(Context context) { final UserManager userManager = context.getSystemService(UserManager.class); final int[] profileIds = userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true); for (int profileId : profileIds) { if (userManager.isManagedProfile(profileId)) { return profileId; } } return -1; } /** Modifies the given lookup result to add contacts found at the given URI. */ private void addContacts(LookupResult lookupResult, Context context, Uri uri) { try (Cursor c = context.getContentResolver().query( uri, LOOKUP_PROJECTION, null, null, null)) { if (c == null) { Slog.w(TAG, "Null cursor from contacts query."); return; } while (c.moveToNext()) { lookupResult.mergeContact(c); } } catch (Throwable t) { Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); } } @VisibleForTesting protected static class LookupResult { private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr Loading Loading @@ -525,18 +619,19 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } } @VisibleForTesting class PeopleRankingReconsideration extends RankingReconsideration { private class PeopleRankingReconsideration extends RankingReconsideration { private final LinkedList<String> mPendingLookups; private final Context mContext; // 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, LinkedList<String> pendingLookups) { super(key); super(key, LOOKUP_TIME); mContext = context; mPendingLookups = pendingLookups; } Loading @@ -547,7 +642,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { long timeStartMs = System.currentTimeMillis(); for (final String handle: mPendingLookups) { final String cacheKey = getCacheKey(mContext.getUserId(), handle); LookupResult lookupResult; LookupResult lookupResult = null; boolean cacheHit = false; synchronized (mPeopleCache) { lookupResult = mPeopleCache.get(cacheKey); Loading Loading @@ -608,102 +703,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } } private static LookupResult resolvePhoneContact(Context context, final String number) { Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); return searchContacts(context, phoneUri); } private static LookupResult resolveEmailContact(Context context, final String email) { Uri numberUri = Uri.withAppendedPath( ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, Uri.encode(email)); return searchContacts(context, numberUri); } @VisibleForTesting static LookupResult searchContacts(Context context, Uri lookupUri) { LookupResult lookupResult = new LookupResult(); final Uri corpLookupUri = ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri); if (corpLookupUri == null) { addContacts(lookupResult, context, lookupUri); } else { addWorkContacts(lookupResult, context, corpLookupUri); } 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. static 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 static void addWorkContacts(LookupResult lookupResult, Context context, Uri corpLookupUri) { final int workUserId = findWorkUserId(context); if (workUserId == -1) { Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri); return; } final Uri corpLookupUriWithUserId = ContentProvider.maybeAddUserId(corpLookupUri, workUserId); addContacts(lookupResult, context, corpLookupUriWithUserId); } /** Returns the user ID of the managed profile or -1 if none is found. */ private static int findWorkUserId(Context context) { final UserManager userManager = context.getSystemService(UserManager.class); final int[] profileIds = userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true); for (int profileId : profileIds) { if (userManager.isManagedProfile(profileId)) { return profileId; } } return -1; } /** Modifies the given lookup result to add contacts found at the given URI. */ private static void addContacts(LookupResult lookupResult, Context context, Uri uri) { try (Cursor c = context.getContentResolver().query( uri, LOOKUP_PROJECTION, null, null, null)) { if (c == null) { Slog.w(TAG, "Null cursor from contacts query."); return; } while (c.moveToNext()) { lookupResult.mergeContact(c); } } catch (Throwable t) { Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); } } @Override public void applyChangesLocked(NotificationRecord operand) { float affinityBound = operand.getContactAffinity(); Loading services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java +8 −29 Original line number Diff line number Diff line Loading @@ -15,8 +15,6 @@ */ package com.android.server.notification; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; Loading Loading @@ -53,8 +51,6 @@ import android.util.LruCache; import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; import com.android.server.notification.ValidateNotificationPeople.LookupResult; import com.android.server.notification.ValidateNotificationPeople.PeopleRankingReconsideration; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -64,7 +60,6 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidJUnit4.class) Loading Loading @@ -220,7 +215,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { ContactsContract.Contacts.CONTENT_LOOKUP_URI, ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX + contactId); PeopleRankingReconsideration.searchContacts(mockContext, lookupUri); new ValidateNotificationPeople().searchContacts(mockContext, lookupUri); ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class); verify(mockContentResolver).query( Loading @@ -247,7 +242,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { final Uri lookupUri = Uri.withAppendedPath( ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId)); PeopleRankingReconsideration.searchContacts(mockContext, lookupUri); new ValidateNotificationPeople().searchContacts(mockContext, lookupUri); ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class); verify(mockContentResolver).query( Loading Loading @@ -282,7 +277,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // call searchContacts and then mergePhoneNumbers, make sure we never actually // query the content resolver for a phone number PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri); new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); verify(mockContentResolver, never()).query( eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION), Loading Loading @@ -325,7 +320,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // call searchContacts and then mergePhoneNumbers, and check that we query // once for the PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri); new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); verify(mockContentResolver, times(1)).query( eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION), Loading @@ -344,7 +339,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // Create validator with empty cache ValidateNotificationPeople vnp = new ValidateNotificationPeople(); LruCache<String, LookupResult> cache = new LruCache<>(5); LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5); vnp.initForTests(mockContext, mockNotificationUsageStats, cache); NotificationRecord record = getNotificationRecord(); Loading @@ -371,8 +366,9 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { float affinity = 0.7f; // Create a fake LookupResult for the data we'll pass in LruCache<String, LookupResult> cache = new LruCache<>(5); LookupResult lr = mock(LookupResult.class); LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5); ValidateNotificationPeople.LookupResult lr = mock(ValidateNotificationPeople.LookupResult.class); when(lr.getAffinity()).thenReturn(affinity); when(lr.getPhoneNumbers()).thenReturn(new ArraySet<>(new String[]{lookupTel})); when(lr.isExpired()).thenReturn(false); Loading @@ -396,23 +392,6 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { assertTrue(record.getPhoneNumbers().contains(lookupTel)); } @Test public void validatePeople_reconsiderationWillNotBeDelayed() { final Context mockContext = mock(Context.class); final ContentResolver mockContentResolver = mock(ContentResolver.class); when(mockContext.getContentResolver()).thenReturn(mockContentResolver); ValidateNotificationPeople vnp = new ValidateNotificationPeople(); vnp.initForTests(mockContext, mock(NotificationUsageStats.class), new LruCache<>(5)); NotificationRecord record = getNotificationRecord(); String[] callNumber = new String[]{"tel:12345678910"}; setNotificationPeople(record, callNumber); RankingReconsideration rr = vnp.validatePeople(mockContext, record); assertThat(rr).isNotNull(); assertThat(rr.getDelay(TimeUnit.MILLISECONDS)).isEqualTo(0); } // 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) { Loading Loading
services/core/java/com/android/server/notification/ValidateNotificationPeople.java +105 −106 Original line number Diff line number Diff line Loading @@ -62,7 +62,7 @@ import java.util.concurrent.TimeUnit; public class ValidateNotificationPeople implements NotificationSignalExtractor { // Using a shorter log tag since setprop has a limit of 32chars on variable name. private static final String TAG = "ValidateNoPeople"; private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean ENABLE_PEOPLE_VALIDATOR = true; Loading Loading @@ -105,13 +105,12 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private int mEvictionCount; private NotificationUsageStats mUsageStats; @Override public void initialize(Context context, NotificationUsageStats usageStats) { if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); mUserToContextMap = new ArrayMap<>(); mBaseContext = context; mUsageStats = usageStats; mPeopleCache = new LruCache<>(PEOPLE_CACHE_SIZE); mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt( mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1); if (mEnabled) { Loading @@ -135,7 +134,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { // For tests: just do the setting of various local variables without actually doing work @VisibleForTesting protected void initForTests(Context context, NotificationUsageStats usageStats, LruCache<String, LookupResult> peopleCache) { LruCache peopleCache) { mUserToContextMap = new ArrayMap<>(); mBaseContext = context; mUsageStats = usageStats; Loading @@ -143,7 +142,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { mEnabled = true; } @Override public RankingReconsideration process(NotificationRecord record) { if (!mEnabled) { if (VERBOSE) Slog.i(TAG, "disabled"); Loading Loading @@ -274,7 +272,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId()); final LinkedList<String> pendingLookups = new LinkedList<>(); final LinkedList<String> pendingLookups = new LinkedList<String>(); int personIdx = 0; for (String handle : people) { if (TextUtils.isEmpty(handle)) continue; Loading Loading @@ -322,6 +320,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return Integer.toString(userId) + ":" + handle; } // VisibleForTesting public static String[] getExtraPeople(Bundle extras) { String[] peopleList = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE_LIST); String[] legacyPeople = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE); Loading Loading @@ -418,6 +417,101 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return null; } private LookupResult resolvePhoneContact(Context context, final String number) { Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); return searchContacts(context, phoneUri); } private LookupResult resolveEmailContact(Context context, final String email) { Uri numberUri = Uri.withAppendedPath( ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, Uri.encode(email)); return searchContacts(context, numberUri); } @VisibleForTesting LookupResult searchContacts(Context context, Uri lookupUri) { LookupResult lookupResult = new LookupResult(); final Uri corpLookupUri = ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri); if (corpLookupUri == null) { addContacts(lookupResult, context, lookupUri); } else { addWorkContacts(lookupResult, context, corpLookupUri); } 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) { Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri); return; } final Uri corpLookupUriWithUserId = ContentProvider.maybeAddUserId(corpLookupUri, workUserId); addContacts(lookupResult, context, corpLookupUriWithUserId); } /** Returns the user ID of the managed profile or -1 if none is found. */ private int findWorkUserId(Context context) { final UserManager userManager = context.getSystemService(UserManager.class); final int[] profileIds = userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true); for (int profileId : profileIds) { if (userManager.isManagedProfile(profileId)) { return profileId; } } return -1; } /** Modifies the given lookup result to add contacts found at the given URI. */ private void addContacts(LookupResult lookupResult, Context context, Uri uri) { try (Cursor c = context.getContentResolver().query( uri, LOOKUP_PROJECTION, null, null, null)) { if (c == null) { Slog.w(TAG, "Null cursor from contacts query."); return; } while (c.moveToNext()) { lookupResult.mergeContact(c); } } catch (Throwable t) { Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); } } @VisibleForTesting protected static class LookupResult { private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr Loading Loading @@ -525,18 +619,19 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } } @VisibleForTesting class PeopleRankingReconsideration extends RankingReconsideration { private class PeopleRankingReconsideration extends RankingReconsideration { private final LinkedList<String> mPendingLookups; private final Context mContext; // 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, LinkedList<String> pendingLookups) { super(key); super(key, LOOKUP_TIME); mContext = context; mPendingLookups = pendingLookups; } Loading @@ -547,7 +642,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { long timeStartMs = System.currentTimeMillis(); for (final String handle: mPendingLookups) { final String cacheKey = getCacheKey(mContext.getUserId(), handle); LookupResult lookupResult; LookupResult lookupResult = null; boolean cacheHit = false; synchronized (mPeopleCache) { lookupResult = mPeopleCache.get(cacheKey); Loading Loading @@ -608,102 +703,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { } } private static LookupResult resolvePhoneContact(Context context, final String number) { Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); return searchContacts(context, phoneUri); } private static LookupResult resolveEmailContact(Context context, final String email) { Uri numberUri = Uri.withAppendedPath( ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, Uri.encode(email)); return searchContacts(context, numberUri); } @VisibleForTesting static LookupResult searchContacts(Context context, Uri lookupUri) { LookupResult lookupResult = new LookupResult(); final Uri corpLookupUri = ContactsContract.Contacts.createCorpLookupUriFromEnterpriseLookupUri(lookupUri); if (corpLookupUri == null) { addContacts(lookupResult, context, lookupUri); } else { addWorkContacts(lookupResult, context, corpLookupUri); } 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. static 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 static void addWorkContacts(LookupResult lookupResult, Context context, Uri corpLookupUri) { final int workUserId = findWorkUserId(context); if (workUserId == -1) { Slog.w(TAG, "Work profile user ID not found for work contact: " + corpLookupUri); return; } final Uri corpLookupUriWithUserId = ContentProvider.maybeAddUserId(corpLookupUri, workUserId); addContacts(lookupResult, context, corpLookupUriWithUserId); } /** Returns the user ID of the managed profile or -1 if none is found. */ private static int findWorkUserId(Context context) { final UserManager userManager = context.getSystemService(UserManager.class); final int[] profileIds = userManager.getProfileIds(context.getUserId(), /* enabledOnly= */ true); for (int profileId : profileIds) { if (userManager.isManagedProfile(profileId)) { return profileId; } } return -1; } /** Modifies the given lookup result to add contacts found at the given URI. */ private static void addContacts(LookupResult lookupResult, Context context, Uri uri) { try (Cursor c = context.getContentResolver().query( uri, LOOKUP_PROJECTION, null, null, null)) { if (c == null) { Slog.w(TAG, "Null cursor from contacts query."); return; } while (c.moveToNext()) { lookupResult.mergeContact(c); } } catch (Throwable t) { Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); } } @Override public void applyChangesLocked(NotificationRecord operand) { float affinityBound = operand.getContactAffinity(); Loading
services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java +8 −29 Original line number Diff line number Diff line Loading @@ -15,8 +15,6 @@ */ package com.android.server.notification; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; Loading Loading @@ -53,8 +51,6 @@ import android.util.LruCache; import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; import com.android.server.notification.ValidateNotificationPeople.LookupResult; import com.android.server.notification.ValidateNotificationPeople.PeopleRankingReconsideration; import org.junit.Test; import org.junit.runner.RunWith; Loading @@ -64,7 +60,6 @@ import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidJUnit4.class) Loading Loading @@ -220,7 +215,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { ContactsContract.Contacts.CONTENT_LOOKUP_URI, ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX + contactId); PeopleRankingReconsideration.searchContacts(mockContext, lookupUri); new ValidateNotificationPeople().searchContacts(mockContext, lookupUri); ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class); verify(mockContentResolver).query( Loading @@ -247,7 +242,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { final Uri lookupUri = Uri.withAppendedPath( ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId)); PeopleRankingReconsideration.searchContacts(mockContext, lookupUri); new ValidateNotificationPeople().searchContacts(mockContext, lookupUri); ArgumentCaptor<Uri> queryUri = ArgumentCaptor.forClass(Uri.class); verify(mockContentResolver).query( Loading Loading @@ -282,7 +277,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // call searchContacts and then mergePhoneNumbers, make sure we never actually // query the content resolver for a phone number PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri); new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); verify(mockContentResolver, never()).query( eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION), Loading Loading @@ -325,7 +320,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // call searchContacts and then mergePhoneNumbers, and check that we query // once for the PeopleRankingReconsideration.searchContactsAndLookupNumbers(mockContext, lookupUri); new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri); verify(mockContentResolver, times(1)).query( eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI), eq(ValidateNotificationPeople.PHONE_LOOKUP_PROJECTION), Loading @@ -344,7 +339,7 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { // Create validator with empty cache ValidateNotificationPeople vnp = new ValidateNotificationPeople(); LruCache<String, LookupResult> cache = new LruCache<>(5); LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5); vnp.initForTests(mockContext, mockNotificationUsageStats, cache); NotificationRecord record = getNotificationRecord(); Loading @@ -371,8 +366,9 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { float affinity = 0.7f; // Create a fake LookupResult for the data we'll pass in LruCache<String, LookupResult> cache = new LruCache<>(5); LookupResult lr = mock(LookupResult.class); LruCache cache = new LruCache<String, ValidateNotificationPeople.LookupResult>(5); ValidateNotificationPeople.LookupResult lr = mock(ValidateNotificationPeople.LookupResult.class); when(lr.getAffinity()).thenReturn(affinity); when(lr.getPhoneNumbers()).thenReturn(new ArraySet<>(new String[]{lookupTel})); when(lr.isExpired()).thenReturn(false); Loading @@ -396,23 +392,6 @@ public class ValidateNotificationPeopleTest extends UiServiceTestCase { assertTrue(record.getPhoneNumbers().contains(lookupTel)); } @Test public void validatePeople_reconsiderationWillNotBeDelayed() { final Context mockContext = mock(Context.class); final ContentResolver mockContentResolver = mock(ContentResolver.class); when(mockContext.getContentResolver()).thenReturn(mockContentResolver); ValidateNotificationPeople vnp = new ValidateNotificationPeople(); vnp.initForTests(mockContext, mock(NotificationUsageStats.class), new LruCache<>(5)); NotificationRecord record = getNotificationRecord(); String[] callNumber = new String[]{"tel:12345678910"}; setNotificationPeople(record, callNumber); RankingReconsideration rr = vnp.validatePeople(mockContext, record); assertThat(rr).isNotNull(); assertThat(rr.getDelay(TimeUnit.MILLISECONDS)).isEqualTo(0); } // 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) { Loading