Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d4ff920 authored by Matías Hernández's avatar Matías Hernández Committed by Automerger Merge Worker
Browse files

Merge "Revert "Eliminate delay before contacts lookup for a Notification""...

Merge "Revert "Eliminate delay before contacts lookup for a Notification"" into udc-dev am: 58197e43

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



Change-Id: Ie9e61bd411be645473d6e028cc1cf900d71676e5
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 63820810 58197e43
Loading
Loading
Loading
Loading
+105 −106
Original line number Diff line number Diff line
@@ -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;
@@ -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) {
@@ -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;
@@ -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");
@@ -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;
@@ -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);
@@ -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
@@ -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;
        }
@@ -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);
@@ -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();
+8 −29
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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)
@@ -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(
@@ -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(
@@ -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),
@@ -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),
@@ -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();
@@ -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);
@@ -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) {