1f953664dc17dca23bd724bd64f89189c16c83263Chris Wren/*
2f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* Copyright (C) 2014 The Android Open Source Project
3f953664dc17dca23bd724bd64f89189c16c83263Chris Wren*
4f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* Licensed under the Apache License, Version 2.0 (the "License");
5f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* you may not use this file except in compliance with the License.
6f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* You may obtain a copy of the License at
7f953664dc17dca23bd724bd64f89189c16c83263Chris Wren*
8f953664dc17dca23bd724bd64f89189c16c83263Chris Wren*      http://www.apache.org/licenses/LICENSE-2.0
9f953664dc17dca23bd724bd64f89189c16c83263Chris Wren*
10f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* Unless required by applicable law or agreed to in writing, software
11f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* distributed under the License is distributed on an "AS IS" BASIS,
12f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* See the License for the specific language governing permissions and
14f953664dc17dca23bd724bd64f89189c16c83263Chris Wren* limitations under the License.
15f953664dc17dca23bd724bd64f89189c16c83263Chris Wren*/
16f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
17f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenpackage com.android.server.notification;
18f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
195390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinekimport android.annotation.Nullable;
20f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.app.Notification;
219acd673c0deb2652a55c52b9b80515d84b1945dcSelim Cinekimport android.app.Person;
22f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.content.Context;
23da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wrenimport android.content.pm.PackageManager;
2499f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wrenimport android.database.ContentObserver;
25f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.database.Cursor;
26f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.net.Uri;
2712aeda802ed91a49977a22166319ce74a3352e30Christoph Studerimport android.os.AsyncTask;
28f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.os.Bundle;
2999f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wrenimport android.os.Handler;
30da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wrenimport android.os.UserHandle;
31f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.provider.ContactsContract;
32f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.provider.ContactsContract.Contacts;
33f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.provider.Settings;
34f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.text.TextUtils;
35da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wrenimport android.util.ArrayMap;
3622f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynoldsimport android.util.ArraySet;
37da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wrenimport android.util.Log;
38f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.util.LruCache;
39f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport android.util.Slog;
40f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
41f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport java.util.ArrayList;
4222f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynoldsimport java.util.Arrays;
43f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenimport java.util.LinkedList;
4422f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynoldsimport java.util.List;
45da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wrenimport java.util.Map;
4622f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynoldsimport java.util.Set;
4712aeda802ed91a49977a22166319ce74a3352e30Christoph Studerimport java.util.concurrent.Semaphore;
4812aeda802ed91a49977a22166319ce74a3352e30Christoph Studerimport java.util.concurrent.TimeUnit;
49f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
50f953664dc17dca23bd724bd64f89189c16c83263Chris Wren/**
51f953664dc17dca23bd724bd64f89189c16c83263Chris Wren * This {@link NotificationSignalExtractor} attempts to validate
52f953664dc17dca23bd724bd64f89189c16c83263Chris Wren * people references. Also elevates the priority of real people.
5392af372fc60f59c628a7169d4a506e9c834c097aChris Wren *
5492af372fc60f59c628a7169d4a506e9c834c097aChris Wren * {@hide}
55f953664dc17dca23bd724bd64f89189c16c83263Chris Wren */
56f953664dc17dca23bd724bd64f89189c16c83263Chris Wrenpublic class ValidateNotificationPeople implements NotificationSignalExtractor {
5712aeda802ed91a49977a22166319ce74a3352e30Christoph Studer    // Using a shorter log tag since setprop has a limit of 32chars on variable name.
5812aeda802ed91a49977a22166319ce74a3352e30Christoph Studer    private static final String TAG = "ValidateNoPeople";
591a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);;
6012aeda802ed91a49977a22166319ce74a3352e30Christoph Studer    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
61f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
62f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
63f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
64f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            "validate_notification_people_enabled";
6544d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren    private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.STARRED };
66f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    private static final int MAX_PEOPLE = 10;
67f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    private static final int PEOPLE_CACHE_SIZE = 200;
68f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
6999f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren    /** Indicates that the notification does not reference any valid contacts. */
7099f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren    static final float NONE = 0f;
7199f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren
7299f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren    /**
7399f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren     * Affinity will be equal to or greater than this value on notifications
7499f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren     * that reference a valid contact.
7599f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren     */
7699f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren    static final float VALID_CONTACT = 0.5f;
7799f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren
7899f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren    /**
7999f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren     * Affinity will be equal to or greater than this value on notifications
8099f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren     * that reference a starred contact.
8199f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren     */
8299f963ea04d7a86219ece00c356f3f6bce33b6d6Chris Wren    static final float STARRED_CONTACT = 1f;
83f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
84f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    protected boolean mEnabled;
85da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private Context mBaseContext;
86f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
87f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    // maps raw person handle to resolved person object
88f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    private LruCache<String, LookupResult> mPeopleCache;
89da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private Map<Integer, Context> mUserToContextMap;
9099f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren    private Handler mHandler;
9199f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren    private ContentObserver mObserver;
9299f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren    private int mEvictionCount;
935eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren    private NotificationUsageStats mUsageStats;
94f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
955eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren    public void initialize(Context context, NotificationUsageStats usageStats) {
96da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (DEBUG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
97da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        mUserToContextMap = new ArrayMap<>();
98da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        mBaseContext = context;
995eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        mUsageStats = usageStats;
100da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
101da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
102da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
10399f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren        if (mEnabled) {
10499f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren            mHandler = new Handler();
10599f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren            mObserver = new ContentObserver(mHandler) {
10699f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                @Override
10799f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                public void onChange(boolean selfChange, Uri uri, int userId) {
10899f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                    super.onChange(selfChange, uri, userId);
10999f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                    if (DEBUG || mEvictionCount % 100 == 0) {
1101a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren                        if (VERBOSE) Slog.i(TAG, "mEvictionCount: " + mEvictionCount);
11199f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                    }
11299f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                    mPeopleCache.evictAll();
11399f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                    mEvictionCount++;
11499f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                }
11599f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren            };
11699f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren            mBaseContext.getContentResolver().registerContentObserver(Contacts.CONTENT_URI, true,
11799f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren                    mObserver, UserHandle.USER_ALL);
11899f4c7d0c9c9c29ced22da0a8af4d1a04b0ef186Chris Wren        }
119da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    }
120da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren
121da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    public RankingReconsideration process(NotificationRecord record) {
122da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (!mEnabled) {
1231a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren            if (VERBOSE) Slog.i(TAG, "disabled");
124da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            return null;
125da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        }
126da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (record == null || record.getNotification() == null) {
1271a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren            if (VERBOSE) Slog.i(TAG, "skipping empty notification");
128da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            return null;
129da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        }
130da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (record.getUserId() == UserHandle.USER_ALL) {
1311a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren            if (VERBOSE) Slog.i(TAG, "skipping global notification");
132da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            return null;
133da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        }
134da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        Context context = getContextAsUser(record.getUser());
135da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (context == null) {
1361a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren            if (VERBOSE) Slog.i(TAG, "skipping notification that lacks a context");
137da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            return null;
138da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        }
139da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        return validatePeople(context, record);
140da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    }
141da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren
142da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    @Override
143da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    public void setConfig(RankingConfig config) {
144da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        // ignore: config has no relevant information yet.
145da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    }
146da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren
147c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds    @Override
148c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds    public void setZenHelper(ZenModeHelper helper) {
149c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds
150c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds    }
151c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds
15212aeda802ed91a49977a22166319ce74a3352e30Christoph Studer    /**
15312aeda802ed91a49977a22166319ce74a3352e30Christoph Studer     * @param extras extras of the notification with EXTRA_PEOPLE populated
15412aeda802ed91a49977a22166319ce74a3352e30Christoph Studer     * @param timeoutMs timeout in milliseconds to wait for contacts response
15512aeda802ed91a49977a22166319ce74a3352e30Christoph Studer     * @param timeoutAffinity affinity to return when the timeout specified via
15612aeda802ed91a49977a22166319ce74a3352e30Christoph Studer     *                        <code>timeoutMs</code> is hit
15712aeda802ed91a49977a22166319ce74a3352e30Christoph Studer     */
15812aeda802ed91a49977a22166319ce74a3352e30Christoph Studer    public float getContactAffinity(UserHandle userHandle, Bundle extras, int timeoutMs,
15912aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            float timeoutAffinity) {
1607381daa0b99ef5beb224ffd2544a156af40e78d1Chris Wren        if (DEBUG) Slog.d(TAG, "checking affinity for " + userHandle);
161da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (extras == null) return NONE;
162da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        final String key = Long.toString(System.nanoTime());
163da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        final float[] affinityOut = new float[1];
164da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        Context context = getContextAsUser(userHandle);
165da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (context == null) {
166da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            return NONE;
167da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        }
16822f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds        final PeopleRankingReconsideration prr =
16922f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds                validatePeople(context, key, extras, null, affinityOut);
170da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        float affinity = affinityOut[0];
17112aeda802ed91a49977a22166319ce74a3352e30Christoph Studer
172da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (prr != null) {
17312aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            // Perform the heavy work on a background thread so we can abort when we hit the
17412aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            // timeout.
17512aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            final Semaphore s = new Semaphore(0);
17612aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
17712aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                @Override
17812aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                public void run() {
17912aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                    prr.work();
18012aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                    s.release();
18112aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                }
18212aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            });
18312aeda802ed91a49977a22166319ce74a3352e30Christoph Studer
18412aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            try {
18512aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
18612aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                    Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "
18712aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                            + "Returning timeoutAffinity=" + timeoutAffinity);
18812aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                    return timeoutAffinity;
18912aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                }
19012aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            } catch (InterruptedException e) {
19112aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "
19212aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                        + "Returning affinity=" + affinity, e);
19312aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                return affinity;
19412aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            }
19512aeda802ed91a49977a22166319ce74a3352e30Christoph Studer
196da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            affinity = Math.max(prr.getContactAffinity(), affinity);
197da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        }
198da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        return affinity;
199da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    }
200da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren
201da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private Context getContextAsUser(UserHandle userHandle) {
202da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        Context context = mUserToContextMap.get(userHandle.getIdentifier());
203da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        if (context == null) {
204da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            try {
205da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                context = mBaseContext.createPackageContextAsUser("android", 0, userHandle);
206da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                mUserToContextMap.put(userHandle.getIdentifier(), context);
207da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            } catch (PackageManager.NameNotFoundException e) {
208da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                Log.e(TAG, "failed to create package context for lookups", e);
209da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            }
210da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        }
211da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        return context;
212da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    }
213da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren
214da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private RankingReconsideration validatePeople(Context context,
215da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            final NotificationRecord record) {
2162b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        final String key = record.getKey();
2172b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        final Bundle extras = record.getNotification().extras;
2182b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        final float[] affinityOut = new float[1];
21922f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds        final PeopleRankingReconsideration rr =
22022f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds                validatePeople(context, key, extras, record.getPeopleOverride(), affinityOut);
2215eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        final float affinity = affinityOut[0];
2225eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        record.setContactAffinity(affinity);
2235eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        if (rr == null) {
2245eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren            mUsageStats.registerPeopleAffinity(record, affinity > NONE, affinity == STARRED_CONTACT,
2255eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren                    true /* cached */);
2265eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        } else {
2275eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren            rr.setRecord(record);
2285eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        }
2292b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        return rr;
2302b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock    }
2312b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
232da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
23322f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds            List<String> peopleOverride, float[] affinityOut) {
234f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        float affinity = NONE;
235f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        if (extras == null) {
236f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            return null;
237f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
23822f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds        final Set<String> people = new ArraySet<>(peopleOverride);
23922f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds        final String[] notificationPeople = getExtraPeople(extras);
24022f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds        if (notificationPeople != null ) {
2415390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek            people.addAll(Arrays.asList(notificationPeople));
242f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
243f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
2441a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren        if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
245f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        final LinkedList<String> pendingLookups = new LinkedList<String>();
24622f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds        int personIdx = 0;
24722f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds        for (String handle : people) {
248f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            if (TextUtils.isEmpty(handle)) continue;
249f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
250f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            synchronized (mPeopleCache) {
251da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                final String cacheKey = getCacheKey(context.getUserId(), handle);
252da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                LookupResult lookupResult = mPeopleCache.get(cacheKey);
253f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                if (lookupResult == null || lookupResult.isExpired()) {
254f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                    pendingLookups.add(handle);
255f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                } else {
2569ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                    if (DEBUG) Slog.d(TAG, "using cached lookupResult");
257f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                }
258f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                if (lookupResult != null) {
259f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                    affinity = Math.max(affinity, lookupResult.getAffinity());
260f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                }
261f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            }
26222f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds            if (++personIdx == MAX_PEOPLE) {
26322f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds                break;
26422f02b3e4acd7c6983f4d4d58b85069d5ec920abJulia Reynolds            }
265f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
266f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
267f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        // record the best available data, so far:
2682b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        affinityOut[0] = affinity;
269f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
270f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        if (pendingLookups.isEmpty()) {
2711a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren            if (VERBOSE) Slog.i(TAG, "final affinity: " + affinity);
272f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            return null;
273f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
274f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
2752b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
276da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        return new PeopleRankingReconsideration(context, key, pendingLookups);
277da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    }
278da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren
279da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private String getCacheKey(int userId, String handle) {
280da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        return Integer.toString(userId) + ":" + handle;
281f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    }
282f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
28392af372fc60f59c628a7169d4a506e9c834c097aChris Wren    // VisibleForTesting
28492af372fc60f59c628a7169d4a506e9c834c097aChris Wren    public static String[] getExtraPeople(Bundle extras) {
2855390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        String[] peopleList = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE_LIST);
2865390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        String[] legacyPeople = getExtraPeopleForKey(extras, Notification.EXTRA_PEOPLE);
2875390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        return combineLists(legacyPeople, peopleList);
2885390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek    }
2895390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek
2905390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek    private static String[] combineLists(String[] first, String[] second) {
2915390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        if (first == null) {
2925390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek            return second;
2935390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        }
2945390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        if (second == null) {
2955390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek            return first;
2965390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        }
2975390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        ArraySet<String> people = new ArraySet<>(first.length + second.length);
2985390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        for (String person: first) {
2995390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek            people.add(person);
3005390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        }
3015390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        for (String person: second) {
3025390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek            people.add(person);
3035390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        }
3045390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        return (String[]) people.toArray();
3055390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek    }
3065390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek
3075390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek    @Nullable
3085390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek    private static String[] getExtraPeopleForKey(Bundle extras, String key) {
3095390e7d7ac0ccba9b8e6b9abf30a95c83e2382d3Selim Cinek        Object people = extras.get(key);
310fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren        if (people instanceof String[]) {
311fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            return (String[]) people;
312f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
313f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
314fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren        if (people instanceof ArrayList) {
315fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            ArrayList arrayList = (ArrayList) people;
316fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren
317fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            if (arrayList.isEmpty()) {
318fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                return null;
319fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            }
320fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren
321fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            if (arrayList.get(0) instanceof String) {
322fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                ArrayList<String> stringArray = (ArrayList<String>) arrayList;
323fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                return stringArray.toArray(new String[stringArray.size()]);
324fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            }
325fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren
326fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            if (arrayList.get(0) instanceof CharSequence) {
327fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
328fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                final int N = charSeqList.size();
329fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                String[] array = new String[N];
330fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                for (int i = 0; i < N; i++) {
331fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                    array[i] = charSeqList.get(i).toString();
332fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                }
333fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                return array;
334fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            }
335fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren
3369acd673c0deb2652a55c52b9b80515d84b1945dcSelim Cinek            if (arrayList.get(0) instanceof Person) {
3379acd673c0deb2652a55c52b9b80515d84b1945dcSelim Cinek                ArrayList<Person> list = (ArrayList<Person>) arrayList;
338e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                final int N = list.size();
339e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                String[] array = new String[N];
340e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                for (int i = 0; i < N; i++) {
341e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                    array[i] = list.get(i).resolveToLegacyUri();
342e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                }
343e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                return array;
344e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek            }
345e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek
346fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            return null;
347f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
348f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
349fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren        if (people instanceof String) {
350fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            String[] array = new String[1];
351fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            array[0] = (String) people;
352fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            return array;
353f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
354fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren
355fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren        if (people instanceof char[]) {
356fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            String[] array = new String[1];
357fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            array[0] = new String((char[]) people);
358fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            return array;
359f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
360f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
361fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren        if (people instanceof CharSequence) {
362fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            String[] array = new String[1];
363fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            array[0] = ((CharSequence) people).toString();
364fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            return array;
365f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
366f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
367fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren        if (people instanceof CharSequence[]) {
368fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            CharSequence[] charSeqArray = (CharSequence[]) people;
369f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            final int N = charSeqArray.length;
370fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            String[] array = new String[N];
371f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            for (int i = 0; i < N; i++) {
372fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren                array[i] = charSeqArray[i].toString();
373f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            }
374fb69da323b97ae4712054d37fb2f7d54cddb7dbdChris Wren            return array;
375f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
376f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
377f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        return null;
378f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    }
379f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
380da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private LookupResult resolvePhoneContact(Context context, final String number) {
38144d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren        Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
38244d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren                Uri.encode(number));
383da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        return searchContacts(context, phoneUri);
38444d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren    }
38544d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren
386da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private LookupResult resolveEmailContact(Context context, final String email) {
38744d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren        Uri numberUri = Uri.withAppendedPath(
38844d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren                ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
38944d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren                Uri.encode(email));
390da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        return searchContacts(context, numberUri);
391f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    }
392f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
393da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren    private LookupResult searchContacts(Context context, Uri lookupUri) {
39444d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren        LookupResult lookupResult = new LookupResult();
395f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        Cursor c = null;
396f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        try {
397da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
3989ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            if (c == null) {
3999ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                Slog.w(TAG, "Null cursor from contacts query.");
4009ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                return lookupResult;
401f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            }
4029ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            while (c.moveToNext()) {
4039ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                lookupResult.mergeContact(c);
4049ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            }
4059ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer        } catch (Throwable t) {
406f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
407f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        } finally {
408f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            if (c != null) {
409f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                c.close();
410f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            }
411f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
412f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        return lookupResult;
413f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    }
414f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
415f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    private static class LookupResult {
416f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
417f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
418f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        private final long mExpireMillis;
4199ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer        private float mAffinity = NONE;
420f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
42144d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren        public LookupResult() {
422f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
423f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
424f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
4259ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer        public void mergeContact(Cursor cursor) {
4269ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            mAffinity = Math.max(mAffinity, VALID_CONTACT);
4279ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer
4289ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            // Contact ID
4299ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            int id;
43044d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            final int idIdx = cursor.getColumnIndex(Contacts._ID);
43144d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            if (idIdx >= 0) {
4329ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                id = cursor.getInt(idIdx);
4339ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                if (DEBUG) Slog.d(TAG, "contact _ID is: " + id);
43444d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            } else {
4359ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                id = -1;
4369ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                Slog.i(TAG, "invalid cursor: no _ID");
43744d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            }
4389ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer
4399ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            // Starred
44044d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            final int starIdx = cursor.getColumnIndex(Contacts.STARRED);
44144d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            if (starIdx >= 0) {
4429ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                boolean isStarred = cursor.getInt(starIdx) != 0;
4439ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                if (isStarred) {
4449ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                    mAffinity = Math.max(mAffinity, STARRED_CONTACT);
4459ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                }
4469ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer                if (DEBUG) Slog.d(TAG, "contact STARRED is: " + isStarred);
44744d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            } else {
44844d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren                if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");
44944d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren            }
45044d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren        }
45144d81a4b86f73ecf79861bc576c90b8f8e4ba761Chris Wren
4529ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer        private boolean isExpired() {
453f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            return mExpireMillis < System.currentTimeMillis();
454f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
455f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
4569ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer        private boolean isInvalid() {
4579ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            return mAffinity == NONE || isExpired();
458f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
459f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
460f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        public float getAffinity() {
461f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            if (isInvalid()) {
462f953664dc17dca23bd724bd64f89189c16c83263Chris Wren                return NONE;
463f953664dc17dca23bd724bd64f89189c16c83263Chris Wren            }
4649ffa50096f5ad980bdaef4d214ad1a835b9b2991Christoph Studer            return mAffinity;
465f953664dc17dca23bd724bd64f89189c16c83263Chris Wren        }
466f953664dc17dca23bd724bd64f89189c16c83263Chris Wren    }
4672b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
4682b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock    private class PeopleRankingReconsideration extends RankingReconsideration {
4692b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        private final LinkedList<String> mPendingLookups;
470da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren        private final Context mContext;
4712b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
4727276e63f4c62e37cae1540c928d5f07d7da52a7dJulia Reynolds        // Amount of time to wait for a result from the contacts db before rechecking affinity.
4737276e63f4c62e37cae1540c928d5f07d7da52a7dJulia Reynolds        private static final long LOOKUP_TIME = 1000;
4742b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        private float mContactAffinity = NONE;
4755eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        private NotificationRecord mRecord;
4762b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
477c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds        private PeopleRankingReconsideration(Context context, String key,
478c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds                LinkedList<String> pendingLookups) {
4797276e63f4c62e37cae1540c928d5f07d7da52a7dJulia Reynolds            super(key, LOOKUP_TIME);
480da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren            mContext = context;
4812b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            mPendingLookups = pendingLookups;
4822b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        }
4832b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
4842b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        @Override
4852b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        public void work() {
4861a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren            if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
48712aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            long timeStartMs = System.currentTimeMillis();
4882b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            for (final String handle: mPendingLookups) {
4892b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                LookupResult lookupResult = null;
4902b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                final Uri uri = Uri.parse(handle);
4912b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                if ("tel".equals(uri.getScheme())) {
4922b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                    if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
493da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                    lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
4942b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                } else if ("mailto".equals(uri.getScheme())) {
4952b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                    if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
496da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                    lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
4972b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
4982b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                    if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
499da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                    lookupResult = searchContacts(mContext, uri);
5002b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                } else {
5012b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                    lookupResult = new LookupResult();  // invalid person for the cache
502e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                    if (!"name".equals(uri.getScheme())) {
503e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                        Slog.w(TAG, "unsupported URI " + handle);
504e7238dd12595cb6be953aed4271636df90de17fcSelim Cinek                    }
5052b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                }
5062b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                if (lookupResult != null) {
5072b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                    synchronized (mPeopleCache) {
508da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                        final String cacheKey = getCacheKey(mContext.getUserId(), handle);
509da4bd209cffad7e47a4bc6e9f02c4bfc333d3d8dChris Wren                        mPeopleCache.put(cacheKey, lookupResult);
5102b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                    }
511c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds                    if (DEBUG) {
512c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds                        Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
513c861a3ddbc9b6cb193e71dbb5da6162d119e2e98Julia Reynolds                    }
5142b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                    mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
515f37dae7b60280279a100fb7870122f4e495c81ecChris Wren                } else {
516f37dae7b60280279a100fb7870122f4e495c81ecChris Wren                    if (DEBUG) Slog.d(TAG, "lookupResult is null");
5172b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock                }
5182b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            }
51912aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            if (DEBUG) {
52012aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                Slog.d(TAG, "Validation finished in " + (System.currentTimeMillis() - timeStartMs) +
52112aeda802ed91a49977a22166319ce74a3352e30Christoph Studer                        "ms");
52212aeda802ed91a49977a22166319ce74a3352e30Christoph Studer            }
523723aa768f6d5b1a6696b9ac46c039d1b5be73dbfChris Wren
5245eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren            if (mRecord != null) {
5255eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren                mUsageStats.registerPeopleAffinity(mRecord, mContactAffinity > NONE,
5265eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren                        mContactAffinity == STARRED_CONTACT, false /* cached */);
5275eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren            }
5282b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        }
5292b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
5302b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        @Override
5312b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        public void applyChangesLocked(NotificationRecord operand) {
5322b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            float affinityBound = operand.getContactAffinity();
5332b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
5341a5dad867419e76928c84f0f4fdc4feacce43bbdChris Wren            if (VERBOSE) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
5352b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        }
5362b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock
5372b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        public float getContactAffinity() {
5382b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock            return mContactAffinity;
5392b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock        }
5405eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren
5415eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        public void setRecord(NotificationRecord record) {
5425eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren            mRecord = record;
5435eab2b72afe5b20dc66c237b1cceedfc09de2d52Chris Wren        }
5442b122f4c2e691f0319e4f9ea5873989792bb56a6John Spurlock    }
545f953664dc17dca23bd724bd64f89189c16c83263Chris Wren}
546f953664dc17dca23bd724bd64f89189c16c83263Chris Wren
547