RankingHelperTest.java revision 423b9fc83d736326b8ddadbfcd6fd5f181bf9fec
1/*
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
18import org.junit.Before;
19import org.junit.Test;
20import org.junit.runner.RunWith;
21
22import com.android.internal.util.FastXmlSerializer;
23
24import org.mockito.Mock;
25import org.mockito.MockitoAnnotations;
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlSerializer;
28
29import android.app.Notification;
30import android.content.Context;
31import android.app.NotificationChannel;
32import android.app.NotificationManager;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.PackageManager;
35import android.net.Uri;
36import android.os.Build;
37import android.os.UserHandle;
38import android.service.notification.StatusBarNotification;
39import android.support.test.InstrumentationRegistry;
40import android.support.test.runner.AndroidJUnit4;
41import android.test.suitebuilder.annotation.SmallTest;
42import android.util.Xml;
43
44import java.io.BufferedInputStream;
45import java.io.BufferedOutputStream;
46import java.io.ByteArrayInputStream;
47import java.io.ByteArrayOutputStream;
48import java.util.ArrayList;
49
50import static org.junit.Assert.assertEquals;
51import static org.junit.Assert.assertFalse;
52import static org.junit.Assert.assertNotNull;
53import static org.junit.Assert.assertTrue;
54import static org.mockito.Matchers.anyInt;
55import static org.mockito.Matchers.anyString;
56import static org.mockito.Matchers.eq;
57import static org.mockito.Mockito.when;
58
59@SmallTest
60@RunWith(AndroidJUnit4.class)
61public class RankingHelperTest {
62    @Mock NotificationUsageStats mUsageStats;
63    @Mock RankingHandler handler;
64    @Mock PackageManager mPm;
65
66    private Notification mNotiGroupGSortA;
67    private Notification mNotiGroupGSortB;
68    private Notification mNotiNoGroup;
69    private Notification mNotiNoGroup2;
70    private Notification mNotiNoGroupSortA;
71    private NotificationRecord mRecordGroupGSortA;
72    private NotificationRecord mRecordGroupGSortB;
73    private NotificationRecord mRecordNoGroup;
74    private NotificationRecord mRecordNoGroup2;
75    private NotificationRecord mRecordNoGroupSortA;
76    private RankingHelper mHelper;
77    private final String pkg = "com.android.server.notification";
78    private final int uid = 0;
79    private final String pkg2 = "pkg2";
80    private final int uid2 = 1111111;
81
82    private Context getContext() {
83        return InstrumentationRegistry.getTargetContext();
84    }
85
86    @Before
87    public void setUp() {
88        MockitoAnnotations.initMocks(this);
89        UserHandle user = UserHandle.ALL;
90
91        mHelper = new RankingHelper(getContext(), mPm, handler, mUsageStats,
92                new String[] {ImportanceExtractor.class.getName()});
93
94        mNotiGroupGSortA = new Notification.Builder(getContext())
95                .setContentTitle("A")
96                .setGroup("G")
97                .setSortKey("A")
98                .setWhen(1205)
99                .build();
100        mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
101                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortA, user,
102                null, System.currentTimeMillis()));
103
104        mNotiGroupGSortB = new Notification.Builder(getContext())
105                .setContentTitle("B")
106                .setGroup("G")
107                .setSortKey("B")
108                .setWhen(1200)
109                .build();
110        mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
111                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiGroupGSortB, user,
112                null, System.currentTimeMillis()));
113
114        mNotiNoGroup = new Notification.Builder(getContext())
115                .setContentTitle("C")
116                .setWhen(1201)
117                .build();
118        mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
119                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup, user,
120                null, System.currentTimeMillis()));
121
122        mNotiNoGroup2 = new Notification.Builder(getContext())
123                .setContentTitle("D")
124                .setWhen(1202)
125                .build();
126        mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
127                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroup2, user,
128                null, System.currentTimeMillis()));
129
130        mNotiNoGroupSortA = new Notification.Builder(getContext())
131                .setContentTitle("E")
132                .setWhen(1201)
133                .setSortKey("A")
134                .build();
135        mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
136                "package", "package", getDefaultChannel(), 1, null, 0, 0, mNotiNoGroupSortA, user,
137                null, System.currentTimeMillis()));
138
139        final ApplicationInfo legacy = new ApplicationInfo();
140        legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
141        final ApplicationInfo upgrade = new ApplicationInfo();
142        upgrade.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
143        try {
144            when(mPm.getApplicationInfoAsUser(eq(pkg), anyInt(), anyInt())).thenReturn(legacy);
145            when(mPm.getApplicationInfoAsUser(eq(pkg2), anyInt(), anyInt())).thenReturn(upgrade);
146        } catch (PackageManager.NameNotFoundException e) {}
147    }
148
149    private NotificationChannel getDefaultChannel() {
150        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
151                NotificationManager.IMPORTANCE_LOW);
152    }
153
154    private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, String... channelIds)
155            throws Exception {
156        XmlSerializer serializer = new FastXmlSerializer();
157        ByteArrayOutputStream baos = new ByteArrayOutputStream();
158        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
159        serializer.startDocument(null, true);
160        serializer.startTag(null, "ranking");
161        mHelper.writeXml(serializer, false);
162        serializer.endTag(null, "ranking");
163        serializer.endDocument();
164        serializer.flush();
165
166        for (String channelId : channelIds) {
167            mHelper.deleteNotificationChannel(pkg, uid, channelId);
168        }
169        return baos;
170    }
171
172    @Test
173    public void testFindAfterRankingWithASplitGroup() throws Exception {
174        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
175        notificationList.add(mRecordGroupGSortA);
176        notificationList.add(mRecordGroupGSortB);
177        notificationList.add(mRecordNoGroup);
178        notificationList.add(mRecordNoGroupSortA);
179        mHelper.sort(notificationList);
180        assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0);
181        assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0);
182        assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0);
183        assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0);
184    }
185
186    @Test
187    public void testSortShouldNotThrowWithPlainNotifications() throws Exception {
188        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
189        notificationList.add(mRecordNoGroup);
190        notificationList.add(mRecordNoGroup2);
191        mHelper.sort(notificationList);
192    }
193
194    @Test
195    public void testSortShouldNotThrowOneSorted() throws Exception {
196        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
197        notificationList.add(mRecordNoGroup);
198        notificationList.add(mRecordNoGroupSortA);
199        mHelper.sort(notificationList);
200    }
201
202    @Test
203    public void testSortShouldNotThrowOneNotification() throws Exception {
204        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
205        notificationList.add(mRecordNoGroup);
206        mHelper.sort(notificationList);
207    }
208
209    @Test
210    public void testSortShouldNotThrowOneSortKey() throws Exception {
211        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
212        notificationList.add(mRecordGroupGSortB);
213        mHelper.sort(notificationList);
214    }
215
216    @Test
217    public void testSortShouldNotThrowOnEmptyList() throws Exception {
218        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
219        mHelper.sort(notificationList);
220    }
221
222    @Test
223    public void testChannelXml() throws Exception {
224        NotificationChannel channel1 =
225                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
226        NotificationChannel channel2 =
227                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
228        channel2.setRingtone(new Uri.Builder().scheme("test").build());
229        channel2.setLights(true);
230        channel2.setBypassDnd(true);
231        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
232
233        mHelper.createNotificationChannel(pkg, uid, channel1);
234        mHelper.createNotificationChannel(pkg, uid, channel2);
235
236        ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(),
237                NotificationChannel.DEFAULT_CHANNEL_ID);
238
239        mHelper.deleteNotificationChannel(pkg, uid, channel1.getId());
240        mHelper.deleteNotificationChannel(pkg, uid, channel2.getId());
241        mHelper.deleteNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
242
243        XmlPullParser parser = Xml.newPullParser();
244        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
245                null);
246        parser.nextTag();
247        mHelper.readXml(parser, false);
248
249        assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId()));
250        assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel2.getId()));
251        assertNotNull(
252                mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID));
253    }
254
255    @Test
256    public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
257        NotificationChannel channel1 =
258                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_DEFAULT);
259
260        mHelper.createNotificationChannel(pkg, uid, channel1);
261
262        ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(),
263                NotificationChannel.DEFAULT_CHANNEL_ID);
264
265        XmlPullParser parser = Xml.newPullParser();
266        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
267                null);
268        parser.nextTag();
269        mHelper.readXml(parser, false);
270
271        final NotificationChannel updated =
272                mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
273        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
274        assertFalse(updated.canBypassDnd());
275        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,updated.getLockscreenVisibility());
276        assertEquals(0, updated.getUserLockedFields());
277    }
278
279    @Test
280    public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
281         NotificationChannel channel1 =
282                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MIN);
283        mHelper.createNotificationChannel(pkg, uid, channel1);
284
285        final NotificationChannel defaultChannel =
286                mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
287        defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
288        mHelper.updateNotificationChannel(pkg, uid, defaultChannel);
289
290        ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(),
291                NotificationChannel.DEFAULT_CHANNEL_ID);
292
293        XmlPullParser parser = Xml.newPullParser();
294        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
295                null);
296        parser.nextTag();
297        mHelper.readXml(parser, false);
298
299        assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel(
300                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID).getImportance());
301    }
302
303    @Test
304    public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
305        final String preupgradeXml = "<ranking version=\"1\">\n"
306             + "<package name=\"" + pkg + "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH
307            + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
308            + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + uid + "\" />\n"
309            + "<package name=\"" + pkg2 + "\" uid=\"" + uid2 + "\" visibility=\""
310            + Notification.VISIBILITY_PRIVATE + "\" />\n"
311            + "</ranking>";
312        XmlPullParser parser = Xml.newPullParser();
313        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
314            null);
315        parser.nextTag();
316        mHelper.readXml(parser, false);
317
318        final NotificationChannel updated1 =
319            mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID);
320        assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
321        assertTrue(updated1.canBypassDnd());
322        assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
323        assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE
324            | NotificationChannel.USER_LOCKED_PRIORITY
325            | NotificationChannel.USER_LOCKED_VISIBILITY, updated1.getUserLockedFields());
326
327        final NotificationChannel updated2 =
328            mHelper.getNotificationChannel(pkg2, uid2, NotificationChannel.DEFAULT_CHANNEL_ID);
329        // clamped
330        assertEquals(NotificationManager.IMPORTANCE_LOW, updated2.getImportance());
331        assertFalse(updated2.canBypassDnd());
332        assertEquals(Notification.VISIBILITY_PRIVATE, updated2.getLockscreenVisibility());
333        assertEquals(NotificationChannel.USER_LOCKED_VISIBILITY, updated2.getUserLockedFields());
334    }
335
336    @Test
337    public void testUpdate_userLockedImportance() throws Exception {
338        // all fields locked by user
339        final NotificationChannel channel =
340            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
341        channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
342
343        mHelper.createNotificationChannel(pkg, uid, channel);
344
345        // same id, try to update
346        final NotificationChannel channel2 =
347            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
348
349        mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2);
350
351        // no fields should be changed
352        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
353    }
354
355    @Test
356    public void testUpdate_userLockedVisibility() throws Exception {
357        // all fields locked by user
358        final NotificationChannel channel =
359            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
360        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
361        channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
362
363        mHelper.createNotificationChannel(pkg, uid, channel);
364
365        // same id, try to update
366        final NotificationChannel channel2 =
367            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
368        channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
369
370        mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2);
371
372        // no fields should be changed
373        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
374    }
375
376    @Test
377    public void testUpdate_userLockedVibration() throws Exception {
378        // all fields locked by user
379        final NotificationChannel channel =
380            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
381        channel.setLights(false);
382        channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
383
384        mHelper.createNotificationChannel(pkg, uid, channel);
385
386        // same id, try to update
387        final NotificationChannel channel2 =
388            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
389        channel2.setVibration(true);
390
391        mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2);
392
393        // no fields should be changed
394        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
395    }
396
397    @Test
398    public void testUpdate_userLockedLights() throws Exception {
399        // all fields locked by user
400        final NotificationChannel channel =
401            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
402        channel.setLights(false);
403        channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
404
405        mHelper.createNotificationChannel(pkg, uid, channel);
406
407        // same id, try to update
408        final NotificationChannel channel2 =
409            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
410        channel2.setLights(true);
411
412        mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2);
413
414        // no fields should be changed
415        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
416    }
417
418    @Test
419    public void testUpdate_userLockedPriority() throws Exception {
420        // all fields locked by user
421        final NotificationChannel channel =
422            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
423        channel.setBypassDnd(true);
424        channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
425
426        mHelper.createNotificationChannel(pkg, uid, channel);
427
428        // same id, try to update all fields
429        final NotificationChannel channel2 =
430            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
431        channel2.setBypassDnd(false);
432
433        mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2);
434
435        // no fields should be changed
436        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
437    }
438
439    @Test
440    public void testUpdate_userLockedRingtone() throws Exception {
441        // all fields locked by user
442        final NotificationChannel channel =
443            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
444        channel.setRingtone(new Uri.Builder().scheme("test").build());
445        channel.lockFields(NotificationChannel.USER_LOCKED_RINGTONE);
446
447        mHelper.createNotificationChannel(pkg, uid, channel);
448
449        // same id, try to update all fields
450        final NotificationChannel channel2 =
451            new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
452        channel2.setRingtone(new Uri.Builder().scheme("test2").build());
453
454        mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2);
455
456        // no fields should be changed
457        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
458    }
459
460    @Test
461    public void testUpdate() throws Exception {
462        // no fields locked by user
463        final NotificationChannel channel =
464                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW);
465        channel.setRingtone(new Uri.Builder().scheme("test").build());
466        channel.setLights(true);
467        channel.setBypassDnd(true);
468        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
469
470        mHelper.createNotificationChannel(pkg, uid, channel);
471
472        // same id, try to update all fields
473        final NotificationChannel channel2 =
474                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
475        channel2.setRingtone(new Uri.Builder().scheme("test2").build());
476        channel2.setLights(false);
477        channel2.setBypassDnd(false);
478        channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
479
480        mHelper.updateNotificationChannel(pkg, uid, channel2);
481
482        // all fields should be changed
483        assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel.getId()));
484    }
485
486    @Test
487    public void testGetChannelWithFallback() throws Exception {
488        NotificationChannel channel =
489                mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage");
490        assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, channel.getId());
491    }
492}
493