Fix CallStyle ranking & ensure correct features are used.

Fixes: 179178086
Test: atest SystemUITests NotificationComparatorTest
Change-Id: I8e0890954300f4fcf31d5927b1919e16e46a7801
This commit is contained in:
Jeff DeCew
2021-04-16 16:08:20 -04:00
parent 8cf06f75a6
commit a91713d836
5 changed files with 129 additions and 10 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection
import android.app.Notification
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_MIN
import android.service.notification.NotificationListenerService.Ranking
@@ -25,6 +26,7 @@ import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
@@ -33,11 +35,11 @@ import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVI
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.notification.stack.PriorityBucket
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Lazy
import java.util.Objects
import javax.inject.Inject
import kotlin.Comparator
private const val TAG = "NotifRankingManager"
@@ -77,6 +79,9 @@ open class NotificationRankingManager @Inject constructor(
val aIsFsn = a.isColorizedForegroundService()
val bIsFsn = b.isColorizedForegroundService()
val aCall = a.isImportantCall()
val bCall = b.isImportantCall()
val aPersonType = a.getPeopleNotificationType()
val bPersonType = b.getPeopleNotificationType()
@@ -96,6 +101,7 @@ open class NotificationRankingManager @Inject constructor(
// Provide consistent ranking with headsUpManager
aHeadsUp -> headsUpManager.compare(a, b)
aIsFsn != bIsFsn -> if (aIsFsn) -1 else 1
aCall != bCall -> if (aCall) -1 else 1
usePeopleFiltering && aPersonType != bPersonType ->
peopleNotificationIdentifier.compareTo(aPersonType, bPersonType)
// Upsort current media notification.
@@ -150,11 +156,12 @@ open class NotificationRankingManager @Inject constructor(
@PriorityBucket
private fun getBucketForEntry(entry: NotificationEntry): Int {
val isImportantCall = entry.isImportantCall()
val isHeadsUp = entry.isRowHeadsUp
val isMedia = entry.isImportantMedia()
val isSystemMax = entry.isSystemMax()
return when {
entry.isColorizedForegroundService() -> BUCKET_FOREGROUND_SERVICE
entry.isColorizedForegroundService() || isImportantCall -> BUCKET_FOREGROUND_SERVICE
usePeopleFiltering && entry.isConversation() -> BUCKET_PEOPLE
isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() -> BUCKET_ALERTING
else -> BUCKET_SILENT
@@ -186,7 +193,7 @@ open class NotificationRankingManager @Inject constructor(
}
private fun NotificationEntry.isImportantMedia() =
key == mediaManager.mediaNotificationKey && ranking.importance > IMPORTANCE_MIN
key == mediaManager.mediaNotificationKey && importance > IMPORTANCE_MIN
private fun NotificationEntry.isConversation() = getPeopleNotificationType() != TYPE_NON_PERSON
@@ -204,6 +211,10 @@ private fun NotificationEntry.isSystemMax() =
private fun StatusBarNotification.isSystemNotification() =
"android" == packageName || "com.android.systemui" == packageName
private fun NotificationEntry.isImportantCall() =
sbn.notification.extras?.getString(Notification.EXTRA_TEMPLATE) ==
"android.app.Notification\$CallStyle" && importance > IMPORTANCE_MIN
private fun NotificationEntry.isColorizedForegroundService() = sbn.notification.run {
isForegroundService && isColorized && importance > IMPORTANCE_MIN
}

View File

@@ -21,6 +21,8 @@ import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.PendingIntent
import android.app.Person
import android.os.SystemClock
import android.service.notification.NotificationListenerService.RankingMap
import android.testing.AndroidTestingRunner
@@ -408,6 +410,74 @@ class NotificationRankingManagerTest : SysuiTestCase() {
assertThat(b.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE)
}
@Test
fun testSort_importantCall() {
whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
val a = NotificationEntryBuilder()
.setImportance(IMPORTANCE_HIGH)
.setPkg("pkg")
.setOpPkg("pkg")
.setTag("tag")
.setNotification(
Notification.Builder(mContext, "test")
.build())
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
val b = NotificationEntryBuilder()
.setImportance(IMPORTANCE_DEFAULT) // high priority
.setPkg("pkg2")
.setOpPkg("pkg2")
.setTag("tag")
.setNotification(mock(Notification::class.java).also { notif ->
whenever(notif.isForegroundService).thenReturn(true)
whenever(notif.isColorized).thenReturn(true)
})
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
val cN = Notification.Builder(mContext, "test")
.setStyle(Notification.MessagingStyle(""))
.build()
val c = NotificationEntryBuilder()
.setImportance(IMPORTANCE_HIGH)
.setPkg("pkg")
.setOpPkg("pkg")
.setTag("tag")
.setNotification(cN)
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.user)
.setOverrideGroupKey("")
.build()
val dN = Notification.Builder(mContext, "test")
.setStyle(Notification.CallStyle.forOngoingCall(
Person.Builder().setName("caller").build(),
mock(PendingIntent::class.java)))
.build()
val d = NotificationEntryBuilder()
.setImportance(IMPORTANCE_DEFAULT) // high priority
.setPkg("pkg2")
.setOpPkg("pkg2")
.setTag("tag")
.setNotification(dN)
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.user)
.setOverrideGroupKey("")
.build()
whenever(personNotificationIdentifier.getPeopleNotificationType(a))
.thenReturn(TYPE_IMPORTANT_PERSON)
assertThat(rankingManager.updateRanking(null, listOf(a, b, c, d), "test"))
.containsExactly(b, d, c, a)
assertThat(d.bucket).isEqualTo(BUCKET_FOREGROUND_SERVICE)
}
internal class TestableNotificationRankingManager(
mediaManager: Lazy<NotificationMediaManager>,
groupManager: NotificationGroupManagerLegacy,

View File

@@ -147,15 +147,16 @@ public class NotificationComparator
}
private boolean isImportantOngoing(NotificationRecord record) {
if (!isOngoing(record)) {
return false;
}
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
return false;
}
return isCall(record) || isMediaNotification(record);
if (isCallStyle(record)) {
return true;
}
if (!isOngoing(record)) {
return false;
}
return isCallCategory(record) || isMediaNotification(record);
}
protected boolean isImportantPeople(NotificationRecord record) {
@@ -181,11 +182,16 @@ public class NotificationComparator
return record.getNotification().hasMediaSession();
}
private boolean isCall(NotificationRecord record) {
private boolean isCallCategory(NotificationRecord record) {
return record.isCategory(Notification.CATEGORY_CALL)
&& isDefaultPhoneApp(record.getSbn().getPackageName());
}
private boolean isCallStyle(NotificationRecord record) {
return "android.app.Notification$CallStyle".equals(
record.getNotification().extras.getString(Notification.EXTRA_TEMPLATE));
}
private boolean isDefaultPhoneApp(String pkg) {
if (mDefaultPhoneApp == null) {
final TelecomManager telecomm =

View File

@@ -6570,6 +6570,17 @@ public class NotificationManagerService extends SystemService {
}
}
if ("android.app.Notification$CallStyle".equals(
n.extras.getString(Notification.EXTRA_TEMPLATE))) {
boolean isForegroundService = (n.flags & FLAG_FOREGROUND_SERVICE) != 0;
boolean hasFullScreenIntent = n.fullScreenIntent != null;
if (!isForegroundService && !hasFullScreenIntent) {
throw new IllegalArgumentException(r.getKey() + " Not posted."
+ " CallStyle notifications must either be for a foreground Service or"
+ " use a fullScreenIntent.");
}
}
// snoozed apps
if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
MetricsLogger.action(r.getLogMaker()

View File

@@ -22,12 +22,15 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -72,6 +75,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
private NotificationRecord mRecordMinCallNonInterruptive;
private NotificationRecord mRecordMinCall;
private NotificationRecord mRecordHighCall;
private NotificationRecord mRecordHighCallStyle;
private NotificationRecord mRecordEmail;
private NotificationRecord mRecordInlineReply;
private NotificationRecord mRecordSms;
@@ -90,9 +94,12 @@ public class NotificationComparatorTest extends UiServiceTestCase {
int userId = UserHandle.myUserId();
when(mContext.getResources()).thenReturn(getContext().getResources());
when(mContext.getTheme()).thenReturn(getContext().getTheme());
when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
when(mContext.getString(anyInt())).thenCallRealMethod();
when(mContext.getColor(anyInt())).thenCallRealMethod();
when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -137,6 +144,19 @@ public class NotificationComparatorTest extends UiServiceTestCase {
new UserHandle(userId), "", 1999), getDefaultChannel());
mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
Notification nHighCallStyle = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setStyle(Notification.CallStyle.forOngoingCall(
new Person.Builder().setName("caller").build(),
mock(PendingIntent.class)
))
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
.build();
mRecordHighCallStyle = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
callPkg, 1, "highCallStyle", callUid, callUid, nHighCallStyle,
new UserHandle(userId), "", 2000), getDefaultChannel());
mRecordHighCallStyle.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
mRecordHighCallStyle.setInterruptive(true);
Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setStyle(new Notification.MessagingStyle("sender!")).build();
mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
@@ -236,6 +256,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
final List<NotificationRecord> expected = new ArrayList<>();
expected.add(mRecordColorizedCall);
expected.add(mRecordColorized);
expected.add(mRecordHighCallStyle);
expected.add(mRecordHighCall);
expected.add(mRecordInlineReply);
if (mRecordSms != null) {