Merge "Update behavior of "global" DND APIs" into main
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index b0332c3..ffb79b3 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1642,6 +1642,7 @@
      *
      * <p>
      */
+    // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
     public Policy getNotificationPolicy() {
         INotificationManager service = getService();
         try {
@@ -1660,6 +1661,7 @@
      *
      * @param policy The new desired policy.
      */
+    // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
     public void setNotificationPolicy(@NonNull Policy policy) {
         checkRequired("policy", policy);
         INotificationManager service = getService();
@@ -2608,6 +2610,7 @@
      * Only available if policy access is granted to this package. See
      * {@link #isNotificationPolicyAccessGranted}.
      */
+    // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior.
     public final void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
         final INotificationManager service = getService();
         try {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index ff4dfc7..305b751 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1004,6 +1004,8 @@
             priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
             conversationSenders = getConversationSendersWithDefault(
                     zenPolicy.getPriorityConversationSenders(), conversationSenders);
+        } else {
+            conversationSenders = CONVERSATION_SENDERS_NONE;
         }
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
@@ -1102,7 +1104,7 @@
         return (policy.suppressedVisualEffects & visualEffect) == 0;
     }
 
-    private int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
+    private static int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
             int defaultPolicySender) {
         switch (senders) {
             case ZenPolicy.PEOPLE_TYPE_ANYONE:
@@ -1116,7 +1118,7 @@
         }
     }
 
-    private int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
+    private static int getConversationSendersWithDefault(@ZenPolicy.ConversationSenders int senders,
             int defaultPolicySender) {
         switch (senders) {
             case ZenPolicy.CONVERSATION_SENDERS_ANYONE:
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 15c188d..cec83de 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5245,6 +5245,11 @@
     <!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] -->
     <string name="zen_mode_default_every_night_name">Sleeping</string>
 
+    <!-- Zen mode - Condition summary when a rule is activated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
+    <string name="zen_mode_implicit_activated">On</string>
+    <!-- Zen mode - Condition summary when a rule is deactivated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
+    <string name="zen_mode_implicit_deactivated">Off</string>
+
     <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
     <string name="muted_by"><xliff:g id="third_party">%1$s</xliff:g> is muting some sounds</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e19d548..16ad5c9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2574,6 +2574,8 @@
   <java-symbol type="string" name="zen_mode_default_weekends_name" />
   <java-symbol type="string" name="zen_mode_default_events_name" />
   <java-symbol type="string" name="zen_mode_default_every_night_name" />
+  <java-symbol type="string" name="zen_mode_implicit_activated" />
+  <java-symbol type="string" name="zen_mode_implicit_deactivated" />
   <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" />
   <java-symbol type="string" name="display_rotation_camera_compat_toast_in_multi_window" />
   <java-symbol type="array" name="config_system_condition_providers" />
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1bdd402..707e990 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -179,6 +179,8 @@
 import android.app.role.RoleManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
+import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
 import android.companion.ICompanionDeviceManager;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
@@ -546,6 +548,15 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L;
 
+    /**
+     * App calls to {@link android.app.NotificationManager#setInterruptionFilter} and
+     * {@link android.app.NotificationManager#setNotificationPolicy} manage DND through the
+     * creation and activation of an implicit {@link android.app.AutomaticZenRule}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    static final long MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES = 308670109L;
+
     private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30);
 
     private IActivityManager mAm;
@@ -5343,6 +5354,12 @@
             if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter);
             final int callingUid = Binder.getCallingUid();
             final boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+
+            if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+                mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, callingUid, zen);
+                return;
+            }
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter",
@@ -5426,6 +5443,16 @@
             }
         }
 
+        private boolean canManageGlobalZenPolicy(String callingPkg, int callingUid) {
+            boolean isCompatChangeEnabled = Binder.withCleanCallingIdentity(
+                    () -> CompatChanges.isChangeEnabled(MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES,
+                            callingUid));
+            return !isCompatChangeEnabled
+                    || isCallerIsSystemOrSystemUi()
+                    || hasCompanionDevice(callingPkg, UserHandle.getUserId(callingUid),
+                            AssociationRequest.DEVICE_PROFILE_WATCH);
+        }
+
         private void enforcePolicyAccess(String pkg, String method) {
             if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission(
                     android.Manifest.permission.MANAGE_NOTIFICATIONS)) {
@@ -5619,6 +5646,10 @@
 
         @Override
         public Policy getNotificationPolicy(String pkg) {
+            final int callingUid = Binder.getCallingUid();
+            if (android.app.Flags.modesApi() && !canManageGlobalZenPolicy(pkg, callingUid)) {
+                return mZenModeHelper.getNotificationPolicyFromImplicitZenRule(pkg);
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 return mZenModeHelper.getNotificationPolicy();
@@ -5649,6 +5680,10 @@
             enforcePolicyAccess(pkg, "setNotificationPolicy");
             int callingUid = Binder.getCallingUid();
             boolean isSystemOrSystemUi = isCallerIsSystemOrSystemUi();
+
+            boolean shouldApplyAsImplicitRule = android.app.Flags.modesApi()
+                    && !canManageGlobalZenPolicy(pkg, callingUid);
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(pkg,
@@ -5687,16 +5722,21 @@
                 policy = new Policy(policy.priorityCategories,
                         policy.priorityCallSenders, policy.priorityMessageSenders,
                         newVisualEffects, policy.priorityConversationSenders);
-                ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
-                mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
+
+                if (shouldApplyAsImplicitRule) {
+                    mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
+                } else {
+                    ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
+                            policy);
+                    mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
+                }
             } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to set notification policy", e);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
 
-
-
         @Override
         public List<String> getEnabledNotificationListenerPackages() {
             checkCallerIsSystem();
@@ -10556,6 +10596,12 @@
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
+        return hasCompanionDevice(info.component.getPackageName(),
+                info.userid, /* withDeviceProfile= */ null);
+    }
+
+    private boolean hasCompanionDevice(String pkg, @UserIdInt int userId,
+            @Nullable @AssociationRequest.DeviceProfile String withDeviceProfile) {
         if (mCompanionManager == null) {
             mCompanionManager = getCompanionManager();
         }
@@ -10565,17 +10611,19 @@
         }
         final long identity = Binder.clearCallingIdentity();
         try {
-            List<?> associations = mCompanionManager.getAssociations(
-                    info.component.getPackageName(), info.userid);
-            if (!ArrayUtils.isEmpty(associations)) {
-                return true;
+            List<AssociationInfo> associations = mCompanionManager.getAssociations(pkg, userId);
+            for (AssociationInfo association : associations) {
+                if (withDeviceProfile == null || withDeviceProfile.equals(
+                        association.getDeviceProfile())) {
+                    return true;
+                }
             }
         } catch (SecurityException se) {
             // Not a privileged listener
         } catch (RemoteException re) {
             Slog.e(TAG, "Cannot reach companion device service", re);
         } catch (Exception e) {
-            Slog.e(TAG, "Cannot verify listener " + info, e);
+            Slog.e(TAG, "Cannot verify caller pkg=" + pkg + ", userId=" + userId, e);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/notification/ZenAdapters.java b/services/core/java/com/android/server/notification/ZenAdapters.java
new file mode 100644
index 0000000..2a65aff
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ZenAdapters.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.NotificationManager.Policy;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
+
+/**
+ * Converters between different Zen representations.
+ */
+class ZenAdapters {
+
+    static ZenPolicy notificationPolicyToZenPolicy(Policy policy) {
+        ZenPolicy.Builder zenPolicyBuilder = new ZenPolicy.Builder()
+                .allowAlarms(policy.allowAlarms())
+                .allowCalls(
+                        policy.allowCalls()
+                                ? ZenModeConfig.getZenPolicySenders(policy.allowCallsFrom())
+                                : ZenPolicy.PEOPLE_TYPE_NONE)
+                .allowConversations(
+                        policy.allowConversations()
+                                ? notificationPolicyConversationSendersToZenPolicy(
+                                        policy.allowConversationsFrom())
+                                : ZenPolicy.CONVERSATION_SENDERS_NONE)
+                .allowEvents(policy.allowEvents())
+                .allowMedia(policy.allowMedia())
+                .allowMessages(
+                        policy.allowMessages()
+                                ? ZenModeConfig.getZenPolicySenders(policy.allowMessagesFrom())
+                                : ZenPolicy.PEOPLE_TYPE_NONE)
+                .allowReminders(policy.allowReminders())
+                .allowRepeatCallers(policy.allowRepeatCallers())
+                .allowSystem(policy.allowSystem());
+
+        if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
+            zenPolicyBuilder.showBadges(policy.showBadges())
+                    .showFullScreenIntent(policy.showFullScreenIntents())
+                    .showInAmbientDisplay(policy.showAmbient())
+                    .showInNotificationList(policy.showInNotificationList())
+                    .showLights(policy.showLights())
+                    .showPeeking(policy.showPeeking())
+                    .showStatusBarIcons(policy.showStatusBarIcons());
+        }
+
+        return zenPolicyBuilder.build();
+    }
+
+    @ZenPolicy.ConversationSenders
+    private static int notificationPolicyConversationSendersToZenPolicy(
+            int npPriorityConversationSenders) {
+        switch (npPriorityConversationSenders) {
+            case Policy.CONVERSATION_SENDERS_ANYONE:
+                return ZenPolicy.CONVERSATION_SENDERS_ANYONE;
+            case Policy.CONVERSATION_SENDERS_IMPORTANT:
+                return ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+            case Policy.CONVERSATION_SENDERS_NONE:
+                return ZenPolicy.CONVERSATION_SENDERS_NONE;
+            case Policy.CONVERSATION_SENDERS_UNSET:
+            default:
+                return ZenPolicy.CONVERSATION_SENDERS_UNSET;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 762c1a1..c637df2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -27,6 +27,7 @@
 
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
@@ -44,6 +45,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -349,11 +351,11 @@
         ZenRule rule;
         synchronized (mConfigLock) {
             if (mConfig == null) return null;
-             rule = mConfig.automaticRules.get(id);
+            rule = mConfig.automaticRules.get(id);
         }
         if (rule == null) return null;
         if (canManageAutomaticZenRule(rule)) {
-             return createAutomaticZenRule(rule);
+            return zenRuleToAutomaticZenRule(rule);
         }
         return null;
     }
@@ -439,6 +441,167 @@
         }
     }
 
+    /**
+     * Create (or activate, or deactivate) an "implicit" {@link ZenRule} when an app that has
+     * Notification Policy Access but is not allowed to manage the global zen state
+     * calls {@link NotificationManager#setInterruptionFilter}.
+     *
+     * <p>When the {@code zenMode} is {@link Global#ZEN_MODE_OFF}, an existing implicit rule will be
+     * deactivated (if there is no implicit rule, the call will be ignored). For other modes, the
+     * rule's interruption filter will match the supplied {@code zenMode}. The policy of the last
+     * call to {@link NotificationManager#setNotificationPolicy} will be used (or, if never called,
+     * the global policy).
+     *
+     * <p>The created rule is owned by the calling package, but it has neither a
+     * {@link ConditionProviderService} nor an associated
+     * {@link AutomaticZenRule#configurationActivity}.
+     *
+     * @param zenMode one of the {@code Global#ZEN_MODE_x} values
+     */
+    void applyGlobalZenModeAsImplicitZenRule(String callingPkg, int callingUid, int zenMode) {
+        if (!android.app.Flags.modesApi()) {
+            Log.wtf(TAG, "applyGlobalZenModeAsImplicitZenRule called with flag off!");
+            return;
+        }
+        synchronized (mConfigLock) {
+            if (mConfig == null) {
+                return;
+            }
+            if (zenMode == Global.ZEN_MODE_OFF) {
+                // Deactivate implicit rule if it exists and is active; otherwise ignore.
+                ZenRule rule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
+                if (rule != null) {
+                    Condition deactivated = new Condition(rule.conditionId,
+                            mContext.getString(R.string.zen_mode_implicit_deactivated),
+                            Condition.STATE_FALSE);
+                    setAutomaticZenRuleState(rule.id, deactivated,
+                            callingUid, /* fromSystemOrSystemUi= */ false);
+                }
+            } else {
+                // Either create a new rule with a default ZenPolicy, or update an existing rule's
+                // filter value. In both cases, also activate (and unsnooze) it.
+                ZenModeConfig newConfig = mConfig.copy();
+                ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
+                if (rule == null) {
+                    rule = newImplicitZenRule(callingPkg);
+                    newConfig.automaticRules.put(rule.id, rule);
+                }
+                rule.zenMode = zenMode;
+                rule.snoozing = false;
+                rule.condition = new Condition(rule.conditionId,
+                        mContext.getString(R.string.zen_mode_implicit_activated),
+                        Condition.STATE_TRUE);
+                setConfigLocked(newConfig, /* triggeringComponent= */ null,
+                        "applyGlobalZenModeAsImplicitZenRule",
+                        callingUid, /* fromSystemOrSystemUi= */ false);
+            }
+        }
+    }
+
+    /**
+     * Create (or update) an "implicit" {@link ZenRule} when an app that has Notification Policy
+     * Access but is not allowed to manage the global zen state calls
+     * {@link NotificationManager#setNotificationPolicy}.
+     *
+     * <p>The created rule is owned by the calling package and has the {@link ZenPolicy}
+     * corresponding to the supplied {@code policy}, but it has neither a
+     * {@link ConditionProviderService} nor an associated
+     * {@link AutomaticZenRule#configurationActivity}. Its zen mode will be set to
+     * {@link Global#ZEN_MODE_IMPORTANT_INTERRUPTIONS}.
+     */
+    void applyGlobalPolicyAsImplicitZenRule(String callingPkg, int callingUid,
+            NotificationManager.Policy policy) {
+        if (!android.app.Flags.modesApi()) {
+            Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
+            return;
+        }
+        synchronized (mConfigLock) {
+            if (mConfig == null) {
+                return;
+            }
+            ZenModeConfig newConfig = mConfig.copy();
+            ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
+            if (rule == null) {
+                rule = newImplicitZenRule(callingPkg);
+                rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+                newConfig.automaticRules.put(rule.id, rule);
+            }
+            // TODO: b/308673679 - Keep user customization of this rule!
+            rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+            setConfigLocked(newConfig, /* triggeringComponent= */ null,
+                    "applyGlobalPolicyAsImplicitZenRule",
+                    callingUid, /* fromSystemOrSystemUi= */ false);
+        }
+    }
+
+    /**
+     * Returns the {@link Policy} associated to the "implicit" {@link ZenRule} of a package that has
+     * Notification Policy Access but is not allowed to manage the global zen state.
+     *
+     * <p>If the implicit rule doesn't exist, or it doesn't specify a {@link ZenPolicy} (because the
+     * app never called {@link NotificationManager#setNotificationPolicy}) then the default policy
+     * is returned (i.e. same as {@link #getNotificationPolicy}.
+     *
+     * <p>Any unset values in the {@link ZenPolicy} will be mapped to their current defaults.
+     */
+    @Nullable
+    Policy getNotificationPolicyFromImplicitZenRule(String callingPkg) {
+        if (!android.app.Flags.modesApi()) {
+            Log.wtf(TAG, "getNotificationPolicyFromImplicitZenRule called with flag off!");
+            return getNotificationPolicy();
+        }
+        synchronized (mConfigLock) {
+            if (mConfig == null) {
+                return null;
+            }
+            ZenRule implicitRule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
+            if (implicitRule != null && implicitRule.zenPolicy != null) {
+                return mConfig.toNotificationPolicy(implicitRule.zenPolicy);
+            } else {
+                return getNotificationPolicy();
+            }
+        }
+    }
+
+    /**
+     * Creates an empty {@link ZenRule} to be used as the implicit rule for {@code pkg}.
+     * Both {@link ZenRule#zenMode} and {@link ZenRule#zenPolicy} are unset.
+     */
+    private ZenRule newImplicitZenRule(String pkg) {
+        ZenRule rule = new ZenRule();
+        rule.id = implicitRuleId(pkg);
+        rule.pkg = pkg;
+        rule.creationTime = System.currentTimeMillis();
+
+        Binder.withCleanCallingIdentity(() -> {
+            try {
+                ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0);
+                rule.name = applicationInfo.loadLabel(mPm).toString();
+            } catch (PackageManager.NameNotFoundException e) {
+                // Should not happen, since it's the app calling us (?)
+                Log.w(TAG, "Package not found for creating implicit zen rule");
+                rule.name = "Unknown";
+            }
+        });
+
+        rule.condition = null;
+        rule.conditionId = new Uri.Builder()
+                .scheme(Condition.SCHEME)
+                .authority("android")
+                .appendPath("implicit")
+                .appendPath(pkg)
+                .build();
+        rule.enabled = true;
+        rule.modified = false;
+        rule.component = null;
+        rule.configurationActivity = null;
+        return rule;
+    }
+
+    private static String implicitRuleId(String forPackage) {
+        return "implicit_" + forPackage;
+    }
+
     public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
             boolean fromSystemOrSystemUi) {
         ZenModeConfig newConfig;
@@ -626,7 +789,7 @@
                         }
                         // update default rule (if locale changed, name of rule will change)
                         currRule.name = defaultRule.name;
