FileDocCategorySizeDatePackage
ValidateNotificationPeople.javaAPI DocAndroid 5.1 API17726Thu Mar 12 22:22:42 GMT 2015com.android.server.notification

ValidateNotificationPeople

public class ValidateNotificationPeople extends Object implements NotificationSignalExtractor
This {@link NotificationSignalExtractor} attempts to validate people references. Also elevates the priority of real people. {@hide}

Fields Summary
private static final String
TAG
private static final boolean
INFO
private static final boolean
DEBUG
private static final boolean
ENABLE_PEOPLE_VALIDATOR
private static final String
SETTING_ENABLE_PEOPLE_VALIDATOR
private static final String[]
LOOKUP_PROJECTION
private static final int
MAX_PEOPLE
private static final int
PEOPLE_CACHE_SIZE
static final float
NONE
Indicates that the notification does not reference any valid contacts.
static final float
VALID_CONTACT
Affinity will be equal to or greater than this value on notifications that reference a valid contact.
static final float
STARRED_CONTACT
Affinity will be equal to or greater than this value on notifications that reference a starred contact.
protected boolean
mEnabled
private android.content.Context
mBaseContext
private android.util.LruCache
mPeopleCache
private Map
mUserToContextMap
private android.os.Handler
mHandler
private android.database.ContentObserver
mObserver
private int
mEvictionCount
Constructors Summary
Methods Summary
private java.lang.StringgetCacheKey(int userId, java.lang.String handle)

        return Integer.toString(userId) + ":" + handle;
    
public floatgetContactAffinity(android.os.UserHandle userHandle, android.os.Bundle extras, int timeoutMs, float timeoutAffinity)

param
extras extras of the notification with EXTRA_PEOPLE populated
param
timeoutMs timeout in milliseconds to wait for contacts response
param
timeoutAffinity affinity to return when the timeout specified via timeoutMs is hit

        if (DEBUG) Slog.d(TAG, "checking affinity for " + userHandle);
        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) {
            // Perform the heavy work on a background thread so we can abort when we hit the
            // timeout.
            final Semaphore s = new Semaphore(0);
            AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
                @Override
                public void run() {
                    prr.work();
                    s.release();
                }
            });

            try {
                if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
                    Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "
                            + "Returning timeoutAffinity=" + timeoutAffinity);
                    return timeoutAffinity;
                }
            } catch (InterruptedException e) {
                Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "
                        + "Returning affinity=" + affinity, e);
                return affinity;
            }

            affinity = Math.max(prr.getContactAffinity(), affinity);
        }
        return affinity;
    
private android.content.ContextgetContextAsUser(android.os.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;
    
public static java.lang.String[]getExtraPeople(android.os.Bundle extras)

        Object people = extras.get(Notification.EXTRA_PEOPLE);
        if (people instanceof String[]) {
            return (String[]) people;
        }

        if (people instanceof ArrayList) {
            ArrayList arrayList = (ArrayList) people;

            if (arrayList.isEmpty()) {
                return null;
            }

            if (arrayList.get(0) instanceof String) {
                ArrayList<String> stringArray = (ArrayList<String>) arrayList;
                return stringArray.toArray(new String[stringArray.size()]);
            }

            if (arrayList.get(0) instanceof CharSequence) {
                ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
                final int N = charSeqList.size();
                String[] array = new String[N];
                for (int i = 0; i < N; i++) {
                    array[i] = charSeqList.get(i).toString();
                }
                return array;
            }

            return null;
        }

        if (people instanceof String) {
            String[] array = new String[1];
            array[0] = (String) people;
            return array;
        }

        if (people instanceof char[]) {
            String[] array = new String[1];
            array[0] = new String((char[]) people);
            return array;
        }

        if (people instanceof CharSequence) {
            String[] array = new String[1];
            array[0] = ((CharSequence) people).toString();
            return array;
        }

        if (people instanceof CharSequence[]) {
            CharSequence[] charSeqArray = (CharSequence[]) people;
            final int N = charSeqArray.length;
            String[] array = new String[N];
            for (int i = 0; i < N; i++) {
                array[i] = charSeqArray[i].toString();
            }
            return array;
        }

        return null;
    
public voidinitialize(android.content.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);
        if (mEnabled) {
            mHandler = new Handler();
            mObserver = new ContentObserver(mHandler) {
                @Override
                public void onChange(boolean selfChange, Uri uri, int userId) {
                    super.onChange(selfChange, uri, userId);
                    if (DEBUG || mEvictionCount % 100 == 0) {
                        if (INFO) Slog.i(TAG, "mEvictionCount: " + mEvictionCount);
                    }
                    mPeopleCache.evictAll();
                    mEvictionCount++;
                }
            };
            mBaseContext.getContentResolver().registerContentObserver(Contacts.CONTENT_URI, true,
                    mObserver, UserHandle.USER_ALL);
        }
    
public RankingReconsiderationprocess(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);
    
private com.android.server.notification.ValidateNotificationPeople$LookupResultresolveEmailContact(android.content.Context context, java.lang.String email)

        Uri numberUri = Uri.withAppendedPath(
                ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
                Uri.encode(email));
        return searchContacts(context, numberUri);
    
private com.android.server.notification.ValidateNotificationPeople$LookupResultresolvePhoneContact(android.content.Context context, java.lang.String number)

        Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
                Uri.encode(number));
        return searchContacts(context, phoneUri);
    
private com.android.server.notification.ValidateNotificationPeople$LookupResultsearchContacts(android.content.Context context, android.net.Uri lookupUri)

        LookupResult lookupResult = new LookupResult();
        Cursor c = null;
        try {
            c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
            if (c == null) {
                Slog.w(TAG, "Null cursor from contacts query.");
                return lookupResult;
            }
            while (c.moveToNext()) {
                lookupResult.mergeContact(c);
            }
        } catch (Throwable t) {
            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return lookupResult;
    
public voidsetConfig(RankingConfig config)

        // ignore: config has no relevant information yet.
    
private RankingReconsiderationvalidatePeople(android.content.Context context, NotificationRecord record)

        final String key = record.getKey();
        final Bundle extras = record.getNotification().extras;
        final float[] affinityOut = new float[1];
        final RankingReconsideration rr = validatePeople(context, key, extras, affinityOut);
        record.setContactAffinity(affinityOut[0]);
        return rr;
    
private com.android.server.notification.ValidateNotificationPeople$PeopleRankingReconsiderationvalidatePeople(android.content.Context context, java.lang.String key, android.os.Bundle extras, float[] affinityOut)

        float affinity = NONE;
        if (extras == null) {
            return null;
        }

        final String[] people = getExtraPeople(extras);
        if (people == null || people.length == 0) {
            return null;
        }

        if (INFO) Slog.i(TAG, "Validating: " + key);
        final LinkedList<String> pendingLookups = new LinkedList<String>();
        for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
            final String handle = people[personIdx];
            if (TextUtils.isEmpty(handle)) continue;

            synchronized (mPeopleCache) {
                final String cacheKey = getCacheKey(context.getUserId(), handle);
                LookupResult lookupResult = mPeopleCache.get(cacheKey);
                if (lookupResult == null || lookupResult.isExpired()) {
                    pendingLookups.add(handle);
                } else {
                    if (DEBUG) Slog.d(TAG, "using cached lookupResult");
                }
                if (lookupResult != null) {
                    affinity = Math.max(affinity, lookupResult.getAffinity());
                }
            }
        }

        // record the best available data, so far:
        affinityOut[0] = affinity;

        if (pendingLookups.isEmpty()) {
            if (INFO) Slog.i(TAG, "final affinity: " + affinity);
            return null;
        }

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