blob: 2abd340e967af835e725610266a35ec27f329869 [file] [log] [blame]
Chris Wren1031c972014-07-23 13:11:45 +00001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.server.notification;
17
Julia Reynoldsb460f1f2019-01-14 10:42:36 -050018import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
Julia Reynolds74856c42017-02-08 14:47:23 -050019import static android.app.NotificationManager.IMPORTANCE_LOW;
20
Julia Reynoldsb460f1f2019-01-14 10:42:36 -050021import static junit.framework.TestCase.assertEquals;
22
Julia Reynolds3d5b3c72017-11-07 09:04:58 -050023import static org.junit.Assert.assertTrue;
24import static org.mockito.ArgumentMatchers.any;
25import static org.mockito.Matchers.anyInt;
Julia Reynolds3d5b3c72017-11-07 09:04:58 -050026import static org.mockito.Matchers.eq;
27import static org.mockito.Mockito.mock;
Julia Reynolds3d5b3c72017-11-07 09:04:58 -050028import static org.mockito.Mockito.when;
29
Chris Wren1031c972014-07-23 13:11:45 +000030import android.app.Notification;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040031import android.app.NotificationChannel;
32import android.app.NotificationManager;
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +010033import android.content.ContentProvider;
34import android.content.Context;
35import android.content.IContentProvider;
Julia Reynolds85769912016-10-25 09:08:57 -040036import android.content.pm.ApplicationInfo;
Julia Reynoldsbcc62ea2018-03-19 10:26:32 -040037import android.content.pm.PackageInfo;
Julia Reynolds85769912016-10-25 09:08:57 -040038import android.content.pm.PackageManager;
Julia Reynoldsbcc62ea2018-03-19 10:26:32 -040039import android.content.pm.Signature;
Julia Reynolds619a69f2017-01-27 15:11:38 -050040import android.media.AudioAttributes;
Julia Reynoldsb5e44b72016-08-16 15:00:25 -040041import android.net.Uri;
Julia Reynolds85769912016-10-25 09:08:57 -040042import android.os.Build;
Chris Wren1031c972014-07-23 13:11:45 +000043import android.os.UserHandle;
44import android.service.notification.StatusBarNotification;
Chris Wren1031c972014-07-23 13:11:45 +000045import android.test.suitebuilder.annotation.SmallTest;
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +010046import android.testing.TestableContentResolver;
Chris Wren1031c972014-07-23 13:11:45 +000047
Brett Chabot84151d92019-02-27 15:37:59 -080048import androidx.test.InstrumentationRegistry;
49import androidx.test.runner.AndroidJUnit4;
50
Jason Monk74f5e362017-12-06 08:56:33 -050051import com.android.server.UiServiceTestCase;
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +010052
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +010053import org.junit.Before;
54import org.junit.Test;
55import org.junit.runner.RunWith;
56import org.mockito.Mock;
57import org.mockito.MockitoAnnotations;
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +010058
Chris Wren1031c972014-07-23 13:11:45 +000059import java.util.ArrayList;
60
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -040061@SmallTest
62@RunWith(AndroidJUnit4.class)
Jason Monk74f5e362017-12-06 08:56:33 -050063public class RankingHelperTest extends UiServiceTestCase {
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -050064 private static final String PKG = "com.android.server.notification";
65 private static final int UID = 0;
Julia Reynolds5fe2eae2017-05-22 08:45:27 -040066 private static final UserHandle USER = UserHandle.of(0);
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -050067 private static final String UPDATED_PKG = "updatedPkg";
Julia Reynolds5fe2eae2017-05-22 08:45:27 -040068 private static final int UID2 = 1111;
Julia Reynoldsbcc62ea2018-03-19 10:26:32 -040069 private static final String SYSTEM_PKG = "android";
70 private static final int SYSTEM_UID= 1000;
Julia Reynolds5fe2eae2017-05-22 08:45:27 -040071 private static final UserHandle USER2 = UserHandle.of(10);
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -050072 private static final String TEST_CHANNEL_ID = "test_channel_id";
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +010073 private static final String TEST_AUTHORITY = "test";
74 private static final Uri SOUND_URI =
75 Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10");
76 private static final Uri CANONICAL_SOUND_URI =
77 Uri.parse("content://" + TEST_AUTHORITY
78 + "/internal/audio/media/10?title=Test&canonical=1");
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -050079
80 @Mock NotificationUsageStats mUsageStats;
81 @Mock RankingHandler mHandler;
82 @Mock PackageManager mPm;
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +010083 @Mock IContentProvider mTestIContentProvider;
Julia Reynoldse0b25742017-05-08 12:55:24 -040084 @Mock Context mContext;
Beverlyc36640f2018-05-21 16:14:15 -040085 @Mock ZenModeHelper mMockZenModeHelper;
Aaron Heuckrothe5bec152018-07-09 16:26:09 -040086 @Mock RankingConfig mConfig;
Chris Wren1031c972014-07-23 13:11:45 +000087
Beverlyc36640f2018-05-21 16:14:15 -040088 private NotificationManager.Policy mTestNotificationPolicy;
Chris Wren1031c972014-07-23 13:11:45 +000089 private Notification mNotiGroupGSortA;
90 private Notification mNotiGroupGSortB;
91 private Notification mNotiNoGroup;
92 private Notification mNotiNoGroup2;
93 private Notification mNotiNoGroupSortA;
94 private NotificationRecord mRecordGroupGSortA;
95 private NotificationRecord mRecordGroupGSortB;
96 private NotificationRecord mRecordNoGroup;
97 private NotificationRecord mRecordNoGroup2;
98 private NotificationRecord mRecordNoGroupSortA;
99 private RankingHelper mHelper;
Julia Reynolds619a69f2017-01-27 15:11:38 -0500100 private AudioAttributes mAudioAttributes;
Chris Wren1031c972014-07-23 13:11:45 +0000101
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -0400102 @Before
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -0500103 public void setUp() throws Exception {
Chris Wren5eab2b72015-06-16 13:56:22 -0400104 MockitoAnnotations.initMocks(this);
Chris Wren1031c972014-07-23 13:11:45 +0000105 UserHandle user = UserHandle.ALL;
106
Julia Reynolds85769912016-10-25 09:08:57 -0400107 final ApplicationInfo legacy = new ApplicationInfo();
108 legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
Julia Reynolds1864ff52016-11-02 09:54:47 -0400109 final ApplicationInfo upgrade = new ApplicationInfo();
Dan Sandlere103b782017-05-17 16:07:56 -0700110 upgrade.targetSdkVersion = Build.VERSION_CODES.O;
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -0500111 when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
112 when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
Julia Reynoldsbcc62ea2018-03-19 10:26:32 -0400113 when(mPm.getApplicationInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(upgrade);
Geoffrey Pitsch1f17e022017-01-03 16:44:20 -0500114 when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
Julia Reynoldsf26eb912017-05-22 15:47:06 -0400115 when(mPm.getPackageUidAsUser(eq(UPDATED_PKG), anyInt())).thenReturn(UID2);
Julia Reynoldsbcc62ea2018-03-19 10:26:32 -0400116 when(mPm.getPackageUidAsUser(eq(SYSTEM_PKG), anyInt())).thenReturn(SYSTEM_UID);
117 PackageInfo info = mock(PackageInfo.class);
118 info.signatures = new Signature[] {mock(Signature.class)};
119 when(mPm.getPackageInfoAsUser(eq(SYSTEM_PKG), anyInt(), anyInt())).thenReturn(info);
120 when(mPm.getPackageInfoAsUser(eq(PKG), anyInt(), anyInt()))
121 .thenReturn(mock(PackageInfo.class));
Julia Reynoldse0b25742017-05-08 12:55:24 -0400122 when(mContext.getResources()).thenReturn(
123 InstrumentationRegistry.getContext().getResources());
Julia Reynolds816797a2017-08-11 15:47:09 -0400124 when(mContext.getContentResolver()).thenReturn(
125 InstrumentationRegistry.getContext().getContentResolver());
Julia Reynoldse0b25742017-05-08 12:55:24 -0400126 when(mContext.getPackageManager()).thenReturn(mPm);
127 when(mContext.getApplicationInfo()).thenReturn(legacy);
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +0100128 TestableContentResolver contentResolver = getContext().getContentResolver();
129 contentResolver.setFallbackToExisting(false);
Julia Reynoldse0b25742017-05-08 12:55:24 -0400130
Bernardo Rufinoc27bb6a2017-10-03 13:55:10 +0100131 ContentProvider testContentProvider = mock(ContentProvider.class);
132 when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
133 contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
134
135 when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
136 .thenReturn(CANONICAL_SOUND_URI);
137 when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
138 .thenReturn(CANONICAL_SOUND_URI);
139 when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
140 .thenReturn(SOUND_URI);
141
Beverlyc36640f2018-05-21 16:14:15 -0400142 mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
143 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
144 when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
Aaron Heuckrothe5bec152018-07-09 16:26:09 -0400145 mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
Julia Reynoldsc861a3d2018-02-15 10:34:49 -0500146 mUsageStats, new String[] {ImportanceExtractor.class.getName()});
Beverly86d076f2018-04-17 14:44:52 -0400147
Julia Reynoldse0b25742017-05-08 12:55:24 -0400148 mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
149 .setContentTitle("A")
150 .setGroup("G")
151 .setSortKey("A")
152 .setWhen(1205)
153 .build();
154 mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification(
155 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user,
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500156 null, System.currentTimeMillis()), getLowChannel());
Julia Reynoldse0b25742017-05-08 12:55:24 -0400157
158 mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID)
159 .setContentTitle("B")
160 .setGroup("G")
161 .setSortKey("B")
162 .setWhen(1200)
163 .build();
164 mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification(
165 PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user,
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500166 null, System.currentTimeMillis()), getLowChannel());
Julia Reynoldse0b25742017-05-08 12:55:24 -0400167
168 mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID)
169 .setContentTitle("C")
170 .setWhen(1201)
171 .build();
172 mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification(
173 PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user,
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500174 null, System.currentTimeMillis()), getLowChannel());
Julia Reynoldse0b25742017-05-08 12:55:24 -0400175
176 mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
177 .setContentTitle("D")
178 .setWhen(1202)
179 .build();
180 mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification(
181 PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user,
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500182 null, System.currentTimeMillis()), getLowChannel());
Julia Reynoldse0b25742017-05-08 12:55:24 -0400183
184 mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
185 .setContentTitle("E")
186 .setWhen(1201)
187 .setSortKey("A")
188 .build();
189 mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification(
190 PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user,
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500191 null, System.currentTimeMillis()), getLowChannel());
Julia Reynoldse0b25742017-05-08 12:55:24 -0400192
193 mAudioAttributes = new AudioAttributes.Builder()
194 .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
195 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
196 .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
197 .build();
Chris Wren1031c972014-07-23 13:11:45 +0000198 }
199
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500200 private NotificationChannel getLowChannel() {
Julia Reynolds85769912016-10-25 09:08:57 -0400201 return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
Julia Reynolds74856c42017-02-08 14:47:23 -0500202 IMPORTANCE_LOW);
Julia Reynolds85769912016-10-25 09:08:57 -0400203 }
204
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500205 private NotificationChannel getDefaultChannel() {
206 return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
207 IMPORTANCE_DEFAULT);
208 }
209
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -0400210 @Test
Brad Stenning9a8b2c82018-08-03 14:14:26 -0700211 public void testSortShouldRespectCritical() throws Exception {
212 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(7);
213 NotificationRecord critical = generateRecord(0);
214 NotificationRecord critical_ish = generateRecord(1);
215 NotificationRecord critical_notAtAll = generateRecord(100);
216
217 notificationList.add(critical_ish);
218 notificationList.add(mRecordGroupGSortA);
219 notificationList.add(critical_notAtAll);
220 notificationList.add(mRecordGroupGSortB);
221 notificationList.add(mRecordNoGroup);
222 notificationList.add(mRecordNoGroupSortA);
223 notificationList.add(critical);
224 mHelper.sort(notificationList);
225
226 assertTrue(mHelper.indexOf(notificationList, critical) == 0);
227 assertTrue(mHelper.indexOf(notificationList, critical_ish) == 1);
228 assertTrue(mHelper.indexOf(notificationList, critical_notAtAll) == 6);
229 }
230
231 private NotificationRecord generateRecord(int criticality) {
232 NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
233 final Notification.Builder builder = new Notification.Builder(getContext())
234 .setContentTitle("foo")
235 .setSmallIcon(android.R.drawable.sym_def_app_icon);
236 Notification n = builder.build();
237 StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
238 0, n, UserHandle.ALL, null, System.currentTimeMillis());
239 NotificationRecord notificationRecord = new NotificationRecord(getContext(), sbn, channel);
240 notificationRecord.setCriticality(criticality);
241 return notificationRecord;
242 }
243
244 @Test
Chris Wren1031c972014-07-23 13:11:45 +0000245 public void testFindAfterRankingWithASplitGroup() throws Exception {
Brad Stenning9a8b2c82018-08-03 14:14:26 -0700246 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(4);
Chris Wren1031c972014-07-23 13:11:45 +0000247 notificationList.add(mRecordGroupGSortA);
248 notificationList.add(mRecordGroupGSortB);
249 notificationList.add(mRecordNoGroup);
250 notificationList.add(mRecordNoGroupSortA);
251 mHelper.sort(notificationList);
252 assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0);
253 assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0);
254 assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0);
255 assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0);
256 }
257
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -0400258 @Test
Chris Wren1031c972014-07-23 13:11:45 +0000259 public void testSortShouldNotThrowWithPlainNotifications() throws Exception {
260 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
261 notificationList.add(mRecordNoGroup);
262 notificationList.add(mRecordNoGroup2);
263 mHelper.sort(notificationList);
264 }
265
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -0400266 @Test
Chris Wren1031c972014-07-23 13:11:45 +0000267 public void testSortShouldNotThrowOneSorted() throws Exception {
268 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
269 notificationList.add(mRecordNoGroup);
270 notificationList.add(mRecordNoGroupSortA);
271 mHelper.sort(notificationList);
272 }
273
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -0400274 @Test
Chris Wren1031c972014-07-23 13:11:45 +0000275 public void testSortShouldNotThrowOneNotification() throws Exception {
276 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
277 notificationList.add(mRecordNoGroup);
278 mHelper.sort(notificationList);
279 }
280
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -0400281 @Test
Chris Wren1031c972014-07-23 13:11:45 +0000282 public void testSortShouldNotThrowOneSortKey() throws Exception {
283 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
284 notificationList.add(mRecordGroupGSortB);
285 mHelper.sort(notificationList);
286 }
287
Geoffrey Pitsch0ffe7552016-08-29 10:07:40 -0400288 @Test
Chris Wren1031c972014-07-23 13:11:45 +0000289 public void testSortShouldNotThrowOnEmptyList() throws Exception {
290 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
291 mHelper.sort(notificationList);
292 }
Julia Reynoldsb460f1f2019-01-14 10:42:36 -0500293
294 @Test
295 public void testGroupNotifications_highestIsProxy() {
296 ArrayList<NotificationRecord> notificationList = new ArrayList<>();
297 // this should be the last in the list, except it's in a group with a high child
298 Notification lowSummaryN = new Notification.Builder(mContext, "")
299 .setGroup("group")
300 .setGroupSummary(true)
301 .build();
302 NotificationRecord lowSummary = new NotificationRecord(mContext, new StatusBarNotification(
303 PKG, PKG, 1, "summary", 0, 0, lowSummaryN, USER,
304 null, System.currentTimeMillis()), getLowChannel());
305 notificationList.add(lowSummary);
306
307 Notification lowN = new Notification.Builder(mContext, "").build();
308 NotificationRecord low = new NotificationRecord(mContext, new StatusBarNotification(
309 PKG, PKG, 1, "low", 0, 0, lowN, USER,
310 null, System.currentTimeMillis()), getLowChannel());
311 low.setContactAffinity(0.5f);
312 notificationList.add(low);
313
314 Notification highChildN = new Notification.Builder(mContext, "")
315 .setGroup("group")
316 .setGroupSummary(false)
317 .build();
318 NotificationRecord highChild = new NotificationRecord(mContext, new StatusBarNotification(
319 PKG, PKG, 1, "child", 0, 0, highChildN, USER,
320 null, System.currentTimeMillis()), getDefaultChannel());
321 notificationList.add(highChild);
322
323 mHelper.sort(notificationList);
324
325 assertEquals(lowSummary, notificationList.get(0));
326 assertEquals(highChild, notificationList.get(1));
327 assertEquals(low, notificationList.get(2));
328 }
Chris Wren1031c972014-07-23 13:11:45 +0000329}