-                        updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+                        updateAutomaticZenRule(defaultRule.id, zenRuleToAutomaticZenRule(currRule),
                                 "locale changed", callingUid, fromSystemOrSystemUi);
                     }
                 }
@@ -669,7 +832,7 @@
         return null;
     }
 
-    private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
+    private static void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
             boolean isNew) {
         if (rule.enabled != automaticZenRule.isEnabled()) {
             rule.snoozing = false;
@@ -699,7 +862,7 @@
         }
     }
 
-    protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
+    private static AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
         AutomaticZenRule azr;
         if (Flags.modesApi()) {
             azr = new AutomaticZenRule.Builder(rule.name, rule.conditionId)
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 25b7ca1..dcac8c9 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -7,8 +7,6 @@
   bug: "290381858"
 }
 
-
-
 flag {
   name: "polite_notifications"
   namespace: "systemui"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 6792cfe..3803244 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,6 +43,7 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -72,6 +73,7 @@
 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
 import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
 import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -163,6 +165,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
 import android.companion.ICompanionDeviceManager;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.BroadcastReceiver;
@@ -3820,6 +3823,7 @@
         when(mCompanionMgr.getAssociations(PKG, mUserId))
                 .thenReturn(singletonList(mock(AssociationInfo.class)));
         mListener = mock(ManagedServices.ManagedServiceInfo.class);
+        mListener.component = new ComponentName(PKG, PKG);
         when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
         when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
 
@@ -3870,6 +3874,7 @@
         when(mCompanionMgr.getAssociations(PKG, mUserId))
                 .thenReturn(emptyList());
         mListener = mock(ManagedServices.ManagedServiceInfo.class);
+        mListener.component = new ComponentName(PKG, PKG);
         when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
         when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
         try {
@@ -12777,6 +12782,145 @@
         verify(mSnoozeHelper).clearData(anyInt());
     }
 
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setNotificationPolicy_mappedToImplicitRule() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mService.setCallerIsNormalPackage();
+        ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+        mBinderService.setNotificationPolicy("package", policy);
+
+        verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy));
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setNotificationPolicy_systemCaller_setsGlobalPolicy() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenModeHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+        mService.isSystemUid = true;
+
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+        mBinderService.setNotificationPolicy("package", policy);
+
+        verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setNotificationPolicy_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mService.setCallerIsNormalPackage();
+        ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenModeHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+        when(mCompanionMgr.getAssociations(anyString(), anyInt()))
+                .thenReturn(ImmutableList.of(
+                        new AssociationInfo.Builder(1, mUserId, "package")
+                                .setDisplayName("My watch")
+                                .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+                                .build()));
+
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+        mBinderService.setNotificationPolicy("package", policy);
+
+        verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+    }
+
+    @Test
+    @DisableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setNotificationPolicy_withoutCompat_setsGlobalPolicy() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mService.setCallerIsNormalPackage();
+        ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenModeHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
+        mBinderService.setNotificationPolicy("package", policy);
+
+        verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyBoolean());
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void getNotificationPolicy_mappedFromImplicitRule() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mService.setCallerIsNormalPackage();
+        ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+
+        mBinderService.getNotificationPolicy("package");
+
+        verify(zenHelper).getNotificationPolicyFromImplicitZenRule(eq("package"));
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setInterruptionFilter_mappedToImplicitRule() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mService.setCallerIsNormalPackage();
+        ZenModeHelper zenHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+
+        mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+        verify(zenHelper).applyGlobalZenModeAsImplicitZenRule(eq("package"), anyInt(),
+                eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setInterruptionFilter_systemCaller_setsGlobalPolicy() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mService.setCallerIsNormalPackage();
+        ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenModeHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+        mService.isSystemUid = true;
+
+        mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+        verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+                eq("package"), anyString(), anyInt(), anyBoolean());
+    }
+
+    @Test
+    @EnableCompatChanges(NotificationManagerService.MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES)
+    public void setInterruptionFilter_watchCompanionApp_setsGlobalPolicy() throws RemoteException {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        ZenModeHelper zenModeHelper = mock(ZenModeHelper.class);
+        mService.mZenModeHelper = zenModeHelper;
+        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
+                .thenReturn(true);
+        when(mCompanionMgr.getAssociations(anyString(), anyInt()))
+                .thenReturn(ImmutableList.of(
+                        new AssociationInfo.Builder(1, mUserId, "package")
+                                .setDisplayName("My watch")
+                                .setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
+                                .build()));
+
+        mBinderService.setInterruptionFilter("package", INTERRUPTION_FILTER_PRIORITY);
+
+        verify(zenModeHelper).setManualZenMode(eq(ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null),
+                eq("package"), anyString(), anyInt(), anyBoolean());
+    }
+
     private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
             throws RemoteException {
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 27e8f36..8f30f41 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -54,6 +54,15 @@
         return mRankingHelper;
     }
 
