pulled atom for Do Not Disturb configuration

Bug: 154254547
Test: atest ZenModeHelperTest ZenModeConfigTest ZenPoicyTest
Change-Id: Ibfcf0f8034b049b1ec790d243e8cb06d71224063
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 052358b..414b53d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -566,6 +566,7 @@
         DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
         BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
                 10083 [(module) = "framework"];
+        DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -6083,6 +6084,76 @@
 }
 
 /**
+ * Atom that represents an item in the list of Do Not Disturb rules, pulled from
+ * NotificationManagerService.java.
+ */
+message DNDModeProto {
+    enum Mode {
+        ROOT_CONFIG = -1;  // Used to distinguish the config (one per user) from the rules.
+        ZEN_MODE_OFF = 0;
+        ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+        ZEN_MODE_NO_INTERRUPTIONS = 2;
+        ZEN_MODE_ALARMS = 3;
+    }
+    optional int32 user = 1;  // Android user ID (0, 1, 10, ...)
+    optional bool enabled = 2;  // true for ROOT_CONFIG if a manualRule is enabled
+    optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
+    optional Mode zen_mode = 4;
+    // id is one of the system default rule IDs, or empty
+    // May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
+    optional string id = 5;
+    optional int32 uid = 6 [(is_uid) = true]; // currently only SYSTEM_UID or 0 for other
+    optional DNDPolicyProto policy = 7;
+}
+
+/**
+ * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ */
+message DNDPolicyProto {
+    enum State {
+        STATE_UNSET = 0;
+        STATE_ALLOW = 1;
+        STATE_DISALLOW = 2;
+    }
+    optional State calls = 1;
+    optional State repeat_callers = 2;
+    optional State messages = 3;
+    optional State conversations = 4;
+    optional State reminders = 5;
+    optional State events = 6;
+    optional State alarms = 7;
+    optional State media = 8;
+    optional State system = 9;
+    optional State fullscreen = 10;
+    optional State lights = 11;
+    optional State peek = 12;
+    optional State status_bar = 13;
+    optional State badge = 14;
+    optional State ambient = 15;
+    optional State notification_list = 16;
+
+    enum PeopleType {
+        PEOPLE_UNSET = 0;
+        PEOPLE_ANYONE = 1;
+        PEOPLE_CONTACTS = 2;
+        PEOPLE_STARRED = 3;
+        PEOPLE_NONE = 4;
+    }
+
+    optional PeopleType allow_calls_from = 17;
+    optional PeopleType allow_messages_from = 18;
+
+    enum ConversationType {
+        CONV_UNSET = 0;
+        CONV_ANYONE = 1;
+        CONV_IMPORTANT = 2;
+        CONV_NONE = 3;
+    }
+
+    optional ConversationType allow_conversations_from = 19;
+}
+
+/**
  * Atom that contains a list of a package's channel group preferences, pulled from
  * NotificationManagerService.java.
  */
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1f6555c..0827fef 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -78,6 +78,7 @@
     private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
     private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
 
+    public static final String MANUAL_RULE_ID = "MANUAL_RULE";
     public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
     public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
     public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
