diff options
| author | 2023-11-13 11:57:33 +0000 | |
|---|---|---|
| committer | 2023-11-13 11:57:33 +0000 | |
| commit | de7f05ed7bf1b5c2e56003ad20af27ef5200b120 (patch) | |
| tree | ba8ef901c6b36bfc08ef85609a7f2b09dcd46fd1 | |
| parent | 02b3211ef962f80aa071c84afe69aec8d58392cf (diff) | |
| parent | 4f16f1590fbd6278b18c697ce07ea8c1f1876bc8 (diff) | |
Merge "Update behavior of "global" DND APIs" into main
12 files changed, 912 insertions, 54 deletions
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index b0332c32bfbc..ffb79b3fc552 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1642,6 +1642,7 @@ public class NotificationManager { * * <p> */ + // TODO(b/309457271): Update documentation with VANILLA_ICE_CREAM behavior. public Policy getNotificationPolicy() { INotificationManager service = getService(); try { @@ -1660,6 +1661,7 @@ public class NotificationManager { * * @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 @@ public class NotificationManager { * 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 ff4dfc7cc079..305b751f0cef 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -1004,6 +1004,8 @@ public class ZenModeConfig implements Parcelable { 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 @@ public class ZenModeConfig implements Parcelable { 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 @@ public class ZenModeConfig implements Parcelable { } } - 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 15c188d2140b..cec83dee9c3a 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 e19d5486768a..16ad5c909575 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 1bdd402cf0b5..707e99019df3 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.OnRoleHoldersChangedListener; 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 @@ public class NotificationManagerService extends SystemService { @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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { } } + 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 @@ public class NotificationManagerService extends SystemService { @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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { } 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 @@ public class NotificationManagerService extends SystemService { } 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 000000000000..2a65aff7f28d --- /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 762c1a162302..c637df2ec99b 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 android.service.notification.NotificationServiceProto.ROOT_CONFIG; 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.ContentResolver; 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 @@ public class ZenModeHelper { 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 @@ public class ZenModeHelper { } } + /** + * 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 @@ public class ZenModeHelper { } // 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 @@ public class ZenModeHelper { 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 @@ public class ZenModeHelper { } } - 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 25b7ca146ff1..dcac8c98d19f 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 @@ flag { 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 6792cfe6e788..3803244c7012 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_HIGH; 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_FULL_SECONDARY; 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.StatsManager; 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 27e8f3664a65..8f30f413d4d0 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 @@ public class TestableNotificationManagerService extends NotificationManagerServi 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 000000000000..6cc1c4365fca --- /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 e8201fdef8de..37aeb57f728e 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_DEACTIVATED; 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_REMINDERS 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_LIGHTS; 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_ALLOW; 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.times; 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.app.NotificationManager.Policy; 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.AudioManagerInternal; 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.provider.Settings.Global; 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.UiServiceTestCase; 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { .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 @@ public class ZenModeHelperTest extends UiServiceTestCase { @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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 class ZenModeHelperTest extends UiServiceTestCase { 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 class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { .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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // - 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // - 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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() */ |