+    /**
+     * Sets {@link #isSystemUid} and {@link #isSystemAppId} to {@code false}, so that calls to NMS
+     * methods don't succeed {@link #isCallingUidSystem()} and similar checks.
+     */
+    void setCallerIsNormalPackage() {
+        isSystemUid = false;
+        isSystemAppId = false;
+    }
+
     @Override
     protected boolean isCallingUidSystem() {
         countSystemChecks++;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
new file mode 100644
index 0000000..6cc1c43
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static com.android.server.notification.ZenAdapters.notificationPolicyToZenPolicy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.NotificationManager.Policy;
+import android.service.notification.ZenPolicy;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ZenAdaptersTest extends UiServiceTestCase {
+
+    @Test
+    public void notificationPolicyToZenPolicy_allCallers() {
+        Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_ANY, 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_ANYONE);
+    }
+
+    @Test
+    public void notificationPolicyToZenPolicy_starredCallers() {
+        Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_STARRED,
+                0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_STARRED);
+    }
+
+    @Test
+    public void notificationPolicyToZenPolicy_repeatCallers() {
+        Policy policy = new Policy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+        assertThat(zenPolicy.getPriorityCategoryRepeatCallers()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_NONE);
+    }
+
+    @Test
+    public void notificationPolicyToZenPolicy_noCallers() {
+        Policy policy = new Policy(0, 0, 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getPriorityCategoryCalls()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+        assertThat(zenPolicy.getPriorityCallSenders()).isEqualTo(ZenPolicy.PEOPLE_TYPE_NONE);
+    }
+
+    @Test
+    public void notificationPolicyToZenPolicy_conversationsAllowedSendersUnset() {
+        Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CONVERSATIONS, 0, 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getPriorityCategoryConversations()).isEqualTo(ZenPolicy.STATE_UNSET);
+    }
+
+    @Test
+    public void notificationPolicyToZenPolicy_conversationsNotAllowedSendersUnset() {
+        Policy policy = new Policy(0, 0, 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getPriorityCategoryConversations()).isEqualTo(
+                ZenPolicy.STATE_DISALLOW);
+    }
+
+    @Test
+    public void notificationPolicyToZenPolicy_setEffects() {
+        Policy policy = new Policy(0, 0, 0,
+                Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getVisualEffectBadge()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+        assertThat(zenPolicy.getVisualEffectLights()).isEqualTo(ZenPolicy.STATE_DISALLOW);
+
+        assertThat(zenPolicy.getVisualEffectAmbient()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(zenPolicy.getVisualEffectFullScreenIntent()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(zenPolicy.getVisualEffectNotificationList()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_ALLOW);
+        assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_ALLOW);
+    }
+
+    @Test
+    public void notificationPolicyToZenPolicy_unsetEffects() {
+        Policy policy = new Policy(0, 0, 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+
+        assertThat(zenPolicy.getVisualEffectAmbient()).isEqualTo(ZenPolicy.STATE_UNSET);
+        assertThat(zenPolicy.getVisualEffectBadge()).isEqualTo(ZenPolicy.STATE_UNSET);
+        assertThat(zenPolicy.getVisualEffectFullScreenIntent()).isEqualTo(ZenPolicy.STATE_UNSET);
+        assertThat(zenPolicy.getVisualEffectLights()).isEqualTo(ZenPolicy.STATE_UNSET);
+        assertThat(zenPolicy.getVisualEffectNotificationList()).isEqualTo(ZenPolicy.STATE_UNSET);
+        assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_UNSET);
+        assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_UNSET);
+    }
+}
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 e8201fd..37aeb57 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
@@ -32,6 +33,7 @@
 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_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
