blob: 46c536c76d46c9bdbf03523a05b81ab50afd70d6 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.graphics.Color;
import android.os.Binder;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class NotificationManagerServiceTest extends NotificationTestCase {
private static final long WAIT_FOR_IDLE_TIMEOUT = 2;
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int uid = Binder.getCallingUid();
private NotificationManagerService mNotificationManagerService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
@Mock
private IPackageManager mPackageManager;
@Mock
private PackageManager mPackageManagerClient;
private Context mContext = getContext();
private final String PKG = mContext.getPackageName();
private TestableLooper mTestableLooper;
@Mock
private RankingHelper mRankingHelper;
@Mock
private NotificationUsageStats mUsageStats;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
@Mock
private NotificationManagerService.NotificationListeners mNotificationListeners;
private ManagedServices.ManagedServiceInfo mListener;
@Mock private ICompanionDeviceManager mCompanionMgr;
@Mock SnoozeHelper mSnoozeHelper;
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
public TestableNotificationManagerService(Context context) { super(context); }
@Override
protected boolean isCallingUidSystem() {
return true;
}
@Override
protected boolean isCallerSystemOrPhone() {
return true;
}
@Override
protected ICompanionDeviceManager getCompanionManager() {
return null;
}
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
// most tests assume badging is enabled
Secure.putIntForUser(getContext().getContentResolver(),
Secure.NOTIFICATION_BADGING, 1,
UserHandle.getUserHandleForUid(uid).getIdentifier());
mNotificationManagerService = new TestableNotificationManagerService(mContext);
// MockPackageManager - default returns ApplicationInfo with matching calling UID
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = uid;
when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
.thenReturn(applicationInfo);
when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenReturn(applicationInfo);
final LightsManager mockLightsManager = mock(LightsManager.class);
when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
// Use this testable looper.
mTestableLooper = TestableLooper.get(this);
mListener = mNotificationListeners.new ManagedServiceInfo(
null, new ComponentName(PKG, "test_class"), uid, true, null, 0);
when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager,
mPackageManagerClient, mockLightsManager, mNotificationListeners, mCompanionMgr,
mSnoozeHelper, mUsageStats);
// Tests call directly into the Binder.
mBinderService = mNotificationManagerService.getBinderService();
mInternalService = mNotificationManagerService.getInternalService();
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
}
public void waitForIdle() throws Exception {
mTestableLooper.processAllMessages();
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
String groupKey, boolean isSummary) {
Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setGroup(groupKey)
.setGroupSummary(isSummary);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", uid, 0,
nb.build(), new UserHandle(uid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
return generateNotificationRecord(channel, null);
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel,
Notification.TvExtender extender) {
if (channel == null) {
channel = mTestNotificationChannel;
}
Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon);
if (extender != null) {
nb.extend(extender);
}
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", uid, 0,
nb.build(), new UserHandle(uid), null, 0);
return new NotificationRecord(mContext, sbn, channel);
}
@Test
public void testCreateNotificationChannels_SingleChannel() throws Exception {
final NotificationChannel channel =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels("test_pkg",
new ParceledListSlice(Arrays.asList(channel)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel("test_pkg", "id");
assertTrue(createdChannel != null);
}
@Test
public void testCreateNotificationChannels_NullChannelThrowsException() throws Exception {
try {
mBinderService.createNotificationChannels("test_pkg",
new ParceledListSlice(Arrays.asList(null)));
fail("Exception should be thrown immediately.");
} catch (NullPointerException e) {
// pass
}
}
@Test
public void testCreateNotificationChannels_TwoChannels() throws Exception {
final NotificationChannel channel1 =
new NotificationChannel("id1", "name", NotificationManager.IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
new NotificationChannel("id2", "name", NotificationManager.IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels("test_pkg",
new ParceledListSlice(Arrays.asList(channel1, channel2)));
assertTrue(mBinderService.getNotificationChannel("test_pkg", "id1") != null);
assertTrue(mBinderService.getNotificationChannel("test_pkg", "id2") != null);
}
@Test
public void testCreateNotificationChannels_SecondCreateDoesNotChangeImportance()
throws Exception {
final NotificationChannel channel =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
mBinderService.createNotificationChannels("test_pkg",
new ParceledListSlice(Arrays.asList(channel)));
// Recreating the channel doesn't throw, but ignores importance.
final NotificationChannel dupeChannel =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
mBinderService.createNotificationChannels("test_pkg",
new ParceledListSlice(Arrays.asList(dupeChannel)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel("test_pkg", "id");
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
public void testCreateNotificationChannels_IdenticalChannelsInListIgnoresSecond()
throws Exception {
final NotificationChannel channel1 =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
final NotificationChannel channel2 =
new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_HIGH);
mBinderService.createNotificationChannels("test_pkg",
new ParceledListSlice(Arrays.asList(channel1, channel2)));
final NotificationChannel createdChannel =
mBinderService.getNotificationChannel("test_pkg", "id");
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, createdChannel.getImportance());
}
@Test
public void testBlockedNotifications_suspended() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r));
}
@Test
public void testBlockedNotifications_blockedChannel() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
channel.setImportance(NotificationManager.IMPORTANCE_NONE);
NotificationRecord r = generateNotificationRecord(channel);
assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerBlocked(eq(r));
}
@Test
public void testEnqueuedBlockedNotifications_blockedApp() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
mBinderService.setNotificationsEnabledForPackage(PKG, uid, false);
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
}
@Test
public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifs.length);
}
@Test
public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertEquals(0, notifs.length);
}
@Test
public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertEquals(0, notifs.length);
}
@Test
public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelNotificationsFromListener(null, null);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
}
@Test
public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
}
@Test
public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
for (int i = 0; i < 10; i++) {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
}
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
}
@Test
public void testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
final NotificationRecord parentAsChild = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", false);
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group1", false);
// fully post parent notification
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
waitForIdle();
// enqueue the child several times
for (int i = 0; i < 10; i++) {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
}
// make the parent a child, which will cancel the child notification
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
parentAsChild.sbn.getUserId());
waitForIdle();
}
@Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(1, notifs.length);
}
@Test
public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(1, notifs.length);
}
@Test
public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(null, sbn.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
}
@Test
public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), UserHandle.USER_ALL);
// Null pkg is how we signal a user switch.
mBinderService.cancelAllNotifications(null, sbn.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(1, notifs.length);
}
@Test
public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
sbn.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs[0].getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE);
}
@Test
public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags =
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
}
@Test
public void testFindGroupNotificationsLocked() throws Exception {
// make sure the same notification can be found in both lists and returned
final NotificationRecord group1 = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
mNotificationManagerService.addEnqueuedNotification(group1);
mNotificationManagerService.addNotification(group1);
// should not be returned
final NotificationRecord group2 = generateNotificationRecord(
mTestNotificationChannel, 2, "group2", true);
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId());
waitForIdle();
// should not be returned
final NotificationRecord nonGroup = generateNotificationRecord(
mTestNotificationChannel, 3, null, false);
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId());
waitForIdle();
// same group, child, should be returned
final NotificationRecord group1Child = generateNotificationRecord(
mTestNotificationChannel, 4, "group1", false);
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, group1Child.sbn.getId(),
group1Child.sbn.getNotification(), group1Child.sbn.getUserId());
waitForIdle();
List<NotificationRecord> inGroup1 =
mNotificationManagerService.findGroupNotificationsLocked(PKG, group1.getGroupKey(),
group1.sbn.getUserId());
assertEquals(3, inGroup1.size());
for (NotificationRecord record : inGroup1) {
assertTrue(record.getGroupKey().equals(group1.getGroupKey()));
assertTrue(record.sbn.getId() == 1 || record.sbn.getId() == 4);
}
}
@Test
public void testTvExtenderChannelOverride_onTv() throws Exception {
mNotificationManagerService.setIsTelevision(true);
mNotificationManagerService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn(
new NotificationChannel("foo", "foo", NotificationManager.IMPORTANCE_HIGH));
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
verify(mRankingHelper, times(1)).getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean());
}
@Test
public void testTvExtenderChannelOverride_notOnTv() throws Exception {
mNotificationManagerService.setIsTelevision(false);
mNotificationManagerService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(
anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
mTestNotificationChannel);
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
verify(mRankingHelper, times(1)).getNotificationChannel(
anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
}
@Test
public void testCreateChannelNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mNotificationManagerService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
NotificationChannel channel2 = new NotificationChannel("a", "b", IMPORTANCE_LOW);
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(channel2.getId()), anyBoolean()))
.thenReturn(channel2);
reset(mNotificationListeners);
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(mTestNotificationChannel, channel2)));
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(channel2),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
}
@Test
public void testCreateChannelGroupNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mNotificationManagerService.setRankingHelper(mRankingHelper);
NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
reset(mNotificationListeners);
mBinderService.createNotificationChannelGroups(PKG,
new ParceledListSlice(Arrays.asList(group1, group2)));
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
eq(Process.myUserHandle()), eq(group1),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
eq(Process.myUserHandle()), eq(group2),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
}
@Test
public void testUpdateChannelNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mNotificationManagerService.setRankingHelper(mRankingHelper);
mTestNotificationChannel.setLightColor(Color.CYAN);
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
reset(mNotificationListeners);
mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel);
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@Test
public void testDeleteChannelNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mNotificationManagerService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
reset(mNotificationListeners);
mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED));
}
@Test
public void testDeleteChannelGroupNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
mNotificationManagerService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
.thenReturn(ncg);
reset(mNotificationListeners);
mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId());
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
eq(Process.myUserHandle()), eq(ncg),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED));
}
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mBinderService.updateNotificationChannelFromPrivilegedListener(
null, PKG, Process.myUserHandle(), mTestNotificationChannel);
verify(mRankingHelper, times(1)).updateNotificationChannel(anyString(), anyInt(), any());
verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
try {
mBinderService.updateNotificationChannelFromPrivilegedListener(
null, PKG, Process.myUserHandle(), mTestNotificationChannel);
fail("listeners that don't have a companion device shouldn't be able to call this");
} catch (SecurityException e) {
// pass
}
verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any());
verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
mListener.component = new ComponentName(PKG, PKG);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
try {
mBinderService.updateNotificationChannelFromPrivilegedListener(
null, PKG, UserHandle.ALL, mTestNotificationChannel);
fail("incorrectly allowed a change to a user listener cannot see");
} catch (SecurityException e) {
// pass
}
verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any());
verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@Test
public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mBinderService.getNotificationChannelsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
verify(mRankingHelper, times(1)).getNotificationChannels(
anyString(), anyInt(), anyBoolean());
}
@Test
public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
try {
mBinderService.getNotificationChannelsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
fail("listeners that don't have a companion device shouldn't be able to call this");
} catch (SecurityException e) {
// pass
}
verify(mRankingHelper, never()).getNotificationChannels(
anyString(), anyInt(), anyBoolean());
}
@Test
public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
try {
mBinderService.getNotificationChannelsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
fail("listener getting channels from a user they cannot see");
} catch (SecurityException e) {
// pass
}
verify(mRankingHelper, never()).getNotificationChannels(
anyString(), anyInt(), anyBoolean());
}
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
verify(mRankingHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt());
}
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
try {
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
fail("listeners that don't have a companion device shouldn't be able to call this");
} catch (SecurityException e) {
// pass
}
verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
}
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
try {
mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
null, PKG, Process.myUserHandle());
fail("listeners that don't have a companion device shouldn't be able to call this");
} catch (SecurityException e) {
// pass
}
verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
}
@Test
public void testHasCompanionDevice_failure() throws Exception {
when(mCompanionMgr.getAssociations(anyString(), anyInt())).thenThrow(
new IllegalArgumentException());
mNotificationManagerService.hasCompanionDevice(mListener);
}
@Test
public void testHasCompanionDevice_noService() throws Exception {
mNotificationManagerService = new TestableNotificationManagerService(mContext);
assertFalse(mNotificationManagerService.hasCompanionDevice(mListener));
}
@Test
public void testSnoozeRunnable_snoozeNonGrouped() throws Exception {
final NotificationRecord nonGrouped = generateNotificationRecord(
mTestNotificationChannel, 1, null, false);
final NotificationRecord grouped = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
mNotificationManagerService.addNotification(grouped);
mNotificationManagerService.addNotification(nonGrouped);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mNotificationManagerService.new SnoozeNotificationRunnable(
nonGrouped.getKey(), 100, null);
snoozeNotificationRunnable.run();
// only snooze the one notification
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
}
@Test
public void testSnoozeRunnable_snoozeSummary_withChildren() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
mNotificationManagerService.addNotification(parent);
mNotificationManagerService.addNotification(child);
mNotificationManagerService.addNotification(child2);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mNotificationManagerService.new SnoozeNotificationRunnable(
parent.getKey(), 100, null);
snoozeNotificationRunnable.run();
// snooze parent and children
verify(mSnoozeHelper, times(3)).snooze(any(NotificationRecord.class), anyLong());
}
@Test
public void testSnoozeRunnable_snoozeGroupChild_fellowChildren() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
mNotificationManagerService.addNotification(parent);
mNotificationManagerService.addNotification(child);
mNotificationManagerService.addNotification(child2);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mNotificationManagerService.new SnoozeNotificationRunnable(
child2.getKey(), 100, null);
snoozeNotificationRunnable.run();
// only snooze the one child
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
}
@Test
public void testSnoozeRunnable_snoozeGroupChild_onlyChildOfSummary() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
assertTrue(parent.sbn.getNotification().isGroupSummary());
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
mNotificationManagerService.addNotification(parent);
mNotificationManagerService.addNotification(child);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mNotificationManagerService.new SnoozeNotificationRunnable(
child.getKey(), 100, null);
snoozeNotificationRunnable.run();
// snooze child and summary
verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong());
}
@Test
public void testSnoozeRunnable_snoozeGroupChild_noOthersInGroup() throws Exception {
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
mNotificationManagerService.addNotification(child);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mNotificationManagerService.new SnoozeNotificationRunnable(
child.getKey(), 100, null);
snoozeNotificationRunnable.run();
// snooze child only
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
}
@Test
public void testPostGroupChild_unsnoozeParent() throws Exception {
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
waitForIdle();
verify(mSnoozeHelper, times(1)).repostGroupSummary(
anyString(), anyInt(), eq(child.getGroupKey()));
}
@Test
public void testPostNonGroup_noUnsnoozing() throws Exception {
final NotificationRecord record = generateNotificationRecord(
mTestNotificationChannel, 2, null, false);
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId());
waitForIdle();
verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString());
}
@Test
public void testPostGroupSummary_noUnsnoozing() throws Exception {
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 2, "group", true);
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
waitForIdle();
verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString());
}
}