NotificationDataTest.java revision 25926af53740667b215457bbde0bda679b400078
1/*
2 * Copyright (C) 2017 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 */
16
17package com.android.systemui.statusbar;
18
19import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
20import static android.app.AppOpsManager.OP_CAMERA;
21import static android.app.Notification.CATEGORY_ALARM;
22import static android.app.Notification.CATEGORY_CALL;
23import static android.app.Notification.CATEGORY_EVENT;
24import static android.app.Notification.CATEGORY_MESSAGE;
25import static android.app.Notification.CATEGORY_REMINDER;
26
27import static junit.framework.Assert.assertEquals;
28
29import static org.junit.Assert.assertFalse;
30import static org.junit.Assert.assertTrue;
31import static org.mockito.ArgumentMatchers.any;
32import static org.mockito.ArgumentMatchers.anyInt;
33import static org.mockito.ArgumentMatchers.anyString;
34import static org.mockito.Matchers.eq;
35import static org.mockito.Mockito.mock;
36import static org.mockito.Mockito.when;
37
38import android.Manifest;
39import android.app.Notification;
40import android.app.NotificationChannel;
41import android.content.pm.IPackageManager;
42import android.content.pm.PackageManager;
43import android.media.session.MediaSession;
44import android.os.Bundle;
45import android.service.notification.NotificationListenerService;
46import android.service.notification.StatusBarNotification;
47import android.support.test.annotation.UiThreadTest;
48import android.support.test.filters.SmallTest;
49import android.support.test.runner.AndroidJUnit4;
50import android.util.ArraySet;
51
52import com.android.systemui.ForegroundServiceController;
53import com.android.systemui.SysuiTestCase;
54import com.android.systemui.statusbar.phone.NotificationGroupManager;
55
56import org.junit.Assert;
57import org.junit.Before;
58import org.junit.Test;
59import org.junit.runner.RunWith;
60import org.mockito.Mock;
61import org.mockito.MockitoAnnotations;
62
63@SmallTest
64@RunWith(AndroidJUnit4.class)
65public class NotificationDataTest extends SysuiTestCase {
66
67    private static final int UID_NORMAL = 123;
68    private static final int UID_ALLOW_DURING_SETUP = 456;
69    private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
70    private static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
71
72    private final StatusBarNotification mMockStatusBarNotification =
73            mock(StatusBarNotification.class);
74    @Mock
75    ForegroundServiceController mFsc;
76    @Mock
77    NotificationData.Environment mEnvironment;
78
79    private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
80    private NotificationData mNotificationData;
81    private ExpandableNotificationRow mRow;
82
83    @Before
84    public void setUp() throws Exception {
85        MockitoAnnotations.initMocks(this);
86        when(mMockStatusBarNotification.getUid()).thenReturn(UID_NORMAL);
87
88        when(mMockPackageManager.checkUidPermission(
89                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
90                eq(UID_NORMAL)))
91                .thenReturn(PackageManager.PERMISSION_DENIED);
92        when(mMockPackageManager.checkUidPermission(
93                eq(Manifest.permission.NOTIFICATION_DURING_SETUP),
94                eq(UID_ALLOW_DURING_SETUP)))
95                .thenReturn(PackageManager.PERMISSION_GRANTED);
96
97        mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
98        when(mEnvironment.getGroupManager()).thenReturn(new NotificationGroupManager());
99        when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
100        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
101        mNotificationData = new TestableNotificationData(mEnvironment);
102        mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
103        mRow = new NotificationTestHelper(getContext()).createRow();
104    }
105
106    @Test
107    @UiThreadTest
108    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoExtra() {
109        initStatusBarNotification(false);
110        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
111
112        assertFalse(
113                NotificationData.showNotificationEvenIfUnprovisioned(
114                        mMockPackageManager,
115                        mMockStatusBarNotification));
116    }
117
118    @Test
119    @UiThreadTest
120    public void testShowNotificationEvenIfUnprovisioned_FalseIfNoPermission() {
121        initStatusBarNotification(true);
122
123        assertFalse(
124                NotificationData.showNotificationEvenIfUnprovisioned(
125                        mMockPackageManager,
126                        mMockStatusBarNotification));
127    }
128
129    @Test
130    @UiThreadTest
131    public void testShowNotificationEvenIfUnprovisioned_TrueIfHasPermissionAndExtra() {
132        initStatusBarNotification(true);
133        when(mMockStatusBarNotification.getUid()).thenReturn(UID_ALLOW_DURING_SETUP);
134
135        assertTrue(
136                NotificationData.showNotificationEvenIfUnprovisioned(
137                        mMockPackageManager,
138                        mMockStatusBarNotification));
139    }
140
141    @Test
142    public void testChannelSetWhenAdded() {
143        mNotificationData.add(mRow.getEntry());
144        Assert.assertTrue(mRow.getEntry().channel != null);
145    }
146
147
148
149    @Test
150    public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
151        mNotificationData.add(mRow.getEntry());
152        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
153        mNotificationData.add(row2.getEntry());
154        ExpandableNotificationRow diffPkg =
155                new NotificationTestHelper(getContext()).createRow("pkg", 4000);
156        mNotificationData.add(diffPkg.getEntry());
157
158        ArraySet<Integer> expectedOps = new ArraySet<>();
159        expectedOps.add(OP_CAMERA);
160        expectedOps.add(OP_ACCEPT_HANDOVER);
161
162        for (int op : expectedOps) {
163            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
164                    NotificationTestHelper.PKG, mRow.getEntry().key, true);
165            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
166                    NotificationTestHelper.PKG, row2.getEntry().key, true);
167        }
168        for (int op : expectedOps) {
169            assertTrue(mRow.getEntry().key + " doesn't have op " + op,
170                    mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(op));
171            assertTrue(row2.getEntry().key + " doesn't have op " + op,
172                    mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(op));
173            assertFalse(diffPkg.getEntry().key + " has op " + op,
174                    mNotificationData.get(diffPkg.getEntry().key).mActiveAppOps.contains(op));
175        }
176    }
177
178    @Test
179    public void testAppOpsRemoval() throws Exception {
180        mNotificationData.add(mRow.getEntry());
181        ExpandableNotificationRow row2 = new NotificationTestHelper(getContext()).createRow();
182        mNotificationData.add(row2.getEntry());
183
184        ArraySet<Integer> expectedOps = new ArraySet<>();
185        expectedOps.add(OP_CAMERA);
186        expectedOps.add(OP_ACCEPT_HANDOVER);
187
188        for (int op : expectedOps) {
189            mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
190                    NotificationTestHelper.PKG, row2.getEntry().key, true);
191        }
192
193        expectedOps.remove(OP_ACCEPT_HANDOVER);
194        mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
195                NotificationTestHelper.PKG, row2.getEntry().key, false);
196
197        assertTrue(mRow.getEntry().key + " doesn't have op " + OP_CAMERA,
198                mNotificationData.get(mRow.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
199        assertTrue(row2.getEntry().key + " doesn't have op " + OP_CAMERA,
200                mNotificationData.get(row2.getEntry().key).mActiveAppOps.contains(OP_CAMERA));
201        assertFalse(mRow.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
202                mNotificationData.get(mRow.getEntry().key)
203                        .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
204        assertFalse(row2.getEntry().key + " has op " + OP_ACCEPT_HANDOVER,
205                mNotificationData.get(row2.getEntry().key)
206                        .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
207    }
208
209    @Test
210    public void testSuppressSystemAlertNotification() {
211        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
212        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
213        StatusBarNotification sbn = mRow.getEntry().notification;
214        Bundle bundle = new Bundle();
215        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
216        sbn.getNotification().extras = bundle;
217
218        assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry()));
219    }
220
221    @Test
222    public void testDoNotSuppressSystemAlertNotification() {
223        StatusBarNotification sbn = mRow.getEntry().notification;
224        Bundle bundle = new Bundle();
225        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
226        sbn.getNotification().extras = bundle;
227
228        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
229        when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
230
231        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
232
233        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
234        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
235
236        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
237
238        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
239        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
240
241        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
242    }
243
244    @Test
245    public void testDoNotSuppressMalformedSystemAlertNotification() {
246        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
247
248        // missing extra
249        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
250
251        StatusBarNotification sbn = mRow.getEntry().notification;
252        Bundle bundle = new Bundle();
253        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
254        sbn.getNotification().extras = bundle;
255
256        // extra missing values
257        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry()));
258    }
259
260    @Test
261    public void testShouldFilterHiddenNotifications() {
262        initStatusBarNotification(false);
263        // setup
264        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
265        when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
266
267        // test should filter out hidden notifications:
268        // hidden
269        when(mMockStatusBarNotification.getKey()).thenReturn(TEST_HIDDEN_NOTIFICATION_KEY);
270        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
271        assertTrue(mNotificationData.shouldFilterOut(entry));
272
273        // not hidden
274        when(mMockStatusBarNotification.getKey()).thenReturn("not hidden");
275        entry = new NotificationData.Entry(mMockStatusBarNotification);
276        assertFalse(mNotificationData.shouldFilterOut(entry));
277    }
278
279    @Test
280    public void testIsExemptFromDndVisualSuppression_foreground() {
281        initStatusBarNotification(false);
282        when(mMockStatusBarNotification.getKey()).thenReturn(
283                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
284        Notification n = mMockStatusBarNotification.getNotification();
285        n.flags = Notification.FLAG_FOREGROUND_SERVICE;
286        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
287
288        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
289        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
290    }
291
292    @Test
293    public void testIsExemptFromDndVisualSuppression_media() {
294        initStatusBarNotification(false);
295        when(mMockStatusBarNotification.getKey()).thenReturn(
296                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
297        Notification n = mMockStatusBarNotification.getNotification();
298        Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
299        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
300        n = nb.build();
301        when(mMockStatusBarNotification.getNotification()).thenReturn(n);
302        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
303
304        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
305        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
306    }
307
308    @Test
309    public void testIsExemptFromDndVisualSuppression_system() {
310        initStatusBarNotification(false);
311        when(mMockStatusBarNotification.getKey()).thenReturn(
312                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
313        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
314        entry.mIsSystemNotification = true;
315
316        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(entry));
317        assertFalse(mNotificationData.shouldSuppressAmbient(entry));
318    }
319
320    @Test
321    public void testIsNotExemptFromDndVisualSuppression_hiddenCategories() {
322        initStatusBarNotification(false);
323        when(mMockStatusBarNotification.getKey()).thenReturn(
324                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
325        NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
326        entry.mIsSystemNotification = true;
327        when(mMockStatusBarNotification.getNotification()).thenReturn(
328                new Notification.Builder(mContext, "").setCategory(CATEGORY_CALL).build());
329
330        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
331        assertTrue(mNotificationData.shouldSuppressAmbient(entry));
332
333        when(mMockStatusBarNotification.getNotification()).thenReturn(
334                new Notification.Builder(mContext, "").setCategory(CATEGORY_REMINDER).build());
335
336        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
337
338        when(mMockStatusBarNotification.getNotification()).thenReturn(
339                new Notification.Builder(mContext, "").setCategory(CATEGORY_ALARM).build());
340
341        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
342
343        when(mMockStatusBarNotification.getNotification()).thenReturn(
344                new Notification.Builder(mContext, "").setCategory(CATEGORY_EVENT).build());
345
346        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
347
348        when(mMockStatusBarNotification.getNotification()).thenReturn(
349                new Notification.Builder(mContext, "").setCategory(CATEGORY_MESSAGE).build());
350
351        assertFalse(mNotificationData.isExemptFromDndVisualSuppression(entry));
352    }
353
354    private void initStatusBarNotification(boolean allowDuringSetup) {
355        Bundle bundle = new Bundle();
356        bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
357        Notification notification = new Notification.Builder(mContext, "test")
358                .addExtras(bundle)
359                .build();
360        when(mMockStatusBarNotification.getNotification()).thenReturn(notification);
361    }
362
363    private class TestableNotificationData extends NotificationData {
364        public TestableNotificationData(Environment environment) {
365            super(environment);
366        }
367
368        @Override
369        public NotificationChannel getChannel(String key) {
370            return new NotificationChannel(null, null, 0);
371        }
372
373        @Override
374        protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) {
375            super.getRanking(key, outRanking);
376            if (key.equals(TEST_HIDDEN_NOTIFICATION_KEY)) {
377                outRanking.populate(key, outRanking.getRank(),
378                        outRanking.matchesInterruptionFilter(),
379                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
380                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
381                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
382                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
383            } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
384                outRanking.populate(key, outRanking.getRank(),
385                        outRanking.matchesInterruptionFilter(),
386                        outRanking.getVisibilityOverride(), 255,
387                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
388                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
389                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
390            } else {
391                outRanking.populate(key, outRanking.getRank(),
392                        outRanking.matchesInterruptionFilter(),
393                        outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
394                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
395                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
396                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false);
397            }
398            return true;
399        }
400    }
401}
402