Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21258a37 authored by John Spurlock's avatar John Spurlock
Browse files

Zen: Store notification zen policy per-user.

 - Keep a zen config per user in NoMan.
 - Add zen config for all users to xml policy storage mechanism.
 - Initialize config to default for new secondary users.
 - Re-evaluate global zen on user switch.
 - Remove some unused code in NoMan.
 - Make ZenModeHelper aware of multiple users, keep all configs,
   add to dump.
 - Log config diffs in addition to the config itself in ZenLog.

Bug: 15845915
Change-Id: Ic847451e5d111c74916def1ea0948db5a76966c9
parent 1b8b22b1
Loading
Loading
Loading
Loading
+141 −2
Original line number Diff line number Diff line
@@ -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);
        }
    }

}
+8 −0
Original line number Diff line number Diff line
@@ -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...
+15 −65
Original line number Diff line number Diff line
@@ -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) {
@@ -806,8 +753,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);
            }
        }
    };
@@ -968,6 +919,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();
@@ -1418,8 +1370,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) {
+5 −2
Original line number Diff line number Diff line
@@ -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) {
+9 −4
Original line number Diff line number Diff line
@@ -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) {
Loading