@@ -959,6 +960,48 @@
     };
 
     /**
+     * Converts a ZenModeConfig to a ZenPolicy
+     */
+    public ZenPolicy toZenPolicy() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder()
+                .allowCalls(allowCalls
+                        ? ZenModeConfig.getZenPolicySenders(allowCallsFrom)
+                        : ZenPolicy.PEOPLE_TYPE_NONE)
+                .allowRepeatCallers(allowRepeatCallers)
+                .allowMessages(allowMessages
+                        ? ZenModeConfig.getZenPolicySenders(allowMessagesFrom)
+                        : ZenPolicy.PEOPLE_TYPE_NONE)
+                .allowReminders(allowReminders)
+                .allowEvents(allowEvents)
+                .allowAlarms(allowAlarms)
+                .allowMedia(allowMedia)
+                .allowSystem(allowSystem)
+                .allowConversations(allowConversations
+                        ? ZenModeConfig.getZenPolicySenders(allowConversationsFrom)
+                        : ZenPolicy.PEOPLE_TYPE_NONE);
+        if (suppressedVisualEffects == 0) {
+            builder.showAllVisualEffects();
+        } else {
+            // configs don't have an unset state: wither true or false.
+            builder.showFullScreenIntent(
+                    (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0);
+            builder.showLights(
+                    (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0);
+            builder.showPeeking(
+                    (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0);
+            builder.showStatusBarIcons(
+                    (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_STATUS_BAR) == 0);
+            builder.showBadges(
+                    (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0);
+            builder.showInAmbientDisplay(
+                    (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_AMBIENT) == 0);
+            builder.showInNotificationList(
+                    (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0);
+        }
+        return builder.build();
+    }
+
+    /**
      * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its
      * defaults for all unset values in zenPolicy
      */
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 87295e1..6d0bcff 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -24,6 +24,7 @@
 import android.os.Parcelable;
 import android.util.proto.ProtoOutputStream;
 
+import java.io.ByteArrayOutputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -1111,6 +1112,40 @@
     }
 
     /**
+     * Converts a policy to a statsd proto.
+     * @hides
+     */
+    public byte[] toProto() {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        ProtoOutputStream proto = new ProtoOutputStream(bytes);
+
+        proto.write(DNDPolicyProto.CALLS, getPriorityCategoryCalls());
+        proto.write(DNDPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
+        proto.write(DNDPolicyProto.MESSAGES, getPriorityCategoryMessages());
+        proto.write(DNDPolicyProto.CONVERSATIONS, getPriorityCategoryConversations());
+        proto.write(DNDPolicyProto.REMINDERS, getPriorityCategoryReminders());
+        proto.write(DNDPolicyProto.EVENTS, getPriorityCategoryEvents());
+        proto.write(DNDPolicyProto.ALARMS, getPriorityCategoryAlarms());
+        proto.write(DNDPolicyProto.MEDIA, getPriorityCategoryMedia());
+        proto.write(DNDPolicyProto.SYSTEM, getPriorityCategorySystem());
+
+        proto.write(DNDPolicyProto.FULLSCREEN, getVisualEffectFullScreenIntent());
+        proto.write(DNDPolicyProto.LIGHTS, getVisualEffectLights());
+        proto.write(DNDPolicyProto.PEEK, getVisualEffectPeek());
+        proto.write(DNDPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
+        proto.write(DNDPolicyProto.BADGE, getVisualEffectBadge());
+        proto.write(DNDPolicyProto.AMBIENT, getVisualEffectAmbient());
+        proto.write(DNDPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
+
+        proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, getPriorityCallSenders());
+        proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, getPriorityMessageSenders());
+        proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, getPriorityConversationSenders());
+
+        proto.flush();
+        return bytes.toByteArray();
+    }
+
+    /**
      * Makes deep copy of this ZenPolicy.
      * @hide
      */
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index ecb4193..8e4006a 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -274,4 +274,74 @@
 // Next Tag: 2
 message NotificationRemoteViewsProto {
     repeated PackageRemoteViewInfoProto package_remote_view_info = 1;
-}
\ No newline at end of file
+}
+
+/**
+ * Atom that represents an item in the list of Do Not Disturb rules, pulled from
+ * NotificationManagerService.java.
+ */
+message DNDModeProto {
+    enum Mode {
+        ROOT_CONFIG = -1;  // Used to distinguish the config (one per user) from the rules.
+        ZEN_MODE_OFF = 0;
+        ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+        ZEN_MODE_NO_INTERRUPTIONS = 2;
+        ZEN_MODE_ALARMS = 3;
+    }
+    optional int32 user = 1;  // Android user ID (0, 1, 10, ...)
+    optional bool enabled = 2;  // true for ROOT_CONFIG if a manualRule is enabled
+    optional bool channels_bypassing = 3; // only valid for ROOT_CONFIG
+    optional Mode zen_mode = 4;
+    // id is one of the system default rule IDs, or empty
+    // May also be "MANUAL_RULE" to indicate app-activation of the manual rule.
+    optional string id = 5;
+    optional int32 uid = 6; // currently only SYSTEM_UID or 0 for other
+    optional DNDPolicyProto policy = 7;
+}
+
+/**
+ * Atom that represents a Do Not Disturb policy, an optional detail proto for DNDModeProto.
+ */
+message DNDPolicyProto {
+    enum State {
+        STATE_UNSET = 0;
+        STATE_ALLOW = 1;
+        STATE_DISALLOW = 2;
+    }
+    optional State calls = 1;
+    optional State repeat_callers = 2;
+    optional State messages = 3;
+    optional State conversations = 4;
+    optional State reminders = 5;
+    optional State events = 6;
+    optional State alarms = 7;
+    optional State media = 8;
+    optional State system = 9;
+    optional State fullscreen = 10;
+    optional State lights = 11;
+    optional State peek = 12;
+    optional State status_bar = 13;
+    optional State badge = 14;
+    optional State ambient = 15;
+    optional State notification_list = 16;
+
+    enum PeopleType {
+        PEOPLE_UNSET = 0;
+        PEOPLE_ANYONE = 1;
+        PEOPLE_CONTACTS = 2;
+        PEOPLE_STARRED = 3;
+        PEOPLE_NONE = 4;
+    }
+
+    optional PeopleType allow_calls_from = 17;
+    optional PeopleType allow_messages_from = 18;
+
+    enum ConversationType {
+        CONV_UNSET = 0;
+        CONV_ANYONE = 1;
+        CONV_IMPORTANT = 2;
+        CONV_NONE = 3;
+    }
+
+    optional ConversationType allow_conversations_from = 19;
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0a8155a..a9139ad 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -92,6 +92,7 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
+import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -1906,7 +1907,8 @@
         mMetricsLogger = new MetricsLogger();
         mRankingHandler = rankingHandler;
         mConditionProviders = conditionProviders;
-        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
+        mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders,
+                new SysUiStatsEvent.BuilderFactory());
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
             public void onConfigChanged() {
@@ -2189,6 +2191,12 @@
                 ConcurrentUtils.DIRECT_EXECUTOR,
                 mPullAtomCallback
         );
+        mStatsManager.setPullAtomCallback(
+                DND_MODE_RULE,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mPullAtomCallback
+        );
     }
 
     private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
@@ -2198,6 +2206,7 @@
                 case PACKAGE_NOTIFICATION_PREFERENCES:
                 case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
                 case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+                case DND_MODE_RULE:
                     return pullNotificationStates(atomTag, data);
                 default:
                     throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
@@ -2216,6 +2225,9 @@
             case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
                 mPreferencesHelper.pullPackageChannelGroupPreferencesStats(data);
                 break;
+            case DND_MODE_RULE:
+                mZenModeHelper.pullRules(data);
+                break;
         }
         return StatsManager.PULL_SUCCESS;
     }
