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

Commit 4ed68c82 authored by Danny Baumann's avatar Danny Baumann Committed by Bruno Martins
Browse files

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

Useful e.g. for messenger apps.

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

    List<String> getAllowedAssistantAdjustments(String pkg);
    void allowAssistantAdjustment(String adjustmentType);
+50 −1
Original line number Diff line number Diff line
@@ -205,6 +205,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import android.view.accessibility.AccessibilityEvent;
@@ -271,6 +272,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -422,6 +424,7 @@ public class NotificationManagerService extends SystemService {
    final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
    final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
    final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
    final ArrayMap<String, Long> mLastSoundTimestamps = new ArrayMap<>();

    // The last key in this list owns the hardware.
    ArrayList<String> mLights = new ArrayList<>();
@@ -2469,6 +2472,19 @@ public class NotificationManagerService extends SystemService {
            handleSavePolicyFile();
        }

        @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 means
@@ -4625,6 +4641,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");
            }
        }
    }

@@ -5739,7 +5763,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(notification, record.sbn.getPackageName());
                        sentAccessibilityEvent = true;
@@ -5793,6 +5818,12 @@ public class NotificationManagerService extends SystemService {
        } else if (wasShowLights) {
            updateLightsLocked();
        }

        if (buzz || beep) {
            mLastSoundTimestamps.put(generateLastSoundTimeoutKey(record),
                    SystemClock.elapsedRealtime());
        }

        if (buzz || beep || blink) {
            // Ignore summary updates because we don't display most of the information.
            if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
@@ -5816,6 +5847,24 @@ public class NotificationManagerService extends SystemService {
        record.setAudiblyAlerted(buzz || beep);
    }

    private boolean isInSoundTimeoutPeriod(NotificationRecord record) {
        long timeoutMillis = mPreferencesHelper.getNotificationSoundTimeout(
                record.sbn.getPackageName(), record.sbn.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.sbn.getPackageName() + "|" + record.sbn.getUid();
    }

    @GuardedBy("mNotificationLock")
    boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
        // device lacks light
+27 −0
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ public class PreferencesHelper implements RankingConfig {
    private static final String ATT_ENABLED = "enabled";
    private static final String ATT_USER_ALLOWED = "allowed";
    private static final String ATT_HIDE_SILENT = "hide_gentle";
    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;
@@ -201,6 +202,8 @@ public class PreferencesHelper implements RankingConfig {
                                    parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
                            r.lockedAppFields = XmlUtils.readIntAttribute(parser,
                                    ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
                            r.soundTimeout = XmlUtils.readIntAttribute(parser,
                                    ATT_SOUND_TIMEOUT, 0);

                            final int innerDepth = parser.getDepth();
                            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -410,6 +413,7 @@ public class PreferencesHelper implements RankingConfig {
                                || r.visibility != DEFAULT_VISIBILITY
                                || r.showBadge != DEFAULT_SHOW_BADGE
                                || r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
                                || r.soundTimeout != 0
                                || r.channels.size() > 0
                                || r.groups.size() > 0
                                || r.delegate != null
@@ -426,6 +430,9 @@ public class PreferencesHelper implements RankingConfig {
                    if (r.visibility != DEFAULT_VISIBILITY) {
                        out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
                    }
                    if (r.soundTimeout != 0) {
                        out.attribute(null, ATT_SOUND_TIMEOUT, Long.toString(r.soundTimeout));
                    }
                    if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) {
                        out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble));
                    }
@@ -1308,6 +1315,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.
     */
@@ -1559,6 +1581,9 @@ public class PreferencesHelper implements RankingConfig {
                        if (r.showBadge != DEFAULT_SHOW_BADGE) {
                            PackagePreferences.put("showBadge", Boolean.valueOf(r.showBadge));
                        }
                        if (r.soundTimeout != 0) {
                            PackagePreferences.put("soundTimeout", r.soundTimeout);
                        }
                        JSONArray channels = new JSONArray();
                        for (NotificationChannel channel : r.channels.values()) {
                            channels.put(channel.toJson());
@@ -1780,6 +1805,7 @@ public class PreferencesHelper implements RankingConfig {
                p.groups = new ArrayMap<>();
                p.delegate = null;
                p.lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
                p.soundTimeout = 0;
                p.allowBubble = DEFAULT_ALLOW_BUBBLE;
                p.importance = DEFAULT_IMPORTANCE;
                p.priority = DEFAULT_PRIORITY;
@@ -1903,6 +1929,7 @@ public class PreferencesHelper implements RankingConfig {
        boolean showBadge = DEFAULT_SHOW_BADGE;
        boolean allowBubble = DEFAULT_ALLOW_BUBBLE;
        int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
        long soundTimeout = 0;
        // these fields are loaded on boot from a different source of truth and so are not
        // written to notification policy xml
        boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE;