Merge "Zen: Store notification zen policy per-user." into mnc-dev

This commit is contained in:
John Spurlock
2015-05-28 13:53:42 +00:00
committed by Android (Google) Code Review
6 changed files with 219 additions and 76 deletions

View File

@@ -79,6 +79,7 @@ public class ZenModeConfig implements Parcelable {
private static final int XML_VERSION = 2;
private static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
private static final String ZEN_ATT_USER = "user";
private static final String ALLOW_TAG = "allow";
private static final String ALLOW_ATT_CALLS = "calls";
private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
@@ -117,6 +118,7 @@ public class ZenModeConfig implements Parcelable {
public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
public int allowCallsFrom = DEFAULT_SOURCE;
public int allowMessagesFrom = DEFAULT_SOURCE;
public int user = UserHandle.USER_OWNER;
public ZenRule manualRule;
public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -131,6 +133,7 @@ public class ZenModeConfig implements Parcelable {
allowEvents = source.readInt() == 1;
allowCallsFrom = source.readInt();
allowMessagesFrom = source.readInt();
user = source.readInt();
manualRule = source.readParcelable(null);
final int len = source.readInt();
if (len > 0) {
@@ -153,6 +156,7 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(allowEvents ? 1 : 0);
dest.writeInt(allowCallsFrom);
dest.writeInt(allowMessagesFrom);
dest.writeInt(user);
dest.writeParcelable(manualRule, 0);
if (!automaticRules.isEmpty()) {
final int len = automaticRules.size();
@@ -173,7 +177,8 @@ public class ZenModeConfig implements Parcelable {
@Override
public String toString() {
return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
.append("allowCalls=").append(allowCalls)
.append("user=").append(user)
.append(",allowCalls=").append(allowCalls)
.append(",allowRepeatCallers=").append(allowRepeatCallers)
.append(",allowMessages=").append(allowMessages)
.append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
@@ -185,6 +190,68 @@ public class ZenModeConfig implements Parcelable {
.append(']').toString();
}
private Diff diff(ZenModeConfig to) {
final Diff d = new Diff();
if (to == null) {
return d.addLine("config", "delete");
}
if (user != to.user) {
d.addLine("user", user, to.user);
}
if (allowCalls != to.allowCalls) {
d.addLine("allowCalls", allowCalls, to.allowCalls);
}
if (allowRepeatCallers != to.allowRepeatCallers) {
d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
}
if (allowMessages != to.allowMessages) {
d.addLine("allowMessages", allowMessages, to.allowMessages);
}
if (allowCallsFrom != to.allowCallsFrom) {
d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom);
}
if (allowMessagesFrom != to.allowMessagesFrom) {
d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
}
if (allowReminders != to.allowReminders) {
d.addLine("allowReminders", allowReminders, to.allowReminders);
}
if (allowEvents != to.allowEvents) {
d.addLine("allowEvents", allowEvents, to.allowEvents);
}
final ArraySet<String> allRules = new ArraySet<>();
addKeys(allRules, automaticRules);
addKeys(allRules, to.automaticRules);
final int N = allRules.size();
for (int i = 0; i < N; i++) {
final String rule = allRules.valueAt(i);
final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null;
final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null;
ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
}
ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
return d;
}
public static Diff diff(ZenModeConfig from, ZenModeConfig to) {
if (from == null) {
final Diff d = new Diff();
if (to != null) {
d.addLine("config", "insert");
}
return d;
}
return from.diff(to);
}
private static <T> void addKeys(ArraySet<T> set, ArrayMap<T, ?> map) {
if (map != null) {
for (int i = 0; i < map.size(); i++) {
set.add(map.keyAt(i));
}
}
}
public boolean isValid() {
if (!isValidManualRule(manualRule)) return false;
final int N = automaticRules.size();
@@ -249,6 +316,7 @@ public class ZenModeConfig implements Parcelable {
&& other.allowMessagesFrom == allowMessagesFrom
&& other.allowReminders == allowReminders
&& other.allowEvents == allowEvents
&& other.user == user
&& Objects.equals(other.automaticRules, automaticRules)
&& Objects.equals(other.manualRule, manualRule);
}
@@ -256,7 +324,7 @@ public class ZenModeConfig implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
allowMessagesFrom, allowReminders, allowEvents, automaticRules, manualRule);
allowMessagesFrom, allowReminders, allowEvents, user, automaticRules, manualRule);
}
private static String toDayList(int[] days) {
@@ -312,6 +380,7 @@ public class ZenModeConfig implements Parcelable {
final XmlV1 v1 = XmlV1.readXml(parser);
return migration.migrate(v1);
}
rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
@@ -357,6 +426,7 @@ public class ZenModeConfig implements Parcelable {
public void writeXml(XmlSerializer out) throws IOException {
out.startTag(null, ZEN_TAG);
out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
out.startTag(null, ALLOW_TAG);
out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
@@ -915,6 +985,45 @@ public class ZenModeConfig implements Parcelable {
.append(']').toString();
}
private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
if (d == null) return;
if (from == null) {
if (to != null) {
d.addLine(item, "insert");
}
return;
}
from.appendDiff(d, item, to);
}
private void appendDiff(Diff d, String item, ZenRule to) {
if (to == null) {
d.addLine(item, "delete");
return;
}
if (enabled != to.enabled) {
d.addLine(item, "enabled", enabled, to.enabled);
}
if (snoozing != to.snoozing) {
d.addLine(item, "snoozing", snoozing, to.snoozing);
}
if (!Objects.equals(name, to.name)) {
d.addLine(item, "name", name, to.name);
}
if (zenMode != to.zenMode) {
d.addLine(item, "zenMode", zenMode, to.zenMode);
}
if (!Objects.equals(conditionId, to.conditionId)) {
d.addLine(item, "conditionId", conditionId, to.conditionId);
}
if (!Objects.equals(condition, to.condition)) {
d.addLine(item, "condition", condition, to.condition);
}
if (!Objects.equals(component, to.component)) {
d.addLine(item, "component", component, to.component);
}
}
@Override
public boolean equals(Object o) {
if (!(o instanceof ZenRule)) return false;
@@ -1073,4 +1182,34 @@ public class ZenModeConfig implements Parcelable {
ZenModeConfig migrate(XmlV1 v1);
}
public static class Diff {
private final ArrayList<String> lines = new ArrayList<>();
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Diff[");
final int N = lines.size();
for (int i = 0; i < N; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(lines.get(i));
}
return sb.append(']').toString();
}
private Diff addLine(String item, String action) {
lines.add(item + ":" + action);
return this;
}
public Diff addLine(String item, String subitem, Object from, Object to) {
return addLine(item + "." + subitem, from, to);
}
public Diff addLine(String item, Object from, Object to) {
return addLine(item, from + "->" + to);
}
}
}

View File

@@ -262,6 +262,14 @@ public class ConditionProviders extends ManagedServices {
return null;
}
public Condition findCondition(ComponentName component, Uri conditionId) {
if (component == null || conditionId == null) return null;
synchronized (mMutex) {
final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
return r != null ? r.condition : null;
}
}
public void ensureRecordExists(ComponentName component, Uri conditionId,
IConditionProvider provider) {
// constructed by convention, make sure the record exists...

View File

@@ -126,7 +126,6 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
/** {@hide} */
@@ -244,15 +243,18 @@ public class NotificationManagerService extends SystemService {
private Archive mArchive;
// Notification control database. For now just contains disabled packages.
// Persistent storage for notification policy
private AtomicFile mPolicyFile;
// Temporary holder for <blocked-packages> config coming from old policy files.
private HashSet<String> mBlockedPackages = new HashSet<String>();
private static final int DB_VERSION = 1;
private static final String TAG_BODY = "notification-policy";
private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
private static final String ATTR_VERSION = "version";
// Obsolete: converted if present, but not resaved to disk.
private static final String TAG_BLOCKED_PKGS = "blocked-packages";
private static final String TAG_PACKAGE = "package";
private static final String ATTR_NAME = "name";
@@ -310,53 +312,9 @@ public class NotificationManagerService extends SystemService {
mBuffer.addLast(nr.cloneLight());
}
public void clear() {
mBuffer.clear();
}
public Iterator<StatusBarNotification> descendingIterator() {
return mBuffer.descendingIterator();
}
public Iterator<StatusBarNotification> ascendingIterator() {
return mBuffer.iterator();
}
public Iterator<StatusBarNotification> filter(
final Iterator<StatusBarNotification> iter, final String pkg, final int userId) {
return new Iterator<StatusBarNotification>() {
StatusBarNotification mNext = findNext();
private StatusBarNotification findNext() {
while (iter.hasNext()) {
StatusBarNotification nr = iter.next();
if ((pkg == null || nr.getPackageName() == pkg)
&& (userId == UserHandle.USER_ALL || nr.getUserId() == userId)) {
return nr;
}
}
return null;
}
@Override
public boolean hasNext() {
return mNext == null;
}
@Override
public StatusBarNotification next() {
StatusBarNotification next = mNext;
if (next == null) {
throw new NoSuchElementException();
}
mNext = findNext();
return next;
}
@Override
public void remove() {
iter.remove();
}
};
}
public StatusBarNotification[] getArray(int count) {
if (count == 0) count = mBufferSize;
@@ -370,21 +328,10 @@ public class NotificationManagerService extends SystemService {
return a;
}
public StatusBarNotification[] getArray(int count, String pkg, int userId) {
if (count == 0) count = mBufferSize;
final StatusBarNotification[] a
= new StatusBarNotification[Math.min(count, mBuffer.size())];
Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
int i=0;
while (iter.hasNext() && i < count) {
a[i++] = iter.next();
}
return a;
}
}
private void loadPolicyFile() {
if (DBG) Slog.d(TAG, "loadPolicyFile");
synchronized(mPolicyFile) {
mBlockedPackages.clear();
@@ -400,7 +347,7 @@ public class NotificationManagerService extends SystemService {
while ((type = parser.next()) != END_DOCUMENT) {
tag = parser.getName();
if (type == START_TAG) {
if (TAG_BODY.equals(tag)) {
if (TAG_NOTIFICATION_POLICY.equals(tag)) {
version = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VERSION));
} else if (TAG_BLOCKED_PKGS.equals(tag)) {
@@ -438,7 +385,7 @@ public class NotificationManagerService extends SystemService {
}
private void handleSavePolicyFile() {
Slog.d(TAG, "handleSavePolicyFile");
if (DBG) Slog.d(TAG, "handleSavePolicyFile");
synchronized (mPolicyFile) {
final FileOutputStream stream;
try {
@@ -452,11 +399,11 @@ public class NotificationManagerService extends SystemService {
final XmlSerializer out = new FastXmlSerializer();
out.setOutput(stream, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_BODY);
out.startTag(null, TAG_NOTIFICATION_POLICY);
out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
mZenModeHelper.writeXml(out);
mRankingHelper.writeXml(out);
out.endTag(null, TAG_BODY);
out.endTag(null, TAG_NOTIFICATION_POLICY);
out.endDocument();
mPolicyFile.finishWrite(stream);
} catch (IOException e) {
@@ -814,8 +761,12 @@ public class NotificationManagerService extends SystemService {
// Refresh managed services
mConditionProviders.onUserSwitched(user);
mListeners.onUserSwitched(user);
mZenModeHelper.onUserSwitched(user);
} else if (action.equals(Intent.ACTION_USER_ADDED)) {
mUserProfiles.updateCache(context);
} else if (action.equals(Intent.ACTION_USER_REMOVED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mZenModeHelper.onUserRemoved(user);
}
}
};
@@ -976,6 +927,7 @@ public class NotificationManagerService extends SystemService {
filter.addAction(Intent.ACTION_USER_STOPPED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
getContext().registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
@@ -1426,8 +1378,6 @@ public class NotificationManagerService extends SystemService {
@Override
public void setNotificationsShownFromListener(INotificationListener token, String[] keys) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {

View File

@@ -115,8 +115,11 @@ public class ZenLog {
append(TYPE_UNSUBSCRIBE, uri + "," + subscribeResult(provider, e));
}
public static void traceConfig(String reason, ZenModeConfig newConfig) {
append(TYPE_CONFIG, reason + "," + (newConfig != null ? newConfig.toString() : null));
public static void traceConfig(String reason, ZenModeConfig oldConfig,
ZenModeConfig newConfig) {
append(TYPE_CONFIG, reason
+ "," + (newConfig != null ? newConfig.toString() : null)
+ "," + ZenModeConfig.diff(oldConfig, newConfig));
}
public static void traceDisableEffects(NotificationRecord record, String reason) {

View File

@@ -63,7 +63,7 @@ public class ZenModeConditions implements ConditionProviders.Callback {
mConditionProviders.requestConditions(callback, relevance);
}
public void evaluateConfig(ZenModeConfig config, boolean processSubscriptione) {
public void evaluateConfig(ZenModeConfig config, boolean processSubscriptions) {
if (config == null) return;
if (config.manualRule != null && config.manualRule.condition != null
&& !config.manualRule.isTrueOrUnknown()) {
@@ -71,16 +71,16 @@ public class ZenModeConditions implements ConditionProviders.Callback {
config.manualRule = null;
}
final ArraySet<Uri> current = new ArraySet<>();
evaluateRule(config.manualRule, current, processSubscriptione);
evaluateRule(config.manualRule, current, processSubscriptions);
for (ZenRule automaticRule : config.automaticRules.values()) {
evaluateRule(automaticRule, current, processSubscriptione);
evaluateRule(automaticRule, current, processSubscriptions);
updateSnoozing(automaticRule);
}
final int N = mSubscriptions.size();
for (int i = N - 1; i >= 0; i--) {
final Uri id = mSubscriptions.keyAt(i);
final ComponentName component = mSubscriptions.valueAt(i);
if (processSubscriptione) {
if (processSubscriptions) {
if (!current.contains(id)) {
mConditionProviders.unsubscribeIfNecessary(component, id);
mSubscriptions.removeAt(i);
@@ -157,6 +157,11 @@ public class ZenModeConditions implements ConditionProviders.Callback {
if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
}
}
if (rule.condition == null) {
rule.condition = mConditionProviders.findCondition(rule.component, rule.conditionId);
if (rule.condition != null && DEBUG) Log.d(TAG, "Found existing condition for: "
+ rule.conditionId);
}
}
private boolean isAutomaticActive(ComponentName component) {

View File

@@ -46,6 +46,7 @@ import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.R;
import com.android.server.LocalServices;
@@ -77,8 +78,10 @@ public class ZenModeHelper {
private final ZenModeFiltering mFiltering;
private final RingerModeDelegate mRingerModeDelegate = new RingerModeDelegate();
private final ZenModeConditions mConditions;
private final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private int mZenMode;
private int mUser = UserHandle.USER_OWNER;
private ZenModeConfig mConfig;
private AudioManagerInternal mAudioManager;
private int mPreviousRingerMode = -1;
@@ -92,6 +95,7 @@ public class ZenModeHelper {
appendDefaultScheduleRules(mDefaultConfig);
appendDefaultEventRules(mDefaultConfig);
mConfig = mDefaultConfig;
mConfigs.put(UserHandle.USER_OWNER, mConfig);
mSettingsObserver = new SettingsObserver(mHandler);
mSettingsObserver.observe();
mFiltering = new ZenModeFiltering(mContext);
@@ -142,6 +146,25 @@ public class ZenModeHelper {
}
}
public void onUserSwitched(int user) {
if (mUser == user || user < UserHandle.USER_OWNER) return;
mUser = user;
if (DEBUG) Log.d(TAG, "onUserSwitched u=" + user);
ZenModeConfig config = mConfigs.get(user);
if (config == null) {
if (DEBUG) Log.d(TAG, "onUserSwitched: generating default config for user " + user);
config = mDefaultConfig.copy();
config.user = user;
}
setConfig(config, "onUserSwitched");
}
public void onUserRemoved(int user) {
if (user < UserHandle.USER_OWNER) return;
if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
mConfigs.remove(user);
}
public void requestZenModeConditions(IConditionListener callback, int relevance) {
mConditions.requestConditions(callback, relevance);
}
@@ -200,8 +223,13 @@ public class ZenModeHelper {
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mZenMode=");
pw.println(Global.zenModeToString(mZenMode));
dump(pw, prefix, "mConfig", mConfig);
dump(pw, prefix, "mDefaultConfig", mDefaultConfig);
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
dump(pw, prefix, "mConfig", mConfig);
pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
mFiltering.dump(pw, prefix);
@@ -237,7 +265,10 @@ public class ZenModeHelper {
}
public void writeXml(XmlSerializer out) throws IOException {
mConfig.writeXml(out);
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
mConfigs.valueAt(i).writeXml(out);
}
}
public Policy getNotificationPolicy() {
@@ -268,10 +299,17 @@ public class ZenModeHelper {
Log.w(TAG, "Invalid config in setConfig; " + config);
return false;
}
if (config.user != mUser) {
// simply store away for background users
mConfigs.put(config.user, config);
if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
return true;
}
mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
mConfigs.put(config.user, config);
if (config.equals(mConfig)) return true;
if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
ZenLog.traceConfig(reason, config);
ZenLog.traceConfig(reason, mConfig, config);
final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
getNotificationPolicy(config));
mConfig = config;