Merge "Log a notification update when fields such as importance or ranking score are updated." into sc-qpr1-dev am: e97b0e8fbb
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15890028 Change-Id: I0f7077a265a409626eb2606474c77c88e685902b
This commit is contained in:
commit
9ddd234717
@ -92,6 +92,8 @@ option java_package com.android.server
|
||||
27533 notification_autogrouped (key|3)
|
||||
# notification was removed from an autogroup
|
||||
275534 notification_unautogrouped (key|3)
|
||||
# when a notification is adjusted via assistant
|
||||
27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3)
|
||||
|
||||
# ---------------------------
|
||||
# Watchdog.java
|
||||
|
@ -5397,6 +5397,7 @@ public class NotificationManagerService extends SystemService {
|
||||
== IMPORTANCE_NONE) {
|
||||
cancelNotificationsFromListener(token, new String[]{r.getKey()});
|
||||
} else {
|
||||
r.setPendingLogUpdate(true);
|
||||
needsSort = true;
|
||||
}
|
||||
}
|
||||
@ -8057,64 +8058,151 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
static class NotificationRecordExtractorData {
|
||||
// Class that stores any field in a NotificationRecord that can change via an extractor.
|
||||
// Used to cache previous data used in a sort.
|
||||
int mPosition;
|
||||
int mVisibility;
|
||||
boolean mShowBadge;
|
||||
boolean mAllowBubble;
|
||||
boolean mIsBubble;
|
||||
NotificationChannel mChannel;
|
||||
String mGroupKey;
|
||||
ArrayList<String> mOverridePeople;
|
||||
ArrayList<SnoozeCriterion> mSnoozeCriteria;
|
||||
Integer mUserSentiment;
|
||||
Integer mSuppressVisually;
|
||||
ArrayList<Notification.Action> mSystemSmartActions;
|
||||
ArrayList<CharSequence> mSmartReplies;
|
||||
int mImportance;
|
||||
|
||||
// These fields may not trigger a reranking but diffs here may be logged.
|
||||
float mRankingScore;
|
||||
boolean mIsConversation;
|
||||
|
||||
NotificationRecordExtractorData(int position, int visibility, boolean showBadge,
|
||||
boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey,
|
||||
ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria,
|
||||
Integer userSentiment, Integer suppressVisually,
|
||||
ArrayList<Notification.Action> systemSmartActions,
|
||||
ArrayList<CharSequence> smartReplies, int importance, float rankingScore,
|
||||
boolean isConversation) {
|
||||
mPosition = position;
|
||||
mVisibility = visibility;
|
||||
mShowBadge = showBadge;
|
||||
mAllowBubble = allowBubble;
|
||||
mIsBubble = isBubble;
|
||||
mChannel = channel;
|
||||
mGroupKey = groupKey;
|
||||
mOverridePeople = overridePeople;
|
||||
mSnoozeCriteria = snoozeCriteria;
|
||||
mUserSentiment = userSentiment;
|
||||
mSuppressVisually = suppressVisually;
|
||||
mSystemSmartActions = systemSmartActions;
|
||||
mSmartReplies = smartReplies;
|
||||
mImportance = importance;
|
||||
mRankingScore = rankingScore;
|
||||
mIsConversation = isConversation;
|
||||
}
|
||||
|
||||
// Returns whether the provided NotificationRecord differs from the cached data in any way.
|
||||
// Should be guarded by mNotificationLock; not annotated here as this class is static.
|
||||
boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) {
|
||||
return mPosition != newPosition
|
||||
|| mVisibility != r.getPackageVisibilityOverride()
|
||||
|| mShowBadge != r.canShowBadge()
|
||||
|| mAllowBubble != r.canBubble()
|
||||
|| mIsBubble != r.getNotification().isBubbleNotification()
|
||||
|| !Objects.equals(mChannel, r.getChannel())
|
||||
|| !Objects.equals(mGroupKey, r.getGroupKey())
|
||||
|| !Objects.equals(mOverridePeople, r.getPeopleOverride())
|
||||
|| !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria())
|
||||
|| !Objects.equals(mUserSentiment, r.getUserSentiment())
|
||||
|| !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects())
|
||||
|| !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions())
|
||||
|| !Objects.equals(mSmartReplies, r.getSmartReplies())
|
||||
|| mImportance != r.getImportance();
|
||||
}
|
||||
|
||||
// Returns whether the NotificationRecord has a change from this data for which we should
|
||||
// log an update. This method specifically targets fields that may be changed via
|
||||
// adjustments from the assistant.
|
||||
//
|
||||
// Fields here are the union of things in NotificationRecordLogger.shouldLogReported
|
||||
// and NotificationRecord.applyAdjustments.
|
||||
//
|
||||
// Should be guarded by mNotificationLock; not annotated here as this class is static.
|
||||
boolean hasDiffForLoggingLocked(NotificationRecord r, int newPosition) {
|
||||
return mPosition != newPosition
|
||||
|| !Objects.equals(mChannel, r.getChannel())
|
||||
|| !Objects.equals(mGroupKey, r.getGroupKey())
|
||||
|| !Objects.equals(mOverridePeople, r.getPeopleOverride())
|
||||
|| !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria())
|
||||
|| !Objects.equals(mUserSentiment, r.getUserSentiment())
|
||||
|| !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions())
|
||||
|| !Objects.equals(mSmartReplies, r.getSmartReplies())
|
||||
|| mImportance != r.getImportance()
|
||||
|| !r.rankingScoreMatches(mRankingScore)
|
||||
|| mIsConversation != r.isConversation();
|
||||
}
|
||||
}
|
||||
|
||||
void handleRankingSort() {
|
||||
if (mRankingHelper == null) return;
|
||||
synchronized (mNotificationLock) {
|
||||
final int N = mNotificationList.size();
|
||||
// Any field that can change via one of the extractors needs to be added here.
|
||||
ArrayList<String> orderBefore = new ArrayList<>(N);
|
||||
int[] visibilities = new int[N];
|
||||
boolean[] showBadges = new boolean[N];
|
||||
boolean[] allowBubbles = new boolean[N];
|
||||
boolean[] isBubble = new boolean[N];
|
||||
ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
|
||||
ArrayList<String> groupKeyBefore = new ArrayList<>(N);
|
||||
ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
|
||||
ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
|
||||
ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
|
||||
ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
|
||||
ArrayList<ArrayList<Notification.Action>> systemSmartActionsBefore = new ArrayList<>(N);
|
||||
ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
|
||||
int[] importancesBefore = new int[N];
|
||||
ArrayMap<String, NotificationRecordExtractorData> extractorDataBefore =
|
||||
new ArrayMap<>(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationRecord r = mNotificationList.get(i);
|
||||
orderBefore.add(r.getKey());
|
||||
visibilities[i] = r.getPackageVisibilityOverride();
|
||||
showBadges[i] = r.canShowBadge();
|
||||
allowBubbles[i] = r.canBubble();
|
||||
isBubble[i] = r.getNotification().isBubbleNotification();
|
||||
channelBefore.add(r.getChannel());
|
||||
groupKeyBefore.add(r.getGroupKey());
|
||||
overridePeopleBefore.add(r.getPeopleOverride());
|
||||
snoozeCriteriaBefore.add(r.getSnoozeCriteria());
|
||||
userSentimentBefore.add(r.getUserSentiment());
|
||||
suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
|
||||
systemSmartActionsBefore.add(r.getSystemGeneratedSmartActions());
|
||||
smartRepliesBefore.add(r.getSmartReplies());
|
||||
importancesBefore[i] = r.getImportance();
|
||||
NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData(
|
||||
i,
|
||||
r.getPackageVisibilityOverride(),
|
||||
r.canShowBadge(),
|
||||
r.canBubble(),
|
||||
r.getNotification().isBubbleNotification(),
|
||||
r.getChannel(),
|
||||
r.getGroupKey(),
|
||||
r.getPeopleOverride(),
|
||||
r.getSnoozeCriteria(),
|
||||
r.getUserSentiment(),
|
||||
r.getSuppressedVisualEffects(),
|
||||
r.getSystemGeneratedSmartActions(),
|
||||
r.getSmartReplies(),
|
||||
r.getImportance(),
|
||||
r.getRankingScore(),
|
||||
r.isConversation());
|
||||
extractorDataBefore.put(r.getKey(), extractorData);
|
||||
mRankingHelper.extractSignals(r);
|
||||
}
|
||||
mRankingHelper.sort(mNotificationList);
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationRecord r = mNotificationList.get(i);
|
||||
if (!orderBefore.get(i).equals(r.getKey())
|
||||
|| visibilities[i] != r.getPackageVisibilityOverride()
|
||||
|| showBadges[i] != r.canShowBadge()
|
||||
|| allowBubbles[i] != r.canBubble()
|
||||
|| isBubble[i] != r.getNotification().isBubbleNotification()
|
||||
|| !Objects.equals(channelBefore.get(i), r.getChannel())
|
||||
|| !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
|
||||
|| !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
|
||||
|| !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
|
||||
|| !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
|
||||
|| !Objects.equals(suppressVisuallyBefore.get(i),
|
||||
r.getSuppressedVisualEffects())
|
||||
|| !Objects.equals(systemSmartActionsBefore.get(i),
|
||||
r.getSystemGeneratedSmartActions())
|
||||
|| !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())
|
||||
|| importancesBefore[i] != r.getImportance()) {
|
||||
if (!extractorDataBefore.containsKey(r.getKey())) {
|
||||
// This shouldn't happen given that we just built this with all the
|
||||
// notifications, but check just to be safe.
|
||||
continue;
|
||||
}
|
||||
if (extractorDataBefore.get(r.getKey()).hasDiffForRankingLocked(r, i)) {
|
||||
mHandler.scheduleSendRankingUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
// If this notification is one for which we wanted to log an update, and
|
||||
// sufficient relevant bits are different, log update.
|
||||
if (r.hasPendingLogUpdate()) {
|
||||
// We need to acquire the previous data associated with this specific
|
||||
// notification, as the one at the current index may be unrelated if
|
||||
// notification order has changed.
|
||||
NotificationRecordExtractorData prevData = extractorDataBefore.get(r.getKey());
|
||||
if (prevData.hasDiffForLoggingLocked(r, i)) {
|
||||
mNotificationRecordLogger.logNotificationAdjusted(r, i, 0,
|
||||
getGroupInstanceId(r.getSbn().getGroupKey()));
|
||||
}
|
||||
|
||||
// Remove whether there was a diff or not; we've sorted the key, so if it
|
||||
// turns out there was nothing to log, that's fine too.
|
||||
r.setPendingLogUpdate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,6 +200,10 @@ public final class NotificationRecord {
|
||||
private boolean mIsAppImportanceLocked;
|
||||
private ArraySet<Uri> mGrantableUris;
|
||||
|
||||
// Whether this notification record should have an update logged the next time notifications
|
||||
// are sorted.
|
||||
private boolean mPendingLogUpdate = false;
|
||||
|
||||
public NotificationRecord(Context context, StatusBarNotification sbn,
|
||||
NotificationChannel channel) {
|
||||
this.sbn = sbn;
|
||||
@ -648,17 +652,23 @@ public final class NotificationRecord {
|
||||
final ArrayList<String> people =
|
||||
adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
|
||||
setPeopleOverride(people);
|
||||
EventLogTags.writeNotificationAdjusted(
|
||||
getKey(), Adjustment.KEY_PEOPLE, people.toString());
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
|
||||
final ArrayList<SnoozeCriterion> snoozeCriterionList =
|
||||
adjustment.getSignals().getParcelableArrayList(
|
||||
Adjustment.KEY_SNOOZE_CRITERIA);
|
||||
setSnoozeCriteria(snoozeCriterionList);
|
||||
EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_SNOOZE_CRITERIA,
|
||||
snoozeCriterionList.toString());
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
|
||||
final String groupOverrideKey =
|
||||
adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
|
||||
setOverrideGroupKey(groupOverrideKey);
|
||||
EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_GROUP_KEY,
|
||||
groupOverrideKey);
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
|
||||
// Only allow user sentiment update from assistant if user hasn't already
|
||||
@ -667,27 +677,42 @@ public final class NotificationRecord {
|
||||
&& (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
|
||||
setUserSentiment(adjustment.getSignals().getInt(
|
||||
Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
|
||||
EventLogTags.writeNotificationAdjusted(getKey(),
|
||||
Adjustment.KEY_USER_SENTIMENT,
|
||||
Integer.toString(getUserSentiment()));
|
||||
}
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_CONTEXTUAL_ACTIONS)) {
|
||||
setSystemGeneratedSmartActions(
|
||||
signals.getParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS));
|
||||
EventLogTags.writeNotificationAdjusted(getKey(),
|
||||
Adjustment.KEY_CONTEXTUAL_ACTIONS,
|
||||
getSystemGeneratedSmartActions().toString());
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_TEXT_REPLIES)) {
|
||||
setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES));
|
||||
EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_TEXT_REPLIES,
|
||||
getSmartReplies().toString());
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
|
||||
int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
|
||||
importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
|
||||
importance = Math.min(IMPORTANCE_HIGH, importance);
|
||||
setAssistantImportance(importance);
|
||||
EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_IMPORTANCE,
|
||||
Integer.toString(importance));
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_RANKING_SCORE)) {
|
||||
mRankingScore = signals.getFloat(Adjustment.KEY_RANKING_SCORE);
|
||||
EventLogTags.writeNotificationAdjusted(getKey(), Adjustment.KEY_RANKING_SCORE,
|
||||
Float.toString(mRankingScore));
|
||||
}
|
||||
if (signals.containsKey(Adjustment.KEY_NOT_CONVERSATION)) {
|
||||
mIsNotConversationOverride = signals.getBoolean(
|
||||
Adjustment.KEY_NOT_CONVERSATION);
|
||||
EventLogTags.writeNotificationAdjusted(getKey(),
|
||||
Adjustment.KEY_NOT_CONVERSATION,
|
||||
Boolean.toString(mIsNotConversationOverride));
|
||||
}
|
||||
if (!signals.isEmpty() && adjustment.getIssuer() != null) {
|
||||
mAdjustmentIssuer = adjustment.getIssuer();
|
||||
@ -1478,6 +1503,24 @@ public final class NotificationRecord {
|
||||
return sbn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this record's ranking score is approximately equal to otherScore
|
||||
* (the difference must be within 0.0001).
|
||||
*/
|
||||
public boolean rankingScoreMatches(float otherScore) {
|
||||
return Math.abs(mRankingScore - otherScore) < 0.0001;
|
||||
}
|
||||
|
||||
protected void setPendingLogUpdate(boolean pendingLogUpdate) {
|
||||
mPendingLogUpdate = pendingLogUpdate;
|
||||
}
|
||||
|
||||
// If a caller of this function subsequently logs the update, they should also call
|
||||
// setPendingLogUpdate to false to make sure other callers don't also do so.
|
||||
protected boolean hasPendingLogUpdate() {
|
||||
return mPendingLogUpdate;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static final class Light {
|
||||
public final int color;
|
||||
|
@ -58,6 +58,20 @@ public interface NotificationRecordLogger {
|
||||
int position, int buzzBeepBlink,
|
||||
InstanceId groupId);
|
||||
|
||||
/**
|
||||
* Logs a NotificationReported atom reflecting an adjustment to a notification.
|
||||
* Unlike maybeLogNotificationPosted, this method is guaranteed to log a notification update,
|
||||
* so the caller must take responsibility for checking that that logging update is necessary,
|
||||
* and that the notification is meaningfully changed.
|
||||
* @param r The NotificationRecord. If null, no action is taken.
|
||||
* @param position The position at which this notification is ranked.
|
||||
* @param buzzBeepBlink Logging code reflecting whether this notification alerted the user.
|
||||
* @param groupId The instance Id of the group summary notification, or null.
|
||||
*/
|
||||
void logNotificationAdjusted(@Nullable NotificationRecord r,
|
||||
int position, int buzzBeepBlink,
|
||||
InstanceId groupId);
|
||||
|
||||
/**
|
||||
* Logs a notification cancel / dismiss event using UiEventReported (event ids from the
|
||||
* NotificationCancelledEvents enum).
|
||||
@ -96,7 +110,9 @@ public interface NotificationRecordLogger {
|
||||
@UiEvent(doc = "New notification enqueued to post")
|
||||
NOTIFICATION_POSTED(162),
|
||||
@UiEvent(doc = "Notification substantially updated, or alerted again.")
|
||||
NOTIFICATION_UPDATED(163);
|
||||
NOTIFICATION_UPDATED(163),
|
||||
@UiEvent(doc = "Notification adjusted by assistant.")
|
||||
NOTIFICATION_ADJUSTED(908);
|
||||
|
||||
private final int mId;
|
||||
NotificationReportedEvent(int id) {
|
||||
@ -349,7 +365,8 @@ public interface NotificationRecordLogger {
|
||||
&& Objects.equals(r.getSbn().getNotification().category,
|
||||
old.getSbn().getNotification().category)
|
||||
&& (r.getImportance() == old.getImportance())
|
||||
&& (getLoggingImportance(r) == getLoggingImportance(old)));
|
||||
&& (getLoggingImportance(r) == getLoggingImportance(old))
|
||||
&& r.rankingScoreMatches(old.getRankingScore()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.notification;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import com.android.internal.logging.InstanceId;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.logging.UiEventLoggerImpl;
|
||||
@ -37,33 +39,49 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
|
||||
if (!p.shouldLogReported(buzzBeepBlink)) {
|
||||
return;
|
||||
}
|
||||
writeNotificationReportedAtom(p, NotificationReportedEvent.fromRecordPair(p),
|
||||
position, buzzBeepBlink, groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logNotificationAdjusted(@Nullable NotificationRecord r,
|
||||
int position, int buzzBeepBlink,
|
||||
InstanceId groupId) {
|
||||
NotificationRecordPair p = new NotificationRecordPair(r, null);
|
||||
writeNotificationReportedAtom(p, NotificationReportedEvent.NOTIFICATION_ADJUSTED,
|
||||
position, buzzBeepBlink, groupId);
|
||||
}
|
||||
|
||||
private void writeNotificationReportedAtom(NotificationRecordPair p,
|
||||
NotificationReportedEvent eventType, int position, int buzzBeepBlink,
|
||||
InstanceId groupId) {
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
|
||||
/* int32 event_id = 1 */ NotificationReportedEvent.fromRecordPair(p).getId(),
|
||||
/* int32 uid = 2 */ r.getUid(),
|
||||
/* string package_name = 3 */ r.getSbn().getPackageName(),
|
||||
/* int32 event_id = 1 */ eventType.getId(),
|
||||
/* int32 uid = 2 */ p.r.getUid(),
|
||||
/* string package_name = 3 */ p.r.getSbn().getPackageName(),
|
||||
/* int32 instance_id = 4 */ p.getInstanceId(),
|
||||
/* int32 notification_id_hash = 5 */ p.getNotificationIdHash(),
|
||||
/* int32 channel_id_hash = 6 */ p.getChannelIdHash(),
|
||||
/* string group_id_hash = 7 */ p.getGroupIdHash(),
|
||||
/* int32 group_instance_id = 8 */ (groupId == null) ? 0 : groupId.getId(),
|
||||
/* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(),
|
||||
/* string category = 10 */ r.getSbn().getNotification().category,
|
||||
/* bool is_group_summary = 9 */ p.r.getSbn().getNotification().isGroupSummary(),
|
||||
/* string category = 10 */ p.r.getSbn().getNotification().category,
|
||||
/* int32 style = 11 */ p.getStyle(),
|
||||
/* int32 num_people = 12 */ p.getNumPeople(),
|
||||
/* int32 position = 13 */ position,
|
||||
/* android.stats.sysui.NotificationImportance importance = 14 */
|
||||
NotificationRecordLogger.getLoggingImportance(r),
|
||||
NotificationRecordLogger.getLoggingImportance(p.r),
|
||||
/* int32 alerting = 15 */ buzzBeepBlink,
|
||||
/* NotificationImportanceExplanation importance_source = 16 */
|
||||
r.getImportanceExplanationCode(),
|
||||
p.r.getImportanceExplanationCode(),
|
||||
/* android.stats.sysui.NotificationImportance importance_initial = 17 */
|
||||
r.getInitialImportance(),
|
||||
p.r.getInitialImportance(),
|
||||
/* NotificationImportanceExplanation importance_initial_source = 18 */
|
||||
r.getInitialImportanceExplanationCode(),
|
||||
p.r.getInitialImportanceExplanationCode(),
|
||||
/* android.stats.sysui.NotificationImportance importance_asst = 19 */
|
||||
r.getAssistantImportance(),
|
||||
p.r.getAssistantImportance(),
|
||||
/* int32 assistant_hash = 20 */ p.getAssistantHash(),
|
||||
/* float assistant_ranking_score = 21 */ r.getRankingScore()
|
||||
/* float assistant_ranking_score = 21 */ p.r.getRankingScore()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4010,6 +4010,80 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
verify(mRankingHandler, times(1)).requestSort();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyAdjustmentsLogged() throws Exception {
|
||||
NotificationManagerService.WorkerHandler handler = mock(
|
||||
NotificationManagerService.WorkerHandler.class);
|
||||
mService.setHandler(handler);
|
||||
when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
|
||||
|
||||
// Set up notifications that will be adjusted
|
||||
final NotificationRecord r1 = generateNotificationRecord(
|
||||
mTestNotificationChannel, 1, null, true);
|
||||
r1.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
|
||||
mService.addNotification(r1);
|
||||
final NotificationRecord r2 = generateNotificationRecord(
|
||||
mTestNotificationChannel, 2, null, true);
|
||||
r2.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
|
||||
mService.addNotification(r2);
|
||||
|
||||
// Third notification that's NOT adjusted, just to make sure that doesn't get spuriously
|
||||
// logged.
|
||||
final NotificationRecord r3 = generateNotificationRecord(
|
||||
mTestNotificationChannel, 3, null, true);
|
||||
r3.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
|
||||
mService.addNotification(r3);
|
||||
|
||||
List<Adjustment> adjustments = new ArrayList<>();
|
||||
|
||||
// Test an adjustment that's associated with a ranking change and one that's not
|
||||
Bundle signals1 = new Bundle();
|
||||
signals1.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_HIGH);
|
||||
Adjustment adjustment1 = new Adjustment(
|
||||
r1.getSbn().getPackageName(), r1.getKey(), signals1, "",
|
||||
r1.getUser().getIdentifier());
|
||||
adjustments.add(adjustment1);
|
||||
|
||||
// This one wouldn't trigger a ranking change, but should still trigger a log.
|
||||
Bundle signals2 = new Bundle();
|
||||
signals2.putFloat(Adjustment.KEY_RANKING_SCORE, -0.5f);
|
||||
Adjustment adjustment2 = new Adjustment(
|
||||
r2.getSbn().getPackageName(), r2.getKey(), signals2, "",
|
||||
r2.getUser().getIdentifier());
|
||||
adjustments.add(adjustment2);
|
||||
|
||||
mBinderService.applyAdjustmentsFromAssistant(null, adjustments);
|
||||
verify(mRankingHandler, times(1)).requestSort();
|
||||
|
||||
// Actually apply the adjustments & recalculate importance when run
|
||||
doAnswer(invocationOnMock -> {
|
||||
((NotificationRecord) invocationOnMock.getArguments()[0])
|
||||
.applyAdjustments();
|
||||
((NotificationRecord) invocationOnMock.getArguments()[0])
|
||||
.calculateImportance();
|
||||
return null;
|
||||
}).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
|
||||
|
||||
// Now make sure that when the sort happens, we actually log the changes.
|
||||
mService.handleRankingSort();
|
||||
|
||||
// Even though the ranking score change is not meant to trigger a ranking update,
|
||||
// during this process the package visibility & canShowBadge values are changing
|
||||
// in all notifications, so all 3 seem to trigger a ranking change. Here we check instead
|
||||
// that scheduleSendRankingUpdate is sent and that the relevant fields have been changed
|
||||
// accordingly to confirm the adjustments happened to the 2 relevant notifications.
|
||||
verify(handler, times(3)).scheduleSendRankingUpdate();
|
||||
assertEquals(IMPORTANCE_HIGH, r1.getImportance());
|
||||
assertTrue(r2.rankingScoreMatches(-0.5f));
|
||||
assertEquals(2, mNotificationRecordLogger.numCalls());
|
||||
assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED,
|
||||
mNotificationRecordLogger.event(0));
|
||||
assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED,
|
||||
mNotificationRecordLogger.event(1));
|
||||
assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
|
||||
assertEquals(2, mNotificationRecordLogger.get(1).getInstanceId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnqueuedAdjustmentAppliesAdjustments() throws Exception {
|
||||
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
|
||||
|
@ -45,6 +45,15 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger {
|
||||
groupInstanceId = groupId;
|
||||
}
|
||||
|
||||
CallRecord(NotificationRecord r, int position, int buzzBeepBlink, InstanceId groupId) {
|
||||
super(r, null);
|
||||
this.position = position;
|
||||
this.buzzBeepBlink = buzzBeepBlink;
|
||||
wasLogged = true;
|
||||
event = NotificationReportedEvent.NOTIFICATION_ADJUSTED;
|
||||
groupInstanceId = groupId;
|
||||
}
|
||||
|
||||
CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
|
||||
super(r, null);
|
||||
wasLogged = true;
|
||||
@ -74,6 +83,12 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger {
|
||||
mCalls.add(new CallRecord(r, old, position, buzzBeepBlink, groupId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logNotificationAdjusted(NotificationRecord r, int position, int buzzBeepBlink,
|
||||
InstanceId groupId) {
|
||||
mCalls.add(new CallRecord(r, position, buzzBeepBlink, groupId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
|
||||
mCalls.add(new CallRecord(r, event));
|
||||
|
Loading…
x
Reference in New Issue
Block a user