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

Commit 9735d938 authored by Matías Hernández's avatar Matías Hernández
Browse files

Apply Night Mode as part of ZenDeviceEffects

(Using a temporary API until the new one from b/313418335 is available).

Update DefaultDeviceEffectsApplier to only apply the "diff" of effects (less service calls).

This CL expands the usage of the "origin enum" to cover all the updates of the ZenModeConfig through ZenModeHelper (including setAZRState, loading, etc) and reorders arguments to make them consistent. The enum was also moved to ZenModeConfig so it can be used in DeviceEffectsApplier.

Bug: 308673343
Test: atest ZenModeHelperTest (unit)
Change-Id: I9b7599efdc8f1a55ed026b371d1be639fadf133a
parent c9b5addb
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.service.notification;

import android.service.notification.ZenModeConfig.ConfigChangeOrigin;

/**
 * Responsible for making any service calls needed to apply the set of {@link ZenDeviceEffects} that
 * make sense for the current platform.
@@ -33,6 +35,13 @@ public interface DeviceEffectsApplier {
     *
     * <p>This will be called whenever the set of consolidated effects changes (normally through
     * the activation or deactivation of zen rules).
     *
     * @param effects The effects that should be active and inactive.
     * @param source The origin of the change. Because the application of specific effects can be
     *               disruptive (e.g. lead to Activity recreation), that operation can in some
     *               cases be deferred (e.g. until screen off). However, if the effects are
     *               changing as a result of an explicit user action, then it makes sense to
     *               apply them immediately regardless.
     */
    void apply(ZenDeviceEffects effects);
    void apply(ZenDeviceEffects effects, @ConfigChangeOrigin int source);
}
+63 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
@@ -61,6 +62,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@@ -79,7 +82,66 @@ import java.util.UUID;
 * @hide
 */
public class ZenModeConfig implements Parcelable {
    private static String TAG = "ZenModeConfig";
    private static final String TAG = "ZenModeConfig";

    /**
     * The {@link ZenModeConfig} is being updated because of an unknown reason.
     */
    public static final int UPDATE_ORIGIN_UNKNOWN = 0;

    /**
     * The {@link ZenModeConfig} is being updated because of system initialization (i.e. load from
     * storage, on device boot).
     */
    public static final int UPDATE_ORIGIN_INIT = 1;

    /** The {@link ZenModeConfig} is being updated (replaced) because of a user switch or unlock. */
    public static final int UPDATE_ORIGIN_INIT_USER = 2;

    /** The {@link ZenModeConfig} is being updated because of a user action, for example:
     * <ul>
     *     <li>{@link NotificationManager#setAutomaticZenRuleState} with a
     *     {@link Condition#source} equal to {@link Condition#SOURCE_USER_ACTION}.</li>
     *     <li>Adding, updating, or removing a rule from Settings.</li>
     *     <li>Directly activating or deactivating/snoozing a rule through some UI affordance (e.g.
     *     Quick Settings).</li>
     * </ul>
     */
    public static final int UPDATE_ORIGIN_USER = 3;

    /**
     * The {@link ZenModeConfig} is being "independently" updated by an app, and not as a result of
     * a user's action inside that app (for example, activating an {@link AutomaticZenRule} based on
     * a previously set schedule).
     */
    public static final int UPDATE_ORIGIN_APP = 4;

    /**
     * The {@link ZenModeConfig} is being updated by the System or SystemUI. Note that this only
     * includes cases where the call is coming from the System/SystemUI but the change is not due to
     * a user action (e.g. automatically activating a schedule-based rule). If the change is a
     * result of a user action (e.g. activating a rule by tapping on its QS tile) then
     * {@link #UPDATE_ORIGIN_USER} is used instead.
     */
    public static final int UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI = 5;

    /**
     * The {@link ZenModeConfig} is being updated (replaced) because the user's DND configuration
     * is being restored from a backup.
     */
    public static final int UPDATE_ORIGIN_RESTORE_BACKUP = 6;

