Merge "Log when notifications in the RankingMap are not in the Collection" into tm-qpr-dev
This commit is contained in:
@@ -88,9 +88,9 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.En
|
|||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.EntryUpdatedEvent;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.EntryUpdatedEvent;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.InitEntryEvent;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.InitEntryEvent;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater;
|
||||||
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionInconsistencyTracker;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLoggerKt;
|
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifEvent;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifEvent;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
|
||||||
@@ -112,7 +112,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -151,6 +150,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
|
|||||||
private final Executor mBgExecutor;
|
private final Executor mBgExecutor;
|
||||||
private final LogBufferEulogizer mEulogizer;
|
private final LogBufferEulogizer mEulogizer;
|
||||||
private final DumpManager mDumpManager;
|
private final DumpManager mDumpManager;
|
||||||
|
private final NotifCollectionInconsistencyTracker mInconsistencyTracker;
|
||||||
|
|
||||||
private final Map<String, NotificationEntry> mNotificationSet = new ArrayMap<>();
|
private final Map<String, NotificationEntry> mNotificationSet = new ArrayMap<>();
|
||||||
private final Collection<NotificationEntry> mReadOnlyNotificationSet =
|
private final Collection<NotificationEntry> mReadOnlyNotificationSet =
|
||||||
@@ -162,7 +162,6 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
|
|||||||
private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
|
private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
|
||||||
private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
|
private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
|
||||||
|
|
||||||
private Set<String> mNotificationsWithoutRankings = Collections.emptySet();
|
|
||||||
|
|
||||||
private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
|
private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
|
||||||
|
|
||||||
@@ -188,6 +187,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
|
|||||||
mBgExecutor = bgExecutor;
|
mBgExecutor = bgExecutor;
|
||||||
mEulogizer = logBufferEulogizer;
|
mEulogizer = logBufferEulogizer;
|
||||||
mDumpManager = dumpManager;
|
mDumpManager = dumpManager;
|
||||||
|
mInconsistencyTracker = new NotifCollectionInconsistencyTracker(mLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initializes the NotifCollection and registers it to receive notification events. */
|
/** Initializes the NotifCollection and registers it to receive notification events. */
|
||||||
@@ -199,6 +199,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
|
|||||||
mAttached = true;
|
mAttached = true;
|
||||||
mDumpManager.registerDumpable(TAG, this);
|
mDumpManager.registerDumpable(TAG, this);
|
||||||
groupCoalescer.setNotificationHandler(mNotifHandler);
|
groupCoalescer.setNotificationHandler(mNotifHandler);
|
||||||
|
mInconsistencyTracker.attach(mNotificationSet::keySet, groupCoalescer::getCoalescedKeySet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -598,14 +599,9 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NotifCollectionLoggerKt.maybeLogInconsistentRankings(
|
|
||||||
mLogger,
|
mInconsistencyTracker.logNewMissingNotifications(rankingMap);
|
||||||
mNotificationsWithoutRankings,
|
mInconsistencyTracker.logNewInconsistentRankings(currentEntriesWithoutRankings, rankingMap);
|
||||||
currentEntriesWithoutRankings,
|
|
||||||
rankingMap
|
|
||||||
);
|
|
||||||
mNotificationsWithoutRankings = currentEntriesWithoutRankings == null
|
|
||||||
? Collections.emptySet() : currentEntriesWithoutRankings.keySet();
|
|
||||||
if (currentEntriesWithoutRankings != null && mNotifPipelineFlags.removeUnrankedNotifs()) {
|
if (currentEntriesWithoutRankings != null && mNotifPipelineFlags.removeUnrankedNotifs()) {
|
||||||
for (NotificationEntry entry : currentEntriesWithoutRankings.values()) {
|
for (NotificationEntry entry : currentEntriesWithoutRankings.values()) {
|
||||||
entry.mCancellationReason = REASON_UNKNOWN;
|
entry.mCancellationReason = REASON_UNKNOWN;
|
||||||
@@ -864,10 +860,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
|
|||||||
true,
|
true,
|
||||||
"\t\t"));
|
"\t\t"));
|
||||||
|
|
||||||
pw.println("\n\tmNotificationsWithoutRankings: " + mNotificationsWithoutRankings.size());
|
mInconsistencyTracker.dump(pw);
|
||||||
for (String key : mNotificationsWithoutRankings) {
|
|
||||||
pw.println("\t * : " + key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -39,9 +39,11 @@ import com.android.systemui.util.time.SystemClock;
|
|||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -118,6 +120,11 @@ public class GroupCoalescer implements Dumpable, PipelineDumpable {
|
|||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return the set of notification keys currently in the coalescer */
|
||||||
|
public Set<String> getCoalescedKeySet() {
|
||||||
|
return Collections.unmodifiableSet(mCoalescedEvents.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
private final NotificationHandler mListener = new NotificationHandler() {
|
private final NotificationHandler mListener = new NotificationHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
|
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.statusbar.notification.collection.notifcollection
|
||||||
|
|
||||||
|
import android.service.notification.NotificationListenerService.RankingMap
|
||||||
|
import android.util.ArrayMap
|
||||||
|
import com.android.internal.annotations.VisibleForTesting
|
||||||
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||||
|
import java.io.PrintWriter
|
||||||
|
|
||||||
|
class NotifCollectionInconsistencyTracker(val logger: NotifCollectionLogger) {
|
||||||
|
fun attach(
|
||||||
|
collectedKeySetAccessor: () -> Set<String>,
|
||||||
|
coalescedKeySetAccessor: () -> Set<String>,
|
||||||
|
) {
|
||||||
|
if (attached) {
|
||||||
|
throw RuntimeException("attach() called twice")
|
||||||
|
}
|
||||||
|
attached = true
|
||||||
|
this.collectedKeySetAccessor = collectedKeySetAccessor
|
||||||
|
this.coalescedKeySetAccessor = coalescedKeySetAccessor
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logNewMissingNotifications(rankingMap: RankingMap) {
|
||||||
|
val currentCollectedKeys = collectedKeySetAccessor()
|
||||||
|
val currentCoalescedKeys = coalescedKeySetAccessor()
|
||||||
|
val newMissingNotifications = rankingMap.orderedKeys.asSequence()
|
||||||
|
.filter { it !in currentCollectedKeys }
|
||||||
|
.filter { it !in currentCoalescedKeys }
|
||||||
|
.toSet()
|
||||||
|
maybeLogMissingNotifications(missingNotifications, newMissingNotifications)
|
||||||
|
missingNotifications = newMissingNotifications
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun maybeLogMissingNotifications(
|
||||||
|
oldMissingKeys: Set<String>,
|
||||||
|
newMissingKeys: Set<String>,
|
||||||
|
) {
|
||||||
|
if (oldMissingKeys.isEmpty() && newMissingKeys.isEmpty()) return
|
||||||
|
if (oldMissingKeys == newMissingKeys) return
|
||||||
|
(oldMissingKeys - newMissingKeys).sorted().let { justFound ->
|
||||||
|
if (justFound.isNotEmpty()) {
|
||||||
|
logger.logFoundNotifications(justFound, newMissingKeys.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(newMissingKeys - oldMissingKeys).sorted().let { goneMissing ->
|
||||||
|
if (goneMissing.isNotEmpty()) {
|
||||||
|
logger.logMissingNotifications(goneMissing, newMissingKeys.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logNewInconsistentRankings(
|
||||||
|
currentEntriesWithoutRankings: ArrayMap<String, NotificationEntry>?,
|
||||||
|
rankingMap: RankingMap,
|
||||||
|
) {
|
||||||
|
maybeLogInconsistentRankings(
|
||||||
|
notificationsWithoutRankings,
|
||||||
|
currentEntriesWithoutRankings ?: emptyMap(),
|
||||||
|
rankingMap
|
||||||
|
)
|
||||||
|
notificationsWithoutRankings = currentEntriesWithoutRankings?.keys ?: emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun maybeLogInconsistentRankings(
|
||||||
|
oldKeysWithoutRankings: Set<String>,
|
||||||
|
newEntriesWithoutRankings: Map<String, NotificationEntry>,
|
||||||
|
rankingMap: RankingMap,
|
||||||
|
) {
|
||||||
|
if (oldKeysWithoutRankings.isEmpty() && newEntriesWithoutRankings.isEmpty()) return
|
||||||
|
if (oldKeysWithoutRankings == newEntriesWithoutRankings.keys) return
|
||||||
|
val newlyConsistent: List<String> = oldKeysWithoutRankings
|
||||||
|
.mapNotNull { key ->
|
||||||
|
key.takeIf { key !in newEntriesWithoutRankings }
|
||||||
|
.takeIf { key in rankingMap.orderedKeys }
|
||||||
|
}.sorted()
|
||||||
|
if (newlyConsistent.isNotEmpty()) {
|
||||||
|
val totalInconsistent: Int = newEntriesWithoutRankings.size
|
||||||
|
logger.logRecoveredRankings(newlyConsistent, totalInconsistent)
|
||||||
|
}
|
||||||
|
val newlyInconsistent: List<NotificationEntry> = newEntriesWithoutRankings
|
||||||
|
.mapNotNull { (key, entry) ->
|
||||||
|
entry.takeIf { key !in oldKeysWithoutRankings }
|
||||||
|
}.sortedBy { it.key }
|
||||||
|
if (newlyInconsistent.isNotEmpty()) {
|
||||||
|
val totalInconsistent: Int = newEntriesWithoutRankings.size
|
||||||
|
logger.logMissingRankings(newlyInconsistent, totalInconsistent, rankingMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dump(pw: PrintWriter) {
|
||||||
|
pw.println("notificationsWithoutRankings: ${notificationsWithoutRankings.size}")
|
||||||
|
for (key in notificationsWithoutRankings) {
|
||||||
|
pw.println("\t * : $key")
|
||||||
|
}
|
||||||
|
pw.println("missingNotifications: ${missingNotifications.size}")
|
||||||
|
for (key in missingNotifications) {
|
||||||
|
pw.println("\t * : $key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var attached: Boolean = false
|
||||||
|
private lateinit var collectedKeySetAccessor: (() -> Set<String>)
|
||||||
|
private lateinit var coalescedKeySetAccessor: (() -> Set<String>)
|
||||||
|
private var notificationsWithoutRankings = emptySet<String>()
|
||||||
|
private var missingNotifications = emptySet<String>()
|
||||||
|
}
|
||||||
@@ -215,8 +215,9 @@ class NotifCollectionLogger @Inject constructor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun logRecoveredRankings(newlyConsistentKeys: List<String>) {
|
fun logRecoveredRankings(newlyConsistentKeys: List<String>, totalInconsistent: Int) {
|
||||||
buffer.log(TAG, INFO, {
|
buffer.log(TAG, INFO, {
|
||||||
|
int1 = totalInconsistent
|
||||||
int1 = newlyConsistentKeys.size
|
int1 = newlyConsistentKeys.size
|
||||||
str1 = newlyConsistentKeys.joinToString { logKey(it) ?: "null" }
|
str1 = newlyConsistentKeys.joinToString { logKey(it) ?: "null" }
|
||||||
}, {
|
}, {
|
||||||
@@ -224,6 +225,32 @@ class NotifCollectionLogger @Inject constructor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun logMissingNotifications(
|
||||||
|
newlyMissingKeys: List<String>,
|
||||||
|
totalMissing: Int,
|
||||||
|
) {
|
||||||
|
buffer.log(TAG, WARNING, {
|
||||||
|
int1 = totalMissing
|
||||||
|
int2 = newlyMissingKeys.size
|
||||||
|
str1 = newlyMissingKeys.joinToString { logKey(it) ?: "null" }
|
||||||
|
}, {
|
||||||
|
"Collection missing $int1 entries in ranking update. Just lost $int2: $str1"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logFoundNotifications(
|
||||||
|
newlyFoundKeys: List<String>,
|
||||||
|
totalMissing: Int,
|
||||||
|
) {
|
||||||
|
buffer.log(TAG, INFO, {
|
||||||
|
int1 = totalMissing
|
||||||
|
int2 = newlyFoundKeys.size
|
||||||
|
str1 = newlyFoundKeys.joinToString { logKey(it) ?: "null" }
|
||||||
|
}, {
|
||||||
|
"Collection missing $int1 entries in ranking update. Just found $int2: $str1"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fun logRemoteExceptionOnNotificationClear(entry: NotificationEntry, e: RemoteException) {
|
fun logRemoteExceptionOnNotificationClear(entry: NotificationEntry, e: RemoteException) {
|
||||||
buffer.log(TAG, WTF, {
|
buffer.log(TAG, WTF, {
|
||||||
str1 = entry.logKey
|
str1 = entry.logKey
|
||||||
@@ -362,29 +389,4 @@ class NotifCollectionLogger @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun maybeLogInconsistentRankings(
|
|
||||||
logger: NotifCollectionLogger,
|
|
||||||
oldKeysWithoutRankings: Set<String>,
|
|
||||||
newEntriesWithoutRankings: Map<String, NotificationEntry>?,
|
|
||||||
rankingMap: RankingMap
|
|
||||||
) {
|
|
||||||
if (oldKeysWithoutRankings.isEmpty() && newEntriesWithoutRankings == null) return
|
|
||||||
val newlyConsistent: List<String> = oldKeysWithoutRankings
|
|
||||||
.mapNotNull { key ->
|
|
||||||
key.takeIf { key !in (newEntriesWithoutRankings ?: emptyMap()) }
|
|
||||||
.takeIf { key in rankingMap.orderedKeys }
|
|
||||||
}.sorted()
|
|
||||||
if (newlyConsistent.isNotEmpty()) {
|
|
||||||
logger.logRecoveredRankings(newlyConsistent)
|
|
||||||
}
|
|
||||||
val newlyInconsistent: List<NotificationEntry> = newEntriesWithoutRankings
|
|
||||||
?.mapNotNull { (key, entry) ->
|
|
||||||
entry.takeIf { key !in oldKeysWithoutRankings }
|
|
||||||
}?.sortedBy { it.key } ?: emptyList()
|
|
||||||
if (newlyInconsistent.isNotEmpty()) {
|
|
||||||
val totalInconsistent: Int = newEntriesWithoutRankings?.size ?: 0
|
|
||||||
logger.logMissingRankings(newlyInconsistent, totalInconsistent, rankingMap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val TAG = "NotifCollection"
|
private const val TAG = "NotifCollection"
|
||||||
|
|||||||
@@ -1518,7 +1518,7 @@ public class NotifCollectionTest extends SysuiTestCase {
|
|||||||
verify(mCollectionListener).onRankingApplied();
|
verify(mCollectionListener).onRankingApplied();
|
||||||
verifyNoMoreInteractions(mCollectionListener);
|
verifyNoMoreInteractions(mCollectionListener);
|
||||||
verify(mLogger).logMissingRankings(eq(List.of(entry1)), eq(1), any());
|
verify(mLogger).logMissingRankings(eq(List.of(entry1)), eq(1), any());
|
||||||
verify(mLogger, never()).logRecoveredRankings(any());
|
verify(mLogger, never()).logRecoveredRankings(any(), anyInt());
|
||||||
clearInvocations(mCollectionListener, mLogger);
|
clearInvocations(mCollectionListener, mLogger);
|
||||||
|
|
||||||
// WHEN a ranking update includes key1 again
|
// WHEN a ranking update includes key1 again
|
||||||
@@ -1530,7 +1530,7 @@ public class NotifCollectionTest extends SysuiTestCase {
|
|||||||
verify(mCollectionListener).onRankingApplied();
|
verify(mCollectionListener).onRankingApplied();
|
||||||
verifyNoMoreInteractions(mCollectionListener);
|
verifyNoMoreInteractions(mCollectionListener);
|
||||||
verify(mLogger, never()).logMissingRankings(any(), anyInt(), any());
|
verify(mLogger, never()).logMissingRankings(any(), anyInt(), any());
|
||||||
verify(mLogger).logRecoveredRankings(eq(List.of(key1)));
|
verify(mLogger).logRecoveredRankings(eq(List.of(key1)), eq(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1556,7 +1556,7 @@ public class NotifCollectionTest extends SysuiTestCase {
|
|||||||
verify(mCollectionListener).onRankingApplied();
|
verify(mCollectionListener).onRankingApplied();
|
||||||
verifyNoMoreInteractions(mCollectionListener);
|
verifyNoMoreInteractions(mCollectionListener);
|
||||||
verify(mLogger).logMissingRankings(eq(List.of(entry1)), eq(1), any());
|
verify(mLogger).logMissingRankings(eq(List.of(entry1)), eq(1), any());
|
||||||
verify(mLogger, never()).logRecoveredRankings(any());
|
verify(mLogger, never()).logRecoveredRankings(any(), anyInt());
|
||||||
clearInvocations(mCollectionListener, mLogger);
|
clearInvocations(mCollectionListener, mLogger);
|
||||||
|
|
||||||
// WHEN a ranking update includes key1 again
|
// WHEN a ranking update includes key1 again
|
||||||
@@ -1568,7 +1568,7 @@ public class NotifCollectionTest extends SysuiTestCase {
|
|||||||
verify(mCollectionListener).onRankingApplied();
|
verify(mCollectionListener).onRankingApplied();
|
||||||
verifyNoMoreInteractions(mCollectionListener);
|
verifyNoMoreInteractions(mCollectionListener);
|
||||||
verify(mLogger, never()).logMissingRankings(any(), anyInt(), any());
|
verify(mLogger, never()).logMissingRankings(any(), anyInt(), any());
|
||||||
verify(mLogger).logRecoveredRankings(eq(List.of(key1)));
|
verify(mLogger).logRecoveredRankings(eq(List.of(key1)), eq(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.statusbar.notification.collection.notifcollection
|
||||||
|
|
||||||
|
import android.service.notification.NotificationListenerService.RankingMap
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.testing.TestableLooper.RunWithLooper
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||||
|
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
|
||||||
|
import com.android.systemui.util.mockito.eq
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mockito.clearInvocations
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@RunWithLooper
|
||||||
|
class NotifCollectionInconsistencyTrackerTest : SysuiTestCase() {
|
||||||
|
private val logger: NotifCollectionLogger = mock()
|
||||||
|
private val entry1: NotificationEntry = NotificationEntryBuilder().setId(1).build()
|
||||||
|
private val entry2: NotificationEntry = NotificationEntryBuilder().setId(2).build()
|
||||||
|
private val collectionSet = mutableSetOf<String>()
|
||||||
|
private val coalescedSet = mutableSetOf<String>()
|
||||||
|
private lateinit var inconsistencyTracker: NotifCollectionInconsistencyTracker
|
||||||
|
|
||||||
|
private fun mapOfEntries(vararg entries: NotificationEntry): Map<String, NotificationEntry> =
|
||||||
|
entries.associateBy { it.key }
|
||||||
|
|
||||||
|
private fun rankingMapOf(vararg entries: NotificationEntry): RankingMap =
|
||||||
|
RankingMap(entries.map { it.ranking }.toTypedArray())
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
inconsistencyTracker = NotifCollectionInconsistencyTracker(logger)
|
||||||
|
inconsistencyTracker.attach({ collectionSet }, { coalescedSet })
|
||||||
|
collectionSet.clear()
|
||||||
|
coalescedSet.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogInconsistentRankings_logsNewlyInconsistentRanking() {
|
||||||
|
val rankingMap = rankingMapOf(entry1)
|
||||||
|
inconsistencyTracker.maybeLogInconsistentRankings(
|
||||||
|
oldKeysWithoutRankings = emptySet(),
|
||||||
|
newEntriesWithoutRankings = mapOfEntries(entry2),
|
||||||
|
rankingMap = rankingMap
|
||||||
|
)
|
||||||
|
verify(logger).logMissingRankings(
|
||||||
|
newlyInconsistentEntries = eq(listOf(entry2)),
|
||||||
|
totalInconsistent = eq(1),
|
||||||
|
rankingMap = eq(rankingMap),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogInconsistentRankings_doesNotLogAlreadyInconsistentRanking() {
|
||||||
|
inconsistencyTracker.maybeLogInconsistentRankings(
|
||||||
|
oldKeysWithoutRankings = setOf(entry2.key),
|
||||||
|
newEntriesWithoutRankings = mapOfEntries(entry2),
|
||||||
|
rankingMap = rankingMapOf(entry1)
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogInconsistentRankings_logsWhenRankingIsAdded() {
|
||||||
|
inconsistencyTracker.maybeLogInconsistentRankings(
|
||||||
|
oldKeysWithoutRankings = setOf(entry2.key),
|
||||||
|
newEntriesWithoutRankings = mapOfEntries(),
|
||||||
|
rankingMap = rankingMapOf(entry1, entry2)
|
||||||
|
)
|
||||||
|
verify(logger).logRecoveredRankings(
|
||||||
|
newlyConsistentKeys = eq(listOf(entry2.key)),
|
||||||
|
totalInconsistent = eq(0),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogInconsistentRankings_doesNotLogsWhenEntryIsRemoved() {
|
||||||
|
inconsistencyTracker.maybeLogInconsistentRankings(
|
||||||
|
oldKeysWithoutRankings = setOf(entry2.key),
|
||||||
|
newEntriesWithoutRankings = mapOfEntries(),
|
||||||
|
rankingMap = rankingMapOf(entry1)
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogMissingNotifications_logsNewlyMissingNotifications() {
|
||||||
|
inconsistencyTracker.maybeLogMissingNotifications(
|
||||||
|
oldMissingKeys = setOf("a"),
|
||||||
|
newMissingKeys = setOf("a", "b"),
|
||||||
|
)
|
||||||
|
verify(logger).logMissingNotifications(
|
||||||
|
newlyMissingKeys = eq(listOf("b")),
|
||||||
|
totalMissing = eq(2),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogMissingNotifications_logsNoLongerMissingNotifications() {
|
||||||
|
inconsistencyTracker.maybeLogMissingNotifications(
|
||||||
|
oldMissingKeys = setOf("a", "b"),
|
||||||
|
newMissingKeys = setOf("a"),
|
||||||
|
)
|
||||||
|
verify(logger).logFoundNotifications(
|
||||||
|
newlyFoundKeys = eq(listOf("b")),
|
||||||
|
totalMissing = eq(1),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogMissingNotifications_logsBothAtOnce() {
|
||||||
|
inconsistencyTracker.maybeLogMissingNotifications(
|
||||||
|
oldMissingKeys = setOf("a"),
|
||||||
|
newMissingKeys = setOf("b"),
|
||||||
|
)
|
||||||
|
verify(logger).logFoundNotifications(
|
||||||
|
newlyFoundKeys = eq(listOf("a")),
|
||||||
|
totalMissing = eq(1),
|
||||||
|
)
|
||||||
|
verify(logger).logMissingNotifications(
|
||||||
|
newlyMissingKeys = eq(listOf("b")),
|
||||||
|
totalMissing = eq(1),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun maybeLogMissingNotifications_logsNothingWhenNoChange() {
|
||||||
|
inconsistencyTracker.maybeLogMissingNotifications(
|
||||||
|
oldMissingKeys = setOf("a"),
|
||||||
|
newMissingKeys = setOf("a"),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun logNewMissingNotifications_doesNotLogForConsistentSets() {
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf())
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
|
||||||
|
collectionSet.add(entry1.key)
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf(entry1))
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
|
||||||
|
coalescedSet.add(entry2.key)
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf(entry1, entry2))
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
|
||||||
|
coalescedSet.add(entry1.key)
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf(entry1, entry2))
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
|
||||||
|
coalescedSet.remove(entry1.key)
|
||||||
|
collectionSet.remove(entry1.key)
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf(entry2))
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
|
||||||
|
coalescedSet.remove(entry2.key)
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf())
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun logNewMissingNotifications_logsAsExpected() {
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf())
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
|
||||||
|
collectionSet.add(entry1.key)
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf(entry1, entry2))
|
||||||
|
verify(logger).logMissingNotifications(
|
||||||
|
newlyMissingKeys = eq(listOf(entry2.key)),
|
||||||
|
totalMissing = eq(1),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
clearInvocations(logger)
|
||||||
|
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf(entry1, entry2))
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
|
||||||
|
coalescedSet.add(entry2.key)
|
||||||
|
inconsistencyTracker.logNewMissingNotifications(rankingMapOf(entry1, entry2))
|
||||||
|
verify(logger).logFoundNotifications(
|
||||||
|
newlyFoundKeys = eq(listOf(entry2.key)),
|
||||||
|
totalMissing = eq(0),
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(logger)
|
||||||
|
clearInvocations(logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.systemui.statusbar.notification.collection.notifcollection
|
|
||||||
|
|
||||||
import android.service.notification.NotificationListenerService.RankingMap
|
|
||||||
import android.testing.AndroidTestingRunner
|
|
||||||
import android.testing.TestableLooper.RunWithLooper
|
|
||||||
import androidx.test.filters.SmallTest
|
|
||||||
import com.android.systemui.SysuiTestCase
|
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
|
|
||||||
import com.android.systemui.util.mockito.eq
|
|
||||||
import com.android.systemui.util.mockito.mock
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
|
||||||
|
|
||||||
@SmallTest
|
|
||||||
@RunWith(AndroidTestingRunner::class)
|
|
||||||
@RunWithLooper
|
|
||||||
class NotifCollectionLoggerTest : SysuiTestCase() {
|
|
||||||
private val logger: NotifCollectionLogger = mock()
|
|
||||||
private val entry1: NotificationEntry = NotificationEntryBuilder().setId(1).build()
|
|
||||||
private val entry2: NotificationEntry = NotificationEntryBuilder().setId(2).build()
|
|
||||||
|
|
||||||
private fun mapOfEntries(vararg entries: NotificationEntry): Map<String, NotificationEntry> =
|
|
||||||
entries.associateBy { it.key }
|
|
||||||
|
|
||||||
private fun rankingMapOf(vararg entries: NotificationEntry): RankingMap =
|
|
||||||
RankingMap(entries.map { it.ranking }.toTypedArray())
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMaybeLogInconsistentRankings_logsNewlyInconsistentRanking() {
|
|
||||||
val rankingMap = rankingMapOf(entry1)
|
|
||||||
maybeLogInconsistentRankings(
|
|
||||||
logger = logger,
|
|
||||||
oldKeysWithoutRankings = emptySet(),
|
|
||||||
newEntriesWithoutRankings = mapOfEntries(entry2),
|
|
||||||
rankingMap = rankingMap
|
|
||||||
)
|
|
||||||
verify(logger).logMissingRankings(
|
|
||||||
newlyInconsistentEntries = eq(listOf(entry2)),
|
|
||||||
totalInconsistent = eq(1),
|
|
||||||
rankingMap = eq(rankingMap),
|
|
||||||
)
|
|
||||||
verifyNoMoreInteractions(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMaybeLogInconsistentRankings_doesNotLogAlreadyInconsistentRanking() {
|
|
||||||
maybeLogInconsistentRankings(
|
|
||||||
logger = logger,
|
|
||||||
oldKeysWithoutRankings = setOf(entry2.key),
|
|
||||||
newEntriesWithoutRankings = mapOfEntries(entry2),
|
|
||||||
rankingMap = rankingMapOf(entry1)
|
|
||||||
)
|
|
||||||
verifyNoMoreInteractions(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMaybeLogInconsistentRankings_logsWhenRankingIsAdded() {
|
|
||||||
maybeLogInconsistentRankings(
|
|
||||||
logger = logger,
|
|
||||||
oldKeysWithoutRankings = setOf(entry2.key),
|
|
||||||
newEntriesWithoutRankings = mapOfEntries(),
|
|
||||||
rankingMap = rankingMapOf(entry1, entry2)
|
|
||||||
)
|
|
||||||
verify(logger).logRecoveredRankings(
|
|
||||||
newlyConsistentKeys = eq(listOf(entry2.key)),
|
|
||||||
)
|
|
||||||
verifyNoMoreInteractions(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testMaybeLogInconsistentRankings_doesNotLogsWhenEntryIsRemoved() {
|
|
||||||
maybeLogInconsistentRankings(
|
|
||||||
logger = logger,
|
|
||||||
oldKeysWithoutRankings = setOf(entry2.key),
|
|
||||||
newEntriesWithoutRankings = mapOfEntries(),
|
|
||||||
rankingMap = rankingMapOf(entry1)
|
|
||||||
)
|
|
||||||
verifyNoMoreInteractions(logger)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user