summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/system-current.txt2
-rw-r--r--api/test-current.txt2
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl2
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java52
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java11
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/Assistant.java15
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java132
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java14
8 files changed, 179 insertions, 51 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index be51491ef2d0..f963c10e6272 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5076,8 +5076,10 @@ package android.service.notification {
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
diff --git a/api/test-current.txt b/api/test-current.txt
index 8f0d4b8fa5f3..0fa83f11a4ba 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1156,8 +1156,10 @@ package android.service.notification {
method public final void adjustNotification(android.service.notification.Adjustment);
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationDirectReply(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
+ method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public void onNotificationsSeen(java.util.List<java.lang.String>);
method public final void unsnoozeNotification(java.lang.String);
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d8bd00281ba5..0988510f5503 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -47,4 +47,6 @@ oneway interface INotificationListener
void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
void onNotificationsSeen(in List<String> keys);
+ void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+ void onNotificationDirectReply(String key);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index c1a3c2b5f150..90f4792face9 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -160,6 +160,21 @@ public abstract class NotificationAssistantService extends NotificationListenerS
}
/**
+ * Implement this to know when a notification is expanded / collapsed.
+ * @param key the notification key
+ * @param isUserAction whether the expanded change is caused by user action.
+ * @param isExpanded whether the notification is expanded.
+ */
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {}
+
+ /**
+ * Implement this to know when a direct reply is sent from a notification.
+ * @param key the notification key
+ */
+ public void onNotificationDirectReply(String key) {}
+
+ /**
* Updates a notification. N.B. this won’t cause
* an existing notification to alert, but might allow a future update to
* this notification to alert.
@@ -255,12 +270,33 @@ public abstract class NotificationAssistantService extends NotificationListenerS
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATIONS_SEEN,
args).sendToTarget();
}
+
+ @Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.argi1 = isUserAction ? 1 : 0;
+ args.argi2 = isExpanded ? 1 : 0;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_EXPANSION_CHANGED, args)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT, args)
+ .sendToTarget();
+ }
}
private final class MyHandler extends Handler {
public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1;
public static final int MSG_ON_NOTIFICATION_SNOOZED = 2;
public static final int MSG_ON_NOTIFICATIONS_SEEN = 3;
+ public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
+ public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -305,6 +341,22 @@ public abstract class NotificationAssistantService extends NotificationListenerS
onNotificationsSeen(keys);
break;
}
+ case MSG_ON_NOTIFICATION_EXPANSION_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ boolean isUserAction = args.argi1 == 1;
+ boolean isExpanded = args.argi2 == 1;
+ args.recycle();
+ onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ break;
+ }
+ case MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ args.recycle();
+ onNotificationDirectReply(key);
+ break;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 64eae0cf4635..a4db4517bd9a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1366,6 +1366,17 @@ public abstract class NotificationListenerService extends Service {
}
@Override
+ public void onNotificationExpansionChanged(
+ String key, boolean isUserAction, boolean isExpanded) {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ // no-op in the listener
+ }
+
+ @Override
public void onNotificationChannelModification(String pkgName, UserHandle user,
NotificationChannel channel,
@ChannelOrGroupModificationTypes int modificationType) {
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 60153fcbc26b..0e4900ab2d37 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -355,6 +355,21 @@ public class Assistant extends NotificationAssistantService {
}
@Override
+ public void onNotificationExpansionChanged(String key, boolean isUserAction,
+ boolean isExpanded) {
+ if (DEBUG) {
+ Log.i(TAG,
+ "onNotificationExpansionChanged " + key + ", isUserAction =" + isUserAction
+ + ", isExpanded = isExpanded");
+ }
+ }
+
+ @Override
+ public void onNotificationDirectReply(String key) {
+ if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
+ }
+
+ @Override
public void onListenerConnected() {
if (DEBUG) Log.i(TAG, "CONNECTED");
try {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 84577e22c41f..2048d5fc9890 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -246,6 +246,7 @@ import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.function.Predicate;
/** {@hide} */
@@ -887,6 +888,7 @@ public class NotificationManagerService extends SystemService {
EventLogTags.writeNotificationExpansion(key,
userAction ? 1 : 0, expanded ? 1 : 0,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
+ mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
}
}
}
@@ -902,6 +904,7 @@ public class NotificationManagerService extends SystemService {
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
.setType(MetricsEvent.TYPE_ACTION));
reportUserInteraction(r);
+ mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
}
}
}
@@ -4730,7 +4733,7 @@ public class NotificationManagerService extends SystemService {
mRankingHelper.extractSignals(r);
// tell the assistant service about the notification
if (mAssistants.isEnabled()) {
- mAssistants.onNotificationEnqueued(r);
+ mAssistants.onNotificationEnqueuedLocked(r);
mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
DELAY_FOR_ASSISTANT_TIME);
} else {
@@ -6847,69 +6850,104 @@ public class NotificationManagerService extends SystemService {
}
}
- public void onNotificationEnqueued(final NotificationRecord r) {
+ @GuardedBy("mNotificationLock")
+ private void onNotificationEnqueuedLocked(final NotificationRecord r) {
final StatusBarNotification sbn = r.sbn;
- TrimCache trimCache = new TrimCache(sbn);
-
- // There should be only one, but it's a list, so while we enforce
- // singularity elsewhere, we keep it general here, to avoid surprises.
- for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info)
- && info.isSameUser(r.getUserId());
- if (!sbnVisible) {
- continue;
- }
+ notifyAssistantLocked(
+ sbn,
+ true /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationEnqueuedWithChannel(sbnHolder, r.getChannel());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ }
+ });
+ }
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyEnqueued(info, sbnToPost, r.getChannel());
- }
- });
- }
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantExpansionChangedLocked(
+ final StatusBarNotification sbn,
+ final boolean isUserAction,
+ final boolean isExpanded) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
}
- private void notifyEnqueued(final ManagedServiceInfo info,
- final StatusBarNotification sbn, final NotificationChannel channel) {
- final INotificationListener assistant = (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
- try {
- assistant.onNotificationEnqueuedWithChannel(sbnHolder, channel);
- } catch (RemoteException ex) {
- Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
- }
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantNotificationDirectReplyLocked(
+ final StatusBarNotification sbn) {
+ final String key = sbn.getKey();
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
+ try {
+ assistant.onNotificationDirectReply(key);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (expanded): " + assistant, ex);
+ }
+ });
}
+
/**
* asynchronously notify the assistant that a notification has been snoozed until a
* context
*/
@GuardedBy("mNotificationLock")
- public void notifyAssistantSnoozedLocked(final StatusBarNotification sbn,
- final String snoozeCriterionId) {
- TrimCache trimCache = new TrimCache(sbn);
- for (final ManagedServiceInfo info : getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, info);
- if (!sbnVisible) {
- continue;
- }
- final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final INotificationListener assistant =
- (INotificationListener) info.service;
- StatusBarNotificationHolder sbnHolder
- = new StatusBarNotificationHolder(sbnToPost);
+ private void notifyAssistantSnoozedLocked(
+ final StatusBarNotification sbn, final String snoozeCriterionId) {
+ notifyAssistantLocked(
+ sbn,
+ false /* sameUserOnly */,
+ (assistant, sbnHolder) -> {
try {
assistant.onNotificationSnoozedUntilContext(
sbnHolder, snoozeCriterionId);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
}
- }
- });
+ });
+ }
+
+ /**
+ * Notifies the assistant something about the specified notification, only assistant
+ * that is visible to the notification will be notified.
+ *
+ * @param sbn the notification object that the update is about.
+ * @param sameUserOnly should the update be sent to the assistant in the same user only.
+ * @param callback the callback that provides the assistant to be notified, executed
+ * in WorkerHandler.
+ */
+ @GuardedBy("mNotificationLock")
+ private void notifyAssistantLocked(
+ final StatusBarNotification sbn,
+ boolean sameUserOnly,
+ BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
+ TrimCache trimCache = new TrimCache(sbn);
+ // There should be only one, but it's a list, so while we enforce
+ // singularity elsewhere, we keep it general here, to avoid surprises.
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info)
+ && (!sameUserOnly || info.isSameUser(sbn.getUserId()));
+ if (!sbnVisible) {
+ continue;
+ }
+ final INotificationListener assistant = (INotificationListener) info.service;
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ final StatusBarNotificationHolder sbnHolder =
+ new StatusBarNotificationHolder(sbnToPost);
+ mHandler.post(() -> callback.accept(assistant, sbnHolder));
}
}
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 5eda14f7f387..f11492aa59e5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,10 +38,8 @@ import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEGATIVE;
-import static android.service.notification.NotificationListenerService.Ranking
- .USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -2508,6 +2506,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+ verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
}
@Test
@@ -2516,8 +2515,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
}
@@ -2528,8 +2530,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ verify(mAssistants).notifyAssistantExpansionChangedLocked(
+ eq(r.sbn), eq(false), eq((false)));
}
@Test