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