RankingHelperTest.java revision 74856c4a611b337ded6d3eee14c710cf5f7aae77
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 static android.app.NotificationManager.IMPORTANCE_LOW;
19
20import static junit.framework.Assert.assertNull;
21import static junit.framework.Assert.fail;
22
23import org.junit.Before;
24import org.junit.Test;
25import org.junit.runner.RunWith;
26
27import com.android.internal.util.FastXmlSerializer;
28
29import org.mockito.Mock;
30import org.mockito.MockitoAnnotations;
31import org.xmlpull.v1.XmlPullParser;
32import org.xmlpull.v1.XmlSerializer;
33
34import android.app.Notification;
35import android.app.NotificationChannelGroup;
36import android.content.Context;
37import android.app.NotificationChannel;
38import android.app.NotificationManager;
39import android.content.pm.ApplicationInfo;
40import android.content.pm.PackageManager;
41import android.graphics.Color;
42import android.media.AudioAttributes;
43import android.net.Uri;
44import android.os.Build;
45import android.os.UserHandle;
46import android.service.notification.StatusBarNotification;
47import android.support.test.InstrumentationRegistry;
48import android.support.test.runner.AndroidJUnit4;
49import android.test.suitebuilder.annotation.SmallTest;
50import android.util.Xml;
51
52import java.io.BufferedInputStream;
53import java.io.BufferedOutputStream;
54import java.io.ByteArrayInputStream;
55import java.io.ByteArrayOutputStream;
56import java.util.ArrayList;
57import java.util.Arrays;
58import java.util.HashMap;
59import java.util.List;
60import java.util.Map;
61import java.util.Objects;
62
63import static org.junit.Assert.assertEquals;
64import static org.junit.Assert.assertFalse;
65import static org.junit.Assert.assertNotNull;
66import static org.junit.Assert.assertTrue;
67import static org.mockito.Matchers.anyInt;
68import static org.mockito.Matchers.eq;
69import static org.mockito.Mockito.when;
70
71@SmallTest
72@RunWith(AndroidJUnit4.class)
73public class RankingHelperTest {
74    @Mock
75    NotificationUsageStats mUsageStats;
76    @Mock
77    RankingHandler handler;
78    @Mock
79    PackageManager mPm;
80
81    private Notification mNotiGroupGSortA;
82    private Notification mNotiGroupGSortB;
83    private Notification mNotiNoGroup;
84    private Notification mNotiNoGroup2;
85    private Notification mNotiNoGroupSortA;
86    private NotificationRecord mRecordGroupGSortA;
87    private NotificationRecord mRecordGroupGSortB;
88    private NotificationRecord mRecordNoGroup;
89    private NotificationRecord mRecordNoGroup2;
90    private NotificationRecord mRecordNoGroupSortA;
91    private RankingHelper mHelper;
92    private final String pkg = "com.android.server.notification";
93    private final int uid = 0;
94    private final String pkg2 = "pkg2";
95    private final int uid2 = 1111111;
96    private AudioAttributes mAudioAttributes;
97
98    private Context getContext() {
99        return InstrumentationRegistry.getTargetContext();
100    }
101
102    @Before
103    public void setUp() {
104        MockitoAnnotations.initMocks(this);
105        UserHandle user = UserHandle.ALL;
106
107        mHelper = new RankingHelper(getContext(), mPm, handler, mUsageStats,
108                new String[]{ImportanceExtractor.class.getName()});
109
110        mNotiGroupGSortA = new Notification.Builder(getContext())
111                .setContentTitle("A")
112                .setGroup("G")
113                .setSortKey("A")
114                .setWhen(1205)
115                .build();
116        mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
117                "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user,
118                null, System.currentTimeMillis()), getDefaultChannel());
119
120        mNotiGroupGSortB = new Notification.Builder(getContext())
121                .setContentTitle("B")
122                .setGroup("G")
123                .setSortKey("B")
124                .setWhen(1200)
125                .build();
126        mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
127                "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user,
128                null, System.currentTimeMillis()), getDefaultChannel());
129
130        mNotiNoGroup = new Notification.Builder(getContext())
131                .setContentTitle("C")
132                .setWhen(1201)
133                .build();
134        mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
135                "package", "package", 1, null, 0, 0, mNotiNoGroup, user,
136                null, System.currentTimeMillis()), getDefaultChannel());
137
138        mNotiNoGroup2 = new Notification.Builder(getContext())
139                .setContentTitle("D")
140                .setWhen(1202)
141                .build();
142        mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
143                "package", "package", 1, null, 0, 0, mNotiNoGroup2, user,
144                null, System.currentTimeMillis()), getDefaultChannel());
145
146        mNotiNoGroupSortA = new Notification.Builder(getContext())
147                .setContentTitle("E")
148                .setWhen(1201)
149                .setSortKey("A")
150                .build();
151        mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
152                "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user,
153                null, System.currentTimeMillis()), getDefaultChannel());
154
155        mAudioAttributes = new AudioAttributes.Builder()
156                .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
157                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
158                .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
159                .build();
160
161        final ApplicationInfo legacy = new ApplicationInfo();
162        legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
163        final ApplicationInfo upgrade = new ApplicationInfo();
164        upgrade.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
165        try {
166            when(mPm.getApplicationInfoAsUser(eq(pkg), anyInt(), anyInt())).thenReturn(legacy);
167            when(mPm.getApplicationInfoAsUser(eq(pkg2), anyInt(), anyInt())).thenReturn(upgrade);
168        } catch (PackageManager.NameNotFoundException e) {
169        }
170    }
171
172    private NotificationChannel getDefaultChannel() {
173        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
174                IMPORTANCE_LOW);
175    }
176
177    private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, String... channelIds)
178            throws Exception {
179        XmlSerializer serializer = new FastXmlSerializer();
180        ByteArrayOutputStream baos = new ByteArrayOutputStream();
181        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
182        serializer.startDocument(null, true);
183        serializer.startTag(null, "ranking");
184        mHelper.writeXml(serializer, false);
185        serializer.endTag(null, "ranking");
186        serializer.endDocument();
187        serializer.flush();
188
189        for (String channelId : channelIds) {
190            mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId);
191        }
192        return baos;
193    }
194
195    private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
196        assertEquals(expected.getId(), actual.getId());
197        assertEquals(expected.getName(), actual.getName());
198        assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
199        assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
200        assertEquals(expected.getImportance(), actual.getImportance());
201        assertEquals(expected.getLockscreenVisibility(), actual.getLockscreenVisibility());
202        assertEquals(expected.getSound(), actual.getSound());
203        assertEquals(expected.canBypassDnd(), actual.canBypassDnd());
204        assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
205        assertEquals(expected.getGroup(), actual.getGroup());
206        assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
207        assertEquals(expected.getLightColor(), actual.getLightColor());
208    }
209
210    @Test
211    public void testFindAfterRankingWithASplitGroup() throws Exception {
212        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
213        notificationList.add(mRecordGroupGSortA);
214        notificationList.add(mRecordGroupGSortB);
215        notificationList.add(mRecordNoGroup);
216        notificationList.add(mRecordNoGroupSortA);
217        mHelper.sort(notificationList);
218        assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0);
219        assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0);
220        assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0);
221        assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0);
222    }
223
224    @Test
225    public void testSortShouldNotThrowWithPlainNotifications() throws Exception {
226        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
227        notificationList.add(mRecordNoGroup);
228        notificationList.add(mRecordNoGroup2);
229        mHelper.sort(notificationList);
230    }
231
232    @Test
233    public void testSortShouldNotThrowOneSorted() throws Exception {
234        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
235        notificationList.add(mRecordNoGroup);
236        notificationList.add(mRecordNoGroupSortA);
237        mHelper.sort(notificationList);
238    }
239
240    @Test
241    public void testSortShouldNotThrowOneNotification() throws Exception {
242        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
243        notificationList.add(mRecordNoGroup);
244        mHelper.sort(notificationList);
245    }
246
247    @Test
248    public void testSortShouldNotThrowOneSortKey() throws Exception {
249        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
250        notificationList.add(mRecordGroupGSortB);
251        mHelper.sort(notificationList);
252    }
253
254    @Test
255    public void testSortShouldNotThrowOnEmptyList() throws Exception {
256        ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
257        mHelper.sort(notificationList);
258    }
259
260    @Test
261    public void testChannelXml() throws Exception {
262        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "2");
263        NotificationChannel channel1 =
264                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
265        NotificationChannel channel2 =
266                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
267        channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
268        channel2.enableLights(true);
269        channel2.setBypassDnd(true);
270        channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
271        channel2.enableVibration(true);
272        channel2.setGroup(ncg.getId());
273        channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
274        channel2.setLightColor(Color.BLUE);
275
276        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
277        mHelper.createNotificationChannel(pkg, uid, channel1, true);
278        mHelper.createNotificationChannel(pkg, uid, channel2, false);
279
280        mHelper.setShowBadge(pkg, uid, true);
281        mHelper.setShowBadge(pkg2, uid2, false);
282
283        ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(),
284                NotificationChannel.DEFAULT_CHANNEL_ID);
285        mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{pkg}, new int[]{uid});
286
287        XmlPullParser parser = Xml.newPullParser();
288        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
289                null);
290        parser.nextTag();
291        mHelper.readXml(parser, false);
292
293        assertFalse(mHelper.canShowBadge(pkg2, uid2));
294        assertTrue(mHelper.canShowBadge(pkg, uid));
295        assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false));
296        compareChannels(channel2,
297                mHelper.getNotificationChannel(pkg, uid, channel2.getId(), false));
298        assertNotNull(mHelper.getNotificationChannel(
299                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false));
300
301        List<NotificationChannelGroup> actualGroups =
302                mHelper.getNotificationChannelGroups(pkg, uid, false).getList();
303        boolean foundNcg = false;
304        for (NotificationChannelGroup actual : actualGroups) {
305            if (ncg.getId().equals(actual.getId())) {
306                foundNcg = true;
307                 break;
308            }
309        }
310        assertTrue(foundNcg);
311
312        boolean foundChannel2Group = false;
313        for (NotificationChannelGroup actual : actualGroups) {
314            if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) {
315                foundChannel2Group = true;
316                break;
317            }
318        }
319        assertTrue(foundChannel2Group);
320    }
321
322    @Test
323    public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception {
324        NotificationChannel channel1 =
325                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_DEFAULT);
326
327        mHelper.createNotificationChannel(pkg, uid, channel1, true);
328
329        ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(),
330                NotificationChannel.DEFAULT_CHANNEL_ID);
331
332        XmlPullParser parser = Xml.newPullParser();
333        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
334                null);
335        parser.nextTag();
336        mHelper.readXml(parser, false);
337
338        final NotificationChannel updated = mHelper.getNotificationChannel(
339                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false);
340        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance());
341        assertFalse(updated.canBypassDnd());
342        assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, updated.getLockscreenVisibility());
343        assertEquals(0, updated.getUserLockedFields());
344    }
345
346    @Test
347    public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception {
348        NotificationChannel channel1 =
349                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MIN);
350        mHelper.createNotificationChannel(pkg, uid, channel1, true);
351
352        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(
353                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false);
354        defaultChannel.setImportance(IMPORTANCE_LOW);
355        mHelper.updateNotificationChannel(pkg, uid, defaultChannel);
356
357        ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(),
358                NotificationChannel.DEFAULT_CHANNEL_ID);
359
360        XmlPullParser parser = Xml.newPullParser();
361        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
362                null);
363        parser.nextTag();
364        mHelper.readXml(parser, false);
365
366        assertEquals(IMPORTANCE_LOW, mHelper.getNotificationChannel(
367                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance());
368    }
369
370    @Test
371    public void testChannelXml_upgradeCreateDefaultChannel() throws Exception {
372        final String preupgradeXml = "<ranking version=\"1\">\n"
373                + "<package name=\"" + pkg + "\" importance=\""
374                + NotificationManager.IMPORTANCE_HIGH
375                + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\""
376                + Notification.VISIBILITY_SECRET + "\"" + " uid=\"" + uid + "\" />\n"
377                + "<package name=\"" + pkg2 + "\" uid=\"" + uid2 + "\" visibility=\""
378                + Notification.VISIBILITY_PRIVATE + "\" />\n"
379                + "</ranking>";
380        XmlPullParser parser = Xml.newPullParser();
381        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
382                null);
383        parser.nextTag();
384        mHelper.readXml(parser, false);
385
386        final NotificationChannel updated1 = mHelper.getNotificationChannel(
387                pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false);
388        assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance());
389        assertTrue(updated1.canBypassDnd());
390        assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility());
391        assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE
392                | NotificationChannel.USER_LOCKED_PRIORITY
393                | NotificationChannel.USER_LOCKED_VISIBILITY, updated1.getUserLockedFields());
394
395        final NotificationChannel updated2 = mHelper.getNotificationChannel(
396                pkg2, uid2, NotificationChannel.DEFAULT_CHANNEL_ID, false);
397        // clamped
398        assertEquals(IMPORTANCE_LOW, updated2.getImportance());
399        assertFalse(updated2.canBypassDnd());
400        assertEquals(Notification.VISIBILITY_PRIVATE, updated2.getLockscreenVisibility());
401        assertEquals(NotificationChannel.USER_LOCKED_VISIBILITY, updated2.getUserLockedFields());
402    }
403
404    @Test
405    public void testCreateChannel_blocked() throws Exception {
406        mHelper.setImportance(pkg, uid, NotificationManager.IMPORTANCE_NONE);
407
408        try {
409            mHelper.createNotificationChannel(pkg, uid,
410                    new NotificationChannel(pkg, "", IMPORTANCE_LOW), true);
411            fail("Channel creation should fail");
412        } catch (IllegalArgumentException e) {
413            // pass
414        }
415    }
416
417    @Test
418    public void testUpdate_userLockedImportance() throws Exception {
419        // all fields locked by user
420        final NotificationChannel channel =
421                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
422        channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
423
424        mHelper.createNotificationChannel(pkg, uid, channel, false);
425
426        // same id, try to update
427        final NotificationChannel channel2 =
428                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
429
430        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
431
432        // no fields should be changed
433        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
434    }
435
436    @Test
437    public void testUpdate_userLockedVisibility() throws Exception {
438        // all fields locked by user
439        final NotificationChannel channel =
440                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
441        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
442        channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
443
444        mHelper.createNotificationChannel(pkg, uid, channel, false);
445
446        // same id, try to update
447        final NotificationChannel channel2 =
448                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
449        channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
450
451        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
452
453        // no fields should be changed
454        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
455    }
456
457    @Test
458    public void testUpdate_userLockedVibration() throws Exception {
459        // all fields locked by user
460        final NotificationChannel channel =
461                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
462        channel.enableLights(false);
463        channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
464
465        mHelper.createNotificationChannel(pkg, uid, channel, false);
466
467        // same id, try to update
468        final NotificationChannel channel2 =
469                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
470        channel2.enableVibration(true);
471        channel2.setVibrationPattern(new long[]{100});
472
473        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
474
475        // no fields should be changed
476        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
477    }
478
479    @Test
480    public void testUpdate_userLockedLights() throws Exception {
481        // all fields locked by user
482        final NotificationChannel channel =
483                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
484        channel.enableLights(false);
485        channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
486
487        mHelper.createNotificationChannel(pkg, uid, channel, false);
488
489        // same id, try to update
490        final NotificationChannel channel2 =
491                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
492        channel2.enableLights(true);
493
494        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
495
496        // no fields should be changed
497        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
498    }
499
500    @Test
501    public void testUpdate_userLockedPriority() throws Exception {
502        // all fields locked by user
503        final NotificationChannel channel =
504                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
505        channel.setBypassDnd(true);
506        channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
507
508        mHelper.createNotificationChannel(pkg, uid, channel, false);
509
510        // same id, try to update all fields
511        final NotificationChannel channel2 =
512                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
513        channel2.setBypassDnd(false);
514
515        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
516
517        // no fields should be changed
518        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
519    }
520
521    @Test
522    public void testUpdate_userLockedRingtone() throws Exception {
523        // all fields locked by user
524        final NotificationChannel channel =
525                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
526        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
527        channel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
528
529        mHelper.createNotificationChannel(pkg, uid, channel, false);
530
531        // same id, try to update all fields
532        final NotificationChannel channel2 =
533                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
534        channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
535
536        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
537
538        // no fields should be changed
539        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
540    }
541
542    @Test
543    public void testUpdate_userLockedBadge() throws Exception {
544        final NotificationChannel channel =
545                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
546        channel.setShowBadge(true);
547        channel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
548
549        mHelper.createNotificationChannel(pkg, uid, channel, false);
550
551        final NotificationChannel channel2 =
552                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
553        channel2.setShowBadge(false);
554
555        mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2);
556
557        // no fields should be changed
558        assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
559    }
560
561    @Test
562    public void testUpdate() throws Exception {
563        // no fields locked by user
564        final NotificationChannel channel =
565                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
566        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
567        channel.enableLights(true);
568        channel.setBypassDnd(true);
569        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
570
571        mHelper.createNotificationChannel(pkg, uid, channel, false);
572
573        // same id, try to update all fields
574        final NotificationChannel channel2 =
575                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
576        channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes);
577        channel2.enableLights(false);
578        channel2.setBypassDnd(false);
579        channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
580
581        mHelper.updateNotificationChannel(pkg, uid, channel2);
582
583        // all fields should be changed
584        assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false));
585    }
586
587    @Test
588    public void testGetChannelWithFallback() throws Exception {
589        NotificationChannel channel =
590                mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage", false);
591        assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, channel.getId());
592    }
593
594    @Test
595    public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
596        final NotificationChannel channel =
597                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
598        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
599        channel.enableLights(true);
600        channel.setBypassDnd(true);
601        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
602        channel.setShowBadge(true);
603        int lockMask = 0;
604        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
605            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
606        }
607        channel.lockFields(lockMask);
608
609        mHelper.createNotificationChannel(pkg, uid, channel, true);
610
611        NotificationChannel savedChannel =
612                mHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
613
614        assertEquals(channel.getName(), savedChannel.getName());
615        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
616        assertFalse(savedChannel.canBypassDnd());
617        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
618        assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
619    }
620
621    @Test
622    public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
623        final NotificationChannel channel =
624                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
625        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
626        channel.enableLights(true);
627        channel.setBypassDnd(true);
628        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
629        channel.setShowBadge(true);
630        int lockMask = 0;
631        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
632            lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
633        }
634        channel.lockFields(lockMask);
635
636        mHelper.createNotificationChannel(pkg, uid, channel, true);
637
638        NotificationChannel savedChannel =
639                mHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
640
641        assertEquals(channel.getName(), savedChannel.getName());
642        assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights());
643        assertFalse(savedChannel.canBypassDnd());
644        assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
645        assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
646    }
647
648    @Test
649    public void testGetDeletedChannel() throws Exception {
650        NotificationChannel channel =
651                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
652        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
653        channel.enableLights(true);
654        channel.setBypassDnd(true);
655        channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
656        channel.enableVibration(true);
657        channel.setVibrationPattern(new long[]{100, 67, 145, 156});
658
659        mHelper.createNotificationChannel(pkg, uid, channel, true);
660        mHelper.deleteNotificationChannel(pkg, uid, channel.getId());
661
662        // Does not return deleted channel
663        NotificationChannel response =
664                mHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
665        assertNull(response);
666
667        // Returns deleted channel
668        response = mHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
669        compareChannels(channel, response);
670        assertTrue(response.isDeleted());
671    }
672
673    @Test
674    public void testGetDeletedChannels() throws Exception {
675        Map<String, NotificationChannel> channelMap = new HashMap<>();
676        NotificationChannel channel =
677                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
678        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
679        channel.enableLights(true);
680        channel.setBypassDnd(true);
681        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
682        channel.enableVibration(true);
683        channel.setVibrationPattern(new long[]{100, 67, 145, 156});
684        channelMap.put(channel.getId(), channel);
685        NotificationChannel channel2 =
686                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
687        channelMap.put(channel2.getId(), channel2);
688        mHelper.createNotificationChannel(pkg, uid, channel, true);
689        mHelper.createNotificationChannel(pkg, uid, channel2, true);
690
691        mHelper.deleteNotificationChannel(pkg, uid, channel.getId());
692
693        // Returns only non-deleted channels
694        List<NotificationChannel> channels =
695                mHelper.getNotificationChannels(pkg, uid, false).getList();
696        assertEquals(2, channels.size());   // Default channel + non-deleted channel
697        for (NotificationChannel nc : channels) {
698            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
699                compareChannels(channel2, nc);
700            }
701        }
702
703        // Returns deleted channels too
704        channels = mHelper.getNotificationChannels(pkg, uid, true).getList();
705        assertEquals(3, channels.size());               // Includes default channel
706        for (NotificationChannel nc : channels) {
707            if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
708                compareChannels(channelMap.get(nc.getId()), nc);
709            }
710        }
711    }
712
713    @Test
714    public void testUpdateDeletedChannels() throws Exception {
715        NotificationChannel channel =
716                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
717        mHelper.createNotificationChannel(pkg, uid, channel, true);
718
719        mHelper.deleteNotificationChannel(pkg, uid, channel.getId());
720
721        channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
722        try {
723            mHelper.updateNotificationChannel(pkg, uid, channel);
724            fail("Updated deleted channel");
725        } catch (IllegalArgumentException e) {
726            // :)
727        }
728
729        try {
730            mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel);
731            fail("Updated deleted channel");
732        } catch (IllegalArgumentException e) {
733            // :)
734        }
735    }
736
737    @Test
738    public void testCreateDeletedChannel() throws Exception {
739        long[] vibration = new long[]{100, 67, 145, 156};
740        NotificationChannel channel =
741                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
742        channel.setVibrationPattern(vibration);
743
744        mHelper.createNotificationChannel(pkg, uid, channel, true);
745        mHelper.deleteNotificationChannel(pkg, uid, channel.getId());
746
747        NotificationChannel newChannel = new NotificationChannel(
748                channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
749        newChannel.setVibrationPattern(new long[]{100});
750
751        mHelper.createNotificationChannel(pkg, uid, newChannel, true);
752
753        // No long deleted, using old settings
754        compareChannels(channel,
755                mHelper.getNotificationChannel(pkg, uid, newChannel.getId(), false));
756    }
757
758    @Test
759    public void testCreateChannel_alreadyExists() throws Exception {
760        long[] vibration = new long[]{100, 67, 145, 156};
761        NotificationChannel channel =
762                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
763        channel.setVibrationPattern(vibration);
764
765        mHelper.createNotificationChannel(pkg, uid, channel, true);
766
767        NotificationChannel newChannel = new NotificationChannel(
768                channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
769        newChannel.setVibrationPattern(new long[]{100});
770
771        mHelper.createNotificationChannel(pkg, uid, newChannel, true);
772
773        // Old settings not overridden
774        compareChannels(channel,
775                mHelper.getNotificationChannel(pkg, uid, newChannel.getId(), false));
776    }
777
778    @Test
779    public void testPermanentlyDeleteChannels() throws Exception {
780        NotificationChannel channel1 =
781                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
782        NotificationChannel channel2 =
783                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
784
785        mHelper.createNotificationChannel(pkg, uid, channel1, true);
786        mHelper.createNotificationChannel(pkg, uid, channel2, false);
787
788        mHelper.permanentlyDeleteNotificationChannels(pkg, uid);
789
790        // Only default channel remains
791        assertEquals(1, mHelper.getNotificationChannels(pkg, uid, true).getList().size());
792    }
793
794    @Test
795    public void testOnPackageChanged_packageRemoval() throws Exception {
796        // Deleted
797        NotificationChannel channel1 =
798                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
799        mHelper.createNotificationChannel(pkg, uid, channel1, true);
800
801        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
802
803        assertEquals(0, mHelper.getNotificationChannels(pkg, uid, true).getList().size());
804
805        // Not deleted
806        mHelper.createNotificationChannel(pkg, uid, channel1, true);
807
808        mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
809        assertEquals(2, mHelper.getNotificationChannels(pkg, uid, false).getList().size());
810    }
811
812    @Test
813    public void testOnPackageChanged_packageRemoval_importance() throws Exception {
814        mHelper.setImportance(pkg, uid, NotificationManager.IMPORTANCE_HIGH);
815
816        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
817
818        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid));
819    }
820
821    @Test
822    public void testOnPackageChanged_packageRemoval_groups() throws Exception {
823        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
824        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
825        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
826        mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true);
827
828        mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
829
830        assertEquals(0, mHelper.getNotificationChannelGroups(pkg, uid, true).getList().size());
831    }
832
833    @Test
834    public void testRecordDefaults() throws Exception {
835        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid));
836        assertEquals(true, mHelper.canShowBadge(pkg, uid));
837        assertEquals(1, mHelper.getNotificationChannels(pkg, uid, false).getList().size());
838    }
839
840    @Test
841    public void testCreateGroup() throws Exception {
842        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
843        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
844        assertEquals(ncg, mHelper.getNotificationChannelGroups(pkg, uid).iterator().next());
845    }
846
847    @Test
848    public void testCannotCreateChannel_badGroup() throws Exception {
849        NotificationChannel channel1 =
850                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
851        channel1.setGroup("garbage");
852        try {
853            mHelper.createNotificationChannel(pkg, uid, channel1, true);
854            fail("Created a channel with a bad group");
855        } catch (IllegalArgumentException e) {
856        }
857    }
858
859    @Test
860    public void testCannotCreateChannel_goodGroup() throws Exception {
861        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
862        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
863        NotificationChannel channel1 =
864                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
865        channel1.setGroup(ncg.getId());
866        mHelper.createNotificationChannel(pkg, uid, channel1, true);
867
868        assertEquals(ncg.getId(),
869                mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false).getGroup());
870    }
871
872    @Test
873    public void testGetChannelGroups() throws Exception {
874        NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
875        mHelper.createNotificationChannelGroup(pkg, uid, unused, true);
876        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
877        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
878        NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
879        mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true);
880
881        NotificationChannel channel1 =
882                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
883        channel1.setGroup(ncg.getId());
884        mHelper.createNotificationChannel(pkg, uid, channel1, true);
885        NotificationChannel channel1a =
886                new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
887        channel1a.setGroup(ncg.getId());
888        mHelper.createNotificationChannel(pkg, uid, channel1a, true);
889
890        NotificationChannel channel2 =
891                new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
892        channel2.setGroup(ncg2.getId());
893        mHelper.createNotificationChannel(pkg, uid, channel2, true);
894
895        NotificationChannel channel3 =
896                new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
897        mHelper.createNotificationChannel(pkg, uid, channel3, true);
898
899        List<NotificationChannelGroup> actual =
900                mHelper.getNotificationChannelGroups(pkg, uid, true).getList();
901        assertEquals(3, actual.size());
902        for (NotificationChannelGroup group : actual) {
903            if (group.getId() == null) {
904                assertEquals(2, group.getChannels().size()); // misc channel too
905                assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
906                        || channel3.getId().equals(group.getChannels().get(1).getId()));
907            } else if (group.getId().equals(ncg.getId())) {
908                assertEquals(2, group.getChannels().size());
909                if (group.getChannels().get(0).getId().equals(channel1.getId())) {
910                    assertTrue(group.getChannels().get(1).getId().equals(channel1a.getId()));
911                } else if (group.getChannels().get(0).getId().equals(channel1a.getId())) {
912                    assertTrue(group.getChannels().get(1).getId().equals(channel1.getId()));
913                } else {
914                    fail("expected channel not found");
915                }
916            } else if (group.getId().equals(ncg2.getId())) {
917                assertEquals(1, group.getChannels().size());
918                assertEquals(channel2.getId(), group.getChannels().get(0).getId());
919            }
920        }
921    }
922
923    @Test
924    public void testGetChannelGroups_noSideEffects() throws Exception {
925        NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
926        mHelper.createNotificationChannelGroup(pkg, uid, ncg, true);
927
928        NotificationChannel channel1 =
929                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
930        channel1.setGroup(ncg.getId());
931        mHelper.createNotificationChannel(pkg, uid, channel1, true);
932        mHelper.getNotificationChannelGroups(pkg, uid, true).getList();
933
934        channel1.setImportance(IMPORTANCE_LOW);
935        mHelper.updateNotificationChannel(pkg, uid, channel1);
936
937        List<NotificationChannelGroup> actual =
938                mHelper.getNotificationChannelGroups(pkg, uid, true).getList();
939
940        assertEquals(2, actual.size());
941        for (NotificationChannelGroup group : actual) {
942            if (Objects.equals(group.getId(),ncg.getId())) {
943                assertEquals(1, group.getChannels().size());
944            }
945        }
946    }
947}
948