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

Commit 4f16f159 authored by Matías Hernández's avatar Matías Hernández
Browse files

Update behavior of "global" DND APIs

* setInterruptionFilter() no longer changes the global DND state, but instead creates (or updates) an AutomaticZenRule with that filter value and enables it.
* setNotificationPolicy() no longer changes the user's DND preferences, but instead creates (or updates) an AutomaticZenRule with that Policy.
* getNotificationPolicy() returns the Policy supplied to setNotificationPolicy(), if it was called before, or the global policy otherwise. This is to ensure that the get/set methods are reasonably symmetrical (also, it's "the notification policy that will be applied when calling setInterruptionFilter", so it's consistent).

The new behavior is target-sdk-gated via CompatChanges (MANAGE_GLOBAL_ZEN_VIA_IMPLICIT_RULES).

Bug: 308670109
Bug: 308670855
Test: atest NotificationManagerServiceTest ZenModeHelperTest ZenAdaptersTest

Change-Id: I662ee26c8b8972e11feee705855574814318831c
parent 7a4bca8a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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 {
+4 −2
Original line number Diff line number Diff line
@@ -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:
+5 −0
Original line number Diff line number Diff line
@@ -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>

+2 −0
Original line number Diff line number Diff line
@@ -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" />
+57 −9
Original line number Diff line number Diff line
@@ -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);
                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)) {
            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);
        }
Loading