ValidateNotificationPeoplepublic class ValidateNotificationPeople extends Object implements NotificationSignalExtractorThis {@link NotificationSignalExtractor} attempts to validate
people references. Also elevates the priority of real people.
{@hide} |
Fields Summary |
---|
private static final String | TAG | private static final boolean | INFO | private static final boolean | DEBUG | private static final boolean | ENABLE_PEOPLE_VALIDATOR | private static final String | SETTING_ENABLE_PEOPLE_VALIDATOR | private static final String[] | LOOKUP_PROJECTION | private static final int | MAX_PEOPLE | private static final int | PEOPLE_CACHE_SIZE | static final float | NONEIndicates that the notification does not reference any valid contacts. | static final float | VALID_CONTACTAffinity will be equal to or greater than this value on notifications
that reference a valid contact. | static final float | STARRED_CONTACTAffinity will be equal to or greater than this value on notifications
that reference a starred contact. | protected boolean | mEnabled | private android.content.Context | mBaseContext | private android.util.LruCache | mPeopleCache | private Map | mUserToContextMap | private android.os.Handler | mHandler | private android.database.ContentObserver | mObserver | private int | mEvictionCount |
Methods Summary |
---|
private java.lang.String | getCacheKey(int userId, java.lang.String handle)
return Integer.toString(userId) + ":" + handle;
| public float | getContactAffinity(android.os.UserHandle userHandle, android.os.Bundle extras, int timeoutMs, float timeoutAffinity)
if (DEBUG) Slog.d(TAG, "checking affinity for " + userHandle);
if (extras == null) return NONE;
final String key = Long.toString(System.nanoTime());
final float[] affinityOut = new float[1];
Context context = getContextAsUser(userHandle);
if (context == null) {
return NONE;
}
final PeopleRankingReconsideration prr = validatePeople(context, key, extras, affinityOut);
float affinity = affinityOut[0];
if (prr != null) {
// Perform the heavy work on a background thread so we can abort when we hit the
// timeout.
final Semaphore s = new Semaphore(0);
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
prr.work();
s.release();
}
});
try {
if (!s.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
Slog.w(TAG, "Timeout while waiting for affinity: " + key + ". "
+ "Returning timeoutAffinity=" + timeoutAffinity);
return timeoutAffinity;
}
} catch (InterruptedException e) {
Slog.w(TAG, "InterruptedException while waiting for affinity: " + key + ". "
+ "Returning affinity=" + affinity, e);
return affinity;
}
affinity = Math.max(prr.getContactAffinity(), affinity);
}
return affinity;
| private android.content.Context | getContextAsUser(android.os.UserHandle userHandle)
Context context = mUserToContextMap.get(userHandle.getIdentifier());
if (context == null) {
try {
context = mBaseContext.createPackageContextAsUser("android", 0, userHandle);
mUserToContextMap.put(userHandle.getIdentifier(), context);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "failed to create package context for lookups", e);
}
}
return context;
| public static java.lang.String[] | getExtraPeople(android.os.Bundle extras)
Object people = extras.get(Notification.EXTRA_PEOPLE);
if (people instanceof String[]) {
return (String[]) people;
}
if (people instanceof ArrayList) {
ArrayList arrayList = (ArrayList) people;
if (arrayList.isEmpty()) {
return null;
}
if (arrayList.get(0) instanceof String) {
ArrayList<String> stringArray = (ArrayList<String>) arrayList;
return stringArray.toArray(new String[stringArray.size()]);
}
if (arrayList.get(0) instanceof CharSequence) {
ArrayList<CharSequence> charSeqList = (ArrayList<CharSequence>) arrayList;
final int N = charSeqList.size();
String[] array = new String[N];
for (int i = 0; i < N; i++) {
array[i] = charSeqList.get(i).toString();
}
return array;
}
return null;
}
if (people instanceof String) {
String[] array = new String[1];
array[0] = (String) people;
return array;
}
if (people instanceof char[]) {
String[] array = new String[1];
array[0] = new String((char[]) people);
return array;
}
if (people instanceof CharSequence) {
String[] array = new String[1];
array[0] = ((CharSequence) people).toString();
return array;
}
if (people instanceof CharSequence[]) {
CharSequence[] charSeqArray = (CharSequence[]) people;
final int N = charSeqArray.length;
String[] array = new String[N];
for (int i = 0; i < N; i++) {
array[i] = charSeqArray[i].toString();
}
return array;
}
return null;
| public void | initialize(android.content.Context context)
if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
mUserToContextMap = new ArrayMap<>();
mBaseContext = context;
mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
if (mEnabled) {
mHandler = new Handler();
mObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
super.onChange(selfChange, uri, userId);
if (DEBUG || mEvictionCount % 100 == 0) {
if (INFO) Slog.i(TAG, "mEvictionCount: " + mEvictionCount);
}
mPeopleCache.evictAll();
mEvictionCount++;
}
};
mBaseContext.getContentResolver().registerContentObserver(Contacts.CONTENT_URI, true,
mObserver, UserHandle.USER_ALL);
}
| public RankingReconsideration | process(NotificationRecord record)
if (!mEnabled) {
if (INFO) Slog.i(TAG, "disabled");
return null;
}
if (record == null || record.getNotification() == null) {
if (INFO) Slog.i(TAG, "skipping empty notification");
return null;
}
if (record.getUserId() == UserHandle.USER_ALL) {
if (INFO) Slog.i(TAG, "skipping global notification");
return null;
}
Context context = getContextAsUser(record.getUser());
if (context == null) {
if (INFO) Slog.i(TAG, "skipping notification that lacks a context");
return null;
}
return validatePeople(context, record);
| private com.android.server.notification.ValidateNotificationPeople$LookupResult | resolveEmailContact(android.content.Context context, java.lang.String email)
Uri numberUri = Uri.withAppendedPath(
ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI,
Uri.encode(email));
return searchContacts(context, numberUri);
| private com.android.server.notification.ValidateNotificationPeople$LookupResult | resolvePhoneContact(android.content.Context context, java.lang.String number)
Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(number));
return searchContacts(context, phoneUri);
| private com.android.server.notification.ValidateNotificationPeople$LookupResult | searchContacts(android.content.Context context, android.net.Uri lookupUri)
LookupResult lookupResult = new LookupResult();
Cursor c = null;
try {
c = context.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null);
if (c == null) {
Slog.w(TAG, "Null cursor from contacts query.");
return lookupResult;
}
while (c.moveToNext()) {
lookupResult.mergeContact(c);
}
} catch (Throwable t) {
Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
} finally {
if (c != null) {
c.close();
}
}
return lookupResult;
| public void | setConfig(RankingConfig config)
// ignore: config has no relevant information yet.
| private RankingReconsideration | validatePeople(android.content.Context context, NotificationRecord record)
final String key = record.getKey();
final Bundle extras = record.getNotification().extras;
final float[] affinityOut = new float[1];
final RankingReconsideration rr = validatePeople(context, key, extras, affinityOut);
record.setContactAffinity(affinityOut[0]);
return rr;
| private com.android.server.notification.ValidateNotificationPeople$PeopleRankingReconsideration | validatePeople(android.content.Context context, java.lang.String key, android.os.Bundle extras, float[] affinityOut)
float affinity = NONE;
if (extras == null) {
return null;
}
final String[] people = getExtraPeople(extras);
if (people == null || people.length == 0) {
return null;
}
if (INFO) Slog.i(TAG, "Validating: " + key);
final LinkedList<String> pendingLookups = new LinkedList<String>();
for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
final String handle = people[personIdx];
if (TextUtils.isEmpty(handle)) continue;
synchronized (mPeopleCache) {
final String cacheKey = getCacheKey(context.getUserId(), handle);
LookupResult lookupResult = mPeopleCache.get(cacheKey);
if (lookupResult == null || lookupResult.isExpired()) {
pendingLookups.add(handle);
} else {
if (DEBUG) Slog.d(TAG, "using cached lookupResult");
}
if (lookupResult != null) {
affinity = Math.max(affinity, lookupResult.getAffinity());
}
}
}
// record the best available data, so far:
affinityOut[0] = affinity;
if (pendingLookups.isEmpty()) {
if (INFO) Slog.i(TAG, "final affinity: " + affinity);
return null;
}
if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
return new PeopleRankingReconsideration(context, key, pendingLookups);
|
|