diff --git a/services/core/java/com/android/server/notification/SysUiStatsEvent.java b/services/core/java/com/android/server/notification/SysUiStatsEvent.java
index 9bc2346..90737bd 100644
--- a/services/core/java/com/android/server/notification/SysUiStatsEvent.java
+++ b/services/core/java/com/android/server/notification/SysUiStatsEvent.java
@@ -58,6 +58,11 @@
             mBuilder.writeBoolean(value);
             return this;
         }
+
+        public Builder writeByteArray(byte[] value) {
+            mBuilder.writeByteArray(value);
+            return this;
+        }
     }
 
     static class BuilderFactory {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a490b9c..4931d3f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -20,6 +20,10 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+
+import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
+import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
@@ -67,6 +71,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsEvent;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -103,6 +108,7 @@
     private final SettingsObserver mSettingsObserver;
     private final AppOpsManager mAppOps;
     @VisibleForTesting protected final NotificationManager mNotificationManager;
+    private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
     @VisibleForTesting protected ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
@@ -130,7 +136,8 @@
 
     private String[] mPriorityOnlyDndExemptPackages;
 
-    public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
+    public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders,
+            SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) {
         mContext = context;
         mHandler = new H(looper);
         addCallback(mMetrics);
@@ -148,6 +155,7 @@
         mFiltering = new ZenModeFiltering(mContext);
         mConditions = new ZenModeConditions(this, conditionProviders);
         mServiceConfig = conditionProviders.getConfig();
+        mStatsEventBuilderFactory = statsEventBuilderFactory;
     }
 
     public Looper getLooper() {
@@ -1170,6 +1178,72 @@
         }
     }
 
+    /**
+     * Generate pulled atoms about do not disturb configurations.
+     */
+    public void pullRules(List<StatsEvent> events) {
+        synchronized (mConfig) {
+            final int numConfigs = mConfigs.size();
+            int id = 0;
+            for (int i = 0; i < numConfigs; i++) {
+                final int user = mConfigs.keyAt(i);
+                final ZenModeConfig config = mConfigs.valueAt(i);
+                SysUiStatsEvent.Builder data = mStatsEventBuilderFactory.newBuilder()
+                        .setAtomId(DND_MODE_RULE)
+                        .writeInt(user)
+                        .writeBoolean(config.manualRule != null) // enabled
+                        .writeBoolean(config.areChannelsBypassingDnd)
+                        .writeInt(ROOT_CONFIG)
+                        .writeString("") // name, empty for root config
+                        .writeInt(Process.SYSTEM_UID) // system owns root config
+                        .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
+                        .writeByteArray(config.toZenPolicy().toProto());
+                events.add(data.build());
+                if (config.manualRule != null && config.manualRule.enabler != null) {
+                    ruleToProto(user, config.manualRule, events);
+                }
+                for (ZenRule rule : config.automaticRules.values()) {
+                    ruleToProto(user, rule, events);
+                }
+            }
+        }
+    }
+
+    private void ruleToProto(int user, ZenRule rule, List<StatsEvent> events) {
+        // Make the ID safe.
+        String id = rule.id == null ? "" : rule.id;
+        if (!ZenModeConfig.DEFAULT_RULE_IDS.contains(id)) {
+            id = "";
+        }
+
+        // Look for packages and enablers, enablers get priority.
+        String pkg = rule.pkg == null ? "" : rule.pkg;
+        if (rule.enabler != null) {
+            pkg = rule.enabler;
+            id = ZenModeConfig.MANUAL_RULE_ID;
+        }
+
+        // TODO: fetch the uid from the package manager
+        int uid = "android".equals(pkg) ? Process.SYSTEM_UID : 0;
+
+        SysUiStatsEvent.Builder data;
+        data = mStatsEventBuilderFactory.newBuilder()
+                .setAtomId(DND_MODE_RULE)
+                .writeInt(user)
+                .writeBoolean(rule.enabled)
+                .writeBoolean(false) // channels_bypassing unused for rules
+                .writeInt(rule.zenMode)
+                .writeString(id)
+                .writeInt(uid)
+                .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+        byte[] policyProto = new byte[]{};
+        if (rule.zenPolicy != null) {
+            policyProto = rule.zenPolicy.toProto();
+        }
+        data.writeByteArray(policyProto);
+        events.add(data.build());
+    }
+
     @VisibleForTesting
     protected final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
         @Override
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java b/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
index f4f64d7..89adc72 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
@@ -88,11 +88,21 @@
             return index < mValues.size() ? mValues.get(index) : null;
         }
 
