blob: 76e576a1119934bff6149d659fa421c83acfcbcb [file] [log] [blame]
/*
* Copyright (C) 2008 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 android.app.cts;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.UiAutomation;
import android.app.stubs.MockNotificationListener;
import android.app.stubs.R;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.provider.Settings;
import android.provider.Telephony.Threads;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.test.AndroidTestCase;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import junit.framework.Assert;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class NotificationManagerTest extends AndroidTestCase {
final String TAG = NotificationManagerTest.class.getSimpleName();
final boolean DEBUG = false;
final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
private PackageManager mPackageManager;
private NotificationManager mNotificationManager;
private ActivityManager mActivityManager;
private String mId;
private MockNotificationListener mListener;
@Override
protected void setUp() throws Exception {
super.setUp();
// This will leave a set of channels on the device with each test run.
mId = UUID.randomUUID().toString();
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
// clear the deck so that our getActiveNotifications results are predictable
mNotificationManager.cancelAll();
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = mContext.getPackageManager();
// delay between tests so notifications aren't dropped by the rate limiter
try {
Thread.sleep(500);
} catch(InterruptedException e) {}
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
mNotificationManager.cancelAll();
List<NotificationChannel> channels = mNotificationManager.getNotificationChannels();
// Delete all channels.
for (NotificationChannel nc : channels) {
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
continue;
}
mNotificationManager.deleteNotificationChannel(nc.getId());
}
toggleListenerAccess(MockNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), false);
List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups();
// Delete all groups.
for (NotificationChannelGroup ncg : groups) {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
}
public void testPostPCanToggleAlarmsMediaSystemTest() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
// Post-P can toggle alarms, media, system
// toggle on alarms, media, system:
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
| NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
| NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM, 0, 0));
NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) != 0);
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0);
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) != 0);
// toggle off alarms, media, system
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
policy = mNotificationManager.getNotificationPolicy();
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0);
assertTrue((policy.priorityCategories &
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) == 0);
assertTrue((policy.priorityCategories &
NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0);
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
}
public void testCreateChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
final NotificationChannel ungrouped =
new NotificationChannel(mId + "!", "name", NotificationManager.IMPORTANCE_DEFAULT);
try {
mNotificationManager.createNotificationChannel(channel);
mNotificationManager.createNotificationChannel(ungrouped);
List<NotificationChannelGroup> ncgs =
mNotificationManager.getNotificationChannelGroups();
assertEquals(1, ncgs.size());
assertEquals(ncg.getName(), ncgs.get(0).getName());
assertEquals(ncg.getDescription(), ncgs.get(0).getDescription());
assertEquals(channel.getId(), ncgs.get(0).getChannels().get(0).getId());
} finally {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
}
public void testGetChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
ncg.setDescription("bananas");
final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2");
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
mNotificationManager.createNotificationChannelGroup(ncg2);
mNotificationManager.createNotificationChannel(channel);
NotificationChannelGroup actual =
mNotificationManager.getNotificationChannelGroup(ncg.getId());
assertEquals(ncg.getId(), actual.getId());
assertEquals(ncg.getName(), actual.getName());
assertEquals(ncg.getDescription(), actual.getDescription());
assertEquals(channel.getId(), actual.getChannels().get(0).getId());
}
public void testDeleteChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
mNotificationManager.createNotificationChannel(channel);
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
assertNull(mNotificationManager.getNotificationChannel(channel.getId()));
assertEquals(0, mNotificationManager.getNotificationChannelGroups().size());
}
public void testCreateChannel() throws Exception {
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("bananas");
channel.enableVibration(true);
channel.setVibrationPattern(new long[] {5, 8, 2, 1});
channel.setSound(new Uri.Builder().scheme("test").build(),
new AudioAttributes.Builder().setUsage(
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED).build());
channel.enableLights(true);
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel createdChannel =
mNotificationManager.getNotificationChannel(mId);
compareChannels(channel, createdChannel);
// Lockscreen Visibility and canBypassDnd no longer settable.
assertTrue(createdChannel.getLockscreenVisibility() != Notification.VISIBILITY_SECRET);
assertFalse(createdChannel.canBypassDnd());
}
public void testCreateChannel_rename() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
channel.setName("new name");
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel createdChannel =
mNotificationManager.getNotificationChannel(mId);
compareChannels(channel, createdChannel);
channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
assertEquals(NotificationManager.IMPORTANCE_DEFAULT,
mNotificationManager.getNotificationChannel(mId).getImportance());
}
public void testCreateChannel_addToGroup() throws Exception {
String oldGroup = null;
String newGroup = "new group";
mNotificationManager.createNotificationChannelGroup(
new NotificationChannelGroup(newGroup, newGroup));
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(oldGroup);
mNotificationManager.createNotificationChannel(channel);
channel.setGroup(newGroup);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel updatedChannel =
mNotificationManager.getNotificationChannel(mId);
assertEquals("Failed to add non-grouped channel to a group on update ",
newGroup, updatedChannel.getGroup());
}
public void testCreateChannel_cannotChangeGroup() throws Exception {
String oldGroup = "old group";
String newGroup = "new group";
mNotificationManager.createNotificationChannelGroup(
new NotificationChannelGroup(oldGroup, oldGroup));
mNotificationManager.createNotificationChannelGroup(
new NotificationChannelGroup(newGroup, newGroup));
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(oldGroup);
mNotificationManager.createNotificationChannel(channel);
channel.setGroup(newGroup);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel updatedChannel =
mNotificationManager.getNotificationChannel(mId);
assertEquals("Channels should not be allowed to change groups",
oldGroup, updatedChannel.getGroup());
}
public void testCreateSameChannelDoesNotUpdate() throws Exception {
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel channelDupe =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channelDupe);
final NotificationChannel createdChannel =
mNotificationManager.getNotificationChannel(mId);
compareChannels(channel, createdChannel);
}
public void testCreateChannelAlreadyExistsNoOp() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
NotificationChannel channelDupe =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channelDupe);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
}
public void testCreateChannelWithGroup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("g", "n");
mNotificationManager.createNotificationChannelGroup(ncg);
try {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannel(channel);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
} finally {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
}
public void testCreateChannelWithBadGroup() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup("garbage");
try {
mNotificationManager.createNotificationChannel(channel);
fail("Created notification with bad group");
} catch (IllegalArgumentException e) {}
}
public void testCreateChannelInvalidImportance() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_UNSPECIFIED);
try {
mNotificationManager.createNotificationChannel(channel);
} catch (IllegalArgumentException e) {
//success
}
}
public void testDeleteChannel() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_LOW);
mNotificationManager.createNotificationChannel(channel);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
mNotificationManager.deleteNotificationChannel(channel.getId());
assertNull(mNotificationManager.getNotificationChannel(channel.getId()));
}
public void testCannotDeleteDefaultChannel() throws Exception {
try {
mNotificationManager.deleteNotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID);
fail("Deleted default channel");
} catch (IllegalArgumentException e) {
//success
}
}
public void testGetChannel() throws Exception {
NotificationChannel channel1 =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel channel2 =
new NotificationChannel(
UUID.randomUUID().toString(), "name2", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel(
UUID.randomUUID().toString(), "name3", NotificationManager.IMPORTANCE_LOW);
NotificationChannel channel4 =
new NotificationChannel(
UUID.randomUUID().toString(), "name4", NotificationManager.IMPORTANCE_MIN);
mNotificationManager.createNotificationChannel(channel1);
mNotificationManager.createNotificationChannel(channel2);
mNotificationManager.createNotificationChannel(channel3);
mNotificationManager.createNotificationChannel(channel4);
compareChannels(channel2,
mNotificationManager.getNotificationChannel(channel2.getId()));
compareChannels(channel3,
mNotificationManager.getNotificationChannel(channel3.getId()));
compareChannels(channel1,
mNotificationManager.getNotificationChannel(channel1.getId()));
compareChannels(channel4,
mNotificationManager.getNotificationChannel(channel4.getId()));
}
public void testGetChannels() throws Exception {
NotificationChannel channel1 =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel channel2 =
new NotificationChannel(
UUID.randomUUID().toString(), "name2", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel(
UUID.randomUUID().toString(), "name3", NotificationManager.IMPORTANCE_LOW);
NotificationChannel channel4 =
new NotificationChannel(
UUID.randomUUID().toString(), "name4", NotificationManager.IMPORTANCE_MIN);
Map<String, NotificationChannel> channelMap = new HashMap<>();
channelMap.put(channel1.getId(), channel1);
channelMap.put(channel2.getId(), channel2);
channelMap.put(channel3.getId(), channel3);
channelMap.put(channel4.getId(), channel4);
mNotificationManager.createNotificationChannel(channel1);
mNotificationManager.createNotificationChannel(channel2);
mNotificationManager.createNotificationChannel(channel3);
mNotificationManager.createNotificationChannel(channel4);
mNotificationManager.deleteNotificationChannel(channel3.getId());
List<NotificationChannel> channels = mNotificationManager.getNotificationChannels();
for (NotificationChannel nc : channels) {
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
continue;
}
if (NOTIFICATION_CHANNEL_ID.equals(nc.getId())) {
continue;
}
assertFalse(channel3.getId().equals(nc.getId()));
if (!channelMap.containsKey(nc.getId())) {
// failed cleanup from prior test run; ignore
continue;
}
compareChannels(channelMap.get(nc.getId()), nc);
}
}
public void testRecreateDeletedChannel() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(true);
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
mNotificationManager.deleteNotificationChannel(channel.getId());
mNotificationManager.createNotificationChannel(newChannel);
compareChannels(channel,
mNotificationManager.getNotificationChannel(newChannel.getId()));
}
public void testNotify() throws Exception {
mNotificationManager.cancelAll();
final int id = 1;
sendNotification(id, R.drawable.black);
// test updating the same notification
sendNotification(id, R.drawable.blue);
sendNotification(id, R.drawable.yellow);
// assume that sendNotification tested to make sure individual notifications were present
StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
if (sbn.getId() != id) {
fail("we got back other notifications besides the one we posted: "
+ sbn.getKey());
}
}
}
public void testSuspendPackage() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(MockNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
mListener = MockNotificationListener.getInstance();
assertNotNull(mListener);
sendNotification(1, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
assertEquals(1, mListener.mPosted.size());
// suspend package, ranking should be updated with suspended = true
suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
true);
Thread.sleep(500); // wait for notification listener to get response
NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap;
NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking();
for (String key : rankingMap.getOrderedKeys()) {
if (key.contains(mListener.getPackageName())) {
rankingMap.getRanking(key, outRanking);
Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended());
assertTrue(outRanking.isSuspended());
}
}
// unsuspend package, ranking should be updated with suspended = false
suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
false);
Thread.sleep(500); // wait for notification listener to get response
rankingMap = mListener.mRankingMap;
for (String key : rankingMap.getOrderedKeys()) {
if (key.contains(mListener.getPackageName())) {
rankingMap.getRanking(key, outRanking);
Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended());
assertFalse(outRanking.isSuspended());
}
}
mListener.resetData();
}
public void testNotify_blockedChannel() throws Exception {
mNotificationManager.cancelAll();
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_NONE);
mNotificationManager.createNotificationChannel(channel);
int id = 1;
final Notification notification =
new Notification.Builder(mContext, mId)
.setSmallIcon(R.drawable.black)
.setWhen(System.currentTimeMillis())
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("found unexpected notification id=" + id);
}
}
public void testNotify_blockedChannelGroup() throws Exception {
mNotificationManager.cancelAll();
NotificationChannelGroup group = new NotificationChannelGroup(mId, "group name");
group.setBlocked(true);
mNotificationManager.createNotificationChannelGroup(group);
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(mId);
mNotificationManager.createNotificationChannel(channel);
int id = 1;
final Notification notification =
new Notification.Builder(mContext, mId)
.setSmallIcon(R.drawable.black)
.setWhen(System.currentTimeMillis())
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("found unexpected notification id=" + id);
}
}
public void testCancel() throws Exception {
final int id = 9;
sendNotification(id, R.drawable.black);
mNotificationManager.cancel(id);
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("canceled notification was still alive, id=" + id);
}
}
public void testCancelAll() throws Exception {
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
if (DEBUG) {
Log.d(TAG, "posted 3 notifications, here they are: ");
StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
Log.d(TAG, " " + sbn);
}
Log.d(TAG, "about to cancel...");
}
mNotificationManager.cancelAll();
for (int id = 1; id <= 3; id++) {
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("Failed to cancel notification id=" + id);
}
}
}
public void testNotifyWithTimeout() throws Exception {
mNotificationManager.cancelAll();
final int id = 128;
final long timeout = 1000;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.setTimeoutAfter(timeout)
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
try {
Thread.sleep(timeout);
} catch (InterruptedException ex) {
// pass
}
checkNotificationExistence(id, false);
}
public void testMediaStyle() throws Exception {
mNotificationManager.cancelAll();
final int id = 99;
MediaSession session = new MediaSession(getContext(), "media");
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"play", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"pause", getPendingIntent()).build())
.setStyle(new Notification.MediaStyle()
.setShowActionsInCompactView(0, 1)
.setMediaSession(session.getSessionToken()))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testInboxStyle() throws Exception {
final int id = 100;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"a1", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"a2", getPendingIntent()).build())
.setStyle(new Notification.InboxStyle().addLine("line")
.setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testBigTextStyle() throws Exception {
final int id = 101;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"a1", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"a2", getPendingIntent()).build())
.setStyle(new Notification.BigTextStyle()
.setBigContentTitle("big title")
.bigText("big text")
.setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testBigPictureStyle() throws Exception {
final int id = 102;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"a1", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"a2", getPendingIntent()).build())
.setStyle(new Notification.BigPictureStyle()
.setBigContentTitle("title")
.bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
.bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
.setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testAutogrouping() throws Exception {
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
sendNotification(4, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception {
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
sendNotification(4, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
for (int i = 4; i > 1; i--) {
cancelAndPoll(i);
assertNotificationCount(i);
assertAllPostedNotificationsAutogrouped();
}
cancelAndPoll(1);
assertNotificationCount(0);
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()
throws Exception {
String newGroup = "new!";
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
sendNotification(4, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
postedIds.add(1);
postedIds.add(2);
postedIds.add(3);
postedIds.add(4);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
for (int i = 4; i > 1; i--) {
sendNotification(i, newGroup, R.drawable.blue);
postedIds.remove(postedIds.size() - 1);
assertNotificationCount(5);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
sendNotification(1, newGroup, R.drawable.blue);
assertNotificationCount(4); // no more autogroup summary
postedIds.remove(0);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()
throws Exception {
String newGroup = "new!";
sendNotification(10, R.drawable.black);
sendNotification(20, R.drawable.blue);
sendNotification(30, R.drawable.yellow);
sendNotification(40, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
postedIds.add(10);
postedIds.add(20);
postedIds.add(30);
postedIds.add(40);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// regroup all but one of the children
for (int i = postedIds.size() - 1; i > 0; i--) {
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
// pass
}
int id = postedIds.remove(i);
sendNotification(id, newGroup, R.drawable.blue);
assertNotificationCount(5);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
// send a new non-grouped notification. since the autogroup summary still exists,
// the notification should be added to it
sendNotification(50, R.drawable.blue);
postedIds.add(50);
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
// pass
}
assertOnlySomeNotificationsAutogrouped(postedIds);
}
public void testSetNotificationPolicy_P_setOldFields() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF);
mNotificationManager.setNotificationPolicy(appPolicy);
int expected = SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF
| SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
}
public void testSetNotificationPolicy_P_setNewFields() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
mNotificationManager.setNotificationPolicy(appPolicy);
int expected = SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_SCREEN_OFF
| SUPPRESSED_EFFECT_AMBIENT | SUPPRESSED_EFFECT_LIGHTS
| SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
}
public void testSetNotificationPolicy_P_setOldNewFields() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR);
mNotificationManager.setNotificationPolicy(appPolicy);
int expected = SUPPRESSED_EFFECT_STATUS_BAR;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
mNotificationManager.setNotificationPolicy(appPolicy);
expected = SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
}
private PendingIntent getPendingIntent() {
return PendingIntent.getActivity(
getContext(), 0, new Intent(getContext(), this.getClass()), 0);
}
private boolean isGroupSummary(Notification n) {
return n.getGroup() != null && (n.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
}
private void assertOnlySomeNotificationsAutogrouped(List<Integer> autoGroupedIds) {
String expectedGroupKey = null;
StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
if (isGroupSummary(sbn.getNotification())
|| autoGroupedIds.contains(sbn.getId())) {
assertTrue(sbn.getKey() + " is unexpectedly not autogrouped",
sbn.getOverrideGroupKey() != null);
if (expectedGroupKey == null) {
expectedGroupKey = sbn.getGroupKey();
}
assertEquals(expectedGroupKey, sbn.getGroupKey());
} else {
assertTrue(sbn.isGroup());
assertTrue(sbn.getKey() + " is unexpectedly autogrouped,",
sbn.getOverrideGroupKey() == null);
assertTrue(sbn.getKey() + " has an unusual group key",
sbn.getGroupKey() != expectedGroupKey);
}
}
}
private void assertAllPostedNotificationsAutogrouped() {
String expectedGroupKey = null;
StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
// all notis should be in a group determined by autogrouping
assertTrue(sbn.getOverrideGroupKey() != null);
if (expectedGroupKey == null) {
expectedGroupKey = sbn.getGroupKey();
}
// all notis should be in the same group
assertEquals(expectedGroupKey, sbn.getGroupKey());
}
}
private void cancelAndPoll(int id) {
mNotificationManager.cancel(id);
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("canceled notification was still alive, id=" + 1);
}
}
private void sendNotification(final int id, final int icon) throws Exception {
sendNotification(id, null, icon);
}
private void sendNotification(final int id, String groupKey, final int icon) throws Exception {
final Intent intent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(Intent.ACTION_MAIN);
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(icon)
.setWhen(System.currentTimeMillis())
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.setContentIntent(pendingIntent)
.setGroup(groupKey)
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
private boolean checkNotificationExistence(int id, boolean shouldExist) {
// notification is a bit asynchronous so it may take a few ms to appear in
// getActiveNotifications()
// we will check for it for up to 300ms before giving up
boolean found = false;
for (int tries = 3; tries--> 0;) {
// Need reset flag.
found = false;
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
Log.d(TAG, "Found " + sbn.getKey());
if (sbn.getId() == id) {
found = true;
break;
}
}
if (found == shouldExist) break;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
// pass
}
}
return found == shouldExist;
}
private void assertNotificationCount(int expectedCount) {
// notification is a bit asynchronous so it may take a few ms to appear in
// getActiveNotifications()
// we will check for it for up to 400ms before giving up
int lastCount = 0;
for (int tries = 4; tries-- > 0;) {
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
lastCount = sbns.length;
if (expectedCount == lastCount) return;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
// pass
}
}
fail("Expected " + expectedCount + " posted notifications, were " + lastCount);
}
private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
if (actual == null) {
fail("actual channel is null");
return;
}
if (expected == null) {
fail("expected channel is null");
return;
}
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getName(), actual.getName());
assertEquals(expected.getDescription(), actual.getDescription());
assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
assertEquals(expected.getImportance(), actual.getImportance());
if (expected.getSound() == null) {
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actual.getSound());
assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, actual.getAudioAttributes());
} else {
assertEquals(expected.getSound(), actual.getSound());
assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
}
assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
assertEquals(expected.getGroup(), actual.getGroup());
}
private void toggleNotificationPolicyAccess(String packageName,
Instrumentation instrumentation, boolean on) throws IOException {
String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName;
runCommand(command, instrumentation);
NotificationManager nm = mContext.getSystemService(NotificationManager.class);
Assert.assertEquals("Notification Policy Access Grant is " +
nm.isNotificationPolicyAccessGranted() + " not " + on, on,
nm.isNotificationPolicyAccessGranted());
}
private void suspendPackage(String packageName,
Instrumentation instrumentation, boolean suspend) throws IOException {
String command = " cmd notification " + (suspend ? "suspend_package "
: "unsuspend_package ") + packageName;
runCommand(command, instrumentation);
}
private void toggleListenerAccess(String componentName, Instrumentation instrumentation,
boolean on) throws IOException {
String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
+ componentName;
runCommand(command, instrumentation);
final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
final ComponentName listenerComponent = MockNotificationListener.getComponentName();
assertTrue(listenerComponent + " has not been granted access",
nm.isNotificationListenerAccessGranted(listenerComponent) == on);
}
private void runCommand(String command, Instrumentation instrumentation) throws IOException {
UiAutomation uiAutomation = instrumentation.getUiAutomation();
// Execute command
try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
Assert.assertNotNull("Failed to execute shell command: " + command, fd);
// Wait for the command to finish by reading until EOF
try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
byte[] buffer = new byte[4096];
while (in.read(buffer) > 0) {}
} catch (IOException e) {
throw new IOException("Could not read stdout of command: " + command, e);
}
} finally {
uiAutomation.destroy();
}
}
}