blob: f5721dd2c2d1ec6a33896a82c297f642d11ceff8 [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.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
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.app.stubs.BubblesTestService.EXTRA_TEST_CASE;
import static android.app.stubs.BubblesTestService.TEST_CALL;
import static android.app.stubs.BubblesTestService.TEST_MESSAGING;
import static android.app.stubs.SendBubbleActivity.BUBBLE_NOTIF_ID;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import android.app.ActivityManager;
import android.app.AutomaticZenRule;
import android.app.Instrumentation;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
import android.app.Person;
import android.app.UiAutomation;
import android.app.stubs.AutomaticZenRuleActivity;
import android.app.stubs.BubbledActivity;
import android.app.stubs.BubblesTestService;
import android.app.stubs.R;
import android.app.stubs.SendBubbleActivity;
import android.app.stubs.TestNotificationListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.OperationApplicationException;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Data;
import android.provider.Settings;
import android.provider.Telephony.Threads;
import android.service.notification.Condition;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
import android.test.AndroidTestCase;
import android.util.ArraySet;
import android.util.Log;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.FeatureUtil;
import com.android.compatibility.common.util.SystemUtil;
import junit.framework.Assert;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/* This tests NotificationListenerService together with NotificationManager, as you need to have
* notifications to manipulate in order to test the listener service. */
public class NotificationManagerTest extends AndroidTestCase {
public static final String NOTIFICATIONPROVIDER = "com.android.test.notificationprovider";
public static final String RICH_NOTIFICATION_ACTIVITY =
"com.android.test.notificationprovider.RichNotificationActivity";
final String TAG = NotificationManagerTest.class.getSimpleName();
final boolean DEBUG = false;
static final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
private static final String DELEGATOR = "com.android.test.notificationdelegator";
private static final String DELEGATE_POST_CLASS = DELEGATOR + ".NotificationDelegateAndPost";
private static final String REVOKE_CLASS = DELEGATOR + ".NotificationRevoker";
private static final long SHORT_WAIT_TIME = 100;
private static final long MAX_WAIT_TIME = 2000;
private static final String SHARE_SHORTCUT_ID = "shareShortcut";
private static final String SHARE_SHORTCUT_CATEGORY =
"android.app.stubs.SHARE_SHORTCUT_CATEGORY";
private PackageManager mPackageManager;
private AudioManager mAudioManager;
private NotificationManager mNotificationManager;
private ActivityManager mActivityManager;
private String mId;
private TestNotificationListener mListener;
private List<String> mRuleIds;
private BroadcastReceiver mBubbleBroadcastReceiver;
private boolean mBubblesEnabledSettingToRestore;
@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();
assertEquals("Previous test left system in a bad state",
0, mNotificationManager.getActiveNotifications().length);
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", IMPORTANCE_DEFAULT));
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = mContext.getPackageManager();
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mRuleIds = new ArrayList<>();
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
// This setting is forced on / off for certain tests, save it & restore what's on the
// device after tests are run
mBubblesEnabledSettingToRestore = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_BUBBLES) == 1;
// 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();
for (String id : mRuleIds) {
mNotificationManager.removeAutomaticZenRule(id);
}
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
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());
}
// Unsuspend package if it was suspended in the test
suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
false);
toggleListenerAccess(false);
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups();
// Delete all groups.
for (NotificationChannelGroup ncg : groups) {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
// Restore bubbles setting
setBubblesGlobal(mBubblesEnabledSettingToRestore);
}
private void assertNotificationCancelled(int id, boolean all) {
for (long totalWait = 0; totalWait < MAX_WAIT_TIME; totalWait += SHORT_WAIT_TIME) {
StatusBarNotification sbn = findNotificationNoWait(id, all);
if (sbn == null) return;
try {
Thread.sleep(SHORT_WAIT_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
assertNull(findNotificationNoWait(id, all));
}
private void insertSingleContact(String name, String phone, String email, boolean starred) {
final ArrayList<ContentProviderOperation> operationList =
new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder =
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI);
builder.withValue(ContactsContract.RawContacts.STARRED, starred ? 1 : 0);
operationList.add(builder.build());
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
builder.withValue(StructuredName.DISPLAY_NAME, name);
operationList.add(builder.build());
if (phone != null) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
builder.withValue(Phone.NUMBER, phone);
builder.withValue(Data.IS_PRIMARY, 1);
operationList.add(builder.build());
}
if (email != null) {
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
builder.withValue(Email.TYPE, Email.TYPE_HOME);
builder.withValue(Email.DATA, email);
operationList.add(builder.build());
}
try {
mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
}
private Uri lookupContact(String phone) {
Cursor c = null;
try {
Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(phone));
String[] projection = new String[] { ContactsContract.Contacts._ID,
ContactsContract.Contacts.LOOKUP_KEY };
c = mContext.getContentResolver().query(phoneUri, projection, null, null, null);
if (c != null && c.getCount() > 0) {
c.moveToFirst();
int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID);
String lookupKey = c.getString(lookupIdx);
long contactId = c.getLong(idIdx);
return ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
}
} catch (Throwable t) {
Log.w(TAG, "Problem getting content resolver or performing contacts query.", t);
} finally {
if (c != null) {
c.close();
}
}
return null;
}
private StatusBarNotification findPostedNotification(int id, boolean all) {
// notification is a bit asynchronous so it may take a few ms to appear in
// getActiveNotifications()
// we will check for it for up to 1000ms before giving up
for (long totalWait = 0; totalWait < MAX_WAIT_TIME; totalWait += SHORT_WAIT_TIME) {
StatusBarNotification n = findNotificationNoWait(id, all);
if (n != null) {
return n;
}
try {
Thread.sleep(SHORT_WAIT_TIME);
} catch (InterruptedException ex) {
// pass
}
}
return findNotificationNoWait(id, all);
}
private StatusBarNotification findNotificationNoWait(int id, boolean all) {
for (StatusBarNotification sbn : getActiveNotifications(all)) {
if (sbn.getId() == id) {
return sbn;
}
}
return null;
}
private StatusBarNotification[] getActiveNotifications(boolean all) {
if (all) {
return mListener.getActiveNotifications();
} else {
return mNotificationManager.getActiveNotifications();
}
}
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;
try {
// Posting can take ~100 ms
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
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;
try {
// Posting can take ~100 ms
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
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);
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
// pass
}
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("canceled notification was still alive, id=" + id);
}
}
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 void setUpNotifListener() {
try {
toggleListenerAccess(true);
mListener = TestNotificationListener.getInstance();
mListener.resetData();
assertNotNull(mListener);
} catch (IOException e) {
}
}
private void sendAndVerifyBubble(final int id, Notification.Builder builder,
Notification.BubbleMetadata data, boolean shouldBeBubble) {
setUpNotifListener();
final Intent intent = new Intent(mContext, BubbledActivity.class);
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);
if (data == null) {
data = new Notification.BubbleMetadata.Builder(pendingIntent,
Icon.createWithResource(mContext, R.drawable.black))
.build();
}
if (builder == null) {
builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setWhen(System.currentTimeMillis())
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.setContentIntent(pendingIntent);
}
builder.setBubbleMetadata(data);
Notification notif = builder.build();
mNotificationManager.notify(id, notif);
verifyNotificationBubbleState(id, shouldBeBubble);
}
/**
* Make sure {@link #setUpNotifListener()} is called prior to sending the notif and verifying
* in this method.
*/
private void verifyNotificationBubbleState(int id, boolean shouldBeBubble) {
try {
// FLAG_BUBBLE relies on notification being posted, wait for notification listener
Thread.sleep(500);
} catch (InterruptedException ex) {
}
for (StatusBarNotification sbn : mListener.mPosted) {
if (sbn.getId() == id) {
boolean isBubble = (sbn.getNotification().flags & FLAG_BUBBLE) != 0;
if (isBubble != shouldBeBubble) {
final String failure = shouldBeBubble
? "Notification with id= " + id + " wasn't a bubble"
: "Notification with id= " + id + " was a bubble and shouldn't be";
fail(failure);
} else {
// pass
return;
}
}
}
fail("Couldn't find posted notification with 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.getLightColor(), actual.getLightColor());
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());
assertEquals(expected.getConversationId(), actual.getConversationId());
assertEquals(expected.getParentChannelId(), actual.getParentChannelId());
}
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);
assertEquals("Notification Policy Access Grant is "
+ nm.isNotificationPolicyAccessGranted() + " not " + on, on,
nm.isNotificationPolicyAccessGranted());
}
private void suspendPackage(String packageName,
Instrumentation instrumentation, boolean suspend) throws IOException {
int userId = mContext.getUserId();
String command = " cmd package " + (suspend ? "suspend " : "unsuspend ")
+ "--user " + userId + " " + packageName;
runCommand(command, instrumentation);
}
private void toggleListenerAccess(boolean on) throws IOException {
String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
+ TestNotificationListener.getId();
runCommand(command, InstrumentationRegistry.getInstrumentation());
final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
final ComponentName listenerComponent = TestNotificationListener.getComponentName();
assertEquals(listenerComponent + " has incorrect listener access",
on, nm.isNotificationListenerAccessGranted(listenerComponent));
}
private void setBubblesGlobal(boolean enabled)
throws InterruptedException {
SystemUtil.runWithShellPermissionIdentity(() ->
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_BUBBLES, enabled ? 1 : 0));
Thread.sleep(500); // wait for ranking update
}
private void setBubblesAppPref(int pref) throws Exception {
int userId = mContext.getUser().getIdentifier();
String pkg = mContext.getPackageName();
String command = " cmd notification set_bubbles " + pkg
+ " " + Integer.toString(pref)
+ " " + userId;
runCommand(command, InstrumentationRegistry.getInstrumentation());
Thread.sleep(500); // wait for ranking update
}
private void setBubblesChannelAllowed(boolean allowed) throws Exception {
int userId = mContext.getUser().getIdentifier();
String pkg = mContext.getPackageName();
String command = " cmd notification set_bubbles_channel " + pkg
+ " " + NOTIFICATION_CHANNEL_ID
+ " " + Boolean.toString(allowed)
+ " " + userId;
runCommand(command, InstrumentationRegistry.getInstrumentation());
Thread.sleep(500); // wait for ranking update
}
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();
}
}
private boolean areRulesSame(AutomaticZenRule a, AutomaticZenRule b) {
return a.isEnabled() == b.isEnabled()
&& Objects.equals(a.getName(), b.getName())
&& a.getInterruptionFilter() == b.getInterruptionFilter()
&& Objects.equals(a.getConditionId(), b.getConditionId())
&& Objects.equals(a.getOwner(), b.getOwner())
&& Objects.equals(a.getZenPolicy(), b.getZenPolicy())
&& Objects.equals(a.getConfigurationActivity(), b.getConfigurationActivity());
}
private AutomaticZenRule createRule(String name, int filter) {
return new AutomaticZenRule(name, null,
new ComponentName(mContext, AutomaticZenRuleActivity.class),
new Uri.Builder().scheme("scheme")
.appendPath("path")
.appendQueryParameter("fake_rule", "fake_value")
.build(), null, filter, true);
}
private AutomaticZenRule createRule(String name) {
return createRule(name, INTERRUPTION_FILTER_PRIORITY);
}
private void assertExpectedDndState(int expectedState) {
int tries = 3;
for (int i = tries; i >=0; i--) {
if (expectedState ==
mNotificationManager.getCurrentInterruptionFilter()) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
assertEquals(expectedState, mNotificationManager.getCurrentInterruptionFilter());
}
/** Creates a dynamic, longlived, sharing shortcut. Call {@link #deleteShortcuts()} after. */
private void createDynamicShortcut() {
Person person = new Person.Builder()
.setBot(false)
.setIcon(Icon.createWithResource(mContext, R.drawable.icon_black))
.setName("BubbleBot")
.setImportant(true)
.build();
Set<String> categorySet = new ArraySet<>();
categorySet.add(SHARE_SHORTCUT_CATEGORY);
Intent shortcutIntent = new Intent(mContext, SendBubbleActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
ShortcutInfo shortcut = new ShortcutInfo.Builder(mContext, SHARE_SHORTCUT_ID)
.setShortLabel(SHARE_SHORTCUT_ID)
.setIcon(Icon.createWithResource(mContext, R.drawable.icon_black))
.setIntent(shortcutIntent)
.setPerson(person)
.setCategories(categorySet)
.setLongLived(true)
.build();
ShortcutManager scManager =
(ShortcutManager) mContext.getSystemService(Context.SHORTCUT_SERVICE);
scManager.addDynamicShortcuts(Arrays.asList(shortcut));
}
private void deleteShortcuts() {
ShortcutManager scManager =
(ShortcutManager) mContext.getSystemService(Context.SHORTCUT_SERVICE);
scManager.removeAllDynamicShortcuts();
scManager.removeLongLivedShortcuts(Collections.singletonList(SHARE_SHORTCUT_ID));
}
/**
* Notification fulfilling conversation policy; for the shortcut to be valid
* call {@link #createDynamicShortcut()}
*/
private Notification.Builder getConversationNotification() {
Person person = new Person.Builder()
.setName("bubblebot")
.build();
Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle("foo")
.setShortcutId(SHARE_SHORTCUT_ID)
.setStyle(new Notification.MessagingStyle(person)
.setConversationTitle("Bubble Chat")
.addMessage("Hello?",
SystemClock.currentThreadTimeMillis() - 300000, person)
.addMessage("Is it me you're looking for?",
SystemClock.currentThreadTimeMillis(), person)
)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
return nb;
}
/**
* Starts an activity that is able to send a bubble; also handles unlocking the device.
* Any tests that use this method should be sure to call {@link #cleanupSendBubbleActivity()}
* to unregister the related broadcast receiver.
*
* @return the SendBubbleActivity that was opened.
*/
private SendBubbleActivity startSendBubbleActivity() {
final CountDownLatch latch = new CountDownLatch(2);
mBubbleBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
latch.countDown();
}
};
IntentFilter filter = new IntentFilter(SendBubbleActivity.BUBBLE_ACTIVITY_OPENED);
mContext.registerReceiver(mBubbleBroadcastReceiver, filter);
// Start & get the activity
Class clazz = SendBubbleActivity.class;
Instrumentation.ActivityResult result =
new Instrumentation.ActivityResult(0, new Intent());
Instrumentation.ActivityMonitor monitor =
new Instrumentation.ActivityMonitor(clazz.getName(), result, false);
InstrumentationRegistry.getInstrumentation().addMonitor(monitor);
Intent i = new Intent(mContext, SendBubbleActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
InstrumentationRegistry.getInstrumentation().startActivitySync(i);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
SendBubbleActivity sendBubbleActivity = (SendBubbleActivity) monitor.waitForActivity();
// Make sure device is unlocked
KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
keyguardManager.requestDismissKeyguard(sendBubbleActivity,
new KeyguardManager.KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() {
latch.countDown();
}
});
try {
latch.await(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return sendBubbleActivity;
}
private void cleanupSendBubbleActivity() {
mContext.unregisterReceiver(mBubbleBroadcastReceiver);
}
public void testConsolidatedNotificationPolicy() throws Exception {
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA,
0, 0));
// turn on manual DND
mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
assertExpectedDndState(INTERRUPTION_FILTER_PRIORITY);
// no custom ZenPolicy, so consolidatedPolicy should equal the default notif policy
assertEquals(mNotificationManager.getConsolidatedNotificationPolicy(),
mNotificationManager.getNotificationPolicy());
// turn off manual DND
mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
// setup custom ZenPolicy for an automatic rule
AutomaticZenRule rule = createRule("test_consolidated_policy",
INTERRUPTION_FILTER_PRIORITY);
rule.setZenPolicy(new ZenPolicy.Builder()
.allowReminders(true)
.build());
String id = mNotificationManager.addAutomaticZenRule(rule);
mRuleIds.add(id);
// set condition of the automatic rule to TRUE
Condition condition = new Condition(rule.getConditionId(), "summary",
Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
assertExpectedDndState(INTERRUPTION_FILTER_PRIORITY);
NotificationManager.Policy consolidatedPolicy =
mNotificationManager.getConsolidatedNotificationPolicy();
// alarms and media are allowed from default notification policy
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_ALARMS) != 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MEDIA) != 0);
// reminders is allowed from the automatic rule's custom ZenPolicy
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_REMINDERS) != 0);
// other sounds aren't allowed
assertTrue((consolidatedPolicy.priorityCategories
& PRIORITY_CATEGORY_CONVERSATIONS) == 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_CALLS) == 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MESSAGES) == 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) == 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_EVENTS) == 0);
} finally {
mNotificationManager.setInterruptionFilter(originalFilter);
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testConsolidatedNotificationPolicyMultiRules() throws Exception {
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
// default allows no sounds
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
PRIORITY_CATEGORY_ALARMS, 0, 0));
// setup custom ZenPolicy for two automatic rules
AutomaticZenRule rule1 = createRule("test_consolidated_policyq",
INTERRUPTION_FILTER_PRIORITY);
rule1.setZenPolicy(new ZenPolicy.Builder()
.allowReminders(false)
.allowAlarms(false)
.allowSystem(true)
.build());
AutomaticZenRule rule2 = createRule("test_consolidated_policy2",
INTERRUPTION_FILTER_PRIORITY);
rule2.setZenPolicy(new ZenPolicy.Builder()
.allowReminders(true)
.allowMedia(true)
.build());
String id1 = mNotificationManager.addAutomaticZenRule(rule1);
String id2 = mNotificationManager.addAutomaticZenRule(rule2);
Condition onCondition1 = new Condition(rule1.getConditionId(), "summary",
Condition.STATE_TRUE);
Condition onCondition2 = new Condition(rule2.getConditionId(), "summary",
Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id1, onCondition1);
mNotificationManager.setAutomaticZenRuleState(id2, onCondition2);
Thread.sleep(300); // wait for rules to be applied - it's done asynchronously
mRuleIds.add(id1);
mRuleIds.add(id2);
assertExpectedDndState(INTERRUPTION_FILTER_PRIORITY);
NotificationManager.Policy consolidatedPolicy =
mNotificationManager.getConsolidatedNotificationPolicy();
// reminders aren't allowed from rule1 overriding rule2
// (not allowed takes precedence over allowed)
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_REMINDERS) == 0);
// alarms aren't allowed from rule1
// (rule's custom zenPolicy overrides default policy)
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_ALARMS) == 0);
// system is allowed from rule1, media is allowed from rule2
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) != 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MEDIA) != 0);
// other sounds aren't allowed (from default policy)
assertTrue((consolidatedPolicy.priorityCategories
& PRIORITY_CATEGORY_CONVERSATIONS) == 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_CALLS) == 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MESSAGES) == 0);
assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_EVENTS) == 0);
} finally {
mNotificationManager.setInterruptionFilter(originalFilter);
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testPostPCanToggleAlarmsMediaSystemTest() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
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(
PRIORITY_CATEGORY_ALARMS
| PRIORITY_CATEGORY_MEDIA
| PRIORITY_CATEGORY_SYSTEM, 0, 0));
NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_ALARMS) != 0);
assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_MEDIA) != 0);
assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) != 0);
// toggle off alarms, media, system
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
policy = mNotificationManager.getNotificationPolicy();
assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_ALARMS) == 0);
assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_MEDIA) == 0);
assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) == 0);
}
} finally {
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testPostRCanToggleConversationsTest() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
0, 0, 0, 0));
NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
assertEquals(0, (policy.priorityCategories & PRIORITY_CATEGORY_CONVERSATIONS));
assertEquals(CONVERSATION_SENDERS_NONE, policy.priorityConversationSenders);
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_ANYONE));
policy = mNotificationManager.getNotificationPolicy();
assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_CONVERSATIONS) != 0);
assertEquals(CONVERSATION_SENDERS_ANYONE, policy.priorityConversationSenders);
} finally {
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testCreateChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
final NotificationChannel channel =
new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
final NotificationChannel ungrouped =
new NotificationChannel(mId + "!", "name", 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", 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 testGetChannelGroups() 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", IMPORTANCE_DEFAULT);
channel.setGroup(ncg2.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
mNotificationManager.createNotificationChannelGroup(ncg2);
mNotificationManager.createNotificationChannel(channel);
List<NotificationChannelGroup> actual =
mNotificationManager.getNotificationChannelGroups();
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId().equals(ncg.getId())) {
assertEquals(group.getName(), ncg.getName());
assertEquals(group.getDescription(), ncg.getDescription());
assertEquals(0, group.getChannels().size());
} else if (group.getId().equals(ncg2.getId())) {
assertEquals(group.getName(), ncg2.getName());
assertEquals(group.getDescription(), ncg2.getDescription());
assertEquals(1, group.getChannels().size());
assertEquals(channel.getId(), group.getChannels().get(0).getId());
} else {
fail("Extra group found " + group.getId());
}
}
}
public void testDeleteChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
final NotificationChannel channel =
new NotificationChannel(mId, "name", 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", 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", 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", 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", 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", IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel channelDupe =
new NotificationChannel(mId, "name", 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", IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
NotificationChannel channelDupe =
new NotificationChannel(mId, "name", 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", 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", 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", IMPORTANCE_UNSPECIFIED);
try {
mNotificationManager.createNotificationChannel(channel);
} catch (IllegalArgumentException e) {
//success
}
}
public void testDeleteChannel() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", 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", IMPORTANCE_DEFAULT);
NotificationChannel channel2 =
new NotificationChannel(
UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel(
UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW);
NotificationChannel channel4 =
new NotificationChannel(
UUID.randomUUID().toString(), "name4", 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", IMPORTANCE_DEFAULT);
NotificationChannel channel2 =
new NotificationChannel(
UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel(
UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW);
NotificationChannel channel4 =
new NotificationChannel(
UUID.randomUUID().toString(), "name4", 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", IMPORTANCE_DEFAULT);
channel.setShowBadge(true);
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), 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_withoutShellPermission() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
try {
Process proc = Runtime.getRuntime().exec("cmd notification suspend_package "
+ mContext.getPackageName());
// read output of command
BufferedReader reader =
new BufferedReader(new InputStreamReader(proc.getInputStream()));
StringBuilder output = new StringBuilder();
String line = reader.readLine();
while (line != null) {
output.append(line);
line = reader.readLine();
}
reader.close();
final String outputString = output.toString();
proc.waitFor();
// check that the output string had an error / disallowed call since it didn't have
// shell permission to suspend the package
assertTrue(outputString, outputString.contains("error"));
assertTrue(outputString, outputString.contains("permission denied"));
} catch (InterruptedException e) {
fail("Unsuccessful shell command");
}
}
public void testSuspendPackage() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.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 testSuspendedPackageSendsNotification() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
// suspend package, post notification while package is suspended, see notification
// in ranking map with suspended = true
suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
true);
sendNotification(1, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
assertEquals(1, mListener.mPosted.size()); // apps targeting P receive notification
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
assertEquals(1, mListener.mPosted.size()); // should see previously posted notification
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 testCanBubble_ranking() throws Exception {
if ((mActivityManager.isLowRamDevice() && !FeatureUtil.isWatch())
|| FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
return;
}
// turn on bubbles globally
setBubblesGlobal(true);
assertEquals(1, Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES));
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
sendNotification(1, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
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);
// by default nothing can bubble
assertFalse(outRanking.canBubble());
}
}
// turn off bubbles globally
setBubblesGlobal(false);
rankingMap = mListener.mRankingMap;
outRanking = new NotificationListenerService.Ranking();
for (String key : rankingMap.getOrderedKeys()) {
if (key.contains(mListener.getPackageName())) {
rankingMap.getRanking(key, outRanking);
assertFalse(outRanking.canBubble());
}
}
mListener.resetData();
}
public void testShowBadging_ranking() throws Exception {
final int originalBadging = Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING);
SystemUtil.runWithShellPermissionIdentity(() ->
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.NOTIFICATION_BADGING, 1));
assertEquals(1, Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING));
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
try {
sendNotification(1, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
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);
assertTrue(outRanking.canShowBadge());
}
}
// turn off badging globally
SystemUtil.runWithShellPermissionIdentity(() ->
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.NOTIFICATION_BADGING, 0));
Thread.sleep(500); // wait for ranking update
rankingMap = mListener.mRankingMap;
outRanking = new NotificationListenerService.Ranking();
for (String key : rankingMap.getOrderedKeys()) {
if (key.contains(mListener.getPackageName())) {
assertFalse(outRanking.canShowBadge());
}
}
mListener.resetData();
} finally {
SystemUtil.runWithShellPermissionIdentity(() ->
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.NOTIFICATION_BADGING, originalBadging));
}
}
public void testGetSuppressedVisualEffectsOff_ranking() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
final int notificationId = 1;
sendNotification(notificationId, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
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);
// check notification key match
assertEquals(0, outRanking.getSuppressedVisualEffects());
}
}
}
public void testGetSuppressedVisualEffects_ranking() throws Exception {
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_PEEK));
} else {
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON));
}
mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
final int notificationId = 1;
// update notification
sendNotification(notificationId, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
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);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
assertEquals(SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_PEEK,
outRanking.getSuppressedVisualEffects());
} else {
assertEquals(SUPPRESSED_EFFECT_SCREEN_ON,
outRanking.getSuppressedVisualEffects());
}
}
}
} finally {
// reset notification policy
mNotificationManager.setInterruptionFilter(originalFilter);
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
final int notificationId = 1;
sendNotification(notificationId, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap;
NotificationListenerService.Ranking outRanking =
new NotificationListenerService.Ranking();
StatusBarNotification sbn = findPostedNotification(notificationId, false);
// check that the key and channel ids are the same in the ranking as the posted notification
for (String key : rankingMap.getOrderedKeys()) {
if (key.contains(mListener.getPackageName())) {
rankingMap.getRanking(key, outRanking);
// check notification key match
assertEquals(sbn.getKey(), outRanking.getKey());
// check notification channel ids match
assertEquals(sbn.getNotification().getChannelId(), outRanking.getChannel().getId());
// check override group key match
assertEquals(sbn.getOverrideGroupKey(), outRanking.getOverrideGroupKey());
// check importance explanation isn't null
assertNotNull(outRanking.getImportanceExplanation());
}
}
}
public void testNotify_blockedChannel() throws Exception {
mNotificationManager.cancelAll();
NotificationChannel channel =
new NotificationChannel(mId, "name", 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", 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 testStyle() throws Exception {
Notification.Style style = new Notification.Style() {
public boolean areNotificationsVisiblyDifferent(Notification.Style other) {
return false;
}
};
Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID);
style.setBuilder(builder);
Notification notification = null;
try {
notification = style.build();
} catch (IllegalArgumentException e) {
fail(e.getMessage());
}
assertNotNull(notification);
Notification builderNotification = builder.build();
assertEquals(builderNotification, notification);
}
public void testStyle_getStandardView() throws Exception {
Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID);
int layoutId = 0;
TestStyle overrideStyle = new TestStyle();
overrideStyle.setBuilder(builder);
RemoteViews result = overrideStyle.testGetStandardView(layoutId);
assertNotNull(result);
assertEquals(layoutId, result.getLayoutId());
}
private class TestStyle extends Notification.Style {
public boolean areNotificationsVisiblyDifferent(Notification.Style other) {
return false;
}
public RemoteViews testGetStandardView(int layoutId) {
// Wrapper method, since getStandardView is protected and otherwise unused in Android
return getStandardView(layoutId);
}
}
public void testMediaStyle_empty() {
Notification.MediaStyle style = new Notification.MediaStyle();
assertNotNull(style);
}
public void testMediaStyle() {
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() {
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() {
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() {
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(801, R.drawable.black);
sendNotification(802, R.drawable.blue);
sendNotification(803, R.drawable.yellow);
sendNotification(804, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception {
sendNotification(701, R.drawable.black);
sendNotification(702, R.drawable.blue);
sendNotification(703, R.drawable.yellow);
sendNotification(704, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
for (int i = 704; i > 701; i--) {
cancelAndPoll(i);
assertNotificationCount(i - 700);
assertAllPostedNotificationsAutogrouped();
}
cancelAndPoll(701);
assertNotificationCount(0);
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()
throws Exception {
String newGroup = "new!";
sendNotification(901, R.drawable.black);
sendNotification(902, R.drawable.blue);
sendNotification(903, R.drawable.yellow);
sendNotification(904, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
postedIds.add(901);
postedIds.add(902);
postedIds.add(903);
postedIds.add(904);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
for (int i = 904; i > 901; i--) {
sendNotification(i, newGroup, R.drawable.blue);
postedIds.remove(postedIds.size() - 1);
assertNotificationCount(5);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
sendNotification(901, 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(910, R.drawable.black);
sendNotification(920, R.drawable.blue);
sendNotification(930, R.drawable.yellow);
sendNotification(940, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
postedIds.add(910);
postedIds.add(920);
postedIds.add(930);
postedIds.add(940);
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(950, R.drawable.blue);
postedIds.add(950);
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
// pass
}
assertOnlySomeNotificationsAutogrouped(postedIds);
}
public void testTotalSilenceOnlyMuteStreams() throws Exception {
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
// ensure volume is not muted/0 to start test
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0));
AutomaticZenRule rule = createRule("test_total_silence", INTERRUPTION_FILTER_NONE);
String id = mNotificationManager.addAutomaticZenRule(rule);
mRuleIds.add(id);
Condition condition =
new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
// delay for streams to get into correct mute states
Thread.sleep(50);
assertTrue("Music (media) stream should be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
assertTrue("System stream should be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
assertTrue("Alarm stream should be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
// Test requires that the phone's default state has no channels that can bypass dnd
assertTrue("Ringer stream should be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_RING));
} finally {
mNotificationManager.setInterruptionFilter(originalFilter);
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testAlarmsOnlyMuteStreams() throws Exception {
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
// ensure volume is not muted/0 to start test
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0));
AutomaticZenRule rule = createRule("test_alarms", INTERRUPTION_FILTER_ALARMS);
String id = mNotificationManager.addAutomaticZenRule(rule);
mRuleIds.add(id);
Condition condition =
new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
// delay for streams to get into correct mute states
Thread.sleep(50);
assertFalse("Music (media) stream should not be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
assertTrue("System stream should be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
assertFalse("Alarm stream should not be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
// Test requires that the phone's default state has no channels that can bypass dnd
assertTrue("Ringer stream should be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_RING));
} finally {
mNotificationManager.setInterruptionFilter(originalFilter);
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testAddAutomaticZenRule_configActivity() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
AutomaticZenRule ruleToCreate = createRule("Rule");
String id = mNotificationManager.addAutomaticZenRule(ruleToCreate);
assertNotNull(id);
mRuleIds.add(id);
assertTrue(areRulesSame(ruleToCreate, mNotificationManager.getAutomaticZenRule(id)));
}
public void testUpdateAutomaticZenRule_configActivity() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
AutomaticZenRule ruleToCreate = createRule("Rule");
String id = mNotificationManager.addAutomaticZenRule(ruleToCreate);
ruleToCreate.setEnabled(false);
mNotificationManager.updateAutomaticZenRule(id, ruleToCreate);
assertNotNull(id);
mRuleIds.add(id);
assertTrue(areRulesSame(ruleToCreate, mNotificationManager.getAutomaticZenRule(id)));
}
public void testRemoveAutomaticZenRule_configActivity() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
AutomaticZenRule ruleToCreate = createRule("Rule");
String id = mNotificationManager.addAutomaticZenRule(ruleToCreate);
assertNotNull(id);
mRuleIds.add(id);
mNotificationManager.removeAutomaticZenRule(id);
assertNull(mNotificationManager.getAutomaticZenRule(id));
assertEquals(0, mNotificationManager.getAutomaticZenRules().size());
}
public void testSetAutomaticZenRuleState() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
AutomaticZenRule ruleToCreate = createRule("Rule");
String id = mNotificationManager.addAutomaticZenRule(ruleToCreate);
mRuleIds.add(id);
// make sure DND is off
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
// enable DND
Condition condition =
new Condition(ruleToCreate.getConditionId(), "summary", Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
assertExpectedDndState(ruleToCreate.getInterruptionFilter());
}
public void testSetAutomaticZenRuleState_turnOff() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
AutomaticZenRule ruleToCreate = createRule("Rule");
String id = mNotificationManager.addAutomaticZenRule(ruleToCreate);
mRuleIds.add(id);
// make sure DND is off
// make sure DND is off
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
// enable DND
Condition condition =
new Condition(ruleToCreate.getConditionId(), "on", Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
assertExpectedDndState(ruleToCreate.getInterruptionFilter());
// disable DND
condition = new Condition(ruleToCreate.getConditionId(), "off", Condition.STATE_FALSE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
// make sure DND is off
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
}
public void testSetAutomaticZenRuleState_deletedRule() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
AutomaticZenRule ruleToCreate = createRule("Rule");
String id = mNotificationManager.addAutomaticZenRule(ruleToCreate);
mRuleIds.add(id);
// make sure DND is off
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
// enable DND
Condition condition =
new Condition(ruleToCreate.getConditionId(), "summary", Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
assertExpectedDndState(ruleToCreate.getInterruptionFilter());
mNotificationManager.removeAutomaticZenRule(id);
// make sure DND is off
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
}
public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
AutomaticZenRule ruleToCreate = createRule("Rule");
String id = mNotificationManager.addAutomaticZenRule(ruleToCreate);
mRuleIds.add(id);
AutomaticZenRule secondRuleToCreate = createRule("Rule 2");
secondRuleToCreate.setInterruptionFilter(INTERRUPTION_FILTER_NONE);
String secondId = mNotificationManager.addAutomaticZenRule(secondRuleToCreate);
mRuleIds.add(secondId);
// make sure DND is off
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
// enable DND
Condition condition =
new Condition(ruleToCreate.getConditionId(), "summary", Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(id, condition);
Condition secondCondition =
new Condition(secondRuleToCreate.getConditionId(), "summary", Condition.STATE_TRUE);
mNotificationManager.setAutomaticZenRuleState(secondId, secondCondition);
// the second rule has a 'more silent' DND filter, so the system wide DND should be
// using its filter
assertExpectedDndState(secondRuleToCreate.getInterruptionFilter());
// remove intense rule, system should fallback to other rule
mNotificationManager.removeAutomaticZenRule(secondId);
assertExpectedDndState(ruleToCreate.getInterruptionFilter());
}
public void testSetNotificationPolicy_P_setOldFields() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
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);
}
} finally {
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testSetNotificationPolicy_P_setNewFields() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
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);
}
} finally {
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testSetNotificationPolicy_P_setOldNewFields() throws Exception {
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
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);
}
} finally {
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
public void testPostFullScreenIntent_permission() {
int id = 6000;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setWhen(System.currentTimeMillis())
.setFullScreenIntent(getPendingIntent(), true)
.setContentText("This is #FSI notification")
.setContentIntent(getPendingIntent())
.build();
mNotificationManager.notify(id, notification);
StatusBarNotification n = findPostedNotification(id, false);
assertNotNull(n);
assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent);
}
public void testNotificationPolicyVisualEffectsEqual() {
NotificationManager.Policy policy = new NotificationManager.Policy(0,0 ,0 ,
SUPPRESSED_EFFECT_SCREEN_ON);
NotificationManager.Policy policy2 = new NotificationManager.Policy(0,0 ,0 ,
SUPPRESSED_EFFECT_PEEK);
assertTrue(policy.equals(policy2));
assertTrue(policy2.equals(policy));
policy = new NotificationManager.Policy(0,0 ,0 ,
SUPPRESSED_EFFECT_SCREEN_ON);
policy2 = new NotificationManager.Policy(0,0 ,0 ,
0);
assertFalse(policy.equals(policy2));
assertFalse(policy2.equals(policy));
policy = new NotificationManager.Policy(0,0 ,0 ,
SUPPRESSED_EFFECT_SCREEN_OFF);
policy2 = new NotificationManager.Policy(0,0 ,0 ,
SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS);
assertTrue(policy.equals(policy2));
assertTrue(policy2.equals(policy));
policy = new NotificationManager.Policy(0,0 ,0 ,
SUPPRESSED_EFFECT_SCREEN_OFF);
policy2 = new NotificationManager.Policy(0,0 ,0 ,
SUPPRESSED_EFFECT_LIGHTS);
assertFalse(policy.equals(policy2));
assertFalse(policy2.equals(policy));
}
public void testNotificationDelegate_grantAndPost() throws Exception {
// grant this test permission to post
final Intent activityIntent = new Intent();
activityIntent.setPackage(DELEGATOR);
activityIntent.setAction(Intent.ACTION_MAIN);
activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// wait for the activity to launch and finish
mContext.startActivity(activityIntent);
Thread.sleep(1000);
// send notification
Notification n = new Notification.Builder(mContext, "channel")
.setSmallIcon(android.R.id.icon)
.build();
mNotificationManager.notifyAsPackage(DELEGATOR, "tag", 0, n);
assertNotNull(findPostedNotification(0, false));
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(revokeIntent);
Thread.sleep(1000);
}
public void testNotificationDelegate_grantAndPostAndCancel() throws Exception {
// grant this test permission to post
final Intent activityIntent = new Intent();
activityIntent.setPackage(DELEGATOR);
activityIntent.setAction(Intent.ACTION_MAIN);
activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// wait for the activity to launch and finish
mContext.startActivity(activityIntent);
Thread.sleep(1000);
// send notification
Notification n = new Notification.Builder(mContext, "channel")
.setSmallIcon(android.R.id.icon)
.build();
mNotificationManager.notifyAsPackage(DELEGATOR, "toBeCanceled", 10000, n);
assertNotNull(findPostedNotification(10000, false));
mNotificationManager.cancelAsPackage(DELEGATOR, "toBeCanceled", 10000);
assertNotificationCancelled(10000, false);
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(revokeIntent);
Thread.sleep(1000);
}
public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()
throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
// grant this test permission to post
final Intent activityIntent = new Intent();
activityIntent.setClassName(DELEGATOR, DELEGATE_POST_CLASS);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(activityIntent);
Thread.sleep(1000);
assertNotNull(findPostedNotification(9, true));
try {
mNotificationManager.cancelAsPackage(DELEGATOR, null, 9);
fail ("Delegate should not be able to cancel notification they did not post");
} catch (SecurityException e) {
// yay
}
// double check that the notification does still exist
assertNotNull(findPostedNotification(9, true));
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(revokeIntent);
Thread.sleep(1000);
}
public void testNotificationDelegate_grantAndReadChannels() throws Exception {
// grant this test permission to post
final Intent activityIntent = new Intent();
activityIntent.setPackage(DELEGATOR);
activityIntent.setAction(Intent.ACTION_MAIN);
activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// wait for the activity to launch and finish
mContext.startActivity(activityIntent);
Thread.sleep(500);
List<NotificationChannel> channels =
mContext.createPackageContextAsUser(DELEGATOR, /* flags= */ 0, mContext.getUser())
.getSystemService(NotificationManager.class)
.getNotificationChannels();
assertNotNull(channels);
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(revokeIntent);
Thread.sleep(500);
}
public void testNotificationDelegate_grantAndReadChannel() throws Exception {
// grant this test permission to post
final Intent activityIntent = new Intent();
activityIntent.setPackage(DELEGATOR);
activityIntent.setAction(Intent.ACTION_MAIN);
activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// wait for the activity to launch and finish
mContext.startActivity(activityIntent);
Thread.sleep(500);
NotificationChannel channel =
mContext.createPackageContextAsUser(DELEGATOR, /* flags= */ 0, mContext.getUser())
.getSystemService(NotificationManager.class)
.getNotificationChannel("channel");
assertNotNull(channel);
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(revokeIntent);
Thread.sleep(500);
}
public void testNotificationDelegate_grantAndRevoke() throws Exception {
// grant this test permission to post
final Intent activityIntent = new Intent();
activityIntent.setPackage(DELEGATOR);
activityIntent.setAction(Intent.ACTION_MAIN);
activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(activityIntent);
Thread.sleep(500);
assertTrue(mNotificationManager.canNotifyAsPackage(DELEGATOR));
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(revokeIntent);
Thread.sleep(500);
try {
// send notification
Notification n = new Notification.Builder(mContext, "channel")
.setSmallIcon(android.R.id.icon)
.build();
mNotificationManager.notifyAsPackage(DELEGATOR, "tag", 0, n);
fail("Should not be able to post as a delegate when permission revoked");
} catch (SecurityException e) {
// yay
}
}
public void testAreBubblesAllowed_appNone() throws Exception {
setBubblesAppPref(0 /* none */);
assertFalse(mNotificationManager.areBubblesAllowed());
}
public void testAreBubblesAllowed_appSelected() throws Exception {
setBubblesAppPref(2 /* selected */);
assertFalse(mNotificationManager.areBubblesAllowed());
}
public void testAreBubblesAllowed_appAll() throws Exception {
setBubblesAppPref(1 /* all */);
assertTrue(mNotificationManager.areBubblesAllowed());
}
public void testNotificationIcon() {
int id = 6000;
Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(android.R.id.icon)
.setWhen(System.currentTimeMillis())
.setFullScreenIntent(getPendingIntent(), true)
.setContentText("This notification has a resource icon")
.setContentIntent(getPendingIntent())
.build();
mNotificationManager.notify(id, notification);
notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(Icon.createWithResource(mContext, android.R.id.icon))
.setWhen(System.currentTimeMillis())
.setFullScreenIntent(getPendingIntent(), true)
.setContentText("This notification has an Icon icon")
.setContentIntent(getPendingIntent())
.build();
mNotificationManager.notify(id, notification);
StatusBarNotification n = findPostedNotification(id, false);
assertNotNull(n);
}
public void testShouldHideSilentStatusIcons() throws Exception {
try {
mNotificationManager.shouldHideSilentStatusBarIcons();
fail("Non-privileged apps should not get this information");
} catch (SecurityException e) {
// pass
}
toggleListenerAccess(true);
// no exception this time
mNotificationManager.shouldHideSilentStatusBarIcons();
}
public void testMatchesCallFilter() throws Exception {
// allow all callers
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
Policy origPolicy = mNotificationManager.getNotificationPolicy();
try {
NotificationManager.Policy currPolicy = mNotificationManager.getNotificationPolicy();
NotificationManager.Policy newPolicy = new NotificationManager.Policy(
NotificationManager.Policy.PRIORITY_CATEGORY_CALLS
| NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS,
NotificationManager.Policy.PRIORITY_SENDERS_ANY,
currPolicy.priorityMessageSenders,
currPolicy.suppressedVisualEffects);
mNotificationManager.setNotificationPolicy(newPolicy);
// add a contact
String ALICE = "Alice";
String ALICE_PHONE = "+16175551212";
String ALICE_EMAIL = "alice@_foo._bar";
insertSingleContact(ALICE, ALICE_PHONE, ALICE_EMAIL, false);
final Bundle peopleExtras = new Bundle();
ArrayList<Person> personList = new ArrayList<>();
personList.add(
new Person.Builder().setUri(lookupContact(ALICE_PHONE).toString()).build());
peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList);
SystemUtil.runWithShellPermissionIdentity(() ->
assertTrue(mNotificationManager.matchesCallFilter(peopleExtras)));
} finally {
mNotificationManager.setNotificationPolicy(origPolicy);
}
}
/* Confirm that the optional methods of TestNotificationListener still exist and
* don't fail. */
public void testNotificationListenerMethods() {
NotificationListenerService listener = new TestNotificationListener();
listener.onListenerConnected();
listener.onSilentStatusBarIconsVisibilityChanged(false);
listener.onNotificationPosted(null);
listener.onNotificationPosted(null, null);
listener.onNotificationRemoved(null);
listener.onNotificationRemoved(null, null);
listener.onNotificationChannelGroupModified("", UserHandle.CURRENT, null,
NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
listener.onNotificationChannelModified("", UserHandle.CURRENT, null,
NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
listener.onListenerDisconnected();
}
private void performNotificationProviderAction(@NonNull String action) {
// Create an intent to launch an activity which just posts or cancels notifications
Intent activityIntent = new Intent();
activityIntent.setClassName(NOTIFICATIONPROVIDER, RICH_NOTIFICATION_ACTIVITY);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activityIntent.putExtra("action", action);
mContext.startActivity(activityIntent);
}
public void testNotificationUriPermissionsGranted() throws Exception {
Uri background7Uri = Uri.parse(
"content://com.android.test.notificationprovider.provider/background7.png");
Uri background8Uri = Uri.parse(
"content://com.android.test.notificationprovider.provider/background8.png");
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
try {
// Post #7
performNotificationProviderAction("send-7");
assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
assertNotificationCancelled(8, true);
assertAccessible(background7Uri);
assertInaccessible(background8Uri);
// Post #8
performNotificationProviderAction("send-8");
assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
assertEquals(background8Uri, getNotificationBackgroundImageUri(8));
assertAccessible(background7Uri);
assertAccessible(background8Uri);
// Cancel #7
performNotificationProviderAction("cancel-7");
assertNotificationCancelled(7, true);
assertEquals(background8Uri, getNotificationBackgroundImageUri(8));
assertInaccessible(background7Uri);
assertAccessible(background8Uri);
// Cancel #8
performNotificationProviderAction("cancel-8");
assertNotificationCancelled(7, true);
assertNotificationCancelled(8, true);
assertInaccessible(background7Uri);
assertInaccessible(background8Uri);
} finally {
// Clean up -- reset any remaining notifications
performNotificationProviderAction("reset");
Thread.sleep(500);
}
}
public void testNotificationUriPermissionsGrantedToNewListeners() throws Exception {
Uri background7Uri = Uri.parse(
"content://com.android.test.notificationprovider.provider/background7.png");
try {
// Post #7
performNotificationProviderAction("send-7");
Thread.sleep(500);
// Don't have access the notification yet, but we can test the URI
assertInaccessible(background7Uri);
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
assertAccessible(background7Uri);
} finally {
// Clean Up -- Cancel #7
performNotificationProviderAction("cancel-7");
Thread.sleep(500);
}
}
private void assertAccessible(Uri uri)
throws IOException {
ContentResolver contentResolver = mContext.getContentResolver();
try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) {
assertNotNull(fd);
} catch (SecurityException e) {
throw new AssertionError("URI should be accessible: " + uri, e);
}
}
private void assertInaccessible(Uri uri)
throws IOException {
ContentResolver contentResolver = mContext.getContentResolver();
try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) {
fail("URI should be inaccessible: " + uri);
} catch (SecurityException e) {
// pass
}
}
@NonNull
private Uri getNotificationBackgroundImageUri(int notificationId) {
StatusBarNotification sbn = findPostedNotification(notificationId, true);
assertNotNull(sbn);
String imageUriString = sbn.getNotification().extras
.getString(Notification.EXTRA_BACKGROUND_IMAGE_URI);
assertNotNull(imageUriString);
return Uri.parse(imageUriString);
}
public void testNotificationListener_setNotificationsShown() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
final int notificationId1 = 1003;
final int notificationId2 = 1004;
sendNotification(notificationId1, R.drawable.black);
sendNotification(notificationId2, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
StatusBarNotification sbn1 = findPostedNotification(notificationId1, false);
StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
mListener.setNotificationsShown(new String[]{ sbn1.getKey() });
toggleListenerAccess(false);
Thread.sleep(500); // wait for listener to be disallowed
try {
mListener.setNotificationsShown(new String[]{ sbn2.getKey() });
fail("Should not be able to set shown if listener access isn't granted");
} catch (SecurityException e) {
// expected
}
}
public void testNotificationListener_getNotificationChannels() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
try {
mListener.getNotificationChannels(mContext.getPackageName(), UserHandle.CURRENT);
fail("Shouldn't be able get channels without CompanionDeviceManager#getAssociations()");
} catch (SecurityException e) {
// expected
}
}
public void testNotificationListener_getNotificationChannelGroups() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
try {
mListener.getNotificationChannelGroups(mContext.getPackageName(), UserHandle.CURRENT);
fail("Should not be able get groups without CompanionDeviceManager#getAssociations()");
} catch (SecurityException e) {
// expected
}
}
public void testNotificationListener_updateNotificationChannel() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", IMPORTANCE_DEFAULT);
try {
mListener.updateNotificationChannel(mContext.getPackageName(), UserHandle.CURRENT,
channel);
fail("Shouldn't be able to update channel without "
+ "CompanionDeviceManager#getAssociations()");
} catch (SecurityException e) {
// expected
}
}
public void testNotificationListener_getActiveNotifications() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
final int notificationId1 = 1001;
final int notificationId2 = 1002;
sendNotification(notificationId1, R.drawable.black);
sendNotification(notificationId2, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
StatusBarNotification sbn1 = findPostedNotification(notificationId1, false);
StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
StatusBarNotification[] notifs =
mListener.getActiveNotifications(new String[]{ sbn2.getKey(), sbn1.getKey() });
assertEquals(sbn2.getKey(), notifs[0].getKey());
assertEquals(sbn2.getId(), notifs[0].getId());
assertEquals(sbn2.getPackageName(), notifs[0].getPackageName());
assertEquals(sbn1.getKey(), notifs[1].getKey());
assertEquals(sbn1.getId(), notifs[1].getId());
assertEquals(sbn1.getPackageName(), notifs[1].getPackageName());
}
public void testNotificationListener_getCurrentRanking() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
sendNotification(1, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
assertEquals(mListener.mRankingMap, mListener.getCurrentRanking());
}
public void testNotificationListener_cancelNotifications() throws Exception {
toggleListenerAccess(true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
final int notificationId = 1006;
sendNotification(notificationId, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
StatusBarNotification sbn = findPostedNotification(notificationId, false);
mListener.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
if (!checkNotificationExistence(notificationId, /*shouldExist=*/ true)) {
fail("Notification shouldn't have been cancelled. "
+ "cancelNotification(String, String, int) shouldn't cancel notif for L+");
}
} else {
// Tested in LegacyNotificationManager20Test
if (checkNotificationExistence(notificationId, /*shouldExist=*/ true)) {
fail("Notification should have been cancelled for targetSdk below L. targetSdk="
+ mContext.getApplicationInfo().targetSdkVersion);
}
}
mListener.cancelNotifications(new String[]{ sbn.getKey() });
if (!checkNotificationExistence(notificationId, /*shouldExist=*/ false)) {
fail("Failed to cancel notification id=" + notificationId);
}
}
public void testNotificationManagerPolicy_priorityCategoriesToString() {
String zeroString = NotificationManager.Policy.priorityCategoriesToString(0);
assertEquals("priorityCategories of 0 produces empty string", "", zeroString);
String oneString = NotificationManager.Policy.priorityCategoriesToString(1);
assertNotNull("priorityCategories of 1 returns a string", oneString);
boolean lengthGreaterThanZero = oneString.length() > 0;
assertTrue("priorityCategories of 1 returns a string with length greater than 0",
lengthGreaterThanZero);
String badNumberString = NotificationManager.Policy.priorityCategoriesToString(1234567);
assertNotNull("priorityCategories with a non-relevant int returns a string",
badNumberString);
}
public void testNotificationManagerPolicy_prioritySendersToString() {
String zeroString = NotificationManager.Policy.prioritySendersToString(0);
assertNotNull("prioritySenders of 1 returns a string", zeroString);
boolean lengthGreaterThanZero = zeroString.length() > 0;
assertTrue("prioritySenders of 1 returns a string with length greater than 0",
lengthGreaterThanZero);
String badNumberString = NotificationManager.Policy.prioritySendersToString(1234567);
assertNotNull("prioritySenders with a non-relevant int returns a string", badNumberString);
}
public void testNotificationManagerPolicy_suppressedEffectsToString() {
String zeroString = NotificationManager.Policy.suppressedEffectsToString(0);
assertEquals("suppressedEffects of 0 produces empty string", "", zeroString);
String oneString = NotificationManager.Policy.suppressedEffectsToString(1);
assertNotNull("suppressedEffects of 1 returns a string", oneString);
boolean lengthGreaterThanZero = oneString.length() > 0;
assertTrue("suppressedEffects of 1 returns a string with length greater than 0",
lengthGreaterThanZero);
String badNumberString = NotificationManager.Policy.suppressedEffectsToString(1234567);
assertNotNull("suppressedEffects with a non-relevant int returns a string",
badNumberString);
}
public void testNotificationManagerBubblePolicy_flag_intentBubble()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.Builder nb = getConversationNotification();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(1, nb, null /* use default metadata */, shouldBeBubble);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_noFlag_service()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_MESSAGING);
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
setUpNotifListener();
mContext.startService(serviceIntent);
// No services in R (allowed in Q)
verifyNotificationBubbleState(BUBBLE_NOTIF_ID, false /* shouldBeBubble */);
} finally {
deleteShortcuts();
mContext.stopService(serviceIntent);
}
}
public void testNotificationManagerBubblePolicy_noFlag_phonecall()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_CALL);
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
setUpNotifListener();
mContext.startService(serviceIntent);
// No phonecalls in R (allowed in Q)
verifyNotificationBubbleState(BUBBLE_NOTIF_ID, false /* shouldBeBubble */);
} finally {
deleteShortcuts();
mContext.stopService(serviceIntent);
}
}
public void testNotificationManagerBubblePolicy_noFlag_foreground() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
setUpNotifListener();
// Start & get the activity
SendBubbleActivity a = startSendBubbleActivity();
// Send a bubble that doesn't fulfill policy from foreground
a.sendInvalidBubble(false /* autoExpand */);
// No foreground bubbles that don't fulfill policy in R (allowed in Q)
verifyNotificationBubbleState(BUBBLE_NOTIF_ID, false /* shouldBeBubble */);
} finally {
deleteShortcuts();
cleanupSendBubbleActivity();
}
}
public void testNotificationManagerBubble_checkActivityFlagsDocumentLaunchMode()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()
|| mActivityManager.isLowRamDevice()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
setUpNotifListener();
// make ourselves foreground so we can auto-expand the bubble & check the intent flags
SendBubbleActivity a = startSendBubbleActivity();
// Prep to find bubbled activity
Class clazz = BubbledActivity.class;
Instrumentation.ActivityResult result =
new Instrumentation.ActivityResult(0, new Intent());
Instrumentation.ActivityMonitor monitor =
new Instrumentation.ActivityMonitor(clazz.getName(), result, false);
InstrumentationRegistry.getInstrumentation().addMonitor(monitor);
a.sendBubble(true /* autoExpand */, false /* suppressNotif */);
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
verifyNotificationBubbleState(BUBBLE_NOTIF_ID, shouldBeBubble);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
BubbledActivity activity = (BubbledActivity) monitor.waitForActivity();
assertTrue((activity.getIntent().getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT) != 0);
assertTrue((activity.getIntent().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0);
} finally {
deleteShortcuts();
cleanupSendBubbleActivity();
}
}
public void testNotificationManagerBubblePolicy_flag_shortcutBubble()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.Builder nb = getConversationNotification();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(1, nb, data, shouldBeBubble);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_noFlag_invalidShortcut()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.Builder nb = getConversationNotification();
nb.setShortcutId("invalid");
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder("invalid")
.build();
sendAndVerifyBubble(1, nb, data, false);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_noFlag_invalidNotif()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
sendAndVerifyBubble(1, null /* use default notif builder */, data,
false /* shouldBeBubble */);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_appAll_globalOn() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
Notification.Builder nb = getConversationNotification();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(1, nb, data, shouldBeBubble);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_appAll_globalOff() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(false);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
Notification.Builder nb = getConversationNotification();
sendAndVerifyBubble(1, nb, data, false);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_appAll_channelNo() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(false);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
Notification.Builder nb = getConversationNotification();
sendAndVerifyBubble(1, nb, data, false);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_appSelected_channelNo() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(2 /* selected */);
setBubblesChannelAllowed(false);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
Notification.Builder nb = getConversationNotification();
sendAndVerifyBubble(1, nb, data, false);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_appSelected_channelYes() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(2 /* selected */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
Notification.Builder nb = getConversationNotification();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(1, nb, data, shouldBeBubble);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_appNone_channelNo() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(0 /* none */);
setBubblesChannelAllowed(false);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
Notification.Builder nb = getConversationNotification();
sendAndVerifyBubble(1, nb, data, false);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubblePolicy_noFlag_shortcutRemoved()
throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()
|| mActivityManager.isLowRamDevice()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
.build();
Notification.Builder nb = getConversationNotification();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(42, nb, data, shouldBeBubble);
mListener.resetData();
deleteShortcuts();
verifyNotificationBubbleState(42, false /* should be bubble */);
} finally {
deleteShortcuts();
}
}
public void testNotificationManagerBubbleNotificationSuppression() throws Exception {
if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()
|| mActivityManager.isLowRamDevice()) {
// These do not support bubbles.
return;
}
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(true);
createDynamicShortcut();
setUpNotifListener();
// make ourselves foreground so we can specify suppress notification flag
SendBubbleActivity a = startSendBubbleActivity();
// send the bubble with notification suppressed
a.sendBubble(false /* autoExpand */, true /* suppressNotif */);
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
verifyNotificationBubbleState(BUBBLE_NOTIF_ID, shouldBeBubble);
// check for the notification
StatusBarNotification sbnSuppressed = mListener.mPosted.get(0);
assertNotNull(sbnSuppressed);
// check for suppression state
Notification.BubbleMetadata metadata =
sbnSuppressed.getNotification().getBubbleMetadata();
assertNotNull(metadata);
assertTrue(metadata.isNotificationSuppressed());
mListener.resetData();
// send the bubble with notification NOT suppressed
a.sendBubble(false /* autoExpand */, false /* suppressNotif */);
verifyNotificationBubbleState(BUBBLE_NOTIF_ID, shouldBeBubble);
// check for the notification
StatusBarNotification sbnNotSuppressed = mListener.mPosted.get(0);
assertNotNull(sbnNotSuppressed);
// check for suppression state
metadata = sbnNotSuppressed.getNotification().getBubbleMetadata();
assertNotNull(metadata);
assertFalse(metadata.isNotificationSuppressed());
} finally {
cleanupSendBubbleActivity();
deleteShortcuts();
}
}
public void testOriginalChannelImportance() {
NotificationChannel channel = new NotificationChannel(
"my channel", "my channel", IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
NotificationChannel actual = mNotificationManager.getNotificationChannel(channel.getId());
assertEquals(IMPORTANCE_HIGH, actual.getImportance());
assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance());
// Apps are allowed to downgrade channel importance if the user has not changed any
// fields on this channel yet.
channel.setImportance(IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
actual = mNotificationManager.getNotificationChannel(channel.getId());
assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance());
}
public void testCreateConversationChannel() {
final NotificationChannel channel =
new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT);
String conversationId = "person a";
final NotificationChannel conversationChannel =
new NotificationChannel(mId + "child",
"Messages from " + conversationId, IMPORTANCE_DEFAULT);
conversationChannel.setConversationId(channel.getId(), conversationId);
mNotificationManager.createNotificationChannel(channel);
mNotificationManager.createNotificationChannel(conversationChannel);
compareChannels(conversationChannel,
mNotificationManager.getNotificationChannel(channel.getId(), conversationId));
}
}