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

Commit c237555c authored by Chris Wren's avatar Chris Wren Committed by Android (Google) Code Review
Browse files

Merge "Keep contact affinities separate across users." into lmp-dev

parents 949bb79f da4bd209
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ interface INotificationManager

    ComponentName getEffectsSuppressor();
    boolean matchesCallFilter(in Bundle extras);
    boolean matchesCallFilterAsUser(in Bundle extras, int userId);

    ZenModeConfig getZenModeConfig();
    boolean setZenModeConfig(in ZenModeConfig config);
+8 −1
Original line number Diff line number Diff line
@@ -1470,7 +1470,14 @@ public class NotificationManagerService extends SystemService {
        @Override
        public boolean matchesCallFilter(Bundle extras) {
            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
            return mZenModeHelper.matchesCallFilter(extras,
            return matchesCallFilterAsUser(extras, Binder.getCallingUid());
        }

        @Override
        public boolean matchesCallFilterAsUser(Bundle extras, int userId) {
            enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
            UserHandle userHandle = new UserHandle(userId);
            return mZenModeHelper.matchesCallFilter(userHandle, extras,
                    mRankingHelper.findExtractor(ValidateNotificationPeople.class));
        }
    };
+4 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.media.AudioAttributes;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;

import com.android.internal.annotations.VisibleForTesting;
@@ -88,8 +89,10 @@ public final class NotificationRecord {

    public Notification getNotification() { return sbn.getNotification(); }
    public int getFlags() { return sbn.getNotification().flags; }
    public int getUserId() { return sbn.getUserId(); }
    public UserHandle getUser() { return sbn.getUser(); }
    public String getKey() { return sbn.getKey(); }
    /** @deprecated Use {@link #getUser()} instead. */
    public int getUserId() { return sbn.getUserId(); }

    void dump(PrintWriter pw, String prefix, Context baseContext) {
        final Notification notification = sbn.getNotification();
+97 −55
Original line number Diff line number Diff line
@@ -18,18 +18,23 @@ package com.android.server.notification;

import android.app.Notification;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.LruCache;
import android.util.Slog;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Map;

/**
 * This {@link NotificationSignalExtractor} attempts to validate
@@ -65,21 +70,88 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
    static final float STARRED_CONTACT = 1f;

    protected boolean mEnabled;
    private Context mContext;
    private Context mBaseContext;

    // maps raw person handle to resolved person object
    private LruCache<String, LookupResult> mPeopleCache;
    private Map<Integer, Context> mUserToContextMap;

    private RankingReconsideration validatePeople(final NotificationRecord record) {
    public void initialize(Context context) {
        if (DEBUG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
        mUserToContextMap = new ArrayMap<>();
        mBaseContext = context;
        mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
        mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
                mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
    }

    public RankingReconsideration process(NotificationRecord record) {
        if (!mEnabled) {
            if (INFO) Slog.i(TAG, "disabled");
            return null;
        }
        if (record == null || record.getNotification() == null) {
            if (INFO) Slog.i(TAG, "skipping empty notification");
            return null;
        }
        if (record.getUserId() == UserHandle.USER_ALL) {
            if (INFO) Slog.i(TAG, "skipping global notification");
            return null;
        }
        Context context = getContextAsUser(record.getUser());
        if (context == null) {
            if (INFO) Slog.i(TAG, "skipping notification that lacks a context");
            return null;
        }
        return validatePeople(context, record);
    }

    @Override
    public void setConfig(RankingConfig config) {
        // ignore: config has no relevant information yet.
    }

    public float getContactAffinity(UserHandle userHandle, Bundle extras) {
        if (extras == null) return NONE;
        final String key = Long.toString(System.nanoTime());
        final float[] affinityOut = new float[1];
        Context context = getContextAsUser(userHandle);
        if (context == null) {
            return NONE;
        }
        final PeopleRankingReconsideration prr = validatePeople(context, key, extras, affinityOut);
        float affinity = affinityOut[0];
        if (prr != null) {
            prr.work();
            affinity = Math.max(prr.getContactAffinity(), affinity);
        }
        return affinity;
    }

    private Context getContextAsUser(UserHandle userHandle) {
        Context context = mUserToContextMap.get(userHandle.getIdentifier());
        if (context == null) {
            try {
                context = mBaseContext.createPackageContextAsUser("android", 0, userHandle);
                mUserToContextMap.put(userHandle.getIdentifier(), context);
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "failed to create package context for lookups", e);
            }
        }
        return context;
    }

    private RankingReconsideration validatePeople(Context context,
            final NotificationRecord record) {
        final String key = record.getKey();
        final Bundle extras = record.getNotification().extras;
        final float[] affinityOut = new float[1];
        final RankingReconsideration rr = validatePeople(key, extras, affinityOut);
        final RankingReconsideration rr = validatePeople(context, key, extras, affinityOut);
        record.setContactAffinity(affinityOut[0]);
        return rr;
    }

    private PeopleRankingReconsideration validatePeople(String key, Bundle extras,
    private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
            float[] affinityOut) {
        float affinity = NONE;
        if (extras == null) {
@@ -98,7 +170,8 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
            if (TextUtils.isEmpty(handle)) continue;

            synchronized (mPeopleCache) {
                LookupResult lookupResult = mPeopleCache.get(handle);
                final String cacheKey = getCacheKey(context.getUserId(), handle);
                LookupResult lookupResult = mPeopleCache.get(cacheKey);
                if (lookupResult == null || lookupResult.isExpired()) {
                    pendingLookups.add(handle);
                } else {
@@ -119,7 +192,11 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
        }

        if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
        return new PeopleRankingReconsideration(key, pendingLookups);
        return new PeopleRankingReconsideration(context, key, pendingLookups);
    }

    private String getCacheKey(int userId, String handle) {
        return Integer.toString(userId) + ":" + handle;
    }

    // VisibleForTesting
@@ -185,24 +262,24 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
        return null;
    }

    private LookupResult resolvePhoneContact(final String number) {
    private LookupResult resolvePhoneContact(Context context, final String number) {
        Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
                Uri.encode(number));
        return searchContacts(phoneUri);
        return searchContacts(context, phoneUri);
    }

    private LookupResult resolveEmailContact(final String email) {
    private LookupResult resolveEmailContact(Context context, final String email) {
        Uri numberUri = Uri.withAppendedPath(
                ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
                Uri.encode(email));
        return searchContacts(numberUri);
        return searchContacts(context, numberUri);
    }

    private LookupResult searchContacts(Uri lookupUri) {
    private LookupResult searchContacts(Context context, Uri lookupUri) {
        LookupResult lookupResult = new LookupResult();
        Cursor c = null;
        try {
            c = mContext.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
            c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
            if (c != null && c.getCount() > 0) {
                c.moveToFirst();
                lookupResult.readContact(c);
@@ -217,44 +294,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
        return lookupResult;
    }

    public void initialize(Context context) {
        if (DEBUG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
        mContext = context;
        mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
        mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
                mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
    }

    public RankingReconsideration process(NotificationRecord record) {
        if (!mEnabled) {
            if (INFO) Slog.i(TAG, "disabled");
            return null;
        }
        if (record == null || record.getNotification() == null) {
            if (INFO) Slog.i(TAG, "skipping empty notification");
            return null;
        }
        return validatePeople(record);
    }

    @Override
    public void setConfig(RankingConfig config) {
        // ignore: config has no relevant information yet.
    }

    public float getContactAffinity(Bundle extras) {
        if (extras == null) return NONE;
        final String key = Long.toString(System.nanoTime());
        final float[] affinityOut = new float[1];
        final PeopleRankingReconsideration prr = validatePeople(key, extras, affinityOut);
        float affinity = affinityOut[0];
        if (prr != null) {
            prr.work();
            affinity = Math.max(prr.getContactAffinity(), affinity);
        }
        return affinity;
    }

    private static class LookupResult {
        private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
        public static final int INVALID_ID = -1;
@@ -317,11 +356,13 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {

    private class PeopleRankingReconsideration extends RankingReconsideration {
        private final LinkedList<String> mPendingLookups;
        private final Context mContext;

        private float mContactAffinity = NONE;

        private PeopleRankingReconsideration(String key, LinkedList<String> pendingLookups) {
        private PeopleRankingReconsideration(Context context, String key, LinkedList<String> pendingLookups) {
            super(key);
            mContext = context;
            mPendingLookups = pendingLookups;
        }

@@ -333,20 +374,21 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
                final Uri uri = Uri.parse(handle);
                if ("tel".equals(uri.getScheme())) {
                    if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
                    lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
                    lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
                } else if ("mailto".equals(uri.getScheme())) {
                    if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
                    lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
                    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(uri);
                    lookupResult = searchContacts(mContext, uri);
                } else {
                    lookupResult = new LookupResult();  // invalid person for the cache
                    Slog.w(TAG, "unsupported URI " + handle);
                }
                if (lookupResult != null) {
                    synchronized (mPeopleCache) {
                        mPeopleCache.put(handle, lookupResult);
                        final String cacheKey = getCacheKey(mContext.getUserId(), handle);
                        mPeopleCache.put(cacheKey, lookupResult);
                    }
                    mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
                }
+3 −2
Original line number Diff line number Diff line
@@ -373,13 +373,14 @@ public class ZenModeHelper {
        return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record);
    }

    public boolean matchesCallFilter(Bundle extras, ValidateNotificationPeople validator) {
    public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
            ValidateNotificationPeople validator) {
        final int zen = mZenMode;
        if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
        if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
            if (!mConfig.allowCalls) return false; // no calls get through
            if (validator != null) {
                final float contactAffinity = validator.getContactAffinity(extras);
                final float contactAffinity = validator.getContactAffinity(userHandle, extras);
                return audienceMatches(contactAffinity);
            }
        }