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