@@ -40,8 +42,11 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
 import static android.service.notification.Condition.STATE_FALSE;
 import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
 
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
 import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
@@ -50,6 +55,8 @@
 import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
 import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -72,6 +79,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -82,6 +90,7 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
@@ -92,6 +101,7 @@
 import android.media.AudioSystem;
 import android.media.VolumePolicy;
 import android.net.Uri;
+import android.os.Parcel;
 import android.os.Process;
 import android.os.UserHandle;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -100,6 +110,7 @@
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
+import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.notification.ZenModeDiff;
 import android.service.notification.ZenPolicy;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -124,6 +135,7 @@
 import com.android.server.notification.ManagedServices.UserProfiles;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Correspondence;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.junit.Before;
@@ -157,27 +169,29 @@
     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 String CUSTOM_PKG_NAME = "not.android";
+    private static final String CUSTOM_APP_LABEL = "This is not Android";
     private static final int CUSTOM_PKG_UID = 1;
     private static final String CUSTOM_RULE_ID = "custom_rule";
 
-    private final String NAME = "name";
-    private final ComponentName OWNER = new ComponentName("pkg", "cls");
-    private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
-    private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
-    private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
+    private static final String NAME = "name";
+    private static final ComponentName OWNER = new ComponentName("pkg", "cls");
+    private static final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
+    private static final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
+    private static final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
             .authority("authority")
             .appendPath("path")
             .appendPath("test")
             .build();
 
