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

Commit ba5ad9b2 authored by brycelee's avatar brycelee Committed by Bryce Lee
Browse files

Allow granular ambient suppression.

This changelist introduces a flag based approach for specifying how
the ambient display should be suppressed. This replaces the existing
binary signal and allows for dreams, aod, or the entire display to
be suppressed.

Test: atest PowerManagerServiceTest
Bug: 408229468
Flag: android.os.low_light_dream_behavior

Change-Id: I1f285f2b499e5bd6773a600e2470114678dbefd8
parent c71425ca
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -146,6 +146,10 @@ interface IPowerManager
    boolean isAmbientDisplayAvailable();
    // suppresses the current ambient display configuration and disables ambient display.
    void suppressAmbientDisplay(String token, boolean suppress);
    // suppresses the current ambient display configuration and disables ambient display based on
    // the specified flags.
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
    oneway void suppressAmbientDisplayBehavior(String token, int suppressionFlags);
    // returns whether ambient display is suppressed by the calling app with the given token.
    boolean isAmbientDisplaySuppressedForToken(String token);
    // returns whether ambient display is suppressed by any app with any token.
+68 −0
Original line number Diff line number Diff line
@@ -392,6 +392,53 @@ public final class PowerManager {
    @Retention(RetentionPolicy.SOURCE)
    public @interface UserActivityEvent{}

    /**
     * Flag to represent no suppression
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LOW_LIGHT_DREAM_BEHAVIOR)
    public static final int FLAG_AMBIENT_SUPPRESSION_NONE = 0;

    /**
     * Flag to represent dream suppression
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LOW_LIGHT_DREAM_BEHAVIOR)
    public static final int FLAG_AMBIENT_SUPPRESSION_DREAM = 1;

    /**
     * Flag to represent AOD suppression
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LOW_LIGHT_DREAM_BEHAVIOR)
    public static final int FLAG_AMBIENT_SUPPRESSION_AOD = 1 << 1;

    /**
     * Flag to represent suppressing everything
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LOW_LIGHT_DREAM_BEHAVIOR)
    public static final int FLAG_AMBIENT_SUPPRESSION_ALL =
            FLAG_AMBIENT_SUPPRESSION_DREAM
                    | FLAG_AMBIENT_SUPPRESSION_AOD;

    /**
     * @hide
     */
    @IntDef(flag = true, prefix = {"FLAG_AMBIENT_SUPPRESSION_"}, value = {
            FLAG_AMBIENT_SUPPRESSION_NONE,
            FLAG_AMBIENT_SUPPRESSION_DREAM,
            FLAG_AMBIENT_SUPPRESSION_AOD,
            FLAG_AMBIENT_SUPPRESSION_ALL,
    })
    @Retention(RetentionPolicy.SOURCE)
    @FlaggedApi(Flags.FLAG_LOW_LIGHT_DREAM_BEHAVIOR)
    public @interface FlagAmbientSuppression{}

    /**
     *
     * Convert the user activity event to a string for debugging purposes.
@@ -3174,6 +3221,27 @@ public final class PowerManager {
        }
    }

    /**
     * Suppresses the current ambient display configuration and disables ambient display.
     *
     * <p>This method has no effect if {@link #isAmbientDisplayAvailable()} is false.
     *
     * @param token A persistable identifier for the ambient display suppression that is unique
     *              within the calling application.
     * @param suppressionFlags Flags that describe how the ambient display should be suppressed.
     * @hide
     */
    @FlaggedApi(Flags.FLAG_LOW_LIGHT_DREAM_BEHAVIOR)
    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
    public void suppressAmbientDisplay(@NonNull String token,
            @FlagAmbientSuppression  int suppressionFlags) {
        try {
            mService.suppressAmbientDisplayBehavior(token, suppressionFlags);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Returns true if ambient display is suppressed by the calling app with the given
     * {@code token}.
+111 −8
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.content.Context;
import android.os.Flags;
import android.os.PowerManager;
import android.os.PowerManager.FlagAmbientSuppression;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;

import com.android.internal.statusbar.IStatusBarService;
@@ -31,7 +33,9 @@ import com.android.internal.statusbar.IStatusBarService;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
@@ -40,7 +44,20 @@ import java.util.Set;
public class AmbientDisplaySuppressionController {
    private static final String TAG = "AmbientDisplaySuppressionController";

    private final Set<Pair<String, Integer>> mSuppressionTokens;
    /**
     * A {@link SuppressionToken} is a unique identifier for a suppression request. It is unique
     * based on an id (determined by the controller) and a tag (determined by the process).
     */
    private record SuppressionToken(int id, String tag) {
        public boolean belongsToId(int id) {
            return this.id() == id;
        }
    }

    private final Set<SuppressionToken> mSuppressionTokens;

    private final Map<SuppressionToken, Integer> mSuppressions;

    private final AmbientDisplaySuppressionChangedCallback mCallback;
    private IStatusBarService mStatusBarService;

@@ -52,23 +69,33 @@ public class AmbientDisplaySuppressionController {
         * @param isSuppressed Whether ambient is suppressed.
         */
        void onSuppressionChanged(boolean isSuppressed);

        /**
         * Called when the suppression state changes.
         *
         * @param suppressionState the new aggregate suppression state.
         */
        void onSuppressionChanged(@FlagAmbientSuppression int suppressionState);
    }

    AmbientDisplaySuppressionController(
            @NonNull AmbientDisplaySuppressionChangedCallback callback) {
        mSuppressionTokens = Collections.synchronizedSet(new ArraySet<>());
        mSuppressions = Collections.synchronizedMap(new HashMap<>());
        mCallback = requireNonNull(callback);
    }

    /**
     * Suppresses ambient display.
     *
     * @deprecated Use {@link #suppress(String, int, int)} instead.
     * @param token A persistible identifier for the ambient display suppression.
     * @param callingUid The uid of the calling application.
     * @param suppress If true, suppresses the ambient display. Otherwise, unsuppresses it.
     */
    @Deprecated
    public void suppress(@NonNull String token, int callingUid, boolean suppress) {
        Pair<String, Integer> suppressionToken = Pair.create(requireNonNull(token), callingUid);
        SuppressionToken suppressionToken = new SuppressionToken(callingUid, requireNonNull(token));
        final boolean wasSuppressed = isSuppressed();

        if (suppress) {
@@ -91,6 +118,41 @@ public class AmbientDisplaySuppressionController {
        }
    }

    /**
     * Suppresses ambient display.
     *
     * @param token A persistible identifier for the ambient display suppression.
     * @param callingUid The uid of the calling application.
     * @param suppressionFlags flags specifying how to suppress the ambient display.
     */
    public void suppress(@NonNull String token, int callingUid,
            @FlagAmbientSuppression int suppressionFlags) {
        final SuppressionToken suppressionToken =
                new SuppressionToken(callingUid, requireNonNull(token));
        final int existingSuppression = calculateSuppression();

        if (suppressionFlags != PowerManager.FLAG_AMBIENT_SUPPRESSION_NONE) {
            mSuppressions.put(suppressionToken, suppressionFlags);
        } else {
            mSuppressions.remove(suppressionToken);
        }

        final int currentSuppression = calculateSuppression();
        if (existingSuppression != currentSuppression) {
            mCallback.onSuppressionChanged(currentSuppression);
        }

        try {
            synchronized (mSuppressionTokens) {
                getStatusBar().suppressAmbientDisplay(
                        (suppressionFlags & PowerManager.FLAG_AMBIENT_SUPPRESSION_AOD)
                                == PowerManager.FLAG_AMBIENT_SUPPRESSION_AOD);
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to suppress ambient display", e);
        }
    }

    /**
     * Returns the tokens used to suppress ambient display through
     * {@link #suppress(String, int, boolean)}.
@@ -99,10 +161,21 @@ public class AmbientDisplaySuppressionController {
     */
    List<String> getSuppressionTokens(int callingUid) {
        List<String> result = new ArrayList<>();

        if (Flags.lowLightDreamBehavior()) {
            synchronized (mSuppressions) {
                for (Map.Entry<SuppressionToken, Integer> entry : mSuppressions.entrySet()) {
                    if (entry.getKey().belongsToId(callingUid)) {
                        result.add(entry.getKey().tag);
                    }
                }
            }
        } else {
            synchronized (mSuppressionTokens) {
            for (Pair<String, Integer> token : mSuppressionTokens) {
                if (token.second == callingUid) {
                    result.add(token.first);
                for (SuppressionToken token: mSuppressionTokens) {
                    if (token.belongsToId(callingUid)) {
                        result.add(token.tag);
                    }
                }
            }
        }
@@ -116,13 +189,39 @@ public class AmbientDisplaySuppressionController {
     * @param callingUid The uid of the calling application.
     */
    public boolean isSuppressed(@NonNull String token, int callingUid) {
        return mSuppressionTokens.contains(Pair.create(requireNonNull(token), callingUid));
        if (Flags.lowLightDreamBehavior()) {
            return mSuppressions.containsKey(
                    new SuppressionToken(callingUid, requireNonNull(token)));
        }

        return mSuppressionTokens.contains(new SuppressionToken(callingUid, requireNonNull(token)));
    }

    /**
     * Calculates the current ambient suppression by aggregating the flags across active
     * suppressions.
     * @return the aggregate suppression flags from all active suppressions
     */
    public @FlagAmbientSuppression int calculateSuppression() {
        int suppression = PowerManager.FLAG_AMBIENT_SUPPRESSION_NONE;
        synchronized (mSuppressions) {
            for (Map.Entry<SuppressionToken, Integer> entry : mSuppressions.entrySet()) {
                suppression |= entry.getValue();
            }
        }

        return suppression;
    }

    /**
     * Returns whether ambient display is suppressed.
     */
    public boolean isSuppressed() {
        if (Flags.lowLightDreamBehavior()) {
            return (calculateSuppression() & PowerManager.FLAG_AMBIENT_SUPPRESSION_DREAM)
                    == PowerManager.FLAG_AMBIENT_SUPPRESSION_DREAM;
        }

        return !mSuppressionTokens.isEmpty();
    }

@@ -134,6 +233,10 @@ public class AmbientDisplaySuppressionController {
        pw.println("AmbientDisplaySuppressionController:");
        pw.println(" ambientDisplaySuppressed=" + isSuppressed());
        pw.println(" mSuppressionTokens=" + mSuppressionTokens);

        if (Flags.lowLightDreamBehavior()) {
            pw.println(" mSuppressions=" + mSuppressions);
        }
    }

    private synchronized IStatusBarService getStatusBar() {
+62 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ import android.os.BatteryManagerInternal;
import android.os.BatterySaverPolicyConfig;
import android.os.Binder;
import android.os.Build;
import android.os.Flags;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -85,6 +86,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelDuration;
import android.os.PowerManager;
import android.os.PowerManager.FlagAmbientSuppression;
import android.os.PowerManager.GoToSleepReason;
import android.os.PowerManager.ServiceType;
import android.os.PowerManager.WakeReason;
@@ -3592,6 +3594,27 @@ public final class PowerManagerService extends SystemService
        }
    }

    @GuardedBy("mLock")
    private void onDreamSuppressionChangedLocked(
            @FlagAmbientSuppression final int suppressionFlags) {
        if (!mDreamsDisabledByAmbientModeSuppressionConfig) {
            return;
        }

        final boolean isSuppressed = suppressionFlags != PowerManager.FLAG_AMBIENT_SUPPRESSION_NONE;

        final PowerGroup defaultPowerGroup = mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP);
        if (!isSuppressed && mIsPowered && mDreamsSupportedConfig && mDreamsEnabledSetting
                && shouldNapAtBedTimeLocked(defaultPowerGroup)
                && isItBedTimeYetLocked(defaultPowerGroup)) {
            napInternal(SystemClock.uptimeMillis(), Process.SYSTEM_UID, /* allowWake= */ true);
        } else if (isSuppressed) {
            mDirty |= DIRTY_SETTINGS;
            updatePowerStateLocked();
        }
    }

    @Deprecated
    @GuardedBy("mLock")
    private void onDreamSuppressionChangedLocked(final boolean isSuppressed) {
        if (!mDreamsDisabledByAmbientModeSuppressionConfig) {
@@ -5438,6 +5461,13 @@ public final class PowerManagerService extends SystemService
                        onDreamSuppressionChangedLocked(isSuppressed);
                    }
                }

                @Override
                public void onSuppressionChanged(int suppressionState) {
                    synchronized (mLock) {
                        onDreamSuppressionChangedLocked(suppressionState);
                    }
                }
            };

    /**
@@ -7044,6 +7074,14 @@ public final class PowerManagerService extends SystemService

        @Override // Binder call
        public void suppressAmbientDisplay(@NonNull String token, boolean suppress) {
            if (Flags.lowLightDreamBehavior()) {
                // Use flagged-based suppression if new behavior is available.
                suppressAmbientDisplayBehavior(token, suppress
                        ? PowerManager.FLAG_AMBIENT_SUPPRESSION_ALL
                        : PowerManager.FLAG_AMBIENT_SUPPRESSION_NONE);
                return;
            }

            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.WRITE_DREAM_STATE, null);

@@ -7056,6 +7094,30 @@ public final class PowerManagerService extends SystemService
            }
        }

        @Override // Binder call
        public void suppressAmbientDisplayBehavior(@NonNull String token,
                @FlagAmbientSuppression int flags) {
            if (!Flags.lowLightDreamBehavior()) {
                throw new IllegalArgumentException("suppressAmbientDisplayBehavior should only"
                        + "be called if lowLightDreamBehavior is enabled");
            }

            // sanitize the input to known flags.
            flags &= PowerManager.FLAG_AMBIENT_SUPPRESSION_ALL;

            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.WRITE_DREAM_STATE, null);

            final int uid = Binder.getCallingUid();
            final long ident = Binder.clearCallingIdentity();
            try {
                mAmbientDisplaySuppressionController.suppress(token, uid, flags);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }


        @Override // Binder call
        public boolean isAmbientDisplaySuppressedForToken(@NonNull String token) {
            mContext.enforceCallingOrSelfPermission(
+31 −3
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Flags;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.PowerManagerInternal;
@@ -138,8 +139,31 @@ class PowerManagerShellCommand extends ShellCommand {

        try {
            String token = getNextArgRequired();
            if (Flags.lowLightDreamBehavior()) {
                int suppressFlags = PowerManager.FLAG_AMBIENT_SUPPRESSION_NONE;
                for (String command : getNextArgRequired().toLowerCase().split(",")) {
                    switch (command) {
                        case "true":
                        case "all":
                            suppressFlags |= PowerManager.FLAG_AMBIENT_SUPPRESSION_ALL;
                            break;
                        case "dream":
                            suppressFlags |= PowerManager.FLAG_AMBIENT_SUPPRESSION_DREAM;
                            break;
                        case "aod":
                            suppressFlags |= PowerManager.FLAG_AMBIENT_SUPPRESSION_AOD;
                            break;
                        case "false":
                        case "none":
                            suppressFlags |= PowerManager.FLAG_AMBIENT_SUPPRESSION_NONE;
                            break;
                    }
                }
                mService.suppressAmbientDisplayBehavior(token, suppressFlags);
            } else {
                boolean enabled = Boolean.parseBoolean(getNextArgRequired());
                mService.suppressAmbientDisplay(token, enabled);
            }
        } catch (RuntimeException ex) {
            pw.println("Error: " + ex.toString());
            return -1;
@@ -310,7 +334,11 @@ class PowerManagerShellCommand extends ShellCommand {
        pw.println("    enables or disables fixed performance mode");
        pw.println("    note: this will affect system performance and should only be used");
        pw.println("          during development");
        if (Flags.lowLightDreamBehavior()) {
            pw.println("  suppress-ambient-display <token> [none|all|dream|aod|true|false]");
        } else {
            pw.println("  suppress-ambient-display <token> [true|false]");
        }
        pw.println("    suppresses the current ambient display configuration and disables");
        pw.println("    ambient display");
        pw.println("  list-ambient-display-suppression-tokens");
Loading