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

Unverified Commit 039169e3 authored by Danny Baumann's avatar Danny Baumann Committed by Michael Bestas
Browse files

Support enforcing a minimum delay between notification sounds of an app.

Useful e.g. for messenger apps.

Change-Id: If8e8cc9e2f02d70537c1f9dc14f22bbd0ec1e9a6
parent e8bf04d0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -79,6 +79,8 @@ interface INotificationManager
    boolean areNotificationsEnabled(String pkg);
    int getPackageImportance(String pkg);
    boolean isImportanceLocked(String pkg, int uid);
    void setNotificationSoundTimeout(String pkg, int uid, long timeout);
    long getNotificationSoundTimeout(String pkg, int uid);

    List<String> getAllowedAssistantAdjustments(String pkg);

+48 −1
Original line number Diff line number Diff line
@@ -271,6 +271,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.StatsEvent;
import android.util.TimeUtils;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -354,6 +355,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -616,6 +618,7 @@ public class NotificationManagerService extends SystemService {
    @GuardedBy("mToastQueue")
    private final Set<Integer> mToastRateLimitingDisabledUids = new ArraySet<>();
    final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
    final ArrayMap<String, Long> mLastSoundTimestamps = new ArrayMap<>();
    // True if the toast that's on top of the queue is being shown at the moment.
    @GuardedBy("mToastQueue")
@@ -3581,6 +3584,19 @@ public class NotificationManagerService extends SystemService {
            // callback from AppOpsManager.
        }
        @Override
        public void setNotificationSoundTimeout(String pkg, int uid, long timeout) {
            checkCallerIsSystem();
            mPreferencesHelper.setNotificationSoundTimeout(pkg, uid, timeout);
            handleSavePolicyFile();
        }
        @Override
        public long getNotificationSoundTimeout(String pkg, int uid) {
            checkCallerIsSystem();
            return mPreferencesHelper.getNotificationSoundTimeout(pkg, uid);
        }
        /**
         * Updates the enabled state for notifications for the given package (and uid).
         * Additionally, this method marks the app importance as locked by the user, which
@@ -6404,6 +6420,14 @@ public class NotificationManagerService extends SystemService {
                pw.println("\n  Usage Stats:");
                mUsageStats.dump(pw, "    ", filter);
            }
            long now = SystemClock.elapsedRealtime();
            pw.println("\n  Last notification sound timestamps:");
            for (Map.Entry<String, Long> entry : mLastSoundTimestamps.entrySet()) {
                pw.print("    " + entry.getKey() + " -> ");
                TimeUtils.formatDuration(entry.getValue(), now, pw);
                pw.println(" ago");
            }
        }
    }
@@ -8312,7 +8336,8 @@ public class NotificationManagerService extends SystemService {
                }
                hasValidVibrate = vibration != null;
                boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
                if (hasAudibleAlert && !shouldMuteNotificationLocked(record)
                        && !isInSoundTimeoutPeriod(record)) {
                    if (!sentAccessibilityEvent) {
                        sendAccessibilityEvent(record);
                        sentAccessibilityEvent = true;
@@ -8383,6 +8408,10 @@ public class NotificationManagerService extends SystemService {
        } else if (wasShowLights) {
            updateLightsLocked();
        }
        if (buzz || beep) {
            mLastSoundTimestamps.put(generateLastSoundTimeoutKey(record),
                    SystemClock.elapsedRealtime());
        }
        final int buzzBeepBlink = (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
        if (buzzBeepBlink > 0) {
            // Ignore summary updates because we don't display most of the information.
@@ -8413,6 +8442,24 @@ public class NotificationManagerService extends SystemService {
        return buzzBeepBlink;
    }
    private boolean isInSoundTimeoutPeriod(NotificationRecord record) {
        long timeoutMillis = mPreferencesHelper.getNotificationSoundTimeout(
                record.getSbn().getPackageName(), record.getSbn().getUid());
        if (timeoutMillis == 0) {
            return false;
        }
        Long value = mLastSoundTimestamps.get(generateLastSoundTimeoutKey(record));
        if (value == null) {
            return false;
        }
        return SystemClock.elapsedRealtime() - value < timeoutMillis;
    }
    private String generateLastSoundTimeoutKey(NotificationRecord record) {
        return record.getSbn().getPackageName() + "|" + record.getSbn().getUid();
    }
    @GuardedBy("mNotificationLock")
    boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
        // device lacks light
+28 −0
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ public class PreferencesHelper implements RankingConfig {
    private static final String ATT_SENT_INVALID_MESSAGE = "sent_invalid_msg";
    private static final String ATT_SENT_VALID_MESSAGE = "sent_valid_msg";
    private static final String ATT_USER_DEMOTED_INVALID_MSG_APP = "user_demote_msg_app";
    private static final String ATT_SOUND_TIMEOUT = "sound-timeout";

    private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
    private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
@@ -174,6 +175,8 @@ public class PreferencesHelper implements RankingConfig {
    private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
    private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;

    private static final int DEFAULT_SOUND_TIMEOUT = 0;

    /**
     * All user-lockable fields for a given application.
     */
@@ -319,6 +322,8 @@ public class PreferencesHelper implements RankingConfig {
            r.hasSentValidMessage = parser.getAttributeBoolean(null, ATT_SENT_VALID_MESSAGE, false);
            r.userDemotedMsgApp = parser.getAttributeBoolean(
                    null, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
            r.soundTimeout = parser.getAttributeLong(
                    null, ATT_SOUND_TIMEOUT, DEFAULT_SOUND_TIMEOUT);

            final int innerDepth = parser.getDepth();
            int type;
@@ -608,6 +613,9 @@ public class PreferencesHelper implements RankingConfig {
                if (r.visibility != DEFAULT_VISIBILITY) {
                    out.attributeInt(null, ATT_VISIBILITY, r.visibility);
                }
                if (r.soundTimeout != DEFAULT_SOUND_TIMEOUT) {
                    out.attributeLong(null, ATT_SOUND_TIMEOUT, r.soundTimeout);
                }
                if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
                    out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference);
                }
@@ -1896,6 +1904,21 @@ public class PreferencesHelper implements RankingConfig {
        updateConfig();
    }

    /**
     * @hide
     */
    public long getNotificationSoundTimeout(String packageName, int uid) {
        return getOrCreatePackagePreferencesLocked(packageName, uid).soundTimeout;
    }

    /**
     * @hide
     */
    public void setNotificationSoundTimeout(String packageName, int uid, long timeout) {
        getOrCreatePackagePreferencesLocked(packageName, uid).soundTimeout = timeout;
        updateConfig();
    }

    /**
     * Returns the delegate for a given package, if it's allowed by the package and the user.
     */
@@ -2393,6 +2416,9 @@ public class PreferencesHelper implements RankingConfig {
                        if (r.showBadge != DEFAULT_SHOW_BADGE) {
                            PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
                        }
                        if (r.soundTimeout != DEFAULT_SOUND_TIMEOUT) {
                            PackagePreferences.put("soundTimeout", r.soundTimeout);
                        }
                        JSONArray channels = new JSONArray();
                        for (NotificationChannel channel : r.channels.values()) {
                            channels.put(channel.toJson());
@@ -2669,6 +2695,7 @@ public class PreferencesHelper implements RankingConfig {
                p.groups = new ArrayMap<>();
                p.delegate = null;
                p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
                p.soundTimeout = DEFAULT_SOUND_TIMEOUT;
                p.bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
                p.importance = DEFAULT_IMPORTANCE;
                p.priority = DEFAULT_PRIORITY;
@@ -2903,6 +2930,7 @@ public class PreferencesHelper implements RankingConfig {
        boolean showBadge = DEFAULT_SHOW_BADGE;
        int bubblePreference = DEFAULT_BUBBLE_PREFERENCE;
        int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
        long soundTimeout = DEFAULT_SOUND_TIMEOUT;
        // these fields are loaded on boot from a different source of truth and so are not
        // written to notification policy xml
        boolean defaultAppLockedImportance = DEFAULT_APP_LOCKED_IMPORTANCE;