-    private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE);
-    private final String TRIGGER_DESC = "Every Night, 10pm to 6am";
-    private final int TYPE = TYPE_BEDTIME;
-    private final boolean ALLOW_MANUAL = true;
-    private final int ICON_RES_ID = 1234;
-    private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
-    private final boolean ENABLED = true;
-    private final int CREATION_TIME = 123;
+    private static final Condition CONDITION = new Condition(CONDITION_ID, "",
+            Condition.STATE_TRUE);
+    private static final String TRIGGER_DESC = "Every Night, 10pm to 6am";
+    private static final int TYPE = TYPE_BEDTIME;
+    private static final boolean ALLOW_MANUAL = true;
+    private static final int ICON_RES_ID = 1234;
+    private static final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
+    private static final boolean ENABLED = true;
+    private static final int CREATION_TIME = 123;
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -227,6 +241,10 @@
                 .thenReturn(CUSTOM_PKG_UID);
         when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
                 new String[] {pkg});
+        ApplicationInfo mockAppInfo = mock(ApplicationInfo.class);
+        when(mockAppInfo.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
+        when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
+                .thenReturn(mockAppInfo);
         mZenModeHelper.mPm = mPackageManager;
 
         mZenModeEventLogger.reset();
@@ -334,7 +352,7 @@
 
     @Test
     public void testZenOff_NoMuteApplied() {
-        mZenModeHelper.mZenMode = Settings.Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
         mZenModeHelper.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS
                 | PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
@@ -635,7 +653,7 @@
 
         // 3. apply zen off - verify zen is set to previous ringer (normal)
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.applyZenToRingerMode();
         verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -721,7 +739,7 @@
 
         // 3.  apply zen off - verify ringer remains normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.applyZenToRingerMode();
         verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -746,7 +764,7 @@
 
         // 3. apply zen-off - verify ringer is still silent
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.applyZenToRingerMode();
         verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
                 mZenModeHelper.TAG);
@@ -781,7 +799,7 @@
 
         // 4.  apply zen off - verify ringer still silenced
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.applyZenToRingerMode();
         verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
                 mZenModeHelper.TAG);
