Skip to content

Commit ac7b58e

Browse files
committed
Support enforcing a minimum delay between notification sounds of an app.
Useful e.g. for messenger apps. Change-Id: If8e8cc9e2f02d70537c1f9dc14f22bbd0ec1e9a6
1 parent e05eda2 commit ac7b58e

3 files changed

Lines changed: 74 additions & 1 deletion

File tree

core/java/android/app/INotificationManager.aidl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ interface INotificationManager
5858
void setShowNotificationForPackageOnKeyguard(String pkg, int uid, int status);
5959
int getShowNotificationForPackageOnKeyguard(String pkg, int uid);
6060

61+
void setPackageNotificationSoundTimeout(String pkg, int uid, long timeout);
62+
long getPackageNotificationSoundTimeout(String pkg, int uid);
63+
6164
// TODO: Remove this when callers have been migrated to the equivalent
6265
// INotificationListener method.
6366
StatusBarNotification[] getActiveNotifications(String callingPkg);

services/core/java/com/android/server/notification/NotificationManagerService.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import android.os.Message;
7777
import android.os.Process;
7878
import android.os.RemoteException;
79+
import android.os.SystemClock;
7980
import android.os.SystemProperties;
8081
import android.os.UserHandle;
8182
import android.os.UserManager;
@@ -100,6 +101,7 @@
100101
import android.util.LruCache;
101102
import android.util.Slog;
102103
import android.util.SparseIntArray;
104+
import android.util.TimeUtils;
103105
import android.util.Xml;
104106
import android.view.accessibility.AccessibilityEvent;
105107
import android.view.accessibility.AccessibilityManager;
@@ -302,6 +304,7 @@ public class NotificationManagerService extends SystemService {
302304
new ArrayMap<String, NotificationRecord>();
303305
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
304306
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
307+
final ArrayMap<String, Long> mLastSoundTimestamps = new ArrayMap<>();
305308
final PolicyAccess mPolicyAccess = new PolicyAccess();
306309

307310
// The last key in this list owns the hardware.
@@ -1595,6 +1598,19 @@ public int getShowNotificationForPackageOnKeyguard(String pkg, int uid) {
15951598
return mRankingHelper.getShowNotificationForPackageOnKeyguard(pkg, uid);
15961599
}
15971600

1601+
@Override
1602+
public void setPackageNotificationSoundTimeout(String pkg, int uid, long timeout) {
1603+
checkCallerIsSystem();
1604+
mRankingHelper.setPackageNotificationSoundTimeout(pkg, uid, timeout);
1605+
savePolicyFile();
1606+
}
1607+
1608+
@Override
1609+
public long getPackageNotificationSoundTimeout(String pkg, int uid) {
1610+
checkCallerIsSystem();
1611+
return mRankingHelper.getPackageNotificationSoundTimeout(pkg, uid);
1612+
}
1613+
15981614
/**
15991615
* System-only API for getting a list of current (i.e. not cleared) notifications.
16001616
*
@@ -2317,6 +2333,14 @@ void dumpImpl(PrintWriter pw, DumpFilter filter) {
23172333
} catch (NameNotFoundException e) {
23182334
// pass
23192335
}
2336+
2337+
long now = SystemClock.elapsedRealtime();
2338+
pw.println("\n Last notification sound timestamps:");
2339+
for (Map.Entry<String, Long> entry: mLastSoundTimestamps.entrySet()) {
2340+
pw.print(" " + entry.getKey() + " -> ");
2341+
TimeUtils.formatDuration(entry.getValue(), now, pw);
2342+
pw.println(" ago");
2343+
}
23202344
}
23212345
}
23222346

@@ -2705,6 +2729,7 @@ private void buzzBeepBlinkLocked(NotificationRecord record) {
27052729
&& (record.getUserId() == UserHandle.USER_ALL ||
27062730
record.getUserId() == currentUser ||
27072731
mUserProfiles.isCurrentProfile(record.getUserId()))
2732+
&& !isInSoundTimeoutPeriod(record)
27082733
&& mSystemReady
27092734
&& mAudioManager != null;
27102735

@@ -2835,13 +2860,35 @@ private void buzzBeepBlinkLocked(NotificationRecord record) {
28352860
} else if (wasShowLights) {
28362861
updateLightsLocked();
28372862
}
2863+
if (buzz || beep) {
2864+
mLastSoundTimestamps.put(generateLastSoundTimeoutKey(record),
2865+
SystemClock.elapsedRealtime());
2866+
}
28382867
if (buzz || beep || blink) {
28392868
EventLogTags.writeNotificationAlert(record.getKey(),
28402869
buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
28412870
mHandler.post(mBuzzBeepBlinked);
28422871
}
28432872
}
28442873

2874+
private boolean isInSoundTimeoutPeriod(NotificationRecord record) {
2875+
long timeoutMillis = mRankingHelper.getPackageNotificationSoundTimeout(
2876+
record.sbn.getPackageName(), record.sbn.getUid());
2877+
if (timeoutMillis == 0) {
2878+
return false;
2879+
}
2880+
2881+
Long value = mLastSoundTimestamps.get(generateLastSoundTimeoutKey(record));
2882+
if (value == null) {
2883+
return false;
2884+
}
2885+
return SystemClock.elapsedRealtime() - value < timeoutMillis;
2886+
}
2887+
2888+
private String generateLastSoundTimeoutKey(NotificationRecord record) {
2889+
return record.sbn.getPackageName() + "|" + record.sbn.getUid();
2890+
}
2891+
28452892
private static AudioAttributes audioAttributesForNotification(Notification n) {
28462893
if (n.audioAttributes != null
28472894
&& !Notification.AUDIO_ATTRIBUTES_DEFAULT.equals(n.audioAttributes)) {

services/core/java/com/android/server/notification/RankingHelper.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class RankingHelper implements RankingConfig {
5252
private static final String ATT_PEEKABLE = "peekable";
5353
private static final String ATT_VISIBILITY = "visibility";
5454
private static final String ATT_KEYGUARD = "keyguard";
55+
private static final String ATT_SOUND_TIMEOUT = "sound-timeout";
5556

5657
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
5758
private static final boolean DEFAULT_PEEKABLE = true;
@@ -146,6 +147,7 @@ public void readXml(XmlPullParser parser, boolean forRestore)
146147
int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
147148
int keyguard = safeInt(parser, ATT_KEYGUARD,
148149
Notification.SHOW_ALL_NOTI_ON_KEYGUARD);
150+
long soundTimeout = safeInt(parser, ATT_SOUND_TIMEOUT, 0);
149151
String name = parser.getAttributeValue(null, ATT_NAME);
150152

151153
if (!TextUtils.isEmpty(name)) {
@@ -178,6 +180,9 @@ public void readXml(XmlPullParser parser, boolean forRestore)
178180
if (keyguard != Notification.SHOW_ALL_NOTI_ON_KEYGUARD) {
179181
r.keyguard = keyguard;
180182
}
183+
if (soundTimeout != 0) {
184+
r.notificationSoundTimeout = soundTimeout;
185+
}
181186
}
182187
}
183188
}
@@ -207,7 +212,8 @@ private void removeDefaultRecords() {
207212
final Record r = mRecords.valueAt(i);
208213
if (r.priority == DEFAULT_PRIORITY && r.peekable == DEFAULT_PEEKABLE
209214
&& r.visibility == DEFAULT_VISIBILITY
210-
&& r.keyguard == Notification.SHOW_ALL_NOTI_ON_KEYGUARD) {
215+
&& r.keyguard == Notification.SHOW_ALL_NOTI_ON_KEYGUARD
216+
&& r.notificationSoundTimeout == 0) {
211217
mRecords.removeAt(i);
212218
}
213219
}
@@ -237,6 +243,9 @@ public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
237243
if (r.keyguard != Notification.SHOW_ALL_NOTI_ON_KEYGUARD) {
238244
out.attribute(null, ATT_KEYGUARD, Integer.toBinaryString(r.keyguard));
239245
}
246+
if (r.notificationSoundTimeout != 0) {
247+
out.attribute(null, ATT_SOUND_TIMEOUT, Long.toString(r.notificationSoundTimeout));
248+
}
240249
if (!forBackup) {
241250
out.attribute(null, ATT_UID, Integer.toString(r.uid));
242251
}
@@ -404,6 +413,19 @@ public void setShowNotificationForPackageOnKeyguard(
404413
updateConfig();
405414
}
406415

416+
public long getPackageNotificationSoundTimeout(String packageName, int uid) {
417+
final Record r = mRecords.get(recordKey(packageName, uid));
418+
return r != null ? r.notificationSoundTimeout : 0;
419+
}
420+
421+
public void setPackageNotificationSoundTimeout(String packageName, int uid, long timeout) {
422+
if (timeout == getPackageNotificationSoundTimeout(packageName, uid)) {
423+
return;
424+
}
425+
getOrCreateRecord(packageName, uid).notificationSoundTimeout = timeout;
426+
removeDefaultRecords();
427+
}
428+
407429
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
408430
if (filter == null) {
409431
final int N = mSignalExtractors.length;
@@ -487,6 +509,7 @@ private static class Record {
487509
boolean peekable = DEFAULT_PEEKABLE;
488510
int visibility = DEFAULT_VISIBILITY;
489511
int keyguard = Notification.SHOW_ALL_NOTI_ON_KEYGUARD;
512+
long notificationSoundTimeout = 0;
490513
}
491514

492515
}

0 commit comments

Comments
 (0)