Loading core/java/android/app/INotificationManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ interface INotificationManager NotificationChannelGroup getNotificationChannelGroup(String pkg, String channelGroupId); ParceledListSlice getNotificationChannelGroups(String pkg); boolean onlyHasDefaultChannel(String pkg, int uid); ParceledListSlice getRecentNotifyingAppsForUser(int userId); // TODO: Remove this when callers have been migrated to the equivalent // INotificationListener method. Loading core/java/android/service/notification/NotifyingApp.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /** * Copyright (c) 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.notification; parcelable NotifyingApp; No newline at end of file core/java/android/service/notification/NotifyingApp.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.notification; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; /** * @hide */ public final class NotifyingApp implements Parcelable, Comparable<NotifyingApp> { private int mUid; private String mPkg; private long mLastNotified; public NotifyingApp() {} protected NotifyingApp(Parcel in) { mUid = in.readInt(); mPkg = in.readString(); mLastNotified = in.readLong(); } public int getUid() { return mUid; } /** * Sets the uid of the package that sent the notification. Returns self. */ public NotifyingApp setUid(int mUid) { this.mUid = mUid; return this; } public String getPackage() { return mPkg; } /** * Sets the package that sent the notification. Returns self. */ public NotifyingApp setPackage(@NonNull String mPkg) { this.mPkg = mPkg; return this; } public long getLastNotified() { return mLastNotified; } /** * Sets the time the notification was originally sent. Returns self. */ public NotifyingApp setLastNotified(long mLastNotified) { this.mLastNotified = mLastNotified; return this; } public static final Creator<NotifyingApp> CREATOR = new Creator<NotifyingApp>() { @Override public NotifyingApp createFromParcel(Parcel in) { return new NotifyingApp(in); } @Override public NotifyingApp[] newArray(int size) { return new NotifyingApp[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mUid); dest.writeString(mPkg); dest.writeLong(mLastNotified); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NotifyingApp that = (NotifyingApp) o; return getUid() == that.getUid() && getLastNotified() == that.getLastNotified() && Objects.equals(mPkg, that.mPkg); } @Override public int hashCode() { return Objects.hash(getUid(), mPkg, getLastNotified()); } /** * Sorts notifying apps from newest last notified date to oldest. */ @Override public int compareTo(NotifyingApp o) { if (getLastNotified() == o.getLastNotified()) { if (getUid() == o.getUid()) { return getPackage().compareTo(o.getPackage()); } return Integer.compare(getUid(), o.getUid()); } return -Long.compare(getLastNotified(), o.getLastNotified()); } @Override public String toString() { return "NotifyingApp{" + "mUid=" + mUid + ", mPkg='" + mPkg + '\'' + ", mLastNotified=" + mLastNotified + '}'; } } services/core/java/com/android/server/notification/NotificationManagerService.java +48 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationRecordProto; import android.service.notification.NotificationServiceDumpProto; import android.service.notification.NotificationStats; import android.service.notification.NotifyingApp; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; Loading Loading @@ -329,6 +330,7 @@ public class NotificationManagerService extends SystemService { final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<>(); final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); final ArrayMap<Integer, ArrayList<NotifyingApp>> mRecentApps = new ArrayMap<>(); // The last key in this list owns the hardware. ArrayList<String> mLights = new ArrayList<>(); Loading Loading @@ -2109,6 +2111,16 @@ public class NotificationManagerService extends SystemService { pkg, Binder.getCallingUid(), false /* includeDeleted */); } @Override public ParceledListSlice<NotifyingApp> getRecentNotifyingAppsForUser(int userId) { checkCallerIsSystem(); synchronized (mNotificationLock) { List<NotifyingApp> apps = new ArrayList<>( mRecentApps.getOrDefault(userId, new ArrayList<>())); return new ParceledListSlice<>(apps); } } @Override public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException { checkCallerIsSystem(); Loading Loading @@ -4096,6 +4108,10 @@ public class NotificationManagerService extends SystemService { mNotificationsByKey.put(n.getKey(), r); if (!r.isUpdate) { logRecentLocked(r); } // Ensure if this is a foreground service that the proper additional // flags are set. if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { Loading Loading @@ -4152,6 +4168,38 @@ public class NotificationManagerService extends SystemService { } } /** * Keeps the last 5 packages that have notified, by user. */ @GuardedBy("mNotificationLock") @VisibleForTesting protected void logRecentLocked(NotificationRecord r) { if (r.isUpdate) { return; } ArrayList<NotifyingApp> recentAppsForUser = mRecentApps.getOrDefault(r.getUser().getIdentifier(), new ArrayList<>(6)); NotifyingApp na = new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime()); // A new notification gets an app moved to the front of the list for (int i = recentAppsForUser.size() - 1; i >= 0; i--) { NotifyingApp naExisting = recentAppsForUser.get(i); if (na.getPackage().equals(naExisting.getPackage()) && na.getUid() == naExisting.getUid()) { recentAppsForUser.remove(i); break; } } // time is always increasing, so always add to the front of the list recentAppsForUser.add(0, na); if (recentAppsForUser.size() > 5) { recentAppsForUser.remove(recentAppsForUser.size() -1); } mRecentApps.put(r.getUser().getIdentifier(), recentAppsForUser); } /** * Ensures that grouped notification receive their special treatment. * Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +111 −1 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import android.provider.Settings.Secure; import android.service.notification.Adjustment; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.NotifyingApp; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; Loading Loading @@ -105,8 +106,10 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) Loading Loading @@ -253,10 +256,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mFile.delete(); } public void waitForIdle() throws Exception { public void waitForIdle() { mTestableLooper.processAllMessages(); } private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) { Notification.Builder nb = new Notification.Builder(mContext, "a") .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, uid, "tag", uid, 0, nb.build(), new UserHandle(userId), null, postTime); return sbn; } private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id, String groupKey, boolean isSummary) { Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) Loading Loading @@ -2291,4 +2303,102 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate(); } @Test public void testRecents() throws Exception { Set<NotifyingApp> expected = new HashSet<>(); final NotificationRecord oldest = new NotificationRecord(mContext, generateSbn("p", 1000, 9, 0), mTestNotificationChannel); mService.logRecentLocked(oldest); for (int i = 1; i <= 5; i++) { NotificationRecord r = new NotificationRecord(mContext, generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel); expected.add(new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime())); mService.logRecentLocked(r); } List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList(); assertTrue(apps.size() == 5); for (NotifyingApp actual : apps) { assertTrue("got unexpected result: " + actual, expected.contains(actual)); } } @Test public void testRecentsNoDuplicatePackages() throws Exception { final NotificationRecord p1 = new NotificationRecord(mContext, generateSbn("p", 1, 1000, 0), mTestNotificationChannel); final NotificationRecord p2 = new NotificationRecord(mContext, generateSbn("p", 1, 2000, 0), mTestNotificationChannel); mService.logRecentLocked(p1); mService.logRecentLocked(p2); List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList(); assertTrue(apps.size() == 1); NotifyingApp expected = new NotifyingApp().setPackage("p").setUid(1).setLastNotified(2000); assertEquals(expected, apps.get(0)); } @Test public void testRecentsWithDuplicatePackage() throws Exception { Set<NotifyingApp> expected = new HashSet<>(); final NotificationRecord oldest = new NotificationRecord(mContext, generateSbn("p", 1000, 9, 0), mTestNotificationChannel); mService.logRecentLocked(oldest); for (int i = 1; i <= 5; i++) { NotificationRecord r = new NotificationRecord(mContext, generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel); expected.add(new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime())); mService.logRecentLocked(r); } NotificationRecord r = new NotificationRecord(mContext, generateSbn("p" + 3, 3, 300000, 0), mTestNotificationChannel); expected.remove(new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(3) .setLastNotified(300)); NotifyingApp newest = new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime()); expected.add(newest); mService.logRecentLocked(r); List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList(); assertTrue(apps.size() == 5); for (NotifyingApp actual : apps) { assertTrue("got unexpected result: " + actual, expected.contains(actual)); } assertEquals(newest, apps.get(0)); } @Test public void testRecentsMultiuser() throws Exception { final NotificationRecord user1 = new NotificationRecord(mContext, generateSbn("p", 1000, 9, 1), mTestNotificationChannel); mService.logRecentLocked(user1); final NotificationRecord user2 = new NotificationRecord(mContext, generateSbn("p2", 100000, 9999, 2), mTestNotificationChannel); mService.logRecentLocked(user2); assertEquals(0, mBinderService.getRecentNotifyingAppsForUser(0).getList().size()); assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(1).getList().size()); assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(2).getList().size()); assertTrue(mBinderService.getRecentNotifyingAppsForUser(2).getList().contains( new NotifyingApp() .setPackage(user2.sbn.getPackageName()) .setUid(user2.sbn.getUid()) .setLastNotified(user2.sbn.getPostTime()))); } } Loading
core/java/android/app/INotificationManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ interface INotificationManager NotificationChannelGroup getNotificationChannelGroup(String pkg, String channelGroupId); ParceledListSlice getNotificationChannelGroups(String pkg); boolean onlyHasDefaultChannel(String pkg, int uid); ParceledListSlice getRecentNotifyingAppsForUser(int userId); // TODO: Remove this when callers have been migrated to the equivalent // INotificationListener method. Loading
core/java/android/service/notification/NotifyingApp.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /** * Copyright (c) 2018, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.notification; parcelable NotifyingApp; No newline at end of file
core/java/android/service/notification/NotifyingApp.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.notification; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; /** * @hide */ public final class NotifyingApp implements Parcelable, Comparable<NotifyingApp> { private int mUid; private String mPkg; private long mLastNotified; public NotifyingApp() {} protected NotifyingApp(Parcel in) { mUid = in.readInt(); mPkg = in.readString(); mLastNotified = in.readLong(); } public int getUid() { return mUid; } /** * Sets the uid of the package that sent the notification. Returns self. */ public NotifyingApp setUid(int mUid) { this.mUid = mUid; return this; } public String getPackage() { return mPkg; } /** * Sets the package that sent the notification. Returns self. */ public NotifyingApp setPackage(@NonNull String mPkg) { this.mPkg = mPkg; return this; } public long getLastNotified() { return mLastNotified; } /** * Sets the time the notification was originally sent. Returns self. */ public NotifyingApp setLastNotified(long mLastNotified) { this.mLastNotified = mLastNotified; return this; } public static final Creator<NotifyingApp> CREATOR = new Creator<NotifyingApp>() { @Override public NotifyingApp createFromParcel(Parcel in) { return new NotifyingApp(in); } @Override public NotifyingApp[] newArray(int size) { return new NotifyingApp[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mUid); dest.writeString(mPkg); dest.writeLong(mLastNotified); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NotifyingApp that = (NotifyingApp) o; return getUid() == that.getUid() && getLastNotified() == that.getLastNotified() && Objects.equals(mPkg, that.mPkg); } @Override public int hashCode() { return Objects.hash(getUid(), mPkg, getLastNotified()); } /** * Sorts notifying apps from newest last notified date to oldest. */ @Override public int compareTo(NotifyingApp o) { if (getLastNotified() == o.getLastNotified()) { if (getUid() == o.getUid()) { return getPackage().compareTo(o.getPackage()); } return Integer.compare(getUid(), o.getUid()); } return -Long.compare(getLastNotified(), o.getLastNotified()); } @Override public String toString() { return "NotifyingApp{" + "mUid=" + mUid + ", mPkg='" + mPkg + '\'' + ", mLastNotified=" + mLastNotified + '}'; } }
services/core/java/com/android/server/notification/NotificationManagerService.java +48 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,7 @@ import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationRecordProto; import android.service.notification.NotificationServiceDumpProto; import android.service.notification.NotificationStats; import android.service.notification.NotifyingApp; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; Loading Loading @@ -329,6 +330,7 @@ public class NotificationManagerService extends SystemService { final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<>(); final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); final ArrayMap<Integer, ArrayList<NotifyingApp>> mRecentApps = new ArrayMap<>(); // The last key in this list owns the hardware. ArrayList<String> mLights = new ArrayList<>(); Loading Loading @@ -2109,6 +2111,16 @@ public class NotificationManagerService extends SystemService { pkg, Binder.getCallingUid(), false /* includeDeleted */); } @Override public ParceledListSlice<NotifyingApp> getRecentNotifyingAppsForUser(int userId) { checkCallerIsSystem(); synchronized (mNotificationLock) { List<NotifyingApp> apps = new ArrayList<>( mRecentApps.getOrDefault(userId, new ArrayList<>())); return new ParceledListSlice<>(apps); } } @Override public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException { checkCallerIsSystem(); Loading Loading @@ -4096,6 +4108,10 @@ public class NotificationManagerService extends SystemService { mNotificationsByKey.put(n.getKey(), r); if (!r.isUpdate) { logRecentLocked(r); } // Ensure if this is a foreground service that the proper additional // flags are set. if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { Loading Loading @@ -4152,6 +4168,38 @@ public class NotificationManagerService extends SystemService { } } /** * Keeps the last 5 packages that have notified, by user. */ @GuardedBy("mNotificationLock") @VisibleForTesting protected void logRecentLocked(NotificationRecord r) { if (r.isUpdate) { return; } ArrayList<NotifyingApp> recentAppsForUser = mRecentApps.getOrDefault(r.getUser().getIdentifier(), new ArrayList<>(6)); NotifyingApp na = new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime()); // A new notification gets an app moved to the front of the list for (int i = recentAppsForUser.size() - 1; i >= 0; i--) { NotifyingApp naExisting = recentAppsForUser.get(i); if (na.getPackage().equals(naExisting.getPackage()) && na.getUid() == naExisting.getUid()) { recentAppsForUser.remove(i); break; } } // time is always increasing, so always add to the front of the list recentAppsForUser.add(0, na); if (recentAppsForUser.size() > 5) { recentAppsForUser.remove(recentAppsForUser.size() -1); } mRecentApps.put(r.getUser().getIdentifier(), recentAppsForUser); } /** * Ensures that grouped notification receive their special treatment. * Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +111 −1 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import android.provider.Settings.Secure; import android.service.notification.Adjustment; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.NotifyingApp; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; Loading Loading @@ -105,8 +106,10 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) Loading Loading @@ -253,10 +256,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mFile.delete(); } public void waitForIdle() throws Exception { public void waitForIdle() { mTestableLooper.processAllMessages(); } private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) { Notification.Builder nb = new Notification.Builder(mContext, "a") .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon); StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, uid, "tag", uid, 0, nb.build(), new UserHandle(userId), null, postTime); return sbn; } private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id, String groupKey, boolean isSummary) { Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) Loading Loading @@ -2291,4 +2303,102 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate(); } @Test public void testRecents() throws Exception { Set<NotifyingApp> expected = new HashSet<>(); final NotificationRecord oldest = new NotificationRecord(mContext, generateSbn("p", 1000, 9, 0), mTestNotificationChannel); mService.logRecentLocked(oldest); for (int i = 1; i <= 5; i++) { NotificationRecord r = new NotificationRecord(mContext, generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel); expected.add(new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime())); mService.logRecentLocked(r); } List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList(); assertTrue(apps.size() == 5); for (NotifyingApp actual : apps) { assertTrue("got unexpected result: " + actual, expected.contains(actual)); } } @Test public void testRecentsNoDuplicatePackages() throws Exception { final NotificationRecord p1 = new NotificationRecord(mContext, generateSbn("p", 1, 1000, 0), mTestNotificationChannel); final NotificationRecord p2 = new NotificationRecord(mContext, generateSbn("p", 1, 2000, 0), mTestNotificationChannel); mService.logRecentLocked(p1); mService.logRecentLocked(p2); List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList(); assertTrue(apps.size() == 1); NotifyingApp expected = new NotifyingApp().setPackage("p").setUid(1).setLastNotified(2000); assertEquals(expected, apps.get(0)); } @Test public void testRecentsWithDuplicatePackage() throws Exception { Set<NotifyingApp> expected = new HashSet<>(); final NotificationRecord oldest = new NotificationRecord(mContext, generateSbn("p", 1000, 9, 0), mTestNotificationChannel); mService.logRecentLocked(oldest); for (int i = 1; i <= 5; i++) { NotificationRecord r = new NotificationRecord(mContext, generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel); expected.add(new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime())); mService.logRecentLocked(r); } NotificationRecord r = new NotificationRecord(mContext, generateSbn("p" + 3, 3, 300000, 0), mTestNotificationChannel); expected.remove(new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(3) .setLastNotified(300)); NotifyingApp newest = new NotifyingApp() .setPackage(r.sbn.getPackageName()) .setUid(r.sbn.getUid()) .setLastNotified(r.sbn.getPostTime()); expected.add(newest); mService.logRecentLocked(r); List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList(); assertTrue(apps.size() == 5); for (NotifyingApp actual : apps) { assertTrue("got unexpected result: " + actual, expected.contains(actual)); } assertEquals(newest, apps.get(0)); } @Test public void testRecentsMultiuser() throws Exception { final NotificationRecord user1 = new NotificationRecord(mContext, generateSbn("p", 1000, 9, 1), mTestNotificationChannel); mService.logRecentLocked(user1); final NotificationRecord user2 = new NotificationRecord(mContext, generateSbn("p2", 100000, 9999, 2), mTestNotificationChannel); mService.logRecentLocked(user2); assertEquals(0, mBinderService.getRecentNotifyingAppsForUser(0).getList().size()); assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(1).getList().size()); assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(2).getList().size()); assertTrue(mBinderService.getRecentNotifyingAppsForUser(2).getList().contains( new NotifyingApp() .setPackage(user2.sbn.getPackageName()) .setUid(user2.sbn.getUid()) .setLastNotified(user2.sbn.getPostTime()))); } }