@@ -795,7 +813,7 @@
 
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
@@ -809,7 +827,7 @@
     public void testSilentRingerSavedOnZenOff_startsZenOn() {
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelper.mAudioManager = mAudioManager;
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.mConfig = new ZenModeConfig();
 
         // previously set silent ringer
@@ -836,7 +854,7 @@
     public void testVibrateRingerSavedOnZenOff_startsZenOn() {
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelper.mAudioManager = mAudioManager;
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.mConfig = new ZenModeConfig();
 
         // previously set silent ringer
@@ -1209,7 +1227,7 @@
                 .allowMedia(false)
                 .allowRepeatCallers(false)
                 .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE)
-                .allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS)
+                .allowMessages(PEOPLE_TYPE_CONTACTS)
                 .allowEvents(true)
                 .allowReminders(false)
                 .build();
@@ -2023,10 +2041,10 @@
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
 
         // and also that it works to turn it back off again
-        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
                 Process.SYSTEM_UID, true);
 
-        assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+        assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
     }
 
     @Test
@@ -2041,7 +2059,7 @@
 
         // Now turn zen mode off, but via a different package UID -- this should get registered as
         // "not an action by the user" because some other app is changing zen mode
-        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
+        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "", CUSTOM_PKG_UID,
                 false);
 
         // In total, this should be 2 loggable changes
