diff options
| author | 2014-06-03 18:11:47 -0400 | |
|---|---|---|
| committer | 2014-06-05 12:38:13 -0400 | |
| commit | a344656a010dc3c88aef39109f1ac459792e7607 (patch) | |
| tree | e35d4036aa763b3dbcfe6fb0b99e7f96960d727a | |
| parent | 7256a852cee3a656615af19ebeb3ad7379a194fb (diff) | |
A notification that emerges from Zen Mode interception should beep.
If a notificaiton emerges from Zen Mode due to a ranking
reconsideration (possibly because a long-running query resolved an
important person) then it should get the oportunity buzz, beep, blink,
and send accessibility events.
Save what we need to know about the old notificaiton record on the new
record so we don't have to hold onto it.
Bug: 15383458
Change-Id: I15c7834fef03ff6a676e78e9d2caae24f00720ef
| -rw-r--r-- | services/core/java/com/android/server/notification/NotificationManagerService.java | 328 | ||||
| -rw-r--r-- | services/core/java/com/android/server/notification/NotificationRecord.java | 7 |
2 files changed, 175 insertions, 160 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 495db20ba7e3..027b669857fc 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -182,7 +182,7 @@ public class NotificationManagerService extends SystemService { new ArrayMap<String, NotificationRecord>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); - ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); + ArrayList<String> mLights = new ArrayList<String>(); NotificationRecord mLedNotification; private AppOpsManager mAppOps; @@ -1478,14 +1478,14 @@ public class NotificationManagerService extends SystemService { } } - // 1. initial score: buckets of 10, around the app - int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] + // 1. initial score: buckets of 10, around the app [-20..20] + final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; // 2. extract ranking signals from the notification data final StatusBarNotification n = new StatusBarNotification( pkg, opPkg, id, tag, callingUid, callingPid, score, notification, user); - NotificationRecord r = new NotificationRecord(n); + NotificationRecord r = new NotificationRecord(n, score); NotificationRecord old = mNotificationsByKey.get(n.getKey()); if (old != null) { // Retain ranking information from previous record @@ -1507,13 +1507,13 @@ public class NotificationManagerService extends SystemService { // blocked apps if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { if (!isSystemNotification) { - score = JUNK_SCORE; + r.score = JUNK_SCORE; Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request."); } } - if (score < SCORE_DISPLAY_THRESHOLD) { + if (r.score < SCORE_DISPLAY_THRESHOLD) { // Notification will be blocked because the score is too low. return; } @@ -1529,28 +1529,16 @@ public class NotificationManagerService extends SystemService { mUsageStats.registerUpdatedByApp(r, old); // Make sure we don't lose the foreground service state. notification.flags |= - old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; + old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE; mNotificationsByKey.remove(old.sbn.getKey()); + r.isUpdate = true; } mNotificationsByKey.put(n.getKey(), r); applyZenModeLocked(r); - // Should this notification make noise, vibe, or use the LED? - final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && - !r.isIntercepted(); - if (DBG || r.isIntercepted()) Slog.v(TAG, - "pkg=" + pkg + " canInterrupt=" + canInterrupt + - " intercept=" + r.isIntercepted()); Collections.sort(mNotificationList, mRankingComparator); - // Ensure if this is a foreground service that the proper additional - // flags are set. - if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { - notification.flags |= Notification.FLAG_ONGOING_EVENT - | Notification.FLAG_NO_CLEAR; - } - final int currentUser; final long token = Binder.clearCallingIdentity(); try { @@ -1571,20 +1559,11 @@ public class NotificationManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { mStatusBar.addNotification(n); - if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 - && canInterrupt) { - mAttentionLight.pulse(); - } } finally { Binder.restoreCallingIdentity(identity); } } - // Send accessibility events only for the current user. - if (currentUser == userId) { - sendAccessibilityEvent(notification, pkg); - } - - mListeners.notifyPostedLocked(r.sbn); + mListeners.notifyPostedLocked(n); } else { Slog.e(TAG, "Not posting notification with icon==0: " + notification); if (old != null && !old.isCanceled) { @@ -1595,7 +1574,7 @@ public class NotificationManagerService extends SystemService { Binder.restoreCallingIdentity(identity); } - mListeners.notifyRemovedLocked(r.sbn); + mListeners.notifyRemovedLocked(n); } // ATTENTION: in a future release we will bail out here // so that we do not play sounds, show lights, etc. for invalid @@ -1604,141 +1583,169 @@ public class NotificationManagerService extends SystemService { + n.getPackageName()); } - // If we're not supposed to beep, vibrate, etc. then don't. - if (!mDisableNotificationAlerts - && (!(old != null - && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) - && (r.getUserId() == UserHandle.USER_ALL || - (r.getUserId() == userId && r.getUserId() == currentUser) || - mUserProfiles.isCurrentProfile(r.getUserId())) - && canInterrupt - && mSystemReady - && mAudioManager != null) { - if (DBG) Slog.v(TAG, "Interrupting!"); - // sound - - // should we use the default notification sound? (indicated either by - // DEFAULT_SOUND or because notification.sound is pointing at - // Settings.System.NOTIFICATION_SOUND) - final boolean useDefaultSound = - (notification.defaults & Notification.DEFAULT_SOUND) != 0 || - Settings.System.DEFAULT_NOTIFICATION_URI - .equals(notification.sound); - - Uri soundUri = null; - boolean hasValidSound = false; - - if (useDefaultSound) { - soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; - - // check to see if the default notification sound is silent - ContentResolver resolver = getContext().getContentResolver(); - hasValidSound = Settings.System.getString(resolver, - Settings.System.NOTIFICATION_SOUND) != null; - } else if (notification.sound != null) { - soundUri = notification.sound; - hasValidSound = (soundUri != null); - } + // Ensure if this is a foreground service that the proper additional + // flags are set. + if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { + notification.flags |= Notification.FLAG_ONGOING_EVENT + | Notification.FLAG_NO_CLEAR; + } - if (hasValidSound) { - boolean looping = - (notification.flags & Notification.FLAG_INSISTENT) != 0; - int audioStreamType; - if (notification.audioStreamType >= 0) { - audioStreamType = notification.audioStreamType; - } else { - audioStreamType = DEFAULT_STREAM_TYPE; - } - mSoundNotification = r; - // do not play notifications if stream volume is 0 (typically because - // ringer mode is silent) or if there is a user of exclusive audio focus - if ((mAudioManager.getStreamVolume(audioStreamType) != 0) - && !mAudioManager.isAudioFocusExclusive()) { - final long identity = Binder.clearCallingIdentity(); - try { - final IRingtonePlayer player = - mAudioManager.getRingtonePlayer(); - if (player != null) { - if (DBG) Slog.v(TAG, "Playing sound " + soundUri - + " on stream " + audioStreamType); - player.playAsync(soundUri, user, looping, audioStreamType); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } + buzzBeepBlinkLocked(r); + } + } + }); - // vibrate - // Does the notification want to specify its own vibration? - final boolean hasCustomVibrate = notification.vibrate != null; - - // new in 4.2: if there was supposed to be a sound and we're in vibrate - // mode, and no other vibration is specified, we fall back to vibration - final boolean convertSoundToVibration = - !hasCustomVibrate - && hasValidSound - && (mAudioManager.getRingerMode() - == AudioManager.RINGER_MODE_VIBRATE); - - // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. - final boolean useDefaultVibrate = - (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; - - if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) - && !(mAudioManager.getRingerMode() - == AudioManager.RINGER_MODE_SILENT)) { - mVibrateNotification = r; - - if (useDefaultVibrate || convertSoundToVibration) { - // Escalate privileges so we can use the vibrator even if the - // notifying app does not have the VIBRATE permission. - long identity = Binder.clearCallingIdentity(); - try { - mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), - useDefaultVibrate ? mDefaultVibrationPattern - : mFallbackVibrationPattern, - ((notification.flags & Notification.FLAG_INSISTENT) != 0) - ? 0: -1, notification.audioStreamType); - } finally { - Binder.restoreCallingIdentity(identity); - } - } else if (notification.vibrate.length > 1) { - // If you want your own vibration pattern, you need the VIBRATE - // permission - mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(), - notification.vibrate, - ((notification.flags & Notification.FLAG_INSISTENT) != 0) - ? 0: -1, notification.audioStreamType); - } + idOut[0] = id; + } + + private void buzzBeepBlinkLocked(NotificationRecord record) { + final Notification notification = record.sbn.getNotification(); + + // Should this notification make noise, vibe, or use the LED? + final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) && + !record.isIntercepted(); + if (DBG || record.isIntercepted()) + Slog.v(TAG, + "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt + + " intercept=" + record.isIntercepted() + ); + + final int currentUser; + final long token = Binder.clearCallingIdentity(); + try { + currentUser = ActivityManager.getCurrentUser(); + } finally { + Binder.restoreCallingIdentity(token); + } + + // If we're not supposed to beep, vibrate, etc. then don't. + if (!mDisableNotificationAlerts + && (!(record.isUpdate + && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) + && (record.getUserId() == UserHandle.USER_ALL || + record.getUserId() == currentUser || + mUserProfiles.isCurrentProfile(record.getUserId())) + && canInterrupt + && mSystemReady + && mAudioManager != null) { + if (DBG) Slog.v(TAG, "Interrupting!"); + + sendAccessibilityEvent(notification, record.sbn.getPackageName()); + + // sound + + // should we use the default notification sound? (indicated either by + // DEFAULT_SOUND or because notification.sound is pointing at + // Settings.System.NOTIFICATION_SOUND) + final boolean useDefaultSound = + (notification.defaults & Notification.DEFAULT_SOUND) != 0 || + Settings.System.DEFAULT_NOTIFICATION_URI + .equals(notification.sound); + + Uri soundUri = null; + boolean hasValidSound = false; + + if (useDefaultSound) { + soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; + + // check to see if the default notification sound is silent + ContentResolver resolver = getContext().getContentResolver(); + hasValidSound = Settings.System.getString(resolver, + Settings.System.NOTIFICATION_SOUND) != null; + } else if (notification.sound != null) { + soundUri = notification.sound; + hasValidSound = (soundUri != null); + } + + if (hasValidSound) { + boolean looping = + (notification.flags & Notification.FLAG_INSISTENT) != 0; + int audioStreamType; + if (notification.audioStreamType >= 0) { + audioStreamType = notification.audioStreamType; + } else { + audioStreamType = DEFAULT_STREAM_TYPE; + } + mSoundNotification = record; + // do not play notifications if stream volume is 0 (typically because + // ringer mode is silent) or if there is a user of exclusive audio focus + if ((mAudioManager.getStreamVolume(audioStreamType) != 0) + && !mAudioManager.isAudioFocusExclusive()) { + final long identity = Binder.clearCallingIdentity(); + try { + final IRingtonePlayer player = + mAudioManager.getRingtonePlayer(); + if (player != null) { + if (DBG) Slog.v(TAG, "Playing sound " + soundUri + + " on stream " + audioStreamType); + player.playAsync(soundUri, record.sbn.getUser(), looping, + audioStreamType); } + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(identity); } + } + } - // light - // the most recent thing gets the light - mLights.remove(old); - if (mLedNotification == old) { - mLedNotification = null; - } - //Slog.i(TAG, "notification.lights=" - // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) - // != 0)); - if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 - && canInterrupt) { - mLights.add(r); - updateLightsLocked(); - } else { - if (old != null - && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) { - updateLightsLocked(); - } + // vibrate + // Does the notification want to specify its own vibration? + final boolean hasCustomVibrate = notification.vibrate != null; + + // new in 4.2: if there was supposed to be a sound and we're in vibrate + // mode, and no other vibration is specified, we fall back to vibration + final boolean convertSoundToVibration = + !hasCustomVibrate + && hasValidSound + && (mAudioManager.getRingerMode() + == AudioManager.RINGER_MODE_VIBRATE); + + // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. + final boolean useDefaultVibrate = + (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; + + if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) + && !(mAudioManager.getRingerMode() + == AudioManager.RINGER_MODE_SILENT)) { + mVibrateNotification = record; + + if (useDefaultVibrate || convertSoundToVibration) { + // Escalate privileges so we can use the vibrator even if the + // notifying app does not have the VIBRATE permission. + long identity = Binder.clearCallingIdentity(); + try { + mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), + useDefaultVibrate ? mDefaultVibrationPattern + : mFallbackVibrationPattern, + ((notification.flags & Notification.FLAG_INSISTENT) != 0) + ? 0: -1, notification.audioStreamType); + } finally { + Binder.restoreCallingIdentity(identity); } + } else if (notification.vibrate.length > 1) { + // If you want your own vibration pattern, you need the VIBRATE + // permission + mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), + notification.vibrate, + ((notification.flags & Notification.FLAG_INSISTENT) != 0) + ? 0: -1, notification.audioStreamType); } } - }); + } - idOut[0] = id; + // light + // release the light + boolean wasShowLights = mLights.remove(record.getKey()); + if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) { + mLedNotification = null; + } + if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) { + mLights.add(record.getKey()); + updateLightsLocked(); + mAttentionLight.pulse(); + } else if (wasShowLights) { + updateLightsLocked(); + } } void showNextToastLocked() { @@ -1866,6 +1873,9 @@ public class NotificationManagerService extends SystemService { int indexAfter = findNotificationRecordIndexLocked(record); boolean interceptAfter = record.isIntercepted(); changed = indexBefore != indexAfter || interceptBefore != interceptAfter; + if (interceptBefore && !interceptAfter) { + buzzBeepBlinkLocked(record); + } } if (changed) { scheduleSendRankingUpdate(); @@ -2011,7 +2021,7 @@ public class NotificationManagerService extends SystemService { } // light - mLights.remove(r); + mLights.remove(r.getKey()); if (mLedNotification == r) { mLedNotification = null; } @@ -2196,7 +2206,7 @@ public class NotificationManagerService extends SystemService { // get next notification, if any int n = mLights.size(); if (n > 0) { - mLedNotification = mLights.get(n-1); + mLedNotification = mNotificationsByKey.get(mLights.get(n-1)); } } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 08f8eb40b155..30d4fecdbd9d 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -42,6 +42,7 @@ public final class NotificationRecord { final StatusBarNotification sbn; NotificationUsageStats.SingleNotificationStats stats; boolean isCanceled; + int score; // These members are used by NotificationSignalExtractors // to communicate with the ranking module. @@ -53,9 +54,13 @@ public final class NotificationRecord { // InterceptedNotifications needs to know if this has been previously evaluated. private boolean mTouchedByZen; - NotificationRecord(StatusBarNotification sbn) + // Is this record an update of an old record? + public boolean isUpdate; + + NotificationRecord(StatusBarNotification sbn, int score) { this.sbn = sbn; + this.score = score; } // copy any notes that the ranking system may have made before the update |