-        /** useful to make assertTrue() statemetns more readable. */
+        /** useful to make assertTrue() statements more readable. */
         public boolean getBoolean(int index) {
             return (Boolean) mValues.get(index);
         }
 
+        /** useful to make assertTrue() statements more readable. */
+        public int getInt(int index) {
+            return (Integer) mValues.get(index);
+        }
+
+        /** useful to make assertTrue() statements more readable. */
+        public String getString(int index) {
+            return (String) mValues.get(index);
+        }
+
         private void addValue(Object value) {
             mLastIndex = mValues.size();
             mValues.add(value);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index f7b435e..013a994 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -101,11 +101,39 @@
 
         Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
                 priorityMessageSenders, suppressedVisualEffects, 0, priorityConversationsSenders);
-
         assertEquals(expectedPolicy, config.toNotificationPolicy(zenPolicy));
     }
 
     @Test
+    public void testZenConfigToZenPolicy() {
+        ZenPolicy expected = new ZenPolicy.Builder()
+                .allowAlarms(true)
+                .allowReminders(true)
+                .allowEvents(true)
+                .showLights(false)
+                .showBadges(false)
+                .showInAmbientDisplay(false)
+                .build();
+
+        ZenModeConfig config = getMutedAllConfig();
+        config.allowAlarms = true;
+        config.allowReminders = true;
+        config.allowEvents = true;
+        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
+        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
+        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+        ZenPolicy actual = config.toZenPolicy();
+
+        assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge());
+        assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms());
+        assertEquals(expected.getPriorityCategoryReminders(),
+                actual.getPriorityCategoryReminders());
+        assertEquals(expected.getPriorityCategoryEvents(), actual.getPriorityCategoryEvents());
+        assertEquals(expected.getVisualEffectLights(), actual.getVisualEffectLights());
+        assertEquals(expected.getVisualEffectAmbient(), actual.getVisualEffectAmbient());
+    }
+
+    @Test
     public void testPriorityOnlyMutingAll() {
         ZenModeConfig config = getMutedAllConfig();
         assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 108af89..3c7206f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -27,16 +27,25 @@
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
-import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+
+import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
+import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.ID_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.UID_FIELD_NUMBER;
+import static com.android.os.AtomsProto.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
 
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -70,10 +79,12 @@
 import android.media.AudioSystem;
 import android.media.VolumePolicy;
 import android.net.Uri;
+import android.os.Process;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
+import android.service.notification.DNDModeProto;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenPolicy;
@@ -82,6 +93,7 @@
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.StatsEvent;
 import android.util.Xml;
 
 import com.android.internal.R;
@@ -106,6 +118,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Objects;
 
 @SmallTest
@@ -115,6 +130,7 @@
 
     private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
     private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
+    private static final int ZEN_MODE_FOR_TESTING = 99;
 
     ConditionProviders mConditionProviders;
     @Mock NotificationManager mNotificationManager;
@@ -124,6 +140,7 @@
     private Context mContext;
     private ContentResolver mContentResolver;
     @Mock AppOpsManager mAppOps;
+    private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
 
     @Before
     public void setUp() {
@@ -140,6 +157,7 @@
             Log.d("ZenModeHelperTest", "Couldn't mock default zen mode config xml file err=" +
                     e.toString());
         }
+        mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
 
         when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps);
         when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
@@ -147,7 +165,7 @@
                 AppGlobals.getPackageManager());
         mConditionProviders.addSystemProvider(new CountdownConditionProvider());
         mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(),
