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

Commit 78e84ecc authored by Alexander Roederer's avatar Alexander Roederer
Browse files

Add diff for ZenPolicy to ZenModeDiff

Adds diff for ZenPolicy to ZenModeDiff and associated tests.

Bug: 319241807
Test: atest ZenModeDiffTest
Flag: android.app.modes_api
Change-Id: I19e02f6d8004973c2b56a8435e2d892af9e0e9a0
parent 173fcdf9
Loading
Loading
Loading
Loading
+197 −0
Original line number Diff line number Diff line
@@ -801,4 +801,201 @@ public class ZenModeDiff {
        }
    }

    /**
     * Diff class representing a change between two {@link android.service.notification.ZenPolicy}.
     */
    @FlaggedApi(Flags.FLAG_MODES_API)
    public static class PolicyDiff extends BaseDiff {
        public static final String FIELD_PRIORITY_CATEGORY_REMINDERS =
                "mPriorityCategories_Reminders";
        public static final String FIELD_PRIORITY_CATEGORY_EVENTS = "mPriorityCategories_Events";
        public static final String FIELD_PRIORITY_CATEGORY_MESSAGES =
                "mPriorityCategories_Messages";
        public static final String FIELD_PRIORITY_CATEGORY_CALLS = "mPriorityCategories_Calls";
        public static final String FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS =
                "mPriorityCategories_RepeatCallers";
        public static final String FIELD_PRIORITY_CATEGORY_ALARMS = "mPriorityCategories_Alarms";
        public static final String FIELD_PRIORITY_CATEGORY_MEDIA = "mPriorityCategories_Media";
        public static final String FIELD_PRIORITY_CATEGORY_SYSTEM = "mPriorityCategories_System";
        public static final String FIELD_PRIORITY_CATEGORY_CONVERSATIONS =
                "mPriorityCategories_Conversations";

        public static final String FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT =
                "mVisualEffects_FullScreenIntent";
        public static final String FIELD_VISUAL_EFFECT_LIGHTS = "mVisualEffects_Lights";
        public static final String FIELD_VISUAL_EFFECT_PEEK = "mVisualEffects_Peek";
        public static final String FIELD_VISUAL_EFFECT_STATUS_BAR = "mVisualEffects_StatusBar";
        public static final String FIELD_VISUAL_EFFECT_BADGE = "mVisualEffects_Badge";
        public static final String FIELD_VISUAL_EFFECT_AMBIENT = "mVisualEffects_Ambient";
        public static final String FIELD_VISUAL_EFFECT_NOTIFICATION_LIST =
                "mVisualEffects_NotificationList";

        public static final String FIELD_PRIORITY_MESSAGES = "mPriorityMessages";
        public static final String FIELD_PRIORITY_CALLS = "mPriorityCalls";
        public static final String FIELD_CONVERSATION_SENDERS = "mConversationSenders";
        public static final String FIELD_ALLOW_CHANNELS = "mAllowChannels";

        /**
         * Create a PolicyDiff representing the difference between two ZenPolicy objects.
         *
         * @param from previous ZenPolicy
         * @param to   new ZenPolicy
         * @return The diff between the two given ZenPolicy
         */
        public PolicyDiff(ZenPolicy from, ZenPolicy to) {
            super(from, to);
            // Short-circuit the both-null case
            if (from == null && to == null) {
                return;
            }
            if (hasExistenceChange()) {
                // either added or removed; return here. otherwise (they're not both null) there's
                // field diffs.
                return;
            }

            // Compare all fields, knowing there's some diff and that neither is null.
            if (from.getPriorityCategoryReminders() != to.getPriorityCategoryReminders()) {
                addField(FIELD_PRIORITY_CATEGORY_REMINDERS,
                        new FieldDiff<>(from.getPriorityCategoryReminders(),
                                to.getPriorityCategoryReminders()));
            }
            if (from.getPriorityCategoryEvents() != to.getPriorityCategoryEvents()) {
                addField(FIELD_PRIORITY_CATEGORY_EVENTS,
                        new FieldDiff<>(from.getPriorityCategoryEvents(),
                                to.getPriorityCategoryEvents()));
            }
            if (from.getPriorityCategoryMessages() != to.getPriorityCategoryMessages()) {
                addField(FIELD_PRIORITY_CATEGORY_MESSAGES,
                        new FieldDiff<>(from.getPriorityCategoryMessages(),
                                to.getPriorityCategoryMessages()));
            }
            if (from.getPriorityCategoryCalls() != to.getPriorityCategoryCalls()) {
                addField(FIELD_PRIORITY_CATEGORY_CALLS,
                        new FieldDiff<>(from.getPriorityCategoryCalls(),
                                to.getPriorityCategoryCalls()));
            }
            if (from.getPriorityCategoryRepeatCallers() != to.getPriorityCategoryRepeatCallers()) {
                addField(FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS,
                        new FieldDiff<>(from.getPriorityCategoryRepeatCallers(),
                                to.getPriorityCategoryRepeatCallers()));
            }
            if (from.getPriorityCategoryAlarms() != to.getPriorityCategoryAlarms()) {
                addField(FIELD_PRIORITY_CATEGORY_ALARMS,
                        new FieldDiff<>(from.getPriorityCategoryAlarms(),
                                to.getPriorityCategoryAlarms()));
            }
            if (from.getPriorityCategoryMedia() != to.getPriorityCategoryMedia()) {
                addField(FIELD_PRIORITY_CATEGORY_MEDIA,
                        new FieldDiff<>(from.getPriorityCategoryMedia(),
                                to.getPriorityCategoryMedia()));
            }
            if (from.getPriorityCategorySystem() != to.getPriorityCategorySystem()) {
                addField(FIELD_PRIORITY_CATEGORY_SYSTEM,
                        new FieldDiff<>(from.getPriorityCategorySystem(),
                                to.getPriorityCategorySystem()));
            }
            if (from.getPriorityCategoryConversations() != to.getPriorityCategoryConversations()) {
                addField(FIELD_PRIORITY_CATEGORY_CONVERSATIONS,
                        new FieldDiff<>(from.getPriorityCategoryConversations(),
                                to.getPriorityCategoryConversations()));
            }
            if (from.getVisualEffectFullScreenIntent() != to.getVisualEffectFullScreenIntent()) {
                addField(FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT,
                        new FieldDiff<>(from.getVisualEffectFullScreenIntent(),
                                to.getVisualEffectFullScreenIntent()));
            }
            if (from.getVisualEffectLights() != to.getVisualEffectLights()) {
                addField(FIELD_VISUAL_EFFECT_LIGHTS,
                        new FieldDiff<>(from.getVisualEffectLights(), to.getVisualEffectLights()));
            }
            if (from.getVisualEffectPeek() != to.getVisualEffectPeek()) {
                addField(FIELD_VISUAL_EFFECT_PEEK, new FieldDiff<>(from.getVisualEffectPeek(),
                        to.getVisualEffectPeek()));
            }
            if (from.getVisualEffectStatusBar() != to.getVisualEffectStatusBar()) {
                addField(FIELD_VISUAL_EFFECT_STATUS_BAR,
                        new FieldDiff<>(from.getVisualEffectStatusBar(),
                                to.getVisualEffectStatusBar()));
            }
            if (from.getVisualEffectBadge() != to.getVisualEffectBadge()) {
                addField(FIELD_VISUAL_EFFECT_BADGE, new FieldDiff<>(from.getVisualEffectBadge(),
                        to.getVisualEffectBadge()));
            }
            if (from.getVisualEffectAmbient() != to.getVisualEffectAmbient()) {
                addField(FIELD_VISUAL_EFFECT_AMBIENT, new FieldDiff<>(from.getVisualEffectAmbient(),
                        to.getVisualEffectAmbient()));
            }
            if (from.getVisualEffectNotificationList() != to.getVisualEffectNotificationList()) {
                addField(FIELD_VISUAL_EFFECT_NOTIFICATION_LIST,
                        new FieldDiff<>(from.getVisualEffectNotificationList(),
                                to.getVisualEffectNotificationList()));
            }
            if (from.getPriorityMessageSenders() != to.getPriorityMessageSenders()) {
                addField(FIELD_PRIORITY_MESSAGES, new FieldDiff<>(from.getPriorityMessageSenders(),
                        to.getPriorityMessageSenders()));
            }
            if (from.getPriorityCallSenders() != to.getPriorityCallSenders()) {
                addField(FIELD_PRIORITY_CALLS, new FieldDiff<>(from.getPriorityCallSenders(),
                        to.getPriorityCallSenders()));
            }
            if (from.getPriorityConversationSenders() != to.getPriorityConversationSenders()) {
                addField(FIELD_CONVERSATION_SENDERS,
                        new FieldDiff<>(from.getPriorityConversationSenders(),
                                to.getPriorityConversationSenders()));
            }
            if (from.getPriorityChannelsAllowed() != to.getPriorityChannelsAllowed()) {
                addField(FIELD_ALLOW_CHANNELS, new FieldDiff<>(from.getPriorityChannelsAllowed(),
                        to.getPriorityChannelsAllowed()));
            }
        }

        /**
         * Returns whether this object represents an actual diff.
         */
        @Override
        public boolean hasDiff() {
            return hasExistenceChange() || hasFieldDiffs();
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("ZenPolicyDiff{");
            // The diff should not have null diffs added, but we add this to be defensive.
            if (!hasDiff()) {
                sb.append("no changes");
            }

            // If added or deleted, we just append that.
            if (hasExistenceChange()) {
                if (wasAdded()) {
                    sb.append("added");
                } else if (wasRemoved()) {
                    sb.append("removed");
                }
            }

            // Go through all of the individual fields
            boolean first = true;
            for (String key : fieldNamesWithDiff()) {
                FieldDiff diff = getDiffForField(key);
                if (diff == null) {
                    // this shouldn't happen...
                    continue;
                }
                if (first) {
                    first = false;
                } else {
                    sb.append(", ");
                }

                sb.append(key);
                sb.append(":");
                sb.append(diff);
            }

            return sb.append("}").toString();
        }
    }

}
+6 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.proto.ProtoOutputStream;