    @IntDef(prefix = { "UPDATE_ORIGIN_" }, value = {
            UPDATE_ORIGIN_UNKNOWN,
            UPDATE_ORIGIN_INIT,
            UPDATE_ORIGIN_INIT_USER,
            UPDATE_ORIGIN_USER,
            UPDATE_ORIGIN_APP,
            UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
            UPDATE_ORIGIN_RESTORE_BACKUP
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ConfigChangeOrigin {}

    public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY;
    public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
+81 −12
Original line number Diff line number Diff line
@@ -16,14 +16,21 @@

package com.android.server.notification;

import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME;

import android.app.UiModeManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.ColorDisplayManager;
import android.os.Binder;
import android.os.PowerManager;
import android.service.notification.DeviceEffectsApplier;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.ConfigChangeOrigin;

/** Default implementation for {@link DeviceEffectsApplier}. */
class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
@@ -34,13 +41,20 @@ class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
    private static final int SATURATION_LEVEL_FULL_COLOR = 100;
    private static final float WALLPAPER_DIM_AMOUNT_DIMMED = 0.6f;
    private static final float WALLPAPER_DIM_AMOUNT_NORMAL = 0f;
    private static final IntentFilter SCREEN_OFF_INTENT_FILTER = new IntentFilter(
            Intent.ACTION_SCREEN_OFF);

    private final Context mContext;
    private final ColorDisplayManager mColorDisplayManager;
    private final PowerManager mPowerManager;
    private final UiModeManager mUiModeManager;
    private final WallpaperManager mWallpaperManager;

    private ZenDeviceEffects mLastAppliedEffects = new ZenDeviceEffects.Builder().build();
    private boolean mPendingNightMode;

    DefaultDeviceEffectsApplier(Context context) {
        mContext = context;
        mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
        mPowerManager = context.getSystemService(PowerManager.class);
        mUiModeManager = context.getSystemService(UiModeManager.class);
@@ -48,24 +62,79 @@ class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
    }

    @Override
    public void apply(ZenDeviceEffects effects) {
    public void apply(ZenDeviceEffects effects, @ConfigChangeOrigin int origin) {
        Binder.withCleanCallingIdentity(() -> {
            if (mLastAppliedEffects.shouldSuppressAmbientDisplay()
                    != effects.shouldSuppressAmbientDisplay()) {
                mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
                        effects.shouldSuppressAmbientDisplay());
            }

            if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) {
                if (mColorDisplayManager != null) {
                    mColorDisplayManager.setSaturationLevel(
                            effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
                                    : SATURATION_LEVEL_FULL_COLOR);
                }
            }

            if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) {
                if (mWallpaperManager != null) {
                    mWallpaperManager.setWallpaperDimAmount(
                            effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
                                    : WALLPAPER_DIM_AMOUNT_NORMAL);
                }
            }

            // TODO: b/308673343 - Apply dark theme (via UiModeManager) when screen is off.
            if (mLastAppliedEffects.shouldUseNightMode() != effects.shouldUseNightMode()) {
                updateOrScheduleNightMode(effects.shouldUseNightMode(), origin);
            }
        });

        mLastAppliedEffects = effects;
    }

    private void updateOrScheduleNightMode(boolean useNightMode, @ConfigChangeOrigin int origin) {
        mPendingNightMode = useNightMode;
        safeUnregisterReceiver(mNightModeWhenScreenOff);

        // Changing the theme can be disruptive for the user (Activities are likely recreated, may
        // lose some state). Therefore we only apply the change immediately if the rule was
        // activated manually, or we are initializing, or the screen is currently off/dreaming.
        if (origin == ZenModeConfig.UPDATE_ORIGIN_INIT
                || origin == ZenModeConfig.UPDATE_ORIGIN_INIT_USER
                || origin == ZenModeConfig.UPDATE_ORIGIN_USER
                || origin == ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                || !mPowerManager.isInteractive()) {
            updateNightModeImmediately(useNightMode);
        } else {
            mContext.registerReceiver(mNightModeWhenScreenOff, SCREEN_OFF_INTENT_FILTER,
                    Context.RECEIVER_NOT_EXPORTED);
        }
    }

    private final BroadcastReceiver mNightModeWhenScreenOff = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            safeUnregisterReceiver(mNightModeWhenScreenOff);
            updateNightModeImmediately(mPendingNightMode);
        }
    };

    private void updateNightModeImmediately(boolean useNightMode) {
        Binder.withCleanCallingIdentity(() -> {
            // TODO: b/314285749 - Placeholder; use real APIs when available.
            mUiModeManager.setNightModeCustomType(MODE_NIGHT_CUSTOM_TYPE_BEDTIME);
            mUiModeManager.setNightModeActivatedForCustomMode(MODE_NIGHT_CUSTOM_TYPE_BEDTIME,
                    useNightMode);
        });
    }

    private void safeUnregisterReceiver(BroadcastReceiver br) {
        try {
            mContext.unregisterReceiver(br);
        } catch (IllegalArgumentException e) {
            // It's fine, we haven't registered it yet.
        }
    }
}
+37 −20
Original line number Diff line number Diff line
@@ -5299,11 +5299,11 @@ public class NotificationManagerService extends SystemService {
        public void setZenMode(int mode, Uri conditionId, String reason) throws RemoteException {
            enforceSystemOrSystemUI("INotificationManager.setZenMode");
            final int callingUid = Binder.getCallingUid();
            final boolean isSystemOrSystemUi = isCallerSystemOrSystemUi();
            final long identity = Binder.clearCallingIdentity();
            try {
                mZenModeHelper.setManualZenMode(mode, conditionId, null, reason, callingUid,
                        isSystemOrSystemUi);
                mZenModeHelper.setManualZenMode(mode, conditionId,
                        ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, // Checked by enforce()
                        reason, /* caller= */ null, callingUid);
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -5360,10 +5360,11 @@ public class NotificationManagerService extends SystemService {
            }
            return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
                    "addAutomaticZenRule", Binder.getCallingUid(),
                    // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
                    isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
                            : ZenModeHelper.FROM_APP);
                    // TODO: b/308670715: Distinguish origin properly (e.g. USER if creating a rule
                    //  manually in Settings).
                    isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                            : ZenModeConfig.UPDATE_ORIGIN_APP,
                    "addAutomaticZenRule", Binder.getCallingUid());
        }
        @Override