@@ -2060,7 +2078,7 @@
         //   - resulting DNDPolicyProto the same as the values in setupZenConfig()
         assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
                 mZenModeEventLogger.getEventId(0));
-        assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+        assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
         assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
         assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
@@ -2080,7 +2098,7 @@
         assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
                 mZenModeEventLogger.getEventId(1));
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
-        assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
+        assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
         assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(1));
         assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
         assertFalse(mZenModeEventLogger.getIsUserAction(1));
@@ -2144,7 +2162,7 @@
         //   - zen policy is the same as the set-up zen config
         assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
                 mZenModeEventLogger.getEventId(0));
-        assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+        assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
         assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(0));
         assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
@@ -2157,7 +2175,7 @@
         assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_OFF.getId(),
                 mZenModeEventLogger.getEventId(1));
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getPrevZenMode(1));
-        assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
+        assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getNewZenMode(1));
         assertEquals(DNDProtoEnums.AUTOMATIC_RULE, mZenModeEventLogger.getChangedRuleType(1));
         assertEquals(0, mZenModeEventLogger.getNumRulesActive(1));
         assertTrue(mZenModeEventLogger.getIsUserAction(1));
@@ -2201,7 +2219,7 @@
 
         // Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
         // is off.
-        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, null, "",
                 Process.SYSTEM_UID, true);
 
         // Change the policy again
@@ -2305,7 +2323,7 @@
         // what the event should reflect. At this time, the policy is the same as initial setup.
         assertEquals(ZenModeEventLogger.ZenStateChangedEvent.DND_TURNED_ON.getId(),
                 mZenModeEventLogger.getEventId(0));
-        assertEquals(Global.ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
+        assertEquals(ZEN_MODE_OFF, mZenModeEventLogger.getPrevZenMode(0));
         assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeEventLogger.getNewZenMode(0));
         assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
         assertFalse(mZenModeEventLogger.getIsUserAction(0));
@@ -2355,7 +2373,7 @@
         mZenModeHelper.evaluateZenModeLocked("test", true);
 
         // Check that the change actually took: zen mode should be off now
-        assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+        assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
 
         // but still, nothing should've been logged
         assertEquals(0, mZenModeEventLogger.numLoggedChanges());
@@ -2483,7 +2501,7 @@
                 true);
 
         // Turn off manual mode, call from a package: don't reset UID even though enabler is set
-        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null,
+        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null,
                 CUSTOM_PKG_NAME, "", 12345, false);
 
         // And likewise when turning it back on again
@@ -2660,8 +2678,11 @@
     }
 
     @Test
-    public void testCreateAutomaticZenRule_allFields() {
+    public void zenRuleToAutomaticZenRule_allFields() {
         mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
+                new String[] {OWNER.getPackageName()});
+
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         rule.configurationActivity = CONFIG_ACTIVITY;
         rule.component = OWNER;
@@ -2682,7 +2703,8 @@
         rule.iconResId = ICON_RES_ID;
         rule.triggerDescription = TRIGGER_DESC;
 
-        AutomaticZenRule actual = mZenModeHelper.createAutomaticZenRule(rule);
+        mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
+        AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(rule.id);
 
         assertEquals(NAME, actual.getName());
         assertEquals(OWNER, actual.getOwner());
@@ -2915,8 +2937,253 @@
         assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
     }
 
