blob: a30e06399614b869444abd0b0907a9d808fee9e3 [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;
Julia Reynolds22f02b32016-12-01 15:05:13 -050034import android.util.ArraySet;
Chris Wrenda4bd202014-09-04 15:53:52 -040035import android.util.Log;
Chris Wrenf9536642014-04-17 10:01:54 -040036import android.util.LruCache;
37import android.util.Slog;
38
Chris Wrenf9536642014-04-17 10:01:54 -040039import java.util.ArrayList;
Julia Reynolds22f02b32016-12-01 15:05:13 -050040import java.util.Arrays;
Chris Wrenf9536642014-04-17 10:01:54 -040041import java.util.LinkedList;
Julia Reynolds22f02b32016-12-01 15:05:13 -050042import java.util.List;
Chris Wrenda4bd202014-09-04 15:53:52 -040043import java.util.Map;
Julia Reynolds22f02b32016-12-01 15:05:13 -050044import java.util.Set;
Christoph Studer12aeda82014-09-23 19:08:56 +020045import java.util.concurrent.Semaphore;
46import java.util.concurrent.TimeUnit;
Chris Wrenf9536642014-04-17 10:01:54 -040047
Chris Wren66dfcc52015-11-19 12:39:38 -050048import android.os.SystemClock;
Chris Wren66dfcc52015-11-19 12:39:38 -050049
Chris Wrenf9536642014-04-17 10:01:54 -040050/**
51 * This {@link NotificationSignalExtractor} attempts to validate
52 * people references. Also elevates the priority of real people.
Chris Wren92af3722014-05-27 16:37:02 -040053 *
54 * {@hide}
Chris Wrenf9536642014-04-17 10:01:54 -040055 */
56public class ValidateNotificationPeople implements NotificationSignalExtractor {
Christoph Studer12aeda82014-09-23 19:08:56 +020057 // Using a shorter log tag since setprop has a limit of 32chars on variable name.
58 private static final String TAG = "ValidateNoPeople";
Chris Wren1a5dad82015-06-16 11:23:12 -040059 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);;
Christoph Studer12aeda82014-09-23 19:08:56 +020060 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Chris Wrenf9536642014-04-17 10:01:54 -040061
62 private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
63 private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
64 "validate_notification_people_enabled";
Chris Wren44d81a42014-05-14 17:38:05 -040065 private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.STARRED };
Chris Wrenf9536642014-04-17 10:01:54 -040066 private static final int MAX_PEOPLE = 10;
67 private static final int PEOPLE_CACHE_SIZE = 200;
68
Chris Wren99f963e2014-05-28 16:52:42 -040069 /** Indicates that the notification does not reference any valid contacts. */
70 static final float NONE = 0f;
71
72 /**
73 * Affinity will be equal to or greater than this value on notifications
74 * that reference a valid contact.
75 */
76 static final float VALID_CONTACT = 0.5f;
77
78 /**
79 * Affinity will be equal to or greater than this value on notifications
80 * that reference a starred contact.
81 */
82 static final float STARRED_CONTACT = 1f;
Chris Wrenf9536642014-04-17 10:01:54 -040083
84 protected boolean mEnabled;
Chris Wrenda4bd202014-09-04 15:53:52 -040085 private Context mBaseContext;
Chris Wrenf9536642014-04-17 10:01:54 -040086
87 // maps raw person handle to resolved person object
88 private LruCache<String, LookupResult> mPeopleCache;
Chris Wrenda4bd202014-09-04 15:53:52 -040089 private Map<Integer, Context> mUserToContextMap;
Chris Wren99f4c7d2014-09-11 13:38:18 -040090 private Handler mHandler;
91 private ContentObserver mObserver;
92 private int mEvictionCount;
Chris Wren5eab2b72015-06-16 13:56:22 -040093 private NotificationUsageStats mUsageStats;
Chris Wrenf9536642014-04-17 10:01:54 -040094
Chris Wren5eab2b72015-06-16 13:56:22 -040095 public void initialize(Context context, NotificationUsageStats usageStats) {
Chris Wrenda4bd202014-09-04 15:53:52 -040096 if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
97 mUserToContextMap = new ArrayMap<>();
98 mBaseContext = context;
Chris Wren5eab2b72015-06-16 13:56:22 -040099 mUsageStats = usageStats;
Chris Wrenda4bd202014-09-04 15:53:52 -0400100 mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
101 mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
102 mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
Chris Wren99f4c7d2014-09-11 13:38:18 -0400103 if (mEnabled) {
104 mHandler = new Handler();
105 mObserver = new ContentObserver(mHandler) {
106 @Override
107 public void onChange(boolean selfChange, Uri uri, int userId) {
108 super.onChange(selfChange, uri, userId);
109 if (DEBUG || mEvictionCount % 100 == 0) {
Chris Wren1a5dad82015-06-16 11:23:12 -0400110 if (VERBOSE) Slog.i(TAG, "mEvictionCount: " + mEvictionCount);
Chris Wren99f4c7d2014-09-11 13:38:18 -0400111 }
112 mPeopleCache.evictAll();
113 mEvictionCount++;
114 }
115 };
116 mBaseContext.getContentResolver().registerContentObserver(Contacts.CONTENT_URI, true,
117 mObserver, UserHandle.USER_ALL);
118 }
Chris Wrenda4bd202014-09-04 15:53:52 -0400119 }
120
121 public RankingReconsideration process(NotificationRecord record) {
122 if (!mEnabled) {
Chris Wren1a5dad82015-06-16 11:23:12 -0400123 if (VERBOSE) Slog.i(TAG, "disabled");
Chris Wrenda4bd202014-09-04 15:53:52 -0400124 return null;
125 }
126 if (record == null || record.getNotification() == null) {
Chris Wren1a5dad82015-06-16 11:23:12 -0400127 if (VERBOSE) Slog.i(TAG, "skipping empty notification");
Chris Wrenda4bd202014-09-04 15:53:52 -0400128 return null;
129 }
130 if (record.getUserId() == UserHandle.USER_ALL) {
Chris Wren1a5dad82015-06-16 11:23:12 -0400131 if (VERBOSE) Slog.i(TAG, "skipping global notification");
Chris Wrenda4bd202014-09-04 15:53:52 -0400132 return null;
133 }
134 Context context = getContextAsUser(record.getUser());
135 if (context == null) {
Chris Wren1a5dad82015-06-16 11:23:12 -0400136 if (VERBOSE) Slog.i(TAG, "skipping notification that lacks a context");
Chris Wrenda4bd202014-09-04 15:53:52 -0400137 return null;
138 }
139 return validatePeople(context, record);
140 }
141
142 @Override
143 public void setConfig(RankingConfig config) {
144 // ignore: config has no relevant information yet.
145 }
146
Christoph Studer12aeda82014-09-23 19:08:56 +0200147 /**
148 * @param extras extras of the notification with EXTRA_PEOPLE populated
149 * @param timeoutMs timeout in milliseconds to wait for contacts response
150 * @param timeoutAffinity affinity to return when the timeout specified via
151 * <code>timeoutMs</code> is hit
152 */
153 public float getContactAffinity(UserHandle userHandle, Bundle extras, int timeoutMs,
154 float timeoutAffinity) {
Chris Wren7381daa2014-09-05 11:30:32 -0400155 if (DEBUG) Slog.d(TAG, "checking affinity for " + userHandle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400156 if (extras == null) return NONE;
157 final String key = Long.toString(System.nanoTime());
158 final float[] affinityOut = new float[1];
159 Context context = getContextAsUser(userHandle);
160 if (context == null) {
161 return NONE;
162 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500163 final PeopleRankingReconsideration prr =
164 validatePeople(context, key, extras, null, affinityOut);
Chris Wrenda4bd202014-09-04 15:53:52 -0400165 float affinity = affinityOut[0];
Christoph Studer12aeda82014-09-23 19:08:56 +0200166
Chris Wrenda4bd202014-09-04 15:53:52 -0400167 if (prr != null) {
Christoph Studer12aeda82014-09-23 19:08:56 +0200168 // Perform the heavy work on a background thread so we can abort when we hit the
169 // timeout.
170 final Semaphore s = new Semaphore(0);
171 AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
172 @Override
173 public void run() {
174 prr.work();
175 s.release();
176 }
177 });
178
179 try {
180 if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
181 Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "
182 + "Returning timeoutAffinity=" + timeoutAffinity);
183 return timeoutAffinity;
184 }
185 } catch (InterruptedException e) {
186 Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "
187 + "Returning affinity=" + affinity, e);
188 return affinity;
189 }
190
Chris Wrenda4bd202014-09-04 15:53:52 -0400191 affinity = Math.max(prr.getContactAffinity(), affinity);
192 }
193 return affinity;
194 }
195
196 private Context getContextAsUser(UserHandle userHandle) {
197 Context context = mUserToContextMap.get(userHandle.getIdentifier());
198 if (context == null) {
199 try {
200 context = mBaseContext.createPackageContextAsUser("android", 0, userHandle);
201 mUserToContextMap.put(userHandle.getIdentifier(), context);
202 } catch (PackageManager.NameNotFoundException e) {
203 Log.e(TAG, "failed to create package context for lookups", e);
204 }
205 }
206 return context;
207 }
208
209 private RankingReconsideration validatePeople(Context context,
210 final NotificationRecord record) {
John Spurlock2b122f42014-08-27 16:29:47 -0400211 final String key = record.getKey();
212 final Bundle extras = record.getNotification().extras;
213 final float[] affinityOut = new float[1];
Julia Reynolds22f02b32016-12-01 15:05:13 -0500214 final PeopleRankingReconsideration rr =
215 validatePeople(context, key, extras, record.getPeopleOverride(), affinityOut);
Chris Wren5eab2b72015-06-16 13:56:22 -0400216 final float affinity = affinityOut[0];
217 record.setContactAffinity(affinity);
218 if (rr == null) {
219 mUsageStats.registerPeopleAffinity(record, affinity > NONE, affinity == STARRED_CONTACT,
220 true /* cached */);
221 } else {
222 rr.setRecord(record);
223 }
John Spurlock2b122f42014-08-27 16:29:47 -0400224 return rr;
225 }
226
Chris Wrenda4bd202014-09-04 15:53:52 -0400227 private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
Julia Reynolds22f02b32016-12-01 15:05:13 -0500228 List<String> peopleOverride, float[] affinityOut) {
Chris Wren66dfcc52015-11-19 12:39:38 -0500229 long start = SystemClock.elapsedRealtime();
Chris Wrenf9536642014-04-17 10:01:54 -0400230 float affinity = NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400231 if (extras == null) {
232 return null;
233 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500234 final Set<String> people = new ArraySet<>(peopleOverride);
235 final String[] notificationPeople = getExtraPeople(extras);
236 if (notificationPeople != null ) {
237 people.addAll(Arrays.asList(getExtraPeople(extras)));
Chris Wrenf9536642014-04-17 10:01:54 -0400238 }
239
Chris Wren1a5dad82015-06-16 11:23:12 -0400240 if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
Chris Wrenf9536642014-04-17 10:01:54 -0400241 final LinkedList<String> pendingLookups = new LinkedList<String>();
Julia Reynolds22f02b32016-12-01 15:05:13 -0500242 int personIdx = 0;
243 for (String handle : people) {
Chris Wrenf9536642014-04-17 10:01:54 -0400244 if (TextUtils.isEmpty(handle)) continue;
245
246 synchronized (mPeopleCache) {
Chris Wrenda4bd202014-09-04 15:53:52 -0400247 final String cacheKey = getCacheKey(context.getUserId(), handle);
248 LookupResult lookupResult = mPeopleCache.get(cacheKey);
Chris Wrenf9536642014-04-17 10:01:54 -0400249 if (lookupResult == null || lookupResult.isExpired()) {
250 pendingLookups.add(handle);
251 } else {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200252 if (DEBUG) Slog.d(TAG, "using cached lookupResult");
Chris Wrenf9536642014-04-17 10:01:54 -0400253 }
254 if (lookupResult != null) {
255 affinity = Math.max(affinity, lookupResult.getAffinity());
256 }
257 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500258 if (++personIdx == MAX_PEOPLE) {
259 break;
260 }
Chris Wrenf9536642014-04-17 10:01:54 -0400261 }
262
263 // record the best available data, so far:
John Spurlock2b122f42014-08-27 16:29:47 -0400264 affinityOut[0] = affinity;
Chris Wrenf9536642014-04-17 10:01:54 -0400265
266 if (pendingLookups.isEmpty()) {
Chris Wren1a5dad82015-06-16 11:23:12 -0400267 if (VERBOSE) Slog.i(TAG, "final affinity: " + affinity);
Chris Wrenf9536642014-04-17 10:01:54 -0400268 return null;
269 }
270
John Spurlock2b122f42014-08-27 16:29:47 -0400271 if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
Chris Wrenda4bd202014-09-04 15:53:52 -0400272 return new PeopleRankingReconsideration(context, key, pendingLookups);
273 }
274
275 private String getCacheKey(int userId, String handle) {
276 return Integer.toString(userId) + ":" + handle;
Chris Wrenf9536642014-04-17 10:01:54 -0400277 }
278
Chris Wren92af3722014-05-27 16:37:02 -0400279 // VisibleForTesting
280 public static String[] getExtraPeople(Bundle extras) {
Chris Wrenfb69da32014-05-15 18:03:11 -0400281 Object people = extras.get(Notification.EXTRA_PEOPLE);
282 if (people instanceof String[]) {
283 return (String[]) people;
Chris Wrenf9536642014-04-17 10:01:54 -0400284 }
285
Chris Wrenfb69da32014-05-15 18:03:11 -0400286 if (people instanceof ArrayList) {
287 ArrayList arrayList = (ArrayList) people;
288
289 if (arrayList.isEmpty()) {
290 return null;
291 }
292
293 if (arrayList.get(0) instanceof String) {
294 ArrayList<String> stringArray = (ArrayList<String>) arrayList;
295 return stringArray.toArray(new String[stringArray.size()]);
296 }
297
298 if (arrayList.get(0) instanceof CharSequence) {
299 ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
300 final int N = charSeqList.size();
301 String[] array = new String[N];
302 for (int i = 0; i < N; i++) {
303 array[i] = charSeqList.get(i).toString();
304 }
305 return array;
306 }
307
308 return null;
Chris Wrenf9536642014-04-17 10:01:54 -0400309 }
310
Chris Wrenfb69da32014-05-15 18:03:11 -0400311 if (people instanceof String) {
312 String[] array = new String[1];
313 array[0] = (String) people;
314 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400315 }
316
Chris Wrenfb69da32014-05-15 18:03:11 -0400317 if (people instanceof char[]) {
318 String[] array = new String[1];
319 array[0] = new String((char[]) people);
320 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400321 }
322
Chris Wrenfb69da32014-05-15 18:03:11 -0400323 if (people instanceof CharSequence) {
324 String[] array = new String[1];
325 array[0] = ((CharSequence) people).toString();
326 return array;
327 }
328
329 if (people instanceof CharSequence[]) {
330 CharSequence[] charSeqArray = (CharSequence[]) people;
Chris Wrenf9536642014-04-17 10:01:54 -0400331 final int N = charSeqArray.length;
Chris Wrenfb69da32014-05-15 18:03:11 -0400332 String[] array = new String[N];
Chris Wrenf9536642014-04-17 10:01:54 -0400333 for (int i = 0; i < N; i++) {
Chris Wrenfb69da32014-05-15 18:03:11 -0400334 array[i] = charSeqArray[i].toString();
Chris Wrenf9536642014-04-17 10:01:54 -0400335 }
Chris Wrenfb69da32014-05-15 18:03:11 -0400336 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400337 }
338
Chris Wrenf9536642014-04-17 10:01:54 -0400339 return null;
340 }
341
Chris Wrenda4bd202014-09-04 15:53:52 -0400342 private LookupResult resolvePhoneContact(Context context, final String number) {
Chris Wren44d81a42014-05-14 17:38:05 -0400343 Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
344 Uri.encode(number));
Chris Wrenda4bd202014-09-04 15:53:52 -0400345 return searchContacts(context, phoneUri);
Chris Wrenf9536642014-04-17 10:01:54 -0400346 }
347
Chris Wrenda4bd202014-09-04 15:53:52 -0400348 private LookupResult resolveEmailContact(Context context, final String email) {
Chris Wren44d81a42014-05-14 17:38:05 -0400349 Uri numberUri = Uri.withAppendedPath(
350 ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
351 Uri.encode(email));
Chris Wrenda4bd202014-09-04 15:53:52 -0400352 return searchContacts(context, numberUri);
Chris Wren44d81a42014-05-14 17:38:05 -0400353 }
354
Chris Wrenda4bd202014-09-04 15:53:52 -0400355 private LookupResult searchContacts(Context context, Uri lookupUri) {
Chris Wren44d81a42014-05-14 17:38:05 -0400356 LookupResult lookupResult = new LookupResult();
Chris Wrenf9536642014-04-17 10:01:54 -0400357 Cursor c = null;
358 try {
Chris Wrenda4bd202014-09-04 15:53:52 -0400359 c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
Christoph Studer9ffa5002014-09-26 16:57:28 +0200360 if (c == null) {
361 Slog.w(TAG, "Null cursor from contacts query.");
362 return lookupResult;
Chris Wrenf9536642014-04-17 10:01:54 -0400363 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200364 while (c.moveToNext()) {
365 lookupResult.mergeContact(c);
366 }
367 } catch (Throwable t) {
Chris Wrenf9536642014-04-17 10:01:54 -0400368 Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
369 } finally {
370 if (c != null) {
371 c.close();
372 }
373 }
Chris Wrenf9536642014-04-17 10:01:54 -0400374 return lookupResult;
375 }
376
Chris Wrenf9536642014-04-17 10:01:54 -0400377 private static class LookupResult {
378 private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
Chris Wrenf9536642014-04-17 10:01:54 -0400379
380 private final long mExpireMillis;
Christoph Studer9ffa5002014-09-26 16:57:28 +0200381 private float mAffinity = NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400382
Chris Wren44d81a42014-05-14 17:38:05 -0400383 public LookupResult() {
Chris Wrenf9536642014-04-17 10:01:54 -0400384 mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
385 }
386
Christoph Studer9ffa5002014-09-26 16:57:28 +0200387 public void mergeContact(Cursor cursor) {
388 mAffinity = Math.max(mAffinity, VALID_CONTACT);
389
390 // Contact ID
391 int id;
Chris Wren44d81a42014-05-14 17:38:05 -0400392 final int idIdx = cursor.getColumnIndex(Contacts._ID);
393 if (idIdx >= 0) {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200394 id = cursor.getInt(idIdx);
395 if (DEBUG) Slog.d(TAG, "contact _ID is: " + id);
Chris Wren44d81a42014-05-14 17:38:05 -0400396 } else {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200397 id = -1;
398 Slog.i(TAG, "invalid cursor: no _ID");
Chris Wren44d81a42014-05-14 17:38:05 -0400399 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200400
401 // Starred
Chris Wren44d81a42014-05-14 17:38:05 -0400402 final int starIdx = cursor.getColumnIndex(Contacts.STARRED);
403 if (starIdx >= 0) {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200404 boolean isStarred = cursor.getInt(starIdx) != 0;
405 if (isStarred) {
406 mAffinity = Math.max(mAffinity, STARRED_CONTACT);
407 }
408 if (DEBUG) Slog.d(TAG, "contact STARRED is: " + isStarred);
Chris Wren44d81a42014-05-14 17:38:05 -0400409 } else {
410 if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");
411 }
412 }
413
Christoph Studer9ffa5002014-09-26 16:57:28 +0200414 private boolean isExpired() {
Chris Wrenf9536642014-04-17 10:01:54 -0400415 return mExpireMillis < System.currentTimeMillis();
416 }
417
Christoph Studer9ffa5002014-09-26 16:57:28 +0200418 private boolean isInvalid() {
419 return mAffinity == NONE || isExpired();
Chris Wrenf9536642014-04-17 10:01:54 -0400420 }
421
422 public float getAffinity() {
423 if (isInvalid()) {
424 return NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400425 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200426 return mAffinity;
Chris Wrenf9536642014-04-17 10:01:54 -0400427 }
428 }
John Spurlock2b122f42014-08-27 16:29:47 -0400429
430 private class PeopleRankingReconsideration extends RankingReconsideration {
431 private final LinkedList<String> mPendingLookups;
Chris Wrenda4bd202014-09-04 15:53:52 -0400432 private final Context mContext;
John Spurlock2b122f42014-08-27 16:29:47 -0400433
434 private float mContactAffinity = NONE;
Chris Wren5eab2b72015-06-16 13:56:22 -0400435 private NotificationRecord mRecord;
John Spurlock2b122f42014-08-27 16:29:47 -0400436
Chris Wrenda4bd202014-09-04 15:53:52 -0400437 private PeopleRankingReconsideration(Context context, String key, LinkedList<String> pendingLookups) {
John Spurlock2b122f42014-08-27 16:29:47 -0400438 super(key);
Chris Wrenda4bd202014-09-04 15:53:52 -0400439 mContext = context;
John Spurlock2b122f42014-08-27 16:29:47 -0400440 mPendingLookups = pendingLookups;
441 }
442
443 @Override
444 public void work() {
Chris Wren66dfcc52015-11-19 12:39:38 -0500445 long start = SystemClock.elapsedRealtime();
Chris Wren1a5dad82015-06-16 11:23:12 -0400446 if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
Christoph Studer12aeda82014-09-23 19:08:56 +0200447 long timeStartMs = System.currentTimeMillis();
John Spurlock2b122f42014-08-27 16:29:47 -0400448 for (final String handle: mPendingLookups) {
449 LookupResult lookupResult = null;
450 final Uri uri = Uri.parse(handle);
451 if ("tel".equals(uri.getScheme())) {
452 if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400453 lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
John Spurlock2b122f42014-08-27 16:29:47 -0400454 } else if ("mailto".equals(uri.getScheme())) {
455 if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400456 lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
John Spurlock2b122f42014-08-27 16:29:47 -0400457 } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
458 if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400459 lookupResult = searchContacts(mContext, uri);
John Spurlock2b122f42014-08-27 16:29:47 -0400460 } else {
461 lookupResult = new LookupResult(); // invalid person for the cache
462 Slog.w(TAG, "unsupported URI " + handle);
463 }
464 if (lookupResult != null) {
465 synchronized (mPeopleCache) {
Chris Wrenda4bd202014-09-04 15:53:52 -0400466 final String cacheKey = getCacheKey(mContext.getUserId(), handle);
467 mPeopleCache.put(cacheKey, lookupResult);
John Spurlock2b122f42014-08-27 16:29:47 -0400468 }
Chris Wrenf37dae72014-09-12 12:09:17 -0400469 if (DEBUG) Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
John Spurlock2b122f42014-08-27 16:29:47 -0400470 mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
Chris Wrenf37dae72014-09-12 12:09:17 -0400471 } else {
472 if (DEBUG) Slog.d(TAG, "lookupResult is null");
John Spurlock2b122f42014-08-27 16:29:47 -0400473 }
474 }
Christoph Studer12aeda82014-09-23 19:08:56 +0200475 if (DEBUG) {
476 Slog.d(TAG, "Validation finished in " + (System.currentTimeMillis() - timeStartMs) +
477 "ms");
478 }
Chris Wren723aa762015-04-09 15:35:23 -0400479
Chris Wren5eab2b72015-06-16 13:56:22 -0400480 if (mRecord != null) {
481 mUsageStats.registerPeopleAffinity(mRecord, mContactAffinity > NONE,
482 mContactAffinity == STARRED_CONTACT, false /* cached */);
483 }
John Spurlock2b122f42014-08-27 16:29:47 -0400484 }
485
486 @Override
487 public void applyChangesLocked(NotificationRecord operand) {
488 float affinityBound = operand.getContactAffinity();
489 operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
Chris Wren1a5dad82015-06-16 11:23:12 -0400490 if (VERBOSE) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
John Spurlock2b122f42014-08-27 16:29:47 -0400491 }
492
493 public float getContactAffinity() {
494 return mContactAffinity;
495 }
Chris Wren5eab2b72015-06-16 13:56:22 -0400496
497 public void setRecord(NotificationRecord record) {
498 mRecord = record;
499 }
John Spurlock2b122f42014-08-27 16:29:47 -0400500 }
Chris Wrenf9536642014-04-17 10:01:54 -0400501}
502