-                mConditionProviders));
+                mConditionProviders, mStatsEventBuilderFactory));
     }
 
     private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException {
@@ -212,6 +230,10 @@
     }
 
     private ArrayMap<String, ZenModeConfig.ZenRule> getCustomAutomaticRules() {
+        return getCustomAutomaticRules(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+    }
+
+    private ArrayMap<String, ZenModeConfig.ZenRule> getCustomAutomaticRules(int zenMode) {
         ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
         final ScheduleInfo customRuleInfo = new ScheduleInfo();
@@ -219,10 +241,10 @@
         customRule.creationTime = 0;
         customRule.id = "customRule";
         customRule.name = "Custom Rule";
-        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.zenMode = zenMode;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.configurationActivity =
-                new ComponentName("android", "ScheduleConditionProvider");
+                new ComponentName("not.android", "ScheduleConditionProvider");
         customRule.pkg = customRule.configurationActivity.getPackageName();
         automaticRules.put("customRule", customRule);
         return automaticRules;
@@ -244,7 +266,7 @@
 
     @Test
     public void testZenOn_NotificationApplied() {
-        mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         // The most permissive policy
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
                 PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
@@ -267,7 +289,7 @@
 
     @Test
     public void testZenOn_StarredCallers_CallTypesBlocked() {
-        mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         // The most permissive policy
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
                 PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
@@ -287,7 +309,7 @@
 
     @Test
     public void testZenOn_AllCallers_CallTypesAllowed() {
-        mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         // The most permissive policy
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
                 PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
@@ -307,7 +329,7 @@
 
     @Test
     public void testZenOn_AllowAlarmsMedia_NoAlarmMediaMuteApplied() {
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
                 PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
 
@@ -320,7 +342,7 @@
 
     @Test
     public void testZenOn_DisallowAlarmsMedia_AlarmMediaMuteApplied() {
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
@@ -406,7 +428,7 @@
     public void testZenAllCannotBypass() {
         // Only audio attributes with SUPPRESIBLE_NEVER can bypass
         // with special case USAGE_ASSISTANCE_SONIFICATION
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
@@ -428,7 +450,7 @@
     @Test
     public void testApplyRestrictions_whitelist_priorityOnlyMode() {
         mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
-        mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
@@ -479,7 +501,7 @@
         Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
         mZenModeHelperSpy.mIsBootComplete = true;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
-        mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelperSpy.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
         verify(mNotificationManager, times(1)).notify(eq(ZenModeHelper.TAG),
@@ -494,7 +516,7 @@
         Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
         Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
         mZenModeHelperSpy.mIsBootComplete = true;
-        mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelperSpy.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         verify(mZenModeHelperSpy, never()).createZenUpgradeNotification();
         verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
@@ -507,7 +529,7 @@
         Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
         Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 1);
         mZenModeHelperSpy.mIsBootComplete = true;
-        mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelperSpy.setZenModeSetting(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         verify(mZenModeHelperSpy, never()).createZenUpgradeNotification();
         verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG),
@@ -524,7 +546,7 @@
         // 1. Current ringer is normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         // Set zen to priority-only with all notification sounds muted (so ringer will be muted)
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowReminders = false;
         mZenModeHelperSpy.mConfig.allowCalls = false;
         mZenModeHelperSpy.mConfig.allowMessages = false;
@@ -568,7 +590,7 @@
         // ringtone, notification and system streams are affected by ringer mode
         mZenModeHelperSpy.mConfig.allowAlarms = true;
         mZenModeHelperSpy.mConfig.allowReminders = true;
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerMuted =
                 mZenModeHelperSpy.new RingerModeDelegate();
 
@@ -615,7 +637,7 @@
 
         // 1. Current ringer is normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowReminders = true;
 
         // 2. apply priority only zen - verify ringer is normal
@@ -640,7 +662,7 @@
 
         // 1. Current ringer is silent
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowReminders = true;
 
         // 2. apply priority only zen - verify ringer is silent
@@ -666,7 +688,7 @@
         // 1. Current ringer is normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         // Set zen to priority-only with all notification sounds muted (so ringer will be muted)
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowReminders = true;
 
         // 2. apply priority only zen - verify zen will still be normal
@@ -728,7 +750,7 @@
 
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
             mZenModeHelperSpy.evaluateZenMode("test", true);
@@ -756,7 +778,7 @@
 
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
             mZenModeHelperSpy.evaluateZenMode("test", true);
@@ -768,7 +790,7 @@
 
     @Test
     public void testParcelConfig() {
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
         mZenModeHelperSpy.mConfig.allowMedia = false;
         mZenModeHelperSpy.mConfig.allowSystem = false;
@@ -792,7 +814,7 @@
 
     @Test
     public void testWriteXml() throws Exception {
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
         mZenModeHelperSpy.mConfig.allowMedia = false;
         mZenModeHelperSpy.mConfig.allowSystem = false;
@@ -806,7 +828,7 @@
         mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
         mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
         mZenModeHelperSpy.mConfig.manualRule.zenMode =
-                Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
         mZenModeHelperSpy.mConfig.manualRule.pkg = "a";
         mZenModeHelperSpy.mConfig.manualRule.enabled = true;
@@ -822,6 +844,102 @@
     }
 
     @Test
+    public void testProto() {
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
+
+        int n = mZenModeHelperSpy.mConfig.automaticRules.size();
+        List<String> ids = new ArrayList<>(n);
+        for (ZenModeConfig.ZenRule rule : mZenModeHelperSpy.mConfig.automaticRules.values()) {
+            ids.add(rule.id);
+        }
+        ids.add("");
+
+        List<StatsEvent> events = new LinkedList<>();
+        mZenModeHelperSpy.pullRules(events);
+        assertEquals(n + 1, events.size());
+        for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+            if (builder.getAtomId() == DND_MODE_RULE) {
+                if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == DNDModeProto.ROOT_CONFIG) {
+                    assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
+                    assertFalse(builder.getBoolean(CHANNELS_BYPASSING_FIELD_NUMBER));
+                }
+                assertEquals(Process.SYSTEM_UID, builder.getInt(UID_FIELD_NUMBER));
+                assertTrue(builder.getBooleanAnnotation(UID_FIELD_NUMBER, ANNOTATION_ID_IS_UID));
+                String name = (String) builder.getValue(ID_FIELD_NUMBER);
+                assertTrue("unexpected rule id", ids.contains(name));
+                ids.remove(name);
+            } else {
+                fail("unexpected atom id: " + builder.getAtomId());
+            }
+        }
+        assertEquals("extra rule in output", 0, ids.size());
+    }
+
+    @Test
+    public void testProtoWithAutoRule() throws Exception {
+        setupZenConfig();
+        // one enabled automatic rule
+        mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules(ZEN_MODE_FOR_TESTING);
+
+        List<StatsEvent> events = new LinkedList<>();
+        mZenModeHelperSpy.pullRules(events);
+
+        boolean foundCustomEvent = false;
+        for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+            if (builder.getAtomId() == DND_MODE_RULE) {
+                if (ZEN_MODE_FOR_TESTING == builder.getInt(ZEN_MODE_FIELD_NUMBER)) {
+                    foundCustomEvent = true;
+                    assertEquals(0, builder.getInt(UID_FIELD_NUMBER));
+                    assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
+                }
+            } else {
+                fail("unexpected atom id: " + builder.getAtomId());
+            }
+        }
+        assertTrue("couldn't find custom rule", foundCustomEvent);
+    }
+
+    @Test
+    public void testProtoRedactsIds() throws Exception {
+        setupZenConfig();
+        // one enabled automatic rule
+        mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules();
+
+        List<StatsEvent> events = new LinkedList<>();
+        mZenModeHelperSpy.pullRules(events);
+
+        boolean foundCustomEvent = false;
+        for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+            if (builder.getAtomId() == DND_MODE_RULE
+                    && "customRule".equals(builder.getString(ID_FIELD_NUMBER))) {
+                fail("non-default IDs should be redacted");
+            }
+        }
+    }
+
+    @Test
+    public void testProtoWithManualRule() throws Exception {
+        setupZenConfig();
+        mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules();
+        mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
+        mZenModeHelperSpy.mConfig.manualRule.enabled = true;
+        mZenModeHelperSpy.mConfig.manualRule.enabler = "com.enabler";
+
+        List<StatsEvent> events = new LinkedList<>();
+        mZenModeHelperSpy.pullRules(events);
+
+        boolean foundManualRule = false;
+        for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+            if (builder.getAtomId() == DND_MODE_RULE
+                    && ZenModeConfig.MANUAL_RULE_ID.equals(builder.getString(ID_FIELD_NUMBER))) {
+                assertEquals(0, builder.getInt(UID_FIELD_NUMBER));
+                foundManualRule = true;
+            }
+        }
+        assertTrue("couldn't find manual rule", foundManualRule);    }
+
+    @Test
     public void testWriteXml_onlyBackupsTargetUser() throws Exception {
         // Setup configs for user 10 and 11.
         setupZenConfig();
@@ -906,7 +1024,7 @@
         customRule.creationTime = 0;
         customRule.id = "customRule";
         customRule.name = "Custom Rule";
-        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.configurationActivity =
                 new ComponentName("android", "ScheduleConditionProvider");
@@ -951,7 +1069,7 @@
         customRule.creationTime = 0;
         customRule.id = ruleId;
         customRule.name = "Custom Rule";
-        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.configurationActivity =
                 new ComponentName("android", "ScheduleConditionProvider");
@@ -986,7 +1104,7 @@
         final ScheduleInfo weeknights = new ScheduleInfo();
         customRule.enabled = true;
         customRule.name = "Custom Rule";
-        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
         enabledAutoRule.put("customRule", customRule);
@@ -1151,7 +1269,7 @@
         final ScheduleInfo weeknights = new ScheduleInfo();
         customRule.enabled = false;
         customRule.name = "Custom Rule";
-        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
         disabledAutoRule.put("customRule", customRule);
@@ -1187,7 +1305,7 @@
         final ScheduleInfo customRuleInfo = new ScheduleInfo();
         customRule.enabled = false;
         customRule.name = "Custom Rule";
-        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
         customRule.zenPolicy = new ZenPolicy.Builder()
@@ -1200,7 +1318,7 @@
         final ScheduleInfo defaultScheduleRuleInfo = new ScheduleInfo();
         defaultScheduleRule.enabled = false;
         defaultScheduleRule.name = "Default Schedule Rule";
-        defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        defaultScheduleRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultScheduleRuleInfo);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
@@ -1238,7 +1356,7 @@
         final ScheduleInfo customRuleInfo = new ScheduleInfo();
         customRule.enabled = false;
         customRule.name = "Custom Rule";
-        customRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.component = new ComponentName("android", "ScheduleConditionProvider");
         customRule.zenPolicy = new ZenPolicy.Builder()
@@ -1251,7 +1369,7 @@
         final ScheduleInfo defaultScheduleRuleInfo = new ScheduleInfo();
         defaultScheduleRule.enabled = false;
         defaultScheduleRule.name = "Default Schedule Rule";
-        defaultScheduleRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        defaultScheduleRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         defaultScheduleRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultScheduleRuleInfo);
         defaultScheduleRule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
@@ -1265,7 +1383,7 @@
         final ScheduleInfo defaultEventRuleInfo = new ScheduleInfo();
         defaultEventRule.enabled = false;
         defaultEventRule.name = "Default Event Rule";
-        defaultEventRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        defaultEventRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         defaultEventRule.conditionId = ZenModeConfig.toScheduleConditionId(
                 defaultEventRuleInfo);
         defaultEventRule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
@@ -1295,6 +1413,10 @@
         assertTrue(rules.containsKey("customRule"));
 
         setupZenConfigMaintained();
+
+        List<StatsEvent> events = new LinkedList<>();
+        mZenModeHelperSpy.pullRules(events);
+        assertEquals(4, events.size());
     }
 
     @Test
@@ -1323,10 +1445,12 @@
 
     @Test
     public void testEmptyDefaultRulesMap() {
+        List<StatsEvent> events = new LinkedList<>();
         ZenModeConfig config = new ZenModeConfig();
         config.automaticRules = new ArrayMap<>();
         mZenModeHelperSpy.mConfig = config;
         mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer
+        mZenModeHelperSpy.pullRules(events); // shouldn't throw null pointer
     }
 
     @Test
@@ -1342,7 +1466,7 @@
         updatedDefaultRule.creationTime = 0;
         updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
         updatedDefaultRule.name = "Schedule Default Rule";
-        updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        updatedDefaultRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
         updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
 
@@ -1368,7 +1492,7 @@
         updatedDefaultRule.creationTime = 0;
         updatedDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
         updatedDefaultRule.name = "Schedule Default Rule";
-        updatedDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        updatedDefaultRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         updatedDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
         updatedDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
 
@@ -1395,7 +1519,7 @@
         customDefaultRule.creationTime = 0;
         customDefaultRule.id = SCHEDULE_DEFAULT_RULE_ID;
         customDefaultRule.name = "Schedule Default Rule";
-        customDefaultRule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        customDefaultRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         customDefaultRule.conditionId = ZenModeConfig.toScheduleConditionId(new ScheduleInfo());
         customDefaultRule.component = new ComponentName("android", "ScheduleConditionProvider");
 
@@ -1433,7 +1557,7 @@
     }
 
     private void setupZenConfig() {
-        mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelperSpy.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
         mZenModeHelperSpy.mConfig.allowMedia = false;
         mZenModeHelperSpy.mConfig.allowSystem = false;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
index a0ee417..b16ca8b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
@@ -17,14 +17,18 @@
 package com.android.server.notification;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
 
 import android.service.notification.ZenPolicy;
+import android.service.notification.nano.DNDPolicyProto;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.UiServiceTestCase;
 
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -190,6 +194,7 @@
         ZenPolicy policy = builder.build();
         assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMessages());
         assertEquals(ZenPolicy.PEOPLE_TYPE_UNSET, policy.getPriorityMessageSenders());
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -201,6 +206,7 @@
         ZenPolicy policy = builder.build();
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
         assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders());
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -210,6 +216,7 @@
         ZenPolicy policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, -1);
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -221,12 +228,14 @@
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS);
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryReminders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowReminders(false);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS);
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryReminders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -238,12 +247,14 @@
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS);
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryEvents());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowEvents(false);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS);
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryEvents());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -256,6 +267,7 @@
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
         assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityMessageSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS);
         policy = builder.build();
