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