NAS Feedback API Change
Add onNotificationFeedbackReceived API and Notification#hasImage
Test: CtsLegacyNotification29TestCases
Test: atest com.android.systemui.statusbar.notification
Test: atest AssistantFeedbackControllerTest
Bug: 177032312
Change-Id: I5e95e7794290ba40c1a1ac0b77d8eb09572f4a01
diff --git a/core/api/current.txt b/core/api/current.txt
index dd57b6a..2a12130 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5526,6 +5526,7 @@
method public android.graphics.drawable.Icon getSmallIcon();
method public String getSortKey();
method public long getTimeoutAfter();
+ method public boolean hasImage();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT;
field public static final int BADGE_ICON_LARGE = 2; // 0x2
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d7dc6ef..ca3df1d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10088,6 +10088,7 @@
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel);
method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel, @NonNull android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
+ method public void onNotificationFeedbackReceived(@NonNull String, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.os.Bundle);
method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String);
method public void onNotificationVisibilityChanged(@NonNull String, boolean);
method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>);
@@ -10095,6 +10096,7 @@
method public void onPanelRevealed(int);
method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
method public final void unsnoozeNotification(@NonNull String);
+ field public static final String FEEDBACK_RATING = "feedback.rating";
field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
field public static final int SOURCE_FROM_APP = 0; // 0x0
field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 694507d..8b2e80d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -300,6 +300,7 @@
method public void clickNotification(@Nullable String, int, int, boolean);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
method public void expandNotificationsPanel();
+ method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b6fc47f..e357b73 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6744,6 +6744,31 @@
}
/**
+ * @return true if the notification has image
+ */
+ public boolean hasImage() {
+ if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) {
+ final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
+ if (!ArrayUtils.isEmpty(messages)) {
+ for (MessagingStyle.Message m : MessagingStyle.Message
+ .getMessagesFromBundleArray(messages)) {
+ if (m.getDataUri() != null
+ && m.getDataMimeType() != null
+ && m.getDataMimeType().startsWith("image/")) {
+ return true;
+ }
+ }
+ }
+ } else if (hasLargeIcon()) {
+ return true;
+ } else if (extras.containsKey(EXTRA_BACKGROUND_IMAGE_URI)) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
* @removed
*/
@SystemApi
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 7d2bc1a..afcf63b 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -286,6 +287,23 @@
}
/**
+ * Simulate notification feedback for testing
+ *
+ * @hide
+ */
+ @TestApi
+ public void sendNotificationFeedback(@Nullable String key, @Nullable Bundle feedback) {
+ try {
+ final IStatusBarService svc = getService();
+ if (svc != null) {
+ svc.onNotificationFeedbackReceived(key, feedback);
+ }
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Expand the notifications panel.
*
* @hide
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 2b7cb1d..b384b66 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -20,6 +20,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.pm.ParceledListSlice;
+import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationStats;
import android.service.notification.IStatusBarNotificationHolder;
@@ -58,4 +59,5 @@
void onActionClicked(String key, in Notification.Action action, int source);
void onNotificationClicked(String key);
void onAllowedAdjustmentsChanged();
+ void onNotificationFeedbackReceived(String key, in NotificationRankingUpdate update, in Bundle feedback);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 1d49a72..4fd36e5 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -30,6 +30,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -90,6 +91,11 @@
= "android.service.notification.NotificationAssistantService";
/**
+ * Data type: int, the feedback rating score provided by user
+ */
+ public static final String FEEDBACK_RATING = "feedback.rating";
+
+ /**
* @hide
*/
protected Handler mHandler;
@@ -272,6 +278,17 @@
}
/**
+ * Implement this to know when user provides a feedback.
+ * @param key the notification key
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications.
+ * @param feedback the feedback detail
+ */
+ public void onNotificationFeedbackReceived(@NonNull String key, @NonNull RankingMap rankingMap,
+ @NonNull Bundle feedback) {
+ }
+
+ /**
* 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.
@@ -455,6 +472,18 @@
public void onAllowedAdjustmentsChanged() {
mHandler.obtainMessage(MyHandler.MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED).sendToTarget();
}
+
+ @Override
+ public void onNotificationFeedbackReceived(String key, NotificationRankingUpdate update,
+ Bundle feedback) {
+ applyUpdateLocked(update);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = key;
+ args.arg2 = getCurrentRanking();
+ args.arg3 = feedback;
+ mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED,
+ args).sendToTarget();
+ }
}
private void setAdjustmentIssuer(@Nullable Adjustment adjustment) {
@@ -476,6 +505,7 @@
public static final int MSG_ON_PANEL_HIDDEN = 10;
public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 11;
public static final int MSG_ON_NOTIFICATION_CLICKED = 12;
+ public static final int MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED = 13;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -589,6 +619,15 @@
onNotificationClicked(key);
break;
}
+ case MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String key = (String) args.arg1;
+ RankingMap ranking = (RankingMap) args.arg2;
+ Bundle feedback = (Bundle) args.arg3;
+ args.recycle();
+ onNotificationFeedbackReceived(key, ranking, feedback);
+ break;
+ }
}
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index f66f85b..73e66d0 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -43,6 +43,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -1543,6 +1544,14 @@
mHandler.obtainMessage(MyHandler.MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED,
hideSilentStatusIcons).sendToTarget();
}
+
+ @Override
+ public void onNotificationFeedbackReceived(String key, NotificationRankingUpdate update,
+ Bundle feedback) {
+ // no-op in the listener
+ }
+
+
}
/**
@@ -1628,6 +1637,7 @@
private int mSuppressedVisualEffects;
private @NotificationManager.Importance int mImportance;
private CharSequence mImportanceExplanation;
+ private float mRankingScore;
// System specified group key.
private String mOverrideGroupKey;
// Notification assistant channel override.
@@ -1668,6 +1678,7 @@
out.writeInt(mSuppressedVisualEffects);
out.writeInt(mImportance);
out.writeCharSequence(mImportanceExplanation);
+ out.writeFloat(mRankingScore);
out.writeString(mOverrideGroupKey);
out.writeParcelable(mChannel, flags);
out.writeStringList(mOverridePeople);
@@ -1705,6 +1716,7 @@
mSuppressedVisualEffects = in.readInt();
mImportance = in.readInt();
mImportanceExplanation = in.readCharSequence(); // may be null
+ mRankingScore = in.readFloat();
mOverrideGroupKey = in.readString(); // may be null
mChannel = in.readParcelable(cl); // may be null
mOverridePeople = in.createStringArrayList();
@@ -1802,6 +1814,17 @@
}
/**
+ * Returns the ranking score provided by the {@link NotificationAssistantService} to
+ * sort the notifications in the shade
+ *
+ * @return the ranking score of the notification, range from -1 to 1
+ * @hide
+ */
+ public float getRankingScore() {
+ return mRankingScore;
+ }
+
+ /**
* If the system has overridden the group key, then this will be non-null, and this
* key should be used to bundle notifications.
*/
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index bb0fd7f..7edc6c8 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -22,6 +22,7 @@
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -82,6 +83,7 @@
void hideCurrentInputMethodForBubbles();
void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName);
void clearInlineReplyUriPermissions(String key);
+ void onNotificationFeedbackReceived(String key, in Bundle feedback);
void onGlobalActionsShown();
void onGlobalActionsHidden();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
index edcf6d4..4b4e513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
@@ -109,10 +109,10 @@
&& newImportance < NotificationManager.IMPORTANCE_DEFAULT) {
return STATUS_SILENCED;
} else if (oldImportance < newImportance
- || ranking.getRankingAdjustment() == ranking.RANKING_PROMOTED) {
+ || ranking.getRankingAdjustment() == Ranking.RANKING_PROMOTED) {
return STATUS_PROMOTED;
} else if (oldImportance > newImportance
- || ranking.getRankingAdjustment() == ranking.RANKING_DEMOTED) {
+ || ranking.getRankingAdjustment() == Ranking.RANKING_DEMOTED) {
return STATUS_DEMOTED;
} else {
return STATUS_UNCHANGED;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index 35f3561..14683ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -16,8 +16,14 @@
package com.android.systemui.statusbar.notification.row;
+import static android.service.notification.NotificationAssistantService.FEEDBACK_RATING;
+
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_ALERTED;
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_DEMOTED;
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_PROMOTED;
+import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_SILENCED;
+
import android.annotation.SuppressLint;
-import android.app.Notification;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -36,20 +42,17 @@
import android.widget.TextView;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent {
private static final String TAG = "FeedbackInfo";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String FEEDBACK_KEY = "feedback_key";
private NotificationGuts mGutsContainer;
private NotificationListenerService.Ranking mRanking;
@@ -146,15 +149,15 @@
sb.append(String.format(
"[DEBUG]: oldImportance=%d, newImportance=%d, ranking=%d\n\n",
mRanking.getChannel().getImportance(), mRanking.getImportance(),
- mRanking.getRankingAdjustment()));
+ mRanking.getRankingScore()));
}
- if (status == mFeedbackController.STATUS_ALERTED) {
+ if (status == STATUS_ALERTED) {
sb.append(mContext.getText(R.string.feedback_alerted));
- } else if (status == mFeedbackController.STATUS_SILENCED) {
+ } else if (status == STATUS_SILENCED) {
sb.append(mContext.getText(R.string.feedback_silenced));
- } else if (status == mFeedbackController.STATUS_PROMOTED) {
+ } else if (status == STATUS_PROMOTED) {
sb.append(mContext.getText(R.string.feedback_promoted));
- } else if (status == mFeedbackController.STATUS_DEMOTED) {
+ } else if (status == STATUS_DEMOTED) {
sb.append(mContext.getText(R.string.feedback_demoted));
}
sb.append(" ");
@@ -182,7 +185,8 @@
private void handleFeedback(boolean positive) {
Bundle feedback = new Bundle();
- feedback.putBoolean(FEEDBACK_KEY, positive);
+ feedback.putInt(FEEDBACK_RATING, positive ? 1 : -1);
+
sendFeedbackToAssistant(feedback);
}
@@ -191,19 +195,8 @@
return;
}
- //TODO(b/154257994): remove this when feedback apis are in place
- final int count = mNotificationEntryManager.getActiveNotificationsCount();
- final int rank = mEntry.getRanking().getRank();
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(mEntry);
- final NotificationVisibility nv = NotificationVisibility.obtain(
- mEntry.getKey(), rank, count, true, location);
- Notification.Action action = new Notification.Action.Builder(null, null,
- null)
- .addExtras(feedback)
- .build();
try {
- mStatusBarService.onNotificationActionClick(mRanking.getKey(), -1, action, nv, true);
+ mStatusBarService.onNotificationFeedbackReceived(mRanking.getKey(), feedback);
} catch (RemoteException e) {
if (DEBUG) {
Log.e(TAG, "Failed to send feedback to assistant", e);
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 1051423..160d2da 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationStats;
@@ -88,5 +89,13 @@
void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply,
int notificationLocation, boolean modifiedBeforeSending);
+ /**
+ * Notifies a user feedback is provided.
+ *
+ * @param key the notification key
+ * @param feedback the feedback detail
+ */
+ void onNotificationFeedbackReceived(String key, Bundle feedback);
+
void prepareForPossibleShutdown();
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 917be29..8b4c639 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -172,7 +172,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
-import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
@@ -215,7 +214,6 @@
import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.Condition;
-import android.service.notification.ConditionProviderService;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
@@ -383,7 +381,8 @@
Adjustment.KEY_TEXT_REPLIES,
Adjustment.KEY_NOT_CONVERSATION,
Adjustment.KEY_IMPORTANCE,
- Adjustment.KEY_RANKING_SCORE};
+ Adjustment.KEY_RANKING_SCORE
+ };
static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
RoleManager.ROLE_DIALER,
@@ -432,8 +431,6 @@
private static final String SCHEME_TIMEOUT = "timeout";
private static final String EXTRA_KEY = "key";
- private static final String FEEDBACK_KEY = "feedback_key";
-
private static final int NOTIFICATION_INSTANCE_ID_MAX = (1 << 13);
/**
@@ -1020,30 +1017,26 @@
return;
}
final long now = System.currentTimeMillis();
- //TODO(b/154257994): remove this when feedback apis are in place
- boolean isFeedback = action.getExtras().containsKey(FEEDBACK_KEY);
- if (!isFeedback) {
- MetricsLogger.action(r.getLogMaker(now)
- .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
- .setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(actionIndex)
- .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
- .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
- .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
- action.isContextual() ? 1 : 0)
- .addTaggedData(
- MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
- generatedByAssistant ? 1 : 0)
- .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION,
- nv.location.toMetricsEventEnum()));
- mNotificationRecordLogger.log(
- NotificationRecordLogger.NotificationEvent.fromAction(actionIndex,
- generatedByAssistant, action.isContextual()), r);
- EventLogTags.writeNotificationActionClicked(key, actionIndex,
- r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
- nv.rank, nv.count);
- nv.recycle();
- }
+ MetricsLogger.action(r.getLogMaker(now)
+ .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(actionIndex)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
+ .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)
+ .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART,
+ action.isContextual() ? 1 : 0)
+ .addTaggedData(
+ MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
+ generatedByAssistant ? 1 : 0)
+ .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION,
+ nv.location.toMetricsEventEnum()));
+ mNotificationRecordLogger.log(
+ NotificationRecordLogger.NotificationEvent.fromAction(actionIndex,
+ generatedByAssistant, action.isContextual()), r);
+ EventLogTags.writeNotificationActionClicked(key, actionIndex,
+ r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
+ nv.rank, nv.count);
+ nv.recycle();
reportUserInteraction(r);
mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant);
}
@@ -1386,6 +1379,20 @@
}
}
}
+
+ @Override
+ public void onNotificationFeedbackReceived(String key, Bundle feedback) {
+ exitIdle();
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r == null) {
+ if (DBG) Slog.w(TAG, "No notification with key: " + key);
+ return;
+ }
+ mAssistants.notifyAssistantFeedbackReceived(r, feedback);
+ }
+ }
+
};
@VisibleForTesting
@@ -9505,6 +9512,26 @@
});
}
+ @GuardedBy("mNotificationLock")
+ void notifyAssistantFeedbackReceived(final NotificationRecord r, Bundle feedback) {
+ final StatusBarNotification sbn = r.getSbn();
+
+ for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
+ boolean sbnVisible = isVisibleToListener(
+ sbn, r.getNotificationType(), info)
+ && info.isSameUser(r.getUserId());
+ if (sbnVisible) {
+ final INotificationListener assistant = (INotificationListener) info.service;
+ try {
+ final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+ assistant.onNotificationFeedbackReceived(sbn.getKey(), update, feedback);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "unable to notify assistant (feedback): " + assistant, ex);
+ }
+ }
+ }
+ }
+
/**
* Notifies the assistant something about the specified notification, only assistant
* that is visible to the notification will be notified.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d664651a..a390df9 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1501,6 +1501,18 @@
}
@Override
+ public void onNotificationFeedbackReceived(String key, Bundle feedback) {
+ enforceStatusBarService();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationFeedbackReceived(key, feedback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new StatusBarShellCommand(this, mContext)).exec(