summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl3
-rw-r--r--packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt2
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java31
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java15
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java13
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java59
7 files changed, 128 insertions, 0 deletions
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index f14e1f63cdf6..ec0954d5590a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -239,4 +239,7 @@ interface IStatusBarService
/** Unbundle a categorized notification */
void unbundleNotification(String key);
+
+ /** Rebundle an (un)categorized notification */
+ void rebundleNotification(String key);
}
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
index 25d1c377ecbd..7ed736158a53 100644
--- a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -435,6 +435,8 @@ class FakeStatusBarService : IStatusBarService.Stub() {
override fun unbundleNotification(key: String) {}
+ override fun rebundleNotification(key: String) {}
+
companion object {
const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
const val SECONDARY_DISPLAY_ID = 2
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 7cbbe2938fd5..5a425057ea89 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -107,4 +107,9 @@ public interface NotificationDelegate {
* @param key the notification key
*/
void unbundleNotification(String key);
+ /**
+ * Called when the notification should be rebundled.
+ * @param key the notification key
+ */
+ void rebundleNotification(String key);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 87930fd421ee..341038f878d9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1888,6 +1888,36 @@ public class NotificationManagerService extends SystemService {
}
}
}
+
+ @Override
+ public void rebundleNotification(String key) {
+ if (!(notificationClassification() && notificationRegroupOnClassification())) {
+ return;
+ }
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) {
+ return;
+ }
+
+ if (DBG) {
+ Slog.v(TAG, "rebundleNotification: " + r);
+ }
+
+ if (r.getBundleType() != Adjustment.TYPE_OTHER) {
+ final Bundle classifBundle = new Bundle();
+ classifBundle.putInt(KEY_TYPE, r.getBundleType());
+ Adjustment adj = new Adjustment(r.getSbn().getPackageName(), r.getKey(),
+ classifBundle, "rebundle", r.getUserId());
+ applyAdjustmentLocked(r, adj, /* isPosted= */ true);
+ mRankingHandler.requestSort();
+ } else {
+ if (DBG) {
+ Slog.w(TAG, "Can't rebundle. No valid bundle type for: " + r);
+ }
+ }
+ }
+ }
};
NotificationManagerPrivate mNotificationManagerPrivate = new NotificationManagerPrivate() {
@@ -7134,6 +7164,7 @@ public class NotificationManagerService extends SystemService {
adjustments.putParcelable(KEY_TYPE, newChannel);
logClassificationChannelAdjustmentReceived(r, isPosted, classification);
+ r.setBundleType(classification);
}
}
r.addAdjustment(adjustment);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0bb3c6a067e3..81af0d8a6d80 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -222,6 +222,9 @@ public final class NotificationRecord {
// lifetime extended.
private boolean mCanceledAfterLifetimeExtension = false;
+ // type of the bundle if the notification was classified
+ private @Adjustment.Types int mBundleType = Adjustment.TYPE_OTHER;
+
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel) {
this.sbn = sbn;
@@ -467,6 +470,10 @@ public final class NotificationRecord {
}
}
+ if (android.service.notification.Flags.notificationClassification()) {
+ mBundleType = previous.mBundleType;
+ }
+
// Don't copy importance information or mGlobalSortKey, recompute them.
}
@@ -1629,6 +1636,14 @@ public final class NotificationRecord {
mCanceledAfterLifetimeExtension = canceledAfterLifetimeExtension;
}
+ public @Adjustment.Types int getBundleType() {
+ return mBundleType;
+ }
+
+ public void setBundleType(@Adjustment.Types int bundleType) {
+ mBundleType = bundleType;
+ }
+
/**
* Whether this notification is a conversation notification.
*/
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 4ed5f90f2852..a19a3422af06 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -2199,6 +2199,19 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
});
}
+ /**
+ * Called when the notification should be rebundled.
+ * @param key the notification key
+ */
+ @Override
+ public void rebundleNotification(String key) {
+ enforceStatusBarService();
+ enforceValidCallingUser();
+ Binder.withCleanCallingIdentity(() -> {
+ mNotificationDelegate.rebundleNotification(key);
+ });
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 8a39e0ecdc07..e43b28bb9404 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -17921,4 +17921,63 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r1), eq(hasOriginalSummary));
}
+ @Test
+ @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
+ FLAG_NOTIFICATION_FORCE_GROUPING,
+ FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
+ public void testRebundleNotification_restoresBundleChannel() throws Exception {
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+ when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+ when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
+ when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
+
+ // Post a single notification
+ final boolean hasOriginalSummary = false;
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ final String keyToUnbundle = r.getKey();
+ mService.addNotification(r);
+
+ // Classify notification into the NEWS bundle
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_TYPE, Adjustment.TYPE_NEWS);
+ Adjustment adjustment = new Adjustment(
+ r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ // Check that the NotificationRecord channel is updated
+ assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+ assertThat(r.getBundleType()).isEqualTo(Adjustment.TYPE_NEWS);
+
+ // Unbundle the notification
+ mService.mNotificationDelegate.unbundleNotification(keyToUnbundle);
+
+ // Check that the original channel was restored
+ assertThat(r.getChannel().getId()).isEqualTo(TEST_CHANNEL_ID);
+ assertThat(r.getBundleType()).isEqualTo(Adjustment.TYPE_NEWS);
+ verify(mGroupHelper, times(1)).onNotificationUnbundled(eq(r), eq(hasOriginalSummary));
+
+ Mockito.reset(mRankingHandler);
+ Mockito.reset(mGroupHelper);
+
+ // Rebundle the notification
+ mService.mNotificationDelegate.rebundleNotification(keyToUnbundle);
+
+ // Actually apply the adjustments
+ doAnswer(invocationOnMock -> {
+ ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments();
+ ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance();
+ return null;
+ }).when(mRankingHelper).extractSignals(any(NotificationRecord.class));
+ mService.handleRankingSort();
+ verify(handler, times(1)).scheduleSendRankingUpdate();
+
+ // Check that the bundle channel was restored
+ verify(mRankingHandler, times(1)).requestSort();
+ assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+ }
+
}