+    @Test
+    public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.values())
+                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .containsExactly(
+                        expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                                null, true));
+    }
+
+    @Test
+    public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_ALARMS);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.values())
+                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .containsExactly(
+                        expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS, null, true));
+    }
+
+    @Test
+    public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+                .isEqualTo(STATE_TRUE);
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_OFF);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+                .isEqualTo(STATE_FALSE);
+    }
+
+    @Test
+    public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_OFF);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+    }
+
+    @Test
+    public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+
+        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, "test", "test", 0, true);
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue();
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_ALARMS);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
+                .isEqualTo(STATE_TRUE);
+    }
+
+    @Test
+    public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() {
+        mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        withoutWtfCrash(
+                () -> mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME,
+                        CUSTOM_PKG_UID,
+                        ZEN_MODE_IMPORTANT_INTERRUPTIONS));
+
+        assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+    }
+
+    @Test
+    public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+                PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+                Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy);
+
+        ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
+                .disallowAllSounds()
+                .allowCalls(PEOPLE_TYPE_CONTACTS)
+                .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+                .hideAllVisualEffects()
+                .build();
+        assertThat(mZenModeHelper.mConfig.automaticRules.values())
+                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .containsExactly(
+                        expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                                expectedZenPolicy, /* conditionActive= */ null));
+    }
+
+    @Test
+    public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+                PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+                Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                original);
+
+        // Change priorityCallSenders: contacts -> starred.
+        Policy updated = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+                PRIORITY_SENDERS_STARRED, PRIORITY_SENDERS_STARRED,
+                Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated);
+
+        ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
+                .disallowAllSounds()
+                .allowCalls(PEOPLE_TYPE_STARRED)
+                .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+                .hideAllVisualEffects()
+                .build();
+        assertThat(mZenModeHelper.mConfig.automaticRules.values())
+                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .containsExactly(
+                        expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                                expectedZenPolicy, /* conditionActive= */ null));
+    }
+
+    @Test
+    public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
+        mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        withoutWtfCrash(
+                () -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME,
+                        CUSTOM_PKG_UID, new Policy(0, 0, 0)));
+
+        assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
+    }
+
+    @Test
+    public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+        Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
+                PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
+                Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
+                CONVERSATION_SENDERS_IMPORTANT);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                writtenPolicy);
+
+        Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+                CUSTOM_PKG_NAME);
+
+        assertThat(readPolicy).isEqualTo(writtenPolicy);
+    }
+
+    @Test
+    public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_returnsGlobalPolicy() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+                ZEN_MODE_ALARMS);
+        mZenModeHelper.mConfig.allowCalls = true;
+        mZenModeHelper.mConfig.allowConversations = false;
+
+        Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+                CUSTOM_PKG_NAME);
+
+        assertThat(readPolicy).isNotNull();
+        assertThat(readPolicy.allowCalls()).isTrue();
+        assertThat(readPolicy.allowConversations()).isFalse();
+    }
+
+    @Test
+    public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+        mZenModeHelper.mConfig.allowCalls = true;
+        mZenModeHelper.mConfig.allowConversations = false;
+
+        Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
+                CUSTOM_PKG_NAME);
+
+        assertThat(readPolicy).isNotNull();
+        assertThat(readPolicy.allowCalls()).isTrue();
+        assertThat(readPolicy.allowConversations()).isFalse();
+    }
+
+    private static final Correspondence<ZenRule, ZenRule> IGNORE_TIMESTAMPS =
+            Correspondence.transforming(zr -> {
+                Parcel p = Parcel.obtain();
+                try {
+                    zr.writeToParcel(p, 0);
+                    p.setDataPosition(0);
+                    ZenRule copy = new ZenRule(p);
+                    copy.creationTime = 0;
+                    return copy;
+                } finally {
+                    p.recycle();
+                }
+            },
+            "Ignoring timestamps");
+
+    private ZenRule expectedImplicitRule(String ownerPkg, int zenMode, ZenPolicy policy,
+            @Nullable Boolean conditionActive) {
+        ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.id = "implicit_" + ownerPkg;
+        rule.conditionId = Uri.parse("condition://android/implicit/" + ownerPkg);
+        if (conditionActive != null) {
+            rule.condition = conditionActive
+                    ? new Condition(rule.conditionId,
+                            mContext.getString(R.string.zen_mode_implicit_activated), STATE_TRUE)
+                    : new Condition(rule.conditionId,
+                            mContext.getString(R.string.zen_mode_implicit_deactivated),
+                            STATE_FALSE);
+        }
+        rule.zenMode = zenMode;
+        rule.zenPolicy = policy;
+        rule.pkg = ownerPkg;
+        rule.name = CUSTOM_APP_LABEL;
+        rule.enabled = true;
+        return rule;
+    }
+
     private void setupZenConfig() {
-        mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
+        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
         mZenModeHelper.mConfig.allowAlarms = false;
         mZenModeHelper.mConfig.allowMedia = false;
         mZenModeHelper.mConfig.allowSystem = false;
@@ -2965,6 +3232,15 @@
         assertEquals(STATE_ALLOW, dndProto.getNotificationList().getNumber());
     }
 
+    private static void withoutWtfCrash(Runnable test) {
+        Log.TerribleFailureHandler oldHandler = Log.setWtfHandler((tag, what, system) -> {});
+        try {
+            test.run();
+        } finally {
+            Log.setWtfHandler(oldHandler);
+        }
+    }
+
     /**
      * Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml()
      */