Merge "Ability to hide/unhide suspended app notifications" into pi-dev

am: 6c0f42b3b5

Change-Id: Iad46af0da71abd4c879b8a0811ebe1943d064b7a
This commit is contained in:
Beverly
2018-03-21 03:36:44 +00:00
committed by android-build-merger
10 changed files with 373 additions and 23 deletions

View File

@@ -40058,6 +40058,7 @@ package android.service.notification {
method public int getSuppressedVisualEffects();
method public int getUserSentiment();
method public boolean isAmbient();
method public boolean isSuspended();
method public boolean matchesInterruptionFilter();
field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0

View File

@@ -1418,6 +1418,7 @@ public abstract class NotificationListenerService extends Service {
private ArrayList<SnoozeCriterion> mSnoozeCriteria;
private boolean mShowBadge;
private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
private boolean mHidden;
public Ranking() {}
@@ -1556,6 +1557,16 @@ public abstract class NotificationListenerService extends Service {
return mShowBadge;
}
/**
* Returns whether the app that posted this notification is suspended, so this notification
* should be hidden.
*
* @return true if the notification should be hidden, false otherwise.
*/
public boolean isSuspended() {
return mHidden;
}
/**
* @hide
*/
@@ -1565,7 +1576,7 @@ public abstract class NotificationListenerService extends Service {
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
int userSentiment) {
int userSentiment, boolean hidden) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1580,6 +1591,7 @@ public abstract class NotificationListenerService extends Service {
mSnoozeCriteria = snoozeCriteria;
mShowBadge = showBadge;
mUserSentiment = userSentiment;
mHidden = hidden;
}
/**
@@ -1628,6 +1640,7 @@ public abstract class NotificationListenerService extends Service {
private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
private ArrayMap<String, Boolean> mShowBadge;
private ArrayMap<String, Integer> mUserSentiment;
private ArrayMap<String, Boolean> mHidden;
private RankingMap(NotificationRankingUpdate rankingUpdate) {
mRankingUpdate = rankingUpdate;
@@ -1656,7 +1669,7 @@ public abstract class NotificationListenerService extends Service {
getVisibilityOverride(key), getSuppressedVisualEffects(key),
getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
getShowBadge(key), getUserSentiment(key));
getShowBadge(key), getUserSentiment(key), getHidden(key));
return rank >= 0;
}
@@ -1784,6 +1797,16 @@ public abstract class NotificationListenerService extends Service {
? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
}
private boolean getHidden(String key) {
synchronized (this) {
if (mHidden == null) {
buildHiddenLocked();
}
}
Boolean hidden = mHidden.get(key);
return hidden == null ? false : hidden.booleanValue();
}
// Locked by 'this'
private void buildRanksLocked() {
String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1892,6 +1915,15 @@ public abstract class NotificationListenerService extends Service {
}
}
// Locked by 'this'
private void buildHiddenLocked() {
Bundle hidden = mRankingUpdate.getHidden();
mHidden = new ArrayMap<>(hidden.size());
for (String key : hidden.keySet()) {
mHidden.put(key, hidden.getBoolean(key));
}
}
// ----------- Parcelable
@Override

View File

@@ -36,12 +36,13 @@ public class NotificationRankingUpdate implements Parcelable {
private final Bundle mSnoozeCriteria;
private final Bundle mShowBadge;
private final Bundle mUserSentiment;
private final Bundle mHidden;
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
Bundle visibilityOverrides, Bundle suppressedVisualEffects,
int[] importance, Bundle explanation, Bundle overrideGroupKeys,
Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
Bundle showBadge, Bundle userSentiment) {
Bundle showBadge, Bundle userSentiment, Bundle hidden) {
mKeys = keys;
mInterceptedKeys = interceptedKeys;
mVisibilityOverrides = visibilityOverrides;
@@ -54,6 +55,7 @@ public class NotificationRankingUpdate implements Parcelable {
mSnoozeCriteria = snoozeCriteria;
mShowBadge = showBadge;
mUserSentiment = userSentiment;
mHidden = hidden;
}
public NotificationRankingUpdate(Parcel in) {
@@ -70,6 +72,7 @@ public class NotificationRankingUpdate implements Parcelable {
mSnoozeCriteria = in.readBundle();
mShowBadge = in.readBundle();
mUserSentiment = in.readBundle();
mHidden = in.readBundle();
}
@Override
@@ -91,6 +94,7 @@ public class NotificationRankingUpdate implements Parcelable {
out.writeBundle(mSnoozeCriteria);
out.writeBundle(mShowBadge);
out.writeBundle(mUserSentiment);
out.writeBundle(mHidden);
}
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -151,4 +155,8 @@ public class NotificationRankingUpdate implements Parcelable {
public Bundle getUserSentiment() {
return mUserSentiment;
}
public Bundle getHidden() {
return mHidden;
}
}

View File

@@ -529,6 +529,14 @@ public class NotificationData {
return null;
}
public boolean shouldHide(String key) {
if (mRankingMap != null) {
getRanking(key, mTmpRanking);
return mTmpRanking.isSuspended();
}
return false;
}
private void updateRankingAndSort(RankingMap ranking) {
if (ranking != null) {
mRankingMap = ranking;
@@ -618,6 +626,10 @@ public class NotificationData {
return true;
}
if (shouldHide(sbn.getKey())) {
return true;
}
if (!StatusBar.ENABLE_CHILD_NOTIFICATIONS
&& mGroupManager.isChildInGroupWithSummary(sbn)) {
return true;

View File

@@ -60,6 +60,7 @@ public class NotificationDataTest extends SysuiTestCase {
private static final int UID_NORMAL = 123;
private static final int UID_ALLOW_DURING_SETUP = 456;
private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
private final StatusBarNotification mMockStatusBarNotification =
mock(StatusBarNotification.class);
@@ -247,6 +248,22 @@ public class NotificationDataTest extends SysuiTestCase {
assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
}
@Test
public void testShouldFilterHiddenNotifications() {
// setup
when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
// test should filter out hidden notifications:
// hidden
when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
assertTrue(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
// not hidden
when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
}
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -269,6 +286,21 @@ public class NotificationDataTest extends SysuiTestCase {
@Override
protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) {
super.getRanking(key, outRanking);
if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
} else {
outRanking.populate(key, outRanking.getRank(),
outRanking.matchesInterruptionFilter(),
outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
outRanking.getImportance(), outRanking.getImportanceExplanation(),
outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
outRanking.canShowBadge(), outRanking.getUserSentiment(), false);
}
return true;
}
}

View File

@@ -140,7 +140,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
0,
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
null, null, null, true, sentiment);
null, null, null, true, sentiment, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}

View File

@@ -959,6 +959,8 @@ public class NotificationManagerService extends SystemService {
boolean queryRemove = false;
boolean packageChanged = false;
boolean cancelNotifications = true;
boolean hideNotifications = false;
boolean unhideNotifications = false;
int reason = REASON_PACKAGE_CHANGED;
if (action.equals(Intent.ACTION_PACKAGE_ADDED)
@@ -967,7 +969,8 @@ public class NotificationManagerService extends SystemService {
|| (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
|| (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
|| action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
|| action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
|| action.equals(Intent.ACTION_PACKAGES_SUSPENDED)
|| action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) {
int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_ALL);
String pkgList[] = null;
@@ -980,7 +983,12 @@ public class NotificationManagerService extends SystemService {
uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
} else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
reason = REASON_PACKAGE_SUSPENDED;
cancelNotifications = false;
hideNotifications = true;
} else if (action.equals(Intent.ACTION_PACKAGES_UNSUSPENDED)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
cancelNotifications = false;
unhideNotifications = true;
} else if (queryRestart) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
uidList = new int[] {intent.getIntExtra(Intent.EXTRA_UID, -1)};
@@ -1022,9 +1030,15 @@ public class NotificationManagerService extends SystemService {
if (cancelNotifications) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
!queryRestart, changeUserId, reason, null);
} else if (hideNotifications) {
hideNotificationsForPackages(pkgList);
} else if (unhideNotifications) {
unhideNotificationsForPackages(pkgList);
}
}
}
mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
@@ -1479,6 +1493,7 @@ public class NotificationManagerService extends SystemService {
IntentFilter suspendedPkgFilter = new IntentFilter();
suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
suspendedPkgFilter, null, null);
@@ -2486,6 +2501,7 @@ public class NotificationManagerService extends SystemService {
try {
synchronized (mNotificationLock) {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
if (keys != null) {
final int N = keys.length;
for (int i = 0; i < N; i++) {
@@ -4271,6 +4287,14 @@ public class NotificationManagerService extends SystemService {
}
}
@GuardedBy("mNotificationLock")
private boolean isPackageSuspendedLocked(NotificationRecord r) {
final String pkg = r.sbn.getPackageName();
final int callingUid = r.sbn.getUid();
return isPackageSuspendedForUser(pkg, callingUid);
}
protected class PostNotificationRunnable implements Runnable {
private final String key;
@@ -4295,6 +4319,8 @@ public class NotificationManagerService extends SystemService {
Slog.i(TAG, "Cannot find enqueued record for key: " + key);
return;
}
r.setHidden(isPackageSuspendedLocked(r));
NotificationRecord old = mNotificationsByKey.get(key);
final StatusBarNotification n = r.sbn;
final Notification notification = n.getNotification();
@@ -4347,7 +4373,7 @@ public class NotificationManagerService extends SystemService {
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(n,
mListeners.notifyRemovedLocked(r,
NotificationListenerService.REASON_ERROR, null);
mHandler.post(new Runnable() {
@Override
@@ -4363,7 +4389,9 @@ public class NotificationManagerService extends SystemService {
+ n.getPackageName());
}
buzzBeepBlinkLocked(r);
if (!r.isHidden()) {
buzzBeepBlinkLocked(r);
}
maybeRecordInterruptionLocked(r);
} finally {
int N = mEnqueuedNotifications.size();
@@ -5022,7 +5050,7 @@ public class NotificationManagerService extends SystemService {
private void handleSendRankingUpdate() {
synchronized (mNotificationLock) {
mListeners.notifyRankingUpdateLocked();
mListeners.notifyRankingUpdateLocked(null);
}
}
@@ -5207,7 +5235,7 @@ public class NotificationManagerService extends SystemService {
if (reason != REASON_SNOOZED) {
r.isCanceled = true;
}
mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats());
mListeners.notifyRemovedLocked(r, reason, r.getStats());
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -5316,6 +5344,7 @@ public class NotificationManagerService extends SystemService {
final String pkg, final String tag, final int id,
final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
final int userId, final int reason, final ManagedServiceInfo listener) {
// In enqueueNotificationInternal notifications are added by scheduling the
// work on the worker handler. Hence, we also schedule the cancel on this
// handler to avoid a scenario where an add notification call followed by a
@@ -5707,6 +5736,42 @@ public class NotificationManagerService extends SystemService {
return -1;
}
@VisibleForTesting
protected void hideNotificationsForPackages(String[] pkgs) {
synchronized (mNotificationLock) {
List<String> pkgList = Arrays.asList(pkgs);
List<NotificationRecord> changedNotifications = new ArrayList<>();
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
if (pkgList.contains(rec.sbn.getPackageName())) {
rec.setHidden(true);
changedNotifications.add(rec);
}
}
mListeners.notifyHiddenLocked(changedNotifications);
}
}
@VisibleForTesting
protected void unhideNotificationsForPackages(String[] pkgs) {
synchronized (mNotificationLock) {
List<String> pkgList = Arrays.asList(pkgs);
List<NotificationRecord> changedNotifications = new ArrayList<>();
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
if (pkgList.contains(rec.sbn.getPackageName())) {
rec.setHidden(false);
changedNotifications.add(rec);
}
}
mListeners.notifyUnhiddenLocked(changedNotifications);
}
}
private void updateNotificationPulse() {
synchronized (mNotificationLock) {
updateLightsLocked();
@@ -5826,6 +5891,7 @@ public class NotificationManagerService extends SystemService {
Bundle snoozeCriteria = new Bundle();
Bundle showBadge = new Bundle();
Bundle userSentiment = new Bundle();
Bundle hidden = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
@@ -5852,6 +5918,7 @@ public class NotificationManagerService extends SystemService {
snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
showBadge.putBoolean(key, record.canShowBadge());
userSentiment.putInt(key, record.getUserSentiment());
hidden.putBoolean(key, record.isHidden());
}
final int M = keys.size();
String[] keysAr = keys.toArray(new String[M]);
@@ -5862,7 +5929,7 @@ public class NotificationManagerService extends SystemService {
}
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -6151,6 +6218,16 @@ public class NotificationManagerService extends SystemService {
*/
@GuardedBy("mNotificationLock")
public void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn) {
notifyPostedLocked(r, oldSbn, true);
}
/**
* @param notifyAllListeners notifies all listeners if true, else only notifies listeners
* targetting <= O_MR1
*/
@GuardedBy("mNotificationLock")
private void notifyPostedLocked(NotificationRecord r, StatusBarNotification oldSbn,
boolean notifyAllListeners) {
// Lazily initialized snapshots of the notification.
StatusBarNotification sbn = r.sbn;
TrimCache trimCache = new TrimCache(sbn);
@@ -6164,6 +6241,21 @@ public class NotificationManagerService extends SystemService {
if (!oldSbnVisible && !sbnVisible) {
continue;
}
// If the notification is hidden, don't notifyPosted listeners targeting < P.
// Instead, those listeners will receive notifyPosted when the notification is
// unhidden.
if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
continue;
}
// If we shouldn't notify all listeners, this means the hidden state of
// a notification was changed. Don't notifyPosted listeners targeting >= P.
// Instead, those listeners will receive notifyRankingUpdate.
if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
continue;
}
final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
// This notification became invisible -> remove the old one.
@@ -6218,8 +6310,9 @@ public class NotificationManagerService extends SystemService {
* asynchronously notify all listeners about a removed notification
*/
@GuardedBy("mNotificationLock")
public void notifyRemovedLocked(StatusBarNotification sbn, int reason,
public void notifyRemovedLocked(NotificationRecord r, int reason,
NotificationStats notificationStats) {
final StatusBarNotification sbn = r.sbn;
// make a copy in case changes are made to the underlying Notification object
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
@@ -6228,6 +6321,21 @@ public class NotificationManagerService extends SystemService {
if (!isVisibleToListener(sbn, info)) {
continue;
}
// don't notifyRemoved for listeners targeting < P
// if not for reason package suspended
if (r.isHidden() && reason != REASON_PACKAGE_SUSPENDED
&& info.targetSdkVersion < Build.VERSION_CODES.P) {
continue;
}
// don't notifyRemoved for listeners targeting >= P
// if the reason is package suspended
if (reason == REASON_PACKAGE_SUSPENDED
&& info.targetSdkVersion >= Build.VERSION_CODES.P) {
continue;
}
// Only assistants can get stats
final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service)
? notificationStats : null;
@@ -6242,21 +6350,44 @@ public class NotificationManagerService extends SystemService {
}
/**
* asynchronously notify all listeners about a reordering of notifications
* Asynchronously notify all listeners about a reordering of notifications
* unless changedHiddenNotifications is populated.
* If changedHiddenNotifications is populated, there was a change in the hidden state
* of the notifications. In this case, we only send updates to listeners that
* target >= P.
*/
@GuardedBy("mNotificationLock")
public void notifyRankingUpdateLocked() {
public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
boolean isHiddenRankingUpdate = changedHiddenNotifications != null
&& changedHiddenNotifications.size() > 0;
for (final ManagedServiceInfo serviceInfo : getServices()) {
if (!serviceInfo.isEnabledForCurrentProfiles()) {
continue;
}
final NotificationRankingUpdate update = makeRankingUpdateLocked(serviceInfo);
mHandler.post(new Runnable() {
@Override
public void run() {
notifyRankingUpdate(serviceInfo, update);
boolean notifyThisListener = false;
if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >=
Build.VERSION_CODES.P) {
for (NotificationRecord rec : changedHiddenNotifications) {
if (isVisibleToListener(rec.sbn, serviceInfo)) {
notifyThisListener = true;
break;
}
}
});
}
if (notifyThisListener || !isHiddenRankingUpdate) {
final NotificationRankingUpdate update = makeRankingUpdateLocked(
serviceInfo);
mHandler.post(new Runnable() {
@Override
public void run() {
notifyRankingUpdate(serviceInfo, update);
}
});
}
}
}
@@ -6275,6 +6406,52 @@ public class NotificationManagerService extends SystemService {
}
}
/**
* asynchronously notify relevant listeners their notification is hidden
* NotificationListenerServices that target P+:
* NotificationListenerService#notifyRankingUpdateLocked()
* NotificationListenerServices that target <= P:
* NotificationListenerService#notifyRemovedLocked() with REASON_PACKAGE_SUSPENDED.
*/
@GuardedBy("mNotificationLock")
public void notifyHiddenLocked(List<NotificationRecord> changedNotifications) {
if (changedNotifications == null || changedNotifications.size() == 0) {
return;
}
notifyRankingUpdateLocked(changedNotifications);
// for listeners that target < P, notifyRemoveLocked
int numChangedNotifications = changedNotifications.size();
for (int i = 0; i < numChangedNotifications; i++) {
NotificationRecord rec = changedNotifications.get(i);
mListeners.notifyRemovedLocked(rec, REASON_PACKAGE_SUSPENDED, rec.getStats());
}
}
/**
* asynchronously notify relevant listeners their notification is unhidden
* NotificationListenerServices that target P+:
* NotificationListenerService#notifyRankingUpdateLocked()
* NotificationListenerServices that target <= P:
* NotificationListeners#notifyPostedLocked()
*/
@GuardedBy("mNotificationLock")
public void notifyUnhiddenLocked(List<NotificationRecord> changedNotifications) {
if (changedNotifications == null || changedNotifications.size() == 0) {
return;
}
notifyRankingUpdateLocked(changedNotifications);
// for listeners that target < P, notifyPostedLocked
int numChangedNotifications = changedNotifications.size();
for (int i = 0; i < numChangedNotifications; i++) {
NotificationRecord rec = changedNotifications.get(i);
mListeners.notifyPostedLocked(rec, rec.sbn, false);
}
}
public void notifyInterruptionFilterChanged(final int interruptionFilter) {
for (final ManagedServiceInfo serviceInfo : getServices()) {
if (!serviceInfo.isEnabledForCurrentProfiles()) {
@@ -6503,6 +6680,22 @@ public class NotificationManagerService extends SystemService {
}
}
@VisibleForTesting
protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
// only use for testing: mimic receive broadcast that package is (un)suspended
// but does not actually (un)suspend the package
final Bundle extras = new Bundle();
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
new String[]{pkg});
final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED;
final Intent intent = new Intent(action);
intent.putExtras(extras);
mPackageIntentReceiver.onReceive(getContext(), intent);
}
/**
* Wrapper for a StatusBarNotification object that allows transfer across a oneway
* binder without sending large amounts of data over a oneway transaction.
@@ -6531,7 +6724,9 @@ public class NotificationManagerService extends SystemService {
+ "allow_assistant COMPONENT\n"
+ "remove_assistant COMPONENT\n"
+ "allow_dnd PACKAGE\n"
+ "disallow_dnd PACKAGE";
+ "disallow_dnd PACKAGE\n"
+ "suspend_package PACKAGE\n"
+ "unsuspend_package PACKAGE";
@Override
public int onCommand(String cmd) {
@@ -6600,7 +6795,16 @@ public class NotificationManagerService extends SystemService {
getBinderService().setNotificationAssistantAccessGranted(cn, false);
}
break;
case "suspend_package": {
// only use for testing
simulatePackageSuspendBroadcast(true, getNextArgRequired());
}
break;
case "unsuspend_package": {
// only use for testing
simulatePackageSuspendBroadcast(false, getNextArgRequired());
}
break;
default:
return handleDefaultCommands(cmd);
}

View File

@@ -103,6 +103,9 @@ public final class NotificationRecord {
// is this notification currently being intercepted by Zen Mode?
private boolean mIntercept;
// is this notification hidden since the app pkg is suspended?
private boolean mHidden;
// The timestamp used for ranking.
private long mRankingTimeMs;
@@ -353,6 +356,7 @@ public final class NotificationRecord {
mPackagePriority = previous.mPackagePriority;
mPackageVisibility = previous.mPackageVisibility;
mIntercept = previous.mIntercept;
mHidden = previous.mHidden;
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
mCreationTimeMs = previous.mCreationTimeMs;
mVisibleSinceMs = previous.mVisibleSinceMs;
@@ -498,6 +502,7 @@ public final class NotificationRecord {
+ NotificationListenerService.Ranking.importanceToString(mImportance));
pw.println(prefix + "mImportanceExplanation=" + mImportanceExplanation);
pw.println(prefix + "mIntercept=" + mIntercept);
pw.println(prefix + "mHidden==" + mHidden);
pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
pw.println(prefix + "mRankingTimeMs=" + mRankingTimeMs);
pw.println(prefix + "mCreationTimeMs=" + mCreationTimeMs);
@@ -702,6 +707,15 @@ public final class NotificationRecord {
return mIntercept;
}
public void setHidden(boolean hidden) {
mHidden = hidden;
}
public boolean isHidden() {
return mHidden;
}
public void setSuppressedVisualEffects(int effects) {
mSuppressedVisualEffects = effects;
}

View File

@@ -70,6 +70,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
assertEquals(getShowBadge(i), ranking.canShowBadge());
assertEquals(getUserSentiment(i), ranking.getUserSentiment());
assertEquals(getHidden(i), ranking.isSuspended());
}
}
@@ -85,6 +86,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
Bundle showBadge = new Bundle();
int[] importance = new int[mKeys.length];
Bundle userSentiment = new Bundle();
Bundle mHidden = new Bundle();
for (int i = 0; i < mKeys.length; i++) {
String key = mKeys[i];
@@ -101,11 +103,12 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
showBadge.putBoolean(key, getShowBadge(i));
userSentiment.putInt(key, getUserSentiment(i));
mHidden.putBoolean(key, getHidden(i));
}
NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
interceptedKeys.toArray(new String[0]), visibilityOverrides,
suppressedVisualEffects, importance, explanation, overrideGroupKeys,
channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden);
return update;
}
@@ -153,6 +156,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
return USER_SENTIMENT_NEUTRAL;
}
private boolean getHidden(int index) {
return index % 2 == 0;
}
private ArrayList<String> getPeople(String key, int index) {
ArrayList<String> people = new ArrayList<>();
for (int i = 0; i < index; i++) {

View File

@@ -2739,4 +2739,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertFalse(mService.isVisuallyInterruptive(r1, r2));
}
@Test
public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
// post 2 notification from this package
final NotificationRecord notif1 = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
final NotificationRecord notif2 = generateNotificationRecord(
mTestNotificationChannel, 2, null, false);
mService.addNotification(notif1);
mService.addNotification(notif2);
// on broadcast, hide the 2 notifications
mService.simulatePackageSuspendBroadcast(true, PKG);
ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(2, captorHide.getValue().size());
// on broadcast, unhide the 2 notifications
mService.simulatePackageSuspendBroadcast(false, PKG);
ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(2, captorUnhide.getValue().size());
}
@Test
public void testNoNotificationsHiddenOnSuspendedPackageBroadcast() {
// post 2 notification from this package
final NotificationRecord notif1 = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
final NotificationRecord notif2 = generateNotificationRecord(
mTestNotificationChannel, 2, null, false);
mService.addNotification(notif1);
mService.addNotification(notif2);
// on broadcast, nothing is hidden since no notifications are of package "test_package"
mService.simulatePackageSuspendBroadcast(true, "test_package");
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
assertEquals(0, captor.getValue().size());
}
}