@@ -263,6 +275,7 @@
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
         assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityMessageSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowMessages(ZenPolicy.PEOPLE_TYPE_STARRED);
         policy = builder.build();
@@ -270,6 +283,7 @@
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages());
         assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityMessageSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowMessages(ZenPolicy.PEOPLE_TYPE_NONE);
         policy = builder.build();
@@ -277,11 +291,13 @@
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMessages());
         assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityMessageSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowMessages(ZenPolicy.PEOPLE_TYPE_UNSET);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, -1);
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -294,6 +310,7 @@
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
         assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS);
         policy = builder.build();
@@ -301,6 +318,7 @@
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
         assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityCallSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowCalls(ZenPolicy.PEOPLE_TYPE_STARRED);
         policy = builder.build();
@@ -308,6 +326,7 @@
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls());
         assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityCallSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowCalls(ZenPolicy.PEOPLE_TYPE_NONE);
         policy = builder.build();
@@ -315,11 +334,13 @@
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryCalls());
         assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityCallSenders());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowCalls(ZenPolicy.PEOPLE_TYPE_UNSET);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, -1);
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -331,12 +352,14 @@
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS);
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryRepeatCallers());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowRepeatCallers(false);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS);
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryRepeatCallers());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -348,12 +371,14 @@
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS);
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryAlarms());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowAlarms(false);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS);
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryAlarms());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -365,12 +390,14 @@
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA);
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMedia());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowMedia(false);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA);
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMedia());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     @Test
@@ -382,12 +409,119 @@
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM);
         assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategorySystem());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
 
         builder.allowSystem(false);
         policy = builder.build();
         assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM);
         assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategorySystem());
         assertAllVisualEffectsUnsetExcept(policy, -1);
