blob: 896480ffb560bc6b54af10a75a4f9c7a4d912d62 [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
Julia Reynoldsc861a3d2018-02-15 10:34:49 -0500147 @Override
148 public void setZenHelper(ZenModeHelper helper) {
149
150 }
151
Christoph Studer12aeda82014-09-23 19:08:56 +0200152 /**
153 * @param extras extras of the notification with EXTRA_PEOPLE populated
154 * @param timeoutMs timeout in milliseconds to wait for contacts response
155 * @param timeoutAffinity affinity to return when the timeout specified via
156 * <code>timeoutMs</code> is hit
157 */
158 public float getContactAffinity(UserHandle userHandle, Bundle extras, int timeoutMs,
159 float timeoutAffinity) {
Chris Wren7381daa2014-09-05 11:30:32 -0400160 if (DEBUG) Slog.d(TAG, "checking affinity for " + userHandle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400161 if (extras == null) return NONE;
162 final String key = Long.toString(System.nanoTime());
163 final float[] affinityOut = new float[1];
164 Context context = getContextAsUser(userHandle);
165 if (context == null) {
166 return NONE;
167 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500168 final PeopleRankingReconsideration prr =
169 validatePeople(context, key, extras, null, affinityOut);
Chris Wrenda4bd202014-09-04 15:53:52 -0400170 float affinity = affinityOut[0];
Christoph Studer12aeda82014-09-23 19:08:56 +0200171
Chris Wrenda4bd202014-09-04 15:53:52 -0400172 if (prr != null) {
Christoph Studer12aeda82014-09-23 19:08:56 +0200173 // Perform the heavy work on a background thread so we can abort when we hit the
174 // timeout.
175 final Semaphore s = new Semaphore(0);
176 AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
177 @Override
178 public void run() {
179 prr.work();
180 s.release();
181 }
182 });
183
184 try {
185 if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
186 Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "
187 + "Returning timeoutAffinity=" + timeoutAffinity);
188 return timeoutAffinity;
189 }
190 } catch (InterruptedException e) {
191 Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "
192 + "Returning affinity=" + affinity, e);
193 return affinity;
194 }
195
Chris Wrenda4bd202014-09-04 15:53:52 -0400196 affinity = Math.max(prr.getContactAffinity(), affinity);
197 }
198 return affinity;
199 }
200
201 private Context getContextAsUser(UserHandle userHandle) {
202 Context context = mUserToContextMap.get(userHandle.getIdentifier());
203 if (context == null) {
204 try {
205 context = mBaseContext.createPackageContextAsUser("android", 0, userHandle);
206 mUserToContextMap.put(userHandle.getIdentifier(), context);
207 } catch (PackageManager.NameNotFoundException e) {
208 Log.e(TAG, "failed to create package context for lookups", e);
209 }
210 }
211 return context;
212 }
213
214 private RankingReconsideration validatePeople(Context context,
215 final NotificationRecord record) {
John Spurlock2b122f42014-08-27 16:29:47 -0400216 final String key = record.getKey();
217 final Bundle extras = record.getNotification().extras;
218 final float[] affinityOut = new float[1];
Julia Reynolds22f02b32016-12-01 15:05:13 -0500219 final PeopleRankingReconsideration rr =
220 validatePeople(context, key, extras, record.getPeopleOverride(), affinityOut);
Chris Wren5eab2b72015-06-16 13:56:22 -0400221 final float affinity = affinityOut[0];
222 record.setContactAffinity(affinity);
223 if (rr == null) {
224 mUsageStats.registerPeopleAffinity(record, affinity > NONE, affinity == STARRED_CONTACT,
225 true /* cached */);
226 } else {
227 rr.setRecord(record);
228 }
John Spurlock2b122f42014-08-27 16:29:47 -0400229 return rr;
230 }
231
Chris Wrenda4bd202014-09-04 15:53:52 -0400232 private PeopleRankingReconsideration validatePeople(Context context, String key, Bundle extras,
Julia Reynolds22f02b32016-12-01 15:05:13 -0500233 List<String> peopleOverride, float[] affinityOut) {
Chris Wren66dfcc52015-11-19 12:39:38 -0500234 long start = SystemClock.elapsedRealtime();
Chris Wrenf9536642014-04-17 10:01:54 -0400235 float affinity = NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400236 if (extras == null) {
237 return null;
238 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500239 final Set<String> people = new ArraySet<>(peopleOverride);
240 final String[] notificationPeople = getExtraPeople(extras);
241 if (notificationPeople != null ) {
242 people.addAll(Arrays.asList(getExtraPeople(extras)));
Chris Wrenf9536642014-04-17 10:01:54 -0400243 }
244
Chris Wren1a5dad82015-06-16 11:23:12 -0400245 if (VERBOSE) Slog.i(TAG, "Validating: " + key + " for " + context.getUserId());
Chris Wrenf9536642014-04-17 10:01:54 -0400246 final LinkedList<String> pendingLookups = new LinkedList<String>();
Julia Reynolds22f02b32016-12-01 15:05:13 -0500247 int personIdx = 0;
248 for (String handle : people) {
Chris Wrenf9536642014-04-17 10:01:54 -0400249 if (TextUtils.isEmpty(handle)) continue;
250
251 synchronized (mPeopleCache) {
Chris Wrenda4bd202014-09-04 15:53:52 -0400252 final String cacheKey = getCacheKey(context.getUserId(), handle);
253 LookupResult lookupResult = mPeopleCache.get(cacheKey);
Chris Wrenf9536642014-04-17 10:01:54 -0400254 if (lookupResult == null || lookupResult.isExpired()) {
255 pendingLookups.add(handle);
256 } else {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200257 if (DEBUG) Slog.d(TAG, "using cached lookupResult");
Chris Wrenf9536642014-04-17 10:01:54 -0400258 }
259 if (lookupResult != null) {
260 affinity = Math.max(affinity, lookupResult.getAffinity());
261 }
262 }
Julia Reynolds22f02b32016-12-01 15:05:13 -0500263 if (++personIdx == MAX_PEOPLE) {
264 break;
265 }
Chris Wrenf9536642014-04-17 10:01:54 -0400266 }
267
268 // record the best available data, so far:
John Spurlock2b122f42014-08-27 16:29:47 -0400269 affinityOut[0] = affinity;
Chris Wrenf9536642014-04-17 10:01:54 -0400270
271 if (pendingLookups.isEmpty()) {
Chris Wren1a5dad82015-06-16 11:23:12 -0400272 if (VERBOSE) Slog.i(TAG, "final affinity: " + affinity);
Chris Wrenf9536642014-04-17 10:01:54 -0400273 return null;
274 }
275
John Spurlock2b122f42014-08-27 16:29:47 -0400276 if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
Chris Wrenda4bd202014-09-04 15:53:52 -0400277 return new PeopleRankingReconsideration(context, key, pendingLookups);
278 }
279
280 private String getCacheKey(int userId, String handle) {
281 return Integer.toString(userId) + ":" + handle;
Chris Wrenf9536642014-04-17 10:01:54 -0400282 }
283
Chris Wren92af3722014-05-27 16:37:02 -0400284 // VisibleForTesting
285 public static String[] getExtraPeople(Bundle extras) {
Selim Cineke7238dd2017-12-14 17:48:32 -0800286 Object people = extras.get(Notification.EXTRA_PEOPLE_LIST);
Chris Wrenfb69da32014-05-15 18:03:11 -0400287 if (people instanceof String[]) {
288 return (String[]) people;
Chris Wrenf9536642014-04-17 10:01:54 -0400289 }
290
Chris Wrenfb69da32014-05-15 18:03:11 -0400291 if (people instanceof ArrayList) {
292 ArrayList arrayList = (ArrayList) people;
293
294 if (arrayList.isEmpty()) {
295 return null;
296 }
297
298 if (arrayList.get(0) instanceof String) {
299 ArrayList<String> stringArray = (ArrayList<String>) arrayList;
300 return stringArray.toArray(new String[stringArray.size()]);
301 }
302
303 if (arrayList.get(0) instanceof CharSequence) {
304 ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
305 final int N = charSeqList.size();
306 String[] array = new String[N];
307 for (int i = 0; i < N; i++) {
308 array[i] = charSeqList.get(i).toString();
309 }
310 return array;
311 }
312
Selim Cineke7238dd2017-12-14 17:48:32 -0800313 if (arrayList.get(0) instanceof Notification.Person) {
314 ArrayList<Notification.Person> list = (ArrayList<Notification.Person>) arrayList;
315 final int N = list.size();
316 String[] array = new String[N];
317 for (int i = 0; i < N; i++) {
318 array[i] = list.get(i).resolveToLegacyUri();
319 }
320 return array;
321 }
322
Chris Wrenfb69da32014-05-15 18:03:11 -0400323 return null;
Chris Wrenf9536642014-04-17 10:01:54 -0400324 }
325
Chris Wrenfb69da32014-05-15 18:03:11 -0400326 if (people instanceof String) {
327 String[] array = new String[1];
328 array[0] = (String) people;
329 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400330 }
331
Chris Wrenfb69da32014-05-15 18:03:11 -0400332 if (people instanceof char[]) {
333 String[] array = new String[1];
334 array[0] = new String((char[]) people);
335 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400336 }
337
Chris Wrenfb69da32014-05-15 18:03:11 -0400338 if (people instanceof CharSequence) {
339 String[] array = new String[1];
340 array[0] = ((CharSequence) people).toString();
341 return array;
342 }
343
344 if (people instanceof CharSequence[]) {
345 CharSequence[] charSeqArray = (CharSequence[]) people;
Chris Wrenf9536642014-04-17 10:01:54 -0400346 final int N = charSeqArray.length;
Chris Wrenfb69da32014-05-15 18:03:11 -0400347 String[] array = new String[N];
Chris Wrenf9536642014-04-17 10:01:54 -0400348 for (int i = 0; i < N; i++) {
Chris Wrenfb69da32014-05-15 18:03:11 -0400349 array[i] = charSeqArray[i].toString();
Chris Wrenf9536642014-04-17 10:01:54 -0400350 }
Chris Wrenfb69da32014-05-15 18:03:11 -0400351 return array;
Chris Wrenf9536642014-04-17 10:01:54 -0400352 }
353
Chris Wrenf9536642014-04-17 10:01:54 -0400354 return null;
355 }
356
Chris Wrenda4bd202014-09-04 15:53:52 -0400357 private LookupResult resolvePhoneContact(Context context, final String number) {
Chris Wren44d81a42014-05-14 17:38:05 -0400358 Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
359 Uri.encode(number));
Chris Wrenda4bd202014-09-04 15:53:52 -0400360 return searchContacts(context, phoneUri);
Chris Wrenf9536642014-04-17 10:01:54 -0400361 }
362
Chris Wrenda4bd202014-09-04 15:53:52 -0400363 private LookupResult resolveEmailContact(Context context, final String email) {
Chris Wren44d81a42014-05-14 17:38:05 -0400364 Uri numberUri = Uri.withAppendedPath(
365 ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
366 Uri.encode(email));
Chris Wrenda4bd202014-09-04 15:53:52 -0400367 return searchContacts(context, numberUri);
Chris Wren44d81a42014-05-14 17:38:05 -0400368 }
369
Chris Wrenda4bd202014-09-04 15:53:52 -0400370 private LookupResult searchContacts(Context context, Uri lookupUri) {
Chris Wren44d81a42014-05-14 17:38:05 -0400371 LookupResult lookupResult = new LookupResult();
Chris Wrenf9536642014-04-17 10:01:54 -0400372 Cursor c = null;
373 try {
Chris Wrenda4bd202014-09-04 15:53:52 -0400374 c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
Christoph Studer9ffa5002014-09-26 16:57:28 +0200375 if (c == null) {
376 Slog.w(TAG, "Null cursor from contacts query.");
377 return lookupResult;
Chris Wrenf9536642014-04-17 10:01:54 -0400378 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200379 while (c.moveToNext()) {
380 lookupResult.mergeContact(c);
381 }
382 } catch (Throwable t) {
Chris Wrenf9536642014-04-17 10:01:54 -0400383 Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
384 } finally {
385 if (c != null) {
386 c.close();
387 }
388 }
Chris Wrenf9536642014-04-17 10:01:54 -0400389 return lookupResult;
390 }
391
Chris Wrenf9536642014-04-17 10:01:54 -0400392 private static class LookupResult {
393 private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
Chris Wrenf9536642014-04-17 10:01:54 -0400394
395 private final long mExpireMillis;
Christoph Studer9ffa5002014-09-26 16:57:28 +0200396 private float mAffinity = NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400397
Chris Wren44d81a42014-05-14 17:38:05 -0400398 public LookupResult() {
Chris Wrenf9536642014-04-17 10:01:54 -0400399 mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
400 }
401
Christoph Studer9ffa5002014-09-26 16:57:28 +0200402 public void mergeContact(Cursor cursor) {
403 mAffinity = Math.max(mAffinity, VALID_CONTACT);
404
405 // Contact ID
406 int id;
Chris Wren44d81a42014-05-14 17:38:05 -0400407 final int idIdx = cursor.getColumnIndex(Contacts._ID);
408 if (idIdx >= 0) {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200409 id = cursor.getInt(idIdx);
410 if (DEBUG) Slog.d(TAG, "contact _ID is: " + id);
Chris Wren44d81a42014-05-14 17:38:05 -0400411 } else {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200412 id = -1;
413 Slog.i(TAG, "invalid cursor: no _ID");
Chris Wren44d81a42014-05-14 17:38:05 -0400414 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200415
416 // Starred
Chris Wren44d81a42014-05-14 17:38:05 -0400417 final int starIdx = cursor.getColumnIndex(Contacts.STARRED);
418 if (starIdx >= 0) {
Christoph Studer9ffa5002014-09-26 16:57:28 +0200419 boolean isStarred = cursor.getInt(starIdx) != 0;
420 if (isStarred) {
421 mAffinity = Math.max(mAffinity, STARRED_CONTACT);
422 }
423 if (DEBUG) Slog.d(TAG, "contact STARRED is: " + isStarred);
Chris Wren44d81a42014-05-14 17:38:05 -0400424 } else {
425 if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");
426 }
427 }
428
Christoph Studer9ffa5002014-09-26 16:57:28 +0200429 private boolean isExpired() {
Chris Wrenf9536642014-04-17 10:01:54 -0400430 return mExpireMillis < System.currentTimeMillis();
431 }
432
Christoph Studer9ffa5002014-09-26 16:57:28 +0200433 private boolean isInvalid() {
434 return mAffinity == NONE || isExpired();
Chris Wrenf9536642014-04-17 10:01:54 -0400435 }
436
437 public float getAffinity() {
438 if (isInvalid()) {
439 return NONE;
Chris Wrenf9536642014-04-17 10:01:54 -0400440 }
Christoph Studer9ffa5002014-09-26 16:57:28 +0200441 return mAffinity;
Chris Wrenf9536642014-04-17 10:01:54 -0400442 }
443 }
John Spurlock2b122f42014-08-27 16:29:47 -0400444
445 private class PeopleRankingReconsideration extends RankingReconsideration {
446 private final LinkedList<String> mPendingLookups;
Chris Wrenda4bd202014-09-04 15:53:52 -0400447 private final Context mContext;
John Spurlock2b122f42014-08-27 16:29:47 -0400448
449 private float mContactAffinity = NONE;
Chris Wren5eab2b72015-06-16 13:56:22 -0400450 private NotificationRecord mRecord;
John Spurlock2b122f42014-08-27 16:29:47 -0400451
Julia Reynoldsc861a3d2018-02-15 10:34:49 -0500452 private PeopleRankingReconsideration(Context context, String key,
453 LinkedList<String> pendingLookups) {
John Spurlock2b122f42014-08-27 16:29:47 -0400454 super(key);
Chris Wrenda4bd202014-09-04 15:53:52 -0400455 mContext = context;
John Spurlock2b122f42014-08-27 16:29:47 -0400456 mPendingLookups = pendingLookups;
457 }
458
459 @Override
460 public void work() {
Chris Wren66dfcc52015-11-19 12:39:38 -0500461 long start = SystemClock.elapsedRealtime();
Chris Wren1a5dad82015-06-16 11:23:12 -0400462 if (VERBOSE) Slog.i(TAG, "Executing: validation for: " + mKey);
Christoph Studer12aeda82014-09-23 19:08:56 +0200463 long timeStartMs = System.currentTimeMillis();
John Spurlock2b122f42014-08-27 16:29:47 -0400464 for (final String handle: mPendingLookups) {
465 LookupResult lookupResult = null;
466 final Uri uri = Uri.parse(handle);
467 if ("tel".equals(uri.getScheme())) {
468 if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400469 lookupResult = resolvePhoneContact(mContext, uri.getSchemeSpecificPart());
John Spurlock2b122f42014-08-27 16:29:47 -0400470 } else if ("mailto".equals(uri.getScheme())) {
471 if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400472 lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
John Spurlock2b122f42014-08-27 16:29:47 -0400473 } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
474 if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
Chris Wrenda4bd202014-09-04 15:53:52 -0400475 lookupResult = searchContacts(mContext, uri);
John Spurlock2b122f42014-08-27 16:29:47 -0400476 } else {
477 lookupResult = new LookupResult(); // invalid person for the cache
Selim Cineke7238dd2017-12-14 17:48:32 -0800478 if (!"name".equals(uri.getScheme())) {
479 Slog.w(TAG, "unsupported URI " + handle);
480 }
John Spurlock2b122f42014-08-27 16:29:47 -0400481 }
482 if (lookupResult != null) {
483 synchronized (mPeopleCache) {
Chris Wrenda4bd202014-09-04 15:53:52 -0400484 final String cacheKey = getCacheKey(mContext.getUserId(), handle);
485 mPeopleCache.put(cacheKey, lookupResult);
John Spurlock2b122f42014-08-27 16:29:47 -0400486 }
Julia Reynoldsc861a3d2018-02-15 10:34:49 -0500487 if (DEBUG) {
488 Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
489 }
John Spurlock2b122f42014-08-27 16:29:47 -0400490 mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
Chris Wrenf37dae72014-09-12 12:09:17 -0400491 } else {
492 if (DEBUG) Slog.d(TAG, "lookupResult is null");
John Spurlock2b122f42014-08-27 16:29:47 -0400493 }
494 }
Christoph Studer12aeda82014-09-23 19:08:56 +0200495 if (DEBUG) {
496 Slog.d(TAG, "Validation finished in " + (System.currentTimeMillis() - timeStartMs) +
497 "ms");
498 }
Chris Wren723aa762015-04-09 15:35:23 -0400499
Chris Wren5eab2b72015-06-16 13:56:22 -0400500 if (mRecord != null) {
501 mUsageStats.registerPeopleAffinity(mRecord, mContactAffinity > NONE,
502 mContactAffinity == STARRED_CONTACT, false /* cached */);
503 }
John Spurlock2b122f42014-08-27 16:29:47 -0400504 }
505
506 @Override
507 public void applyChangesLocked(NotificationRecord operand) {
508 float affinityBound = operand.getContactAffinity();
509 operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
Chris Wren1a5dad82015-06-16 11:23:12 -0400510 if (VERBOSE) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
John Spurlock2b122f42014-08-27 16:29:47 -0400511 }
512
513 public float getContactAffinity() {
514 return mContactAffinity;
515 }
Chris Wren5eab2b72015-06-16 13:56:22 -0400516
517 public void setRecord(NotificationRecord record) {
518 mRecord = record;
519 }
John Spurlock2b122f42014-08-27 16:29:47 -0400520 }
Chris Wrenf9536642014-04-17 10:01:54 -0400521}
522