import androidx.annotation.VisibleForTesting;

import java.io.ByteArrayOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -207,8 +209,10 @@ public final class ZenPolicy implements Parcelable {

    /**
     * Total number of priority categories. Keep updated with any updates to PriorityCategory enum.
     * If this changes, you must update {@link ZenModeDiff.PolicyDiff} to include new categories.
     * @hide
     */
    @VisibleForTesting
    public static final int NUM_PRIORITY_CATEGORIES = 9;

    /** @hide */
@@ -241,8 +245,10 @@ public final class ZenPolicy implements Parcelable {

    /**
     * Total number of visual effects. Keep updated with any updates to VisualEffect enum.
     * If this changes, you must update {@link ZenModeDiff.PolicyDiff} to include new categories.
     * @hide
     */
    @VisibleForTesting
    public static final int NUM_VISUAL_EFFECTS = 7;

    /** @hide */
+195 −0
Original line number Diff line number Diff line
@@ -225,6 +225,101 @@ public class ZenModeDiffTest extends UiServiceTestCase {
    }


    @Test
    public void testPolicyDiff_addRemoveSame() {
        // Test add, remove, and both sides same
        ZenPolicy effects = new ZenPolicy.Builder().build();

        // Both sides same rule
        ZenModeDiff.PolicyDiff dSame = new ZenModeDiff.PolicyDiff(effects, effects);
        assertFalse(dSame.hasDiff());

        // from existent rule to null: expect deleted
        ZenModeDiff.PolicyDiff deleted = new ZenModeDiff.PolicyDiff(effects, null);
        assertTrue(deleted.hasDiff());
        assertTrue(deleted.wasRemoved());

        // from null to new rule: expect added
        ZenModeDiff.PolicyDiff added = new ZenModeDiff.PolicyDiff(null, effects);
        assertTrue(added.hasDiff());
        assertTrue(added.wasAdded());
    }

    @Test
    public void testPolicyDiff_fieldDiffs() throws Exception {
        // Start these the same
        ZenPolicy policy1 = new ZenPolicy.Builder().build();
        ZenPolicy policy2 = new ZenPolicy.Builder().build();

        // maps mapping field name -> expected output value as we set diffs
        ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
        ArrayMap<String, Object> expectedTo = new ArrayMap<>();

        List<Field> fieldsForDiff = getFieldsForDiffCheck(ZenPolicy.class, Collections.emptySet(),
                false);
        generateFieldDiffsForZenPolicy(policy1, policy2, fieldsForDiff, expectedFrom, expectedTo);

        ZenModeDiff.PolicyDiff d = new ZenModeDiff.PolicyDiff(policy1, policy2);
        assertTrue(d.hasDiff());

        // Now diff them and check that each of the fields has a diff.
        // Because ZenPolicy consolidates priority category and visual effect fields in a list,
        // we cannot use reflection on ZenPolicy to get the list of fields.
        ArrayList<String> diffFields = new ArrayList<>();
        Field[] fields = ZenModeDiff.PolicyDiff.class.getDeclaredFields();

        for (Field field : fields) {
            int m = field.getModifiers();
            if (Modifier.isStatic(m) && Modifier.isFinal(m)) {
                diffFields.add((String) field.get(policy1));
            }
        }

        for (String name : diffFields) {
            assertNotNull("diff not found for field: " + name, d.getDiffForField(name));
            assertTrue(d.getDiffForField(name).hasDiff());
            assertTrue("unexpected field: " + name, expectedFrom.containsKey(name));
            assertTrue("unexpected field: " + name, expectedTo.containsKey(name));
            assertEquals(expectedFrom.get(name), d.getDiffForField(name).from());
            assertEquals(expectedTo.get(name), d.getDiffForField(name).to());
        }
    }

    @Test
    public void testPolicyDiff_toString() throws Exception {
        // Ensure device effects toString is readable.
        ZenPolicy policy1 = new ZenPolicy.Builder().build();
        ZenPolicy policy2 = new ZenPolicy.Builder().build();

        ZenModeDiff.PolicyDiff d = new ZenModeDiff.PolicyDiff(policy1, policy2);
        assertThat(d.toString()).isEqualTo("ZenPolicyDiff{no changes}");

        d = new ZenModeDiff.PolicyDiff(policy1, null);
        assertThat(d.toString()).isEqualTo("ZenPolicyDiff{removed}");

        d = new ZenModeDiff.PolicyDiff(null, policy2);
        assertThat(d.toString()).isEqualTo("ZenPolicyDiff{added}");

        ArrayMap<String, Object> expectedFrom = new ArrayMap<>();
        ArrayMap<String, Object> expectedTo = new ArrayMap<>();
        List<Field> fieldsForDiff = getFieldsForDiffCheck(
                ZenPolicy.class, Collections.emptySet() /*no exempt fields*/, false);
        generateFieldDiffsForZenPolicy(policy1, policy2, fieldsForDiff, expectedFrom, expectedTo);

        d = new ZenModeDiff.PolicyDiff(policy1, policy2);
        assertThat(d.toString()).isEqualTo("ZenPolicyDiff{mPriorityCalls:2->1, "
                + "mVisualEffects_StatusBar:1->2, mPriorityCategories_RepeatCallers:1->2, "
                + "mPriorityCategories_Calls:1->2, mPriorityCategories_Media:1->2, "
                + "mConversationSenders:2->1, mPriorityCategories_Reminders:1->2, "
                + "mVisualEffects_Badge:1->2, mPriorityCategories_Messages:1->2, "
                + "mAllowChannels:2->1, mPriorityMessages:2->1, "
                + "mVisualEffects_NotificationList:1->2, mVisualEffects_FullScreenIntent:1->2, "
                + "mPriorityCategories_Alarms:1->2, mVisualEffects_Lights:1->2, "
                + "mPriorityCategories_Events:1->2, mVisualEffects_Ambient:1->2, "
                + "mPriorityCategories_System:1->2, mPriorityCategories_Conversations:1->2, "
                + "mVisualEffects_Peek:1->2}");
    }

    private static Set<String> getZenRuleExemptFields() {
        // "Metadata" fields are never compared.
        Set<String> exemptFields = new LinkedHashSet<>(
@@ -463,6 +558,106 @@ public class ZenModeDiffTest extends UiServiceTestCase {
        return out;
    }

    // Generate a set of diffs for two ZenPolicy objects. Store the results in the provided
    // expectation maps.
    private void generateFieldDiffsForZenPolicy(ZenPolicy a, ZenPolicy b, List<Field> fields,
            ArrayMap<String, Object> expectedA, ArrayMap<String, Object> expectedB)
            throws Exception {
        // Loop through fields for which we want to check diffs, set a diff and keep track of
        // what we set.
        for (Field f : fields) {
            f.setAccessible(true);
            // Just double-check also that the fields actually are for the class declared
            assertEquals(f.getDeclaringClass(), a.getClass());
            Class<?> t = f.getType();

            if (int.class.equals(t)) {
                // these will not be valid for arbitrary int enums, but should suffice for a diff.
                f.setInt(a, 2);
                expectedA.put(f.getName(), 2);
                f.setInt(b, 1);
                expectedB.put(f.getName(), 1);
            } else if (List.class.equals(t)) {
                // Fieds mPriorityCategories and mVisualEffects store multiple values and
                // must be treated separately.
                List<Integer> aList = (ArrayList<Integer>) f.get(a);
                List<Integer> bList = (ArrayList<Integer>) f.get(b);
                if (f.getName().equals("mPriorityCategories")) {
                    // PRIORITY_CATEGORY_REMINDERS
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 0,
                            "mPriorityCategories_Reminders");
                    // PRIORITY_CATEGORY_EVENTS
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 1,
                            "mPriorityCategories_Events");
                    // PRIORITY_CATEGORY_MESSAGES
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 2,
                            "mPriorityCategories_Messages");
                    // PRIORITY_CATEGORY_CALLS
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 3,
                            "mPriorityCategories_Calls");
                    // PRIORITY_CATEGORY_REPEAT_CALLERS
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 4,
                            "mPriorityCategories_RepeatCallers");
                    // PRIORITY_CATEGORY_ALARMS
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 5,
                            "mPriorityCategories_Alarms");
                    // PRIORITY_CATEGORY_MEDIA
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 6,
                            "mPriorityCategories_Media");
                    // PRIORITY_CATEGORY_SYSTEM
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 7,
                            "mPriorityCategories_System");
                    // PRIORITY_CATEGORY_CONVERSATIONS
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 8,
                            "mPriorityCategories_Conversations");
                    // Assert that we've set every PriorityCategory enum value.
                    assertThat(Collections.frequency(aList, ZenPolicy.STATE_ALLOW))
                            .isEqualTo(ZenPolicy.NUM_PRIORITY_CATEGORIES);
                } else if (f.getName().equals("mVisualEffects")) {
                    // VISUAL_EFFECT_FULL_SCREEN_INTENT
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 0,
                            "mVisualEffects_FullScreenIntent");
                    // VISUAL_EFFECT_LIGHTS
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 1,
                            "mVisualEffects_Lights");
                    // VISUAL_EFFECT_PEEK
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 2,
                            "mVisualEffects_Peek");
                    // VISUAL_EFFECT_STATUS_BAR
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 3,
                            "mVisualEffects_StatusBar");
                    // VISUAL_EFFECT_BADGE
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 4,
                            "mVisualEffects_Badge");
                    // VISUAL_EFFECT_AMBIENT
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 5,
                            "mVisualEffects_Ambient");
                    // VISUAL_EFFECT_NOTIFICATION_LIST
                    setPolicyListValueDiff(aList, bList, expectedA, expectedB, 6,
                            "mVisualEffects_NotificationList");
                    // Assert that we've set every VisualeEffect enum value.
                    assertThat(Collections.frequency(aList, ZenPolicy.STATE_ALLOW))
                            .isEqualTo(ZenPolicy.NUM_VISUAL_EFFECTS);
                } else {
                    // Any other lists that are added should be added to the diff.
                    fail("could not generate field diffs for policy list: " + f.getName());
                }
            }
        }
    }

    // Helper function to create a diff in two list values at a given index, and record that
    // diff's values in the associated expected maps under the provided field name.
    private void setPolicyListValueDiff(List<Integer> aList, List<Integer> bList,
                                        ArrayMap<String, Object> expectedA,
                                        ArrayMap<String, Object> expectedB,
                                        int index, String fieldName) {
        aList.set(index, ZenPolicy.STATE_ALLOW);
        expectedA.put(fieldName, ZenPolicy.STATE_ALLOW);
        bList.set(index, ZenPolicy.STATE_DISALLOW);
        expectedB.put(fieldName, ZenPolicy.STATE_DISALLOW);
    }

    // Generate a set of generic diffs for the specified two objects and the fields to generate
    // diffs for, and store the results in the provided expectation maps to be able to check the
    // output later.