+        assertProtoMatches(policy, policy.toProto());
+    }
+
+    @Test
+    public void tesShowFullScreenIntent() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+        builder.showFullScreenIntent(true);
+        ZenPolicy policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT);
+        assertProtoMatches(policy, policy.toProto());
+
+        builder.showFullScreenIntent(false);
+        policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT);
+        assertProtoMatches(policy, policy.toProto());
+    }
+
+    @Test
+    public void tesShowLights() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+        builder.showLights(true);
+        ZenPolicy policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_LIGHTS);
+        assertProtoMatches(policy, policy.toProto());
+
+        builder.showLights(false);
+        policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_LIGHTS);
+        assertProtoMatches(policy, policy.toProto());
+    }
+
+    @Test
+    public void tesShowPeeking() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+        builder.showPeeking(true);
+        ZenPolicy policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_PEEK);
+        assertProtoMatches(policy, policy.toProto());
+
+        builder.showPeeking(false);
+        policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_PEEK);
+        assertProtoMatches(policy, policy.toProto());
+    }
+
+    @Test
+    public void tesShowStatusBarIcons() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+        builder.showStatusBarIcons(true);
+        ZenPolicy policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_STATUS_BAR);
+        assertProtoMatches(policy, policy.toProto());
+
+        builder.showStatusBarIcons(false);
+        policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_STATUS_BAR);
+        assertProtoMatches(policy, policy.toProto());
+    }
+
+    @Test
+    public void tesShowBadges() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+        builder.showBadges(true);
+        ZenPolicy policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_BADGE);
+        assertProtoMatches(policy, policy.toProto());
+
+        builder.showBadges(false);
+        policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_BADGE);
+        assertProtoMatches(policy, policy.toProto());
+    }
+
+    @Test
+    public void tesShowInAmbientDisplay() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+        builder.showInAmbientDisplay(true);
+        ZenPolicy policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_AMBIENT);
+        assertProtoMatches(policy, policy.toProto());
+
+        builder.showInAmbientDisplay(false);
+        policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_AMBIENT);
+        assertProtoMatches(policy, policy.toProto());
+    }
+
+    @Test
+    public void tesShowInNotificationList() {
+        ZenPolicy.Builder builder = new ZenPolicy.Builder();
+
+        builder.showInNotificationList(true);
+        ZenPolicy policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST);
+        assertProtoMatches(policy, policy.toProto());
+
+        builder.showInNotificationList(false);
+        policy = builder.build();
+        assertAllVisualEffectsUnsetExcept(policy, ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST);
+        assertProtoMatches(policy, policy.toProto());
     }
 
     private void assertAllPriorityCategoriesUnsetExcept(ZenPolicy policy, int except) {
@@ -453,4 +587,35 @@
             assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectNotificationList());
         }
     }
+
+    private void assertProtoMatches(ZenPolicy policy, byte[] bytes) {
+        try {
+            DNDPolicyProto proto = DNDPolicyProto.parseFrom(bytes);
+
+            assertEquals(policy.getPriorityCategoryCalls(), proto.calls);
+            assertEquals(policy.getPriorityCategoryRepeatCallers(), proto.repeatCallers);
+            assertEquals(policy.getPriorityCategoryMessages(), proto.messages);
+            assertEquals(policy.getPriorityCategoryConversations(), proto.conversations);
+            assertEquals(policy.getPriorityCategoryReminders(), proto.reminders);
+            assertEquals(policy.getPriorityCategoryEvents(), proto.events);
+            assertEquals(policy.getPriorityCategoryAlarms(), proto.alarms);
+            assertEquals(policy.getPriorityCategoryMedia(), proto.media);
+            assertEquals(policy.getPriorityCategorySystem(), proto.system);
+
+            assertEquals(policy.getVisualEffectFullScreenIntent(), proto.fullscreen);
+            assertEquals(policy.getVisualEffectLights(), proto.lights);
+            assertEquals(policy.getVisualEffectPeek(), proto.peek);
+            assertEquals(policy.getVisualEffectStatusBar(), proto.statusBar);
+            assertEquals(policy.getVisualEffectBadge(), proto.badge);
+            assertEquals(policy.getVisualEffectAmbient(), proto.ambient);
+            assertEquals(policy.getVisualEffectNotificationList(), proto.notificationList);
+
+            assertEquals(policy.getPriorityCallSenders(), proto.allowCallsFrom);
+            assertEquals(policy.getPriorityMessageSenders(), proto.allowMessagesFrom);
+            assertEquals(policy.getPriorityConversationSenders(), proto.allowConversationsFrom);
+        } catch (InvalidProtocolBufferNanoException e) {
+            fail("could not parse proto bytes");
+        }
+
+    }
 }