@@ -5379,11 +5380,12 @@ public class NotificationManagerService extends SystemService {
            Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
            enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
            // TODO: b/308670715: Distinguish origin properly (e.g. USER if updating a rule
            //  manually in Settings).
            return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
                    "updateAutomaticZenRule", Binder.getCallingUid(),
                    // TODO: b/308670715: Distinguish FROM_APP from FROM_USER
                    isCallerSystemOrSystemUi() ? ZenModeHelper.FROM_SYSTEM_OR_SYSTEMUI
                            : ZenModeHelper.FROM_APP);
                    isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                            : ZenModeConfig.UPDATE_ORIGIN_APP,
                    "updateAutomaticZenRule", Binder.getCallingUid());
        }
        @Override
@@ -5392,8 +5394,12 @@ public class NotificationManagerService extends SystemService {
            // Verify that they can modify zen rules.
            enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
            return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule",
                    Binder.getCallingUid(), isCallerSystemOrSystemUi());
            // TODO: b/308670715: Distinguish origin properly (e.g. USER if removing a rule
            //  manually in Settings).
            return mZenModeHelper.removeAutomaticZenRule(id,
                    isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                            : ZenModeConfig.UPDATE_ORIGIN_APP,
                    "removeAutomaticZenRule", Binder.getCallingUid());
        }
        @Override
@@ -5402,8 +5408,9 @@ public class NotificationManagerService extends SystemService {
            enforceSystemOrSystemUI("removeAutomaticZenRules");
            return mZenModeHelper.removeAutomaticZenRules(packageName,
                    packageName + "|removeAutomaticZenRules", Binder.getCallingUid(),
                    isCallerSystemOrSystemUi());
                    isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                            : ZenModeConfig.UPDATE_ORIGIN_APP,
                    packageName + "|removeAutomaticZenRules", Binder.getCallingUid());
        }
        @Override
@@ -5421,8 +5428,12 @@ public class NotificationManagerService extends SystemService {
            enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
            mZenModeHelper.setAutomaticZenRuleState(id, condition, Binder.getCallingUid(),
                    isCallerSystemOrSystemUi());
            // TODO: b/308670715: Distinguish origin properly (e.g. USER if toggling a rule
            //  manually in Settings).
            mZenModeHelper.setAutomaticZenRuleState(id, condition,
                    isCallerSystemOrSystemUi() ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                            : ZenModeConfig.UPDATE_ORIGIN_APP,
                    Binder.getCallingUid());
        }
        @Override
@@ -5440,8 +5451,11 @@ public class NotificationManagerService extends SystemService {
            final long identity = Binder.clearCallingIdentity();
            try {
                mZenModeHelper.setManualZenMode(zen, null, pkg, "setInterruptionFilter",
                        callingUid, isSystemOrSystemUi);
                mZenModeHelper.setManualZenMode(zen, null,
                        isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                                : ZenModeConfig.UPDATE_ORIGIN_APP,
                        /* reason= */ "setInterruptionFilter", /* caller= */ pkg,
                        callingUid);
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -5806,7 +5820,10 @@ public class NotificationManagerService extends SystemService {
                } else {
                    ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
                            policy);
                    mZenModeHelper.setNotificationPolicy(policy, callingUid, isSystemOrSystemUi);
                    mZenModeHelper.setNotificationPolicy(policy,
                            isSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                                    : ZenModeConfig.UPDATE_ORIGIN_APP,
                            callingUid);
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to set notification policy", e);
+11 −6
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.RankingHelperProto;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -1862,12 +1863,16 @@ public class PreferencesHelper implements RankingConfig {
    public void updateZenPolicy(boolean areChannelsBypassingDnd, int callingUid,
            boolean fromSystemOrSystemUi) {
        NotificationManager.Policy policy = mZenModeHelper.getNotificationPolicy();
        mZenModeHelper.setNotificationPolicy(new NotificationManager.Policy(
        mZenModeHelper.setNotificationPolicy(
                new NotificationManager.Policy(
                        policy.priorityCategories, policy.priorityCallSenders,
                        policy.priorityMessageSenders, policy.suppressedVisualEffects,
                (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
                        : 0),
                policy.priorityConversationSenders), callingUid, fromSystemOrSystemUi);
                        (areChannelsBypassingDnd
                                ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND : 0),
                        policy.priorityConversationSenders),
                fromSystemOrSystemUi ? ZenModeConfig.UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
                        : ZenModeConfig.UPDATE_ORIGIN_APP,
                callingUid);
    }

    // TODO: b/310620812 - rename to hasPriorityChannels() when modes_api is inlined.
Loading