blob: 10f16960ff417e05116d3abeaf73e06f7d6b003f [file] [log] [blame]
Chris Wrenf9536642014-04-17 10:01:54 -04001/*
2* Copyright (C) 2014 The Android Open Source Project
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17package com.android.server.notification;
18
19import android.app.Notification;
20import android.content.Context;
Chris Wrenda4bd202014-09-04 15:53:52 -040021import android.content.pm.PackageManager;
Chris Wren99f4c7d2014-09-11 13:38:18 -040022import android.database.ContentObserver;
Chris Wrenf9536642014-04-17 10:01:54 -040023import android.database.Cursor;
24import android.net.Uri;
Christoph Studer12aeda82014-09-23 19:08:56 +020025import android.os.AsyncTask;
Chris Wrenf9536642014-04-17 10:01:54 -040026import android.os.Bundle;
Chris Wren99f4c7d2014-09-11 13:38:18 -040027import android.os.Handler;
Chris Wrenda4bd202014-09-04 15:53:52 -040028import android.os.UserHandle;
Chris Wrenf9536642014-04-17 10:01:54 -040029import android.provider.ContactsContract;
30import android.provider.ContactsContract.Contacts;
31import android.provider.Settings;
32import android.text.TextUtils;
Chris Wrenda4bd202014-09-04 15:53:52 -040033import android.util.ArrayMap;
34import android.util.Log;
Chris Wrenf9536642014-04-17 10:01:54 -040035import android.util.LruCache;
36import android.util.Slog;
Chris Wren723aa762015-04-09 15:35:23 -040037import com.android.internal.logging.MetricsLogger;
Chris Wrenf9536642014-04-17 10:01:54 -040038
Chris Wrenf9536642014-04-17 10:01:54 -040039import java.util.ArrayList;
40import java.util.LinkedList;
Chris Wrenda4bd202014-09-04 15:53:52 -040041import java.util.Map;
Christoph Studer12aeda82014-09-23 19:08:56 +020042import java.util.concurrent.Semaphore;
43import java.util.concurrent.TimeUnit;
Chris Wrenf9536642014-04-17 10:01:54 -040044
45/**
46 * This {@link NotificationSignalExtractor} attempts to validate
47 * people references. Also elevates the priority of real people.
Chris Wren92af3722014-05-27 16:37:02 -040048 *
49 * {@hide}
Chris Wrenf9536642014-04-17 10:01:54 -040050 */
51public class ValidateNotificationPeople implements NotificationSignalExtractor {
Christoph Studer12aeda82014-09-23 19:08:56 +020052 // Using a shorter log tag since setprop has a limit of 32chars on variable name.
53 private static final String TAG = "ValidateNoPeople";
Chris Wrenf9536642014-04-17 10:01:54 -040054 private static final boolean INFO = true;
Christoph Studer12aeda82014-09-23 19:08:56 +020055 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Chris Wrenf9536642014-04-17 10:01:54 -040056
57 private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
58 private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
59 "validate_notification_people_enabled";
Chris Wren44d81a42014-05-14 17:38:05 -040060 private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.STARRED };
Chris Wrenf9536642014-04-17 10:01:54 -040061 private static final int MAX_PEOPLE = 10;
62 private static final int PEOPLE_CACHE_SIZE = 200;
63
Chris Wren99f963e2014-05-28 16:52:42 -040064 /** Indicates that the notification does not reference any valid contacts. */
65 static final float NONE = 0f;
66
67 /**
68 * Affinity will be equal to or greater than this value on notifications
69 * that reference a valid contact.
70 */
71 static final float VALID_CONTACT = 0.5f;
72
73 /**
74 * Affinity will be equal to or greater than this value on notifications
75 * that reference a starred contact.
76 */
77 static final float STARRED_CONTACT = 1f;
Chris Wrenf9536642014-04-17 10:01:54 -040078
79 protected boolean mEnabled;
Chris Wrenda4bd202014-09-04 15:53:52 -040080 private Context mBaseContext;
Chris Wrenf9536642014-04-17 10:01:54 -040081
82 // maps raw person handle to resolved person object
83 private LruCache<String, LookupResult> mPeopleCache;
Chris Wrenda4bd202014-09-04 15:53:52 -040084 private Map<Integer, Context> mUserToContextMap;
Chris Wren99f4c7d2014-09-11 13:38:18 -040085 private Handler mHandler;
86 private ContentObserver mObserver;
87 private int mEvictionCount;
Chris Wrenf9536642014-04-17 10:01:54 -040088
Chris Wrenda4bd202014-09-04 15:53:52 -040089 public void initialize(Context context) {
90 if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
91 mUserToContextMap = new ArrayMap<>();
92 mBaseContext = context;
93 mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
94 mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
95 mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
Chris Wren99f4c7d2014-09-11 13:38:18 -040096 if (mEnabled) {
97 mHandler = new Handler();
98 mObserver = new ContentObserver(mHandler) {
99 @Override
100 public void onChange(boolean selfChange, Uri uri, int userId) {
101 super.onChange(selfChange, uri, userId);
102 if (DEBUG || mEvictionCount % 100 == 0) {
103 if (INFO) Slog.i(TAG, "mEvictionCount: " + mEvictionCount);
104 }
105 mPeopleCache.evictAll();
106 mEvictionCount++;
107 }
108 };
109 mBaseContext.getContentResolver().registerContentObserver(Contacts.CONTENT_URI, true,
110 mObserver, UserHandle.USER_ALL);
111 }
Chris Wrenda4bd202014-09-04 15:53:52 -0400112 }
113
114 public RankingReconsideration process(NotificationRecord record) {
115 if (!mEnabled) {
116 if (INFO) Slog.i(TAG, "disabled");
117 return null;
118 }
119 if (record == null || record.getNotification() == null) {
120 if (INFO) Slog.i(TAG, "skipping empty notification");
121 return null;
122 }
123 if (record.getUserId() == UserHandle.USER_ALL) {
124 if (INFO) Slog.i(TAG, "skipping global notification");
125 return null;
126 }
127 Context context = getContextAsUser(record.getUser());
128 if (context == null) {
129 if (INFO) Slog.i(TAG, "skipping notification that lacks a context");
130 return null;
131 }
132 return validatePeople(context, record);
133 }
134
135 @Override
136 public void setConfig(RankingConfig config) {
137 // ignore: config has no relevant information yet.
138 }
139
Christoph Studer12aeda82014-09-23 19:08:56 +0200140 /**
141 * @param extras extras of the notification with EXTRA_PEOPLE populated
142 * @param timeoutMs timeout in milliseconds to wait for contacts response
143 * @param timeoutAffinity affinity to return when the timeout specified via
144 * <code>timeoutMs</code> is hit
145 */
146 public float getContactAffinity(UserHandle userHandle, Bundle extras, int timeoutMs,
147 float timeoutAffinity) {
Chris Wren7381daa2014-09-05 11:30:32 -0400148 if (DEBUG) Slog.d(TAG, "checking affinity for " + userHandle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400149 if (extras == null) return NONE;
150 final String key = Long.toString(System.nanoTime());
151 final float[] affinityOut = new float[1];
152 Context context = getContextAsUser(userHandle);
153 if (context == null) {
154 return NONE;
155 }
156 final PeopleRankingReconsideration prr = validatePeople(context, key, extras, affinityOut);
157 float affinity = affinityOut[0];
Christoph Studer12aeda82014-09-23 19:08:56 +0200158
Chris Wrenda4bd202014-09-04 15:53:52 -0400159 if (prr != null) {
Christoph Studer12aeda82014-09-23 19:08:56 +0200160 // Perform the heavy work on a background thread so we can abort when we hit the
161 // timeout.
162 final Semaphore s = new Semaphore(0);
163 AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
164 @Override
165 public void run() {
166 prr.work();
167 s.release();
168 }
169 });
170
171 try {
172 if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
173 Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "
174 + "Returning timeoutAffinity=" + timeoutAffinity);
175 return timeoutAffinity;
176 }
177 } catch (InterruptedException e) {
178 Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "
179 + "Returning affinity=" + affinity, e);
180 return affinity;
181 }
182
Chris Wrenda4bd202014-09-04 15:53:52 -0400183 affinity = Math.max(prr.getContactAffinity(), affinity);
184 }
185 return affinity;
186 }
187
188 private Context getContextAsUser(UserHandle userHandle) {
189 Context context = mUserToContextMap.get(userHandle.getIdentifier());
190 if (context == null) {
191 try {
192 context = mBaseContext.createPackageContextAsUser("android", 0, userHandle);
193 mUserToContextMap.put(userHandle.getIdentifier(), context);
194 } catch (PackageManager.NameNotFoundException e) {
195 Log.e(TAG, "failed to create package context for lookups", e);
196 }
197 }
198 return context;
199 }
200
201 private RankingReconsideration validatePeople(Context context,
202 final NotificationRecord record) {
John Spurlock2b122f42014-08-27 16:29:47 -0400203 final String key = record.getKey();
204 final Bundle extras = record.getNotification().extras;
205 final float[] affinityOut = new float[1];
Chris Wrenda4bd202014-09-04 15:53:52 -0400206 final RankingReconsideration rr = validatePeople(context, key, extras, affinityOut);
John Spurlock2b122f42014-08-27 16:29:47 -0400207 record.setContactAffinity(affinityOut[0]);
208 return rr;
209 }
210
Chris Wrenda4bd202014-09-04 15:53:52 -0400211 private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
John Spurlock2b122f42014-08-27 16:29:47 -0400212 float[] affinityOut) {
Chris Wrenf9536642014-04-17 10:01:54 -0400213 float affinity = NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400214 if (extras == null) {
215 return null;
216 }
217
218 final String[] people = getExtraPeople(extras);
219 if (people == null || people.length == 0) {
220 return null;
221 }
222
Chris Wrenf37dae72014-09-12 12:09:17 -0400223 if (INFO) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
Chris Wrenf9536642014-04-17 10:01:54 -0400224 final LinkedList<String> pendingLookups = new LinkedList<String>();
225 for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
226 final String handle = people[personIdx];
227 if (TextUtils.isEmpty(handle)) continue;
228
229 synchronized (mPeopleCache) {
Chris Wrenda4bd202014-09-04 15:53:52 -0400230 final String cacheKey = getCacheKey(context.getUserId(), handle);
231 LookupResult lookupResult = mPeopleCache.get(cacheKey);
Chris Wrenf9536642014-04-17 10:01:54 -0400232 if (lookupResult == null || lookupResult.isExpired()) {
233 pendingLookups.add(handle);
234 } else {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200235 if (DEBUG) Slog.d(TAG, "using cached lookupResult");
Chris Wrenf9536642014-04-17 10:01:54 -0400236 }
237 if (lookupResult != null) {
238 affinity = Math.max(affinity, lookupResult.getAffinity());
239 }
240 }
241 }
242
243 // record the best available data, so far:
John Spurlock2b122f42014-08-27 16:29:47 -0400244 affinityOut[0] = affinity;
Chris Wrenf9536642014-04-17 10:01:54 -0400245
246 if (pendingLookups.isEmpty()) {
247 if (INFO) Slog.i(TAG, "final affinity: " + affinity);
Chris Wren723aa762015-04-09 15:35:23 -0400248 if (affinity != NONE) MetricsLogger.count(mBaseContext, "note_with_people", 1);
Chris Wrenf9536642014-04-17 10:01:54 -0400249 return null;
250 }
251
John Spurlock2b122f42014-08-27 16:29:47 -0400252 if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
Chris Wrenda4bd202014-09-04 15:53:52 -0400253 return new PeopleRankingReconsideration(context, key, pendingLookups);
254 }
255
256 private String getCacheKey(int userId, String handle) {
257 return Integer.toString(userId) + ":" + handle;
Chris Wrenf9536642014-04-17 10:01:54 -0400258 }
259
Chris Wren92af3722014-05-27 16:37:02 -0400260 // VisibleForTesting
261 public static String[] getExtraPeople(Bundle extras) {
Chris Wrenfb69da32014-05-15 18:03:11 -0400262 Object people = extras.get(Notification.EXTRA_PEOPLE);
263 if (people instanceof String[]) {
264 return (String[]) people;
Chris Wrenf9536642014-04-17 10:01:54 -0400265 }
266
Chris Wrenfb69da32014-05-15 18:03:11 -0400267 if (people instanceof ArrayList) {
268 ArrayList arrayList = (ArrayList) people;
269
270 if (arrayList.isEmpty()) {
271 return null;
272 }
273
274 if (arrayList.get(0) instanceof String) {
275 ArrayList<String> stringArray = (ArrayList<String>) arrayList;
276 return stringArray.toArray(new String[stringArray.size()]);
277 }
278
279 if (arrayList.get(0) instanceof CharSequence) {
280 ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
281 final int N = charSeqList.size();
282 String[] array = new String[N];
283 for (int i = 0; i < N; i++) {
284 array[i] = charSeqList.get(i).toString();
285 }
286 return array;
287 }
288
289 return null;
Chris Wrenf9536642014-04-17 10:01:54 -0400290 }
291
Chris Wrenfb69da32014-05-15 18:03:11 -0400292 if (people instanceof String) {
293 String[] array = new String[1];
294 array[0] = (String) people;
295 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400296 }
297
Chris Wrenfb69da32014-05-15 18:03:11 -0400298 if (people instanceof char[]) {
299 String[] array = new String[1];
300 array[0] = new String((char[]) people);
301 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400302 }
303
Chris Wrenfb69da32014-05-15 18:03:11 -0400304 if (people instanceof CharSequence) {
305 String[] array = new String[1];
306 array[0] = ((CharSequence) people).toString();
307 return array;
308 }
309
310 if (people instanceof CharSequence[]) {
311 CharSequence[] charSeqArray = (CharSequence[]) people;
Chris Wrenf9536642014-04-17 10:01:54 -0400312 final int N = charSeqArray.length;
Chris Wrenfb69da32014-05-15 18:03:11 -0400313 String[] array = new String[N];
Chris Wrenf9536642014-04-17 10:01:54 -0400314 for (int i = 0; i < N; i++) {
Chris Wrenfb69da32014-05-15 18:03:11 -0400315 array[i] = charSeqArray[i].toString();
Chris Wrenf9536642014-04-17 10:01:54 -0400316 }
Chris Wrenfb69da32014-05-15 18:03:11 -0400317 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400318 }
319
Chris Wrenf9536642014-04-17 10:01:54 -0400320 return null;
321 }
322
Chris Wrenda4bd202014-09-04 15:53:52 -0400323 private LookupResult resolvePhoneContact(Context context, final String number) {
Chris Wren44d81a42014-05-14 17:38:05 -0400324 Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
325 Uri.encode(number));
Chris Wrenda4bd202014-09-04 15:53:52 -0400326 return searchContacts(context, phoneUri);
Chris Wrenf9536642014-04-17 10:01:54 -0400327 }
328
Chris Wrenda4bd202014-09-04 15:53:52 -0400329 private LookupResult resolveEmailContact(Context context, final String email) {
Chris Wren44d81a42014-05-14 17:38:05 -0400330 Uri numberUri = Uri.withAppendedPath(
331 ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
332 Uri.encode(email));
Chris Wrenda4bd202014-09-04 15:53:52 -0400333 return searchContacts(context, numberUri);
Chris Wren44d81a42014-05-14 17:38:05 -0400334 }
335
Chris Wrenda4bd202014-09-04 15:53:52 -0400336 private LookupResult searchContacts(Context context, Uri lookupUri) {
Chris Wren44d81a42014-05-14 17:38:05 -0400337 LookupResult lookupResult = new LookupResult();
Chris Wrenf9536642014-04-17 10:01:54 -0400338 Cursor c = null;
339 try {
Chris Wrenda4bd202014-09-04 15:53:52 -0400340 c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
Christoph Studer9ffa5002014-09-26 16:57:28 +0200341 if (c == null) {
342 Slog.w(TAG, "Null cursor from contacts query.");
343 return lookupResult;
Chris Wrenf9536642014-04-17 10:01:54 -0400344 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200345 while (c.moveToNext()) {
346 lookupResult.mergeContact(c);
347 }
348 } catch (Throwable t) {
Chris Wrenf9536642014-04-17 10:01:54 -0400349 Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
350 } finally {
351 if (c != null) {
352 c.close();
353 }
354 }
Chris Wrenf9536642014-04-17 10:01:54 -0400355 return lookupResult;
356 }
357
Chris Wrenf9536642014-04-17 10:01:54 -0400358 private static class LookupResult {
359 private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
Chris Wrenf9536642014-04-17 10:01:54 -0400360
361 private final long mExpireMillis;
Christoph Studer9ffa5002014-09-26 16:57:28 +0200362 private float mAffinity = NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400363
Chris Wren44d81a42014-05-14 17:38:05 -0400364 public LookupResult() {
Chris Wrenf9536642014-04-17 10:01:54 -0400365 mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
366 }
367
Christoph Studer9ffa5002014-09-26 16:57:28 +0200368 public void mergeContact(Cursor cursor) {
369 mAffinity = Math.max(mAffinity, VALID_CONTACT);
370
371 // Contact ID
372 int id;
Chris Wren44d81a42014-05-14 17:38:05 -0400373 final int idIdx = cursor.getColumnIndex(Contacts._ID);
374 if (idIdx >= 0) {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200375 id = cursor.getInt(idIdx);
376 if (DEBUG) Slog.d(TAG, "contact _ID is: " + id);
Chris Wren44d81a42014-05-14 17:38:05 -0400377 } else {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200378 id = -1;
379 Slog.i(TAG, "invalid cursor: no _ID");
Chris Wren44d81a42014-05-14 17:38:05 -0400380 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200381
382 // Starred
Chris Wren44d81a42014-05-14 17:38:05 -0400383 final int starIdx = cursor.getColumnIndex(Contacts.STARRED);
384 if (starIdx >= 0) {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200385 boolean isStarred = cursor.getInt(starIdx) != 0;
386 if (isStarred) {
387 mAffinity = Math.max(mAffinity, STARRED_CONTACT);
388 }
389 if (DEBUG) Slog.d(TAG, "contact STARRED is: " + isStarred);
Chris Wren44d81a42014-05-14 17:38:05 -0400390 } else {
391 if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");
392 }
393 }
394
Christoph Studer9ffa5002014-09-26 16:57:28 +0200395 private boolean isExpired() {
Chris Wrenf9536642014-04-17 10:01:54 -0400396 return mExpireMillis < System.currentTimeMillis();
397 }
398
Christoph Studer9ffa5002014-09-26 16:57:28 +0200399 private boolean isInvalid() {
400 return mAffinity == NONE || isExpired();
Chris Wrenf9536642014-04-17 10:01:54 -0400401 }
402
403 public float getAffinity() {
404 if (isInvalid()) {
405 return NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400406 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200407 return mAffinity;
Chris Wrenf9536642014-04-17 10:01:54 -0400408 }
409 }
John Spurlock2b122f42014-08-27 16:29:47 -0400410
411 private class PeopleRankingReconsideration extends RankingReconsideration {
412 private final LinkedList<String> mPendingLookups;
Chris Wrenda4bd202014-09-04 15:53:52 -0400413 private final Context mContext;
John Spurlock2b122f42014-08-27 16:29:47 -0400414
415 private float mContactAffinity = NONE;
416
Chris Wrenda4bd202014-09-04 15:53:52 -0400417 private PeopleRankingReconsideration(Context context, String key, LinkedList<String> pendingLookups) {
John Spurlock2b122f42014-08-27 16:29:47 -0400418 super(key);
Chris Wrenda4bd202014-09-04 15:53:52 -0400419 mContext = context;
John Spurlock2b122f42014-08-27 16:29:47 -0400420 mPendingLookups = pendingLookups;
421 }
422
423 @Override
424 public void work() {
425 if (INFO) Slog.i(TAG, "Executing: validation for: " + mKey);
Christoph Studer12aeda82014-09-23 19:08:56 +0200426 long timeStartMs = System.currentTimeMillis();
John Spurlock2b122f42014-08-27 16:29:47 -0400427 for (final String handle: mPendingLookups) {
428 LookupResult lookupResult = null;
429 final Uri uri = Uri.parse(handle);
430 if ("tel".equals(uri.getScheme())) {
431 if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400432 lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
John Spurlock2b122f42014-08-27 16:29:47 -0400433 } else if ("mailto".equals(uri.getScheme())) {
434 if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400435 lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
John Spurlock2b122f42014-08-27 16:29:47 -0400436 } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
437 if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400438 lookupResult = searchContacts(mContext, uri);
John Spurlock2b122f42014-08-27 16:29:47 -0400439 } else {
440 lookupResult = new LookupResult(); // invalid person for the cache
441 Slog.w(TAG, "unsupported URI " + handle);
442 }
443 if (lookupResult != null) {
444 synchronized (mPeopleCache) {
Chris Wrenda4bd202014-09-04 15:53:52 -0400445 final String cacheKey = getCacheKey(mContext.getUserId(), handle);
446 mPeopleCache.put(cacheKey, lookupResult);
John Spurlock2b122f42014-08-27 16:29:47 -0400447 }
Chris Wrenf37dae72014-09-12 12:09:17 -0400448 if (DEBUG) Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
John Spurlock2b122f42014-08-27 16:29:47 -0400449 mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
Chris Wrenf37dae72014-09-12 12:09:17 -0400450 } else {
451 if (DEBUG) Slog.d(TAG, "lookupResult is null");
John Spurlock2b122f42014-08-27 16:29:47 -0400452 }
453 }
Christoph Studer12aeda82014-09-23 19:08:56 +0200454 if (DEBUG) {
455 Slog.d(TAG, "Validation finished in " + (System.currentTimeMillis() - timeStartMs) +
456 "ms");
457 }
Chris Wren723aa762015-04-09 15:35:23 -0400458
459 if (mContactAffinity != NONE) MetricsLogger.count(mBaseContext, "note_with_people", 1);
John Spurlock2b122f42014-08-27 16:29:47 -0400460 }
461
462 @Override
463 public void applyChangesLocked(NotificationRecord operand) {
464 float affinityBound = operand.getContactAffinity();
465 operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
466 if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
467 }
468
469 public float getContactAffinity() {
470 return mContactAffinity;
471 }
472 }
Chris Wrenf9536642014-04-17 10:01:54 -0400473}
474