am 84a39cb0: Merge "Add a hidden system method to check call filter." into lmp-dev

* commit '84a39cb09e300386d6875c1f555c969089814fd8':
  Add a hidden system method to check call filter.
This commit is contained in:
John Spurlock
2014-08-29 23:34:51 +00:00
committed by Android Git Automerger
6 changed files with 130 additions and 46 deletions

View File

@@ -23,6 +23,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.net.Uri;
import android.os.Bundle;
import android.service.notification.Condition;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
@@ -66,6 +67,7 @@ interface INotificationManager
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
ComponentName getEffectsSuppressor();
boolean matchesCallFilter(in Bundle extras);
ZenModeConfig getZenModeConfig();
boolean setZenModeConfig(in ZenModeConfig config);

View File

@@ -20,6 +20,7 @@ import android.annotation.SdkConstant;
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -251,5 +252,17 @@ public class NotificationManager
}
}
/**
* @hide
*/
public boolean matchesCallFilter(Bundle extras) {
INotificationManager service = getService();
try {
return service.matchesCallFilter(extras);
} catch (RemoteException e) {
return false;
}
}
private Context mContext;
}

View File

@@ -52,6 +52,7 @@ import android.media.AudioManager;
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -1438,7 +1439,7 @@ public class NotificationManagerService extends SystemService {
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump NotificationManager from from pid="
pw.println("Permission Denial: can't dump NotificationManager from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
@@ -1452,6 +1453,13 @@ public class NotificationManagerService extends SystemService {
enforceSystemOrSystemUI("INotificationManager.getEffectsSuppressor");
return mEffectsSuppressor;
}
@Override
public boolean matchesCallFilter(Bundle extras) {
enforceSystemOrSystemUI("INotificationManager.matchesCallFilter");
return mZenModeHelper.matchesCallFilter(extras,
mRankingHelper.findExtractor(ValidateNotificationPeople.class));
}
};
private String[] getActiveNotificationKeys(INotificationListener token) {

View File

@@ -87,6 +87,17 @@ public class RankingHelper implements RankingConfig {
mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
}
public <T extends NotificationSignalExtractor> T findExtractor(Class<T> extractorClass) {
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {
final NotificationSignalExtractor extractor = mSignalExtractors[i];
if (extractorClass.equals(extractor.getClass())) {
return (T) extractor;
}
}
return null;
}
public void extractSignals(NotificationRecord r) {
final int N = mSignalExtractors.length;
for (int i = 0; i < N; i++) {

View File

@@ -71,8 +71,17 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
private LruCache<String, LookupResult> mPeopleCache;
private RankingReconsideration validatePeople(final NotificationRecord record) {
final String key = record.getKey();
final Bundle extras = record.getNotification().extras;
final float[] affinityOut = new float[1];
final RankingReconsideration rr = validatePeople(key, extras, affinityOut);
record.setContactAffinity(affinityOut[0]);
return rr;
}
private PeopleRankingReconsideration validatePeople(String key, Bundle extras,
float[] affinityOut) {
float affinity = NONE;
Bundle extras = record.getNotification().extras;
if (extras == null) {
return null;
}
@@ -82,7 +91,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return null;
}
if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey());
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];
@@ -102,51 +111,15 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
}
// record the best available data, so far:
record.setContactAffinity(affinity);
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: " + record.sbn.getKey());
return new RankingReconsideration(record.getKey()) {
float mContactAffinity = NONE;
@Override
public void work() {
if (INFO) Slog.i(TAG, "Executing: validation for: " + record.getKey());
for (final String handle: pendingLookups) {
LookupResult lookupResult = null;
final Uri uri = Uri.parse(handle);
if ("tel".equals(uri.getScheme())) {
if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
} else if ("mailto".equals(uri.getScheme())) {
if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
} else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
lookupResult = searchContacts(uri);
} else {
lookupResult = new LookupResult(); // invalid person for the cache
Slog.w(TAG, "unsupported URI " + handle);
}
if (lookupResult != null) {
synchronized (mPeopleCache) {
mPeopleCache.put(handle, lookupResult);
}
mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
}
}
}
@Override
public void applyChangesLocked(NotificationRecord operand) {
float affinityBound = operand.getContactAffinity();
operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
}
};
if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + key);
return new PeopleRankingReconsideration(key, pendingLookups);
}
// VisibleForTesting
@@ -269,6 +242,19 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
// ignore: config has no relevant information yet.
}
public float getContactAffinity(Bundle extras) {
if (extras == null) return NONE;
final String key = Long.toString(System.nanoTime());
final float[] affinityOut = new float[1];
final PeopleRankingReconsideration prr = validatePeople(key, extras, affinityOut);
float affinity = affinityOut[0];
if (prr != null) {
prr.work();
affinity = Math.max(prr.getContactAffinity(), affinity);
}
return affinity;
}
private static class LookupResult {
private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
public static final int INVALID_ID = -1;
@@ -328,5 +314,55 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor {
return this;
}
}
private class PeopleRankingReconsideration extends RankingReconsideration {
private final LinkedList<String> mPendingLookups;
private float mContactAffinity = NONE;
private PeopleRankingReconsideration(String key, LinkedList<String> pendingLookups) {
super(key);
mPendingLookups = pendingLookups;
}
@Override
public void work() {
if (INFO) Slog.i(TAG, "Executing: validation for: " + mKey);
for (final String handle: mPendingLookups) {
LookupResult lookupResult = null;
final Uri uri = Uri.parse(handle);
if ("tel".equals(uri.getScheme())) {
if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart());
} else if ("mailto".equals(uri.getScheme())) {
if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle);
lookupResult = resolveEmailContact(uri.getSchemeSpecificPart());
} else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
lookupResult = searchContacts(uri);
} else {
lookupResult = new LookupResult(); // invalid person for the cache
Slog.w(TAG, "unsupported URI " + handle);
}
if (lookupResult != null) {
synchronized (mPeopleCache) {
mPeopleCache.put(handle, lookupResult);
}
mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
}
}
}
@Override
public void applyChangesLocked(NotificationRecord operand) {
float affinityBound = operand.getContactAffinity();
operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
if (INFO) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
}
public float getContactAffinity() {
return mContactAffinity;
}
}
}

View File

@@ -34,6 +34,7 @@ import android.database.ContentObserver;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings.Global;
@@ -189,7 +190,7 @@ public class ZenModeHelper {
}
private boolean shouldInterceptAudience(NotificationRecord record) {
if (!audienceMatches(record)) {
if (!audienceMatches(record.getContactAffinity())) {
ZenLog.traceIntercepted(record, "!audienceMatches");
return true;
}
@@ -372,14 +373,27 @@ public class ZenModeHelper {
return record.isCategory(Notification.CATEGORY_MESSAGE) || isDefaultMessagingApp(record);
}
private boolean audienceMatches(NotificationRecord record) {
public boolean matchesCallFilter(Bundle extras, ValidateNotificationPeople validator) {
final int zen = mZenMode;
if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) return false; // nothing gets through
if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
if (!mConfig.allowCalls) return false; // no calls get through
if (validator != null) {
final float contactAffinity = validator.getContactAffinity(extras);
return audienceMatches(contactAffinity);
}
}
return true;
}
private boolean audienceMatches(float contactAffinity) {
switch (mConfig.allowFrom) {
case ZenModeConfig.SOURCE_ANYONE:
return true;
case ZenModeConfig.SOURCE_CONTACT:
return record.getContactAffinity() >= ValidateNotificationPeople.VALID_CONTACT;
return contactAffinity >= ValidateNotificationPeople.VALID_CONTACT;
case ZenModeConfig.SOURCE_STAR:
return record.getContactAffinity() >= ValidateNotificationPeople.STARRED_CONTACT;
return contactAffinity >= ValidateNotificationPeople.STARRED_CONTACT;
default:
Slog.w(TAG, "Encountered unknown source: " + mConfig.allowFrom);
return true;