blob: fb6854a6192c521bcfb969d7b9c5b2e19c7e5c4c [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.CATEGORY_CALL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
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.BubblesTestActivity.BUBBLE_NOTIF_ID;
import static android.app.stubs.BubblesTestService.EXTRA_TEST_CASE;
import static android.app.stubs.BubblesTestService.TEST_NO_BUBBLE_METADATA;
import static android.app.stubs.BubblesTestService.TEST_NO_CATEGORY;
import static android.app.stubs.BubblesTestService.TEST_NO_PERSON;
import static android.app.stubs.BubblesTestService.TEST_SUCCESS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import android.app.Activity;
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.PendingIntent;
import android.app.Person;
import android.app.RemoteInput;
import android.app.UiAutomation;
import android.app.stubs.AutomaticZenRuleActivity;
import android.app.stubs.BubblesTestActivity;
import android.app.stubs.BubblesTestNotDocumentLaunchModeActivity;
import android.app.stubs.BubblesTestNotEmbeddableActivity;
import android.app.stubs.BubblesTestService;
import android.app.stubs.R;
import android.app.stubs.TestNotificationListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderOperation;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.OperationApplicationException;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Build;
import android.os.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.server.wm.ActivityManagerTestBase;
import android.service.notification.Condition;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.test.AndroidTestCase;
import android.util.Log;
import android.widget.RemoteViews;
import androidx.test.filters.FlakyTest;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.SystemUtil;
import junit.framework.Assert;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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 {
final String TAG = NotificationManagerTest.class.getSimpleName();
final boolean DEBUG = false;
final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
private static final String DELEGATOR = "com.android.test.notificationdelegator";
private static final String REVOKE_CLASS = DELEGATOR + ".NotificationRevoker";
private static final int WAIT_TIME = 2000;
private PackageManager mPackageManager;
private NotificationManager mNotificationManager;
private ActivityManager mActivityManager;
private String mId;
private TestNotificationListener mListener;
private List<String> mRuleIds;
@Override
protected void setUp() throws Exception {
super.setUp();
// This will leave a set of channels on the device with each test run.
mId = UUID.randomUUID().toString();
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
// clear the deck so that our getActiveNotifications results are predictable
mNotificationManager.cancelAll();
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = mContext.getPackageManager();
mRuleIds = new ArrayList<>();
// 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());
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), false);
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), false);
List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups();
// Delete all groups.
for (NotificationChannelGroup ncg : groups) {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
}
private void toggleBubbleSetting(boolean enabled) throws InterruptedException {
SystemUtil.runWithShellPermissionIdentity(() ->
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.NOTIFICATION_BUBBLES, enabled ? 1 : 0));
Thread.sleep(500); // wait for ranking update
}
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) {
// 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
StatusBarNotification n = null;
for (int tries = 3; tries-- > 0; ) {
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
Log.d(TAG, "Found " + sbn.getKey());
if (sbn.getId() == id) {
n = sbn;
break;
}
}
if (n != null) break;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
// pass
}
}
return n;
}
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 sendAndVerifyBubble(final int id, Notification.Builder builder,
Notification.BubbleMetadata data, boolean shouldBeBubble) {
final Intent intent = new Intent(mContext, BubblesTestActivity.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()
.setIcon(Icon.createWithResource(mContext, R.drawable.black))
.setIntent(pendingIntent)
.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);
if (!checkNotificationExistence(id, /*shouldExist=*/ true, shouldBeBubble)) {
fail("couldn't find posted notification bubble with id=" + id);
}
}
private boolean checkNotificationExistence(int id, boolean shouldExist) {
return checkNotificationExistence(id, shouldExist, false /* shouldBeBubble */);
}
private boolean checkNotificationExistence(int id, boolean shouldExist,
boolean shouldBeBubble) {
// 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;
boolean isBubble = false;
for (int tries = 3; tries--> 0;) {
// Need reset flag.
found = false;
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
isBubble = (sbn.getNotification().flags & FLAG_BUBBLE) != 0;
Log.d(TAG, "Found " + sbn.getKey() + " Bubble? " + isBubble);
if (sbn.getId() == id) {
found = true;
break;
}
}
if (found == shouldExist) break;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
// pass
}
}
return (found == shouldExist) && (isBubble == shouldBeBubble);
}
private void assertNotificationCount(int expectedCount) {
// notification is a bit asynchronous so it may take a few ms to appear in
// getActiveNotifications()
// we will check for it for up to 400ms before giving up
int lastCount = 0;
for (int tries = 4; tries-- > 0;) {
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
lastCount = sbns.length;
if (expectedCount == lastCount) return;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
// pass
}
}
fail("Expected " + expectedCount + " posted notifications, were " + lastCount);
}
private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
if (actual == null) {
fail("actual channel is null");
return;
}
if (expected == null) {
fail("expected channel is null");
return;
}
assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getName(), actual.getName());
assertEquals(expected.getDescription(), actual.getDescription());
assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
assertEquals(expected.getImportance(), actual.getImportance());
if (expected.getSound() == null) {
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actual.getSound());
assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, actual.getAudioAttributes());
} else {
assertEquals(expected.getSound(), actual.getSound());
assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes());
}
assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
assertEquals(expected.getGroup(), actual.getGroup());
}
private void toggleNotificationPolicyAccess(String packageName,
Instrumentation instrumentation, boolean on) throws IOException {
String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName;
runCommand(command, instrumentation);
NotificationManager nm = mContext.getSystemService(NotificationManager.class);
Assert.assertEquals("Notification Policy Access Grant is " +
nm.isNotificationPolicyAccessGranted() + " not " + on, on,
nm.isNotificationPolicyAccessGranted());
}
private void suspendPackage(String packageName,
Instrumentation instrumentation, boolean suspend) throws IOException {
int userId = mContext.getUserId();
String command = " cmd package " + (suspend ? "suspend " : "unsuspend ")
+ "--user " + userId + " " + packageName;
runCommand(command, instrumentation);
}
private void toggleListenerAccess(String componentName, Instrumentation instrumentation,
boolean on) throws IOException {
String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
+ componentName;
runCommand(command, instrumentation);
final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
final ComponentName listenerComponent = TestNotificationListener.getComponentName();
assertTrue(listenerComponent + " has not been granted access",
nm.isNotificationListenerAccessGranted(listenerComponent) == on);
}
private void runCommand(String command, Instrumentation instrumentation) throws IOException {
UiAutomation uiAutomation = instrumentation.getUiAutomation();
// Execute command
try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
Assert.assertNotNull("Failed to execute shell command: " + command, fd);
// Wait for the command to finish by reading until EOF
try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
byte[] buffer = new byte[4096];
while (in.read(buffer) > 0) {}
} catch (IOException e) {
throw new IOException("Could not read stdout of command: " + command, e);
}
} finally {
uiAutomation.destroy();
}
}
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) {
return new AutomaticZenRule(name, null,
new ComponentName(mContext, AutomaticZenRuleActivity.class),
new Uri.Builder().scheme("scheme")
.appendPath("path")
.appendQueryParameter("fake_rule", "fake_value")
.build(), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
}
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());
}
private Activity launchSendBubbleActivity() {
Class clazz = BubblesTestActivity.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, BubblesTestActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
InstrumentationRegistry.getInstrumentation().startActivitySync(i);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
return monitor.waitForActivity();
}
private class HomeHelper extends ActivityManagerTestBase implements AutoCloseable {
HomeHelper() throws Exception {
setUp();
}
public void goHome() {
launchHomeActivity();
}
@Override
public void close() throws Exception {
tearDown();
}
}
public void testPostPCanToggleAlarmsMediaSystemTest() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
// Post-P can toggle alarms, media, system
// toggle on alarms, media, system:
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
| NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
| NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM, 0, 0));
NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy();
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) != 0);
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) != 0);
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) != 0);
// toggle off alarms, media, system
mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0));
policy = mNotificationManager.getNotificationPolicy();
assertTrue((policy.priorityCategories
& NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0);
assertTrue((policy.priorityCategories &
NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) == 0);
assertTrue((policy.priorityCategories &
NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0);
}
}
public void testCreateChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
final NotificationChannel ungrouped =
new NotificationChannel(mId + "!", "name", NotificationManager.IMPORTANCE_DEFAULT);
try {
mNotificationManager.createNotificationChannel(channel);
mNotificationManager.createNotificationChannel(ungrouped);
List<NotificationChannelGroup> ncgs =
mNotificationManager.getNotificationChannelGroups();
assertEquals(1, ncgs.size());
assertEquals(ncg.getName(), ncgs.get(0).getName());
assertEquals(ncg.getDescription(), ncgs.get(0).getDescription());
assertEquals(channel.getId(), ncgs.get(0).getChannels().get(0).getId());
} finally {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
}
public void testGetChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
ncg.setDescription("bananas");
final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2");
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
mNotificationManager.createNotificationChannelGroup(ncg2);
mNotificationManager.createNotificationChannel(channel);
NotificationChannelGroup actual =
mNotificationManager.getNotificationChannelGroup(ncg.getId());
assertEquals(ncg.getId(), actual.getId());
assertEquals(ncg.getName(), actual.getName());
assertEquals(ncg.getDescription(), actual.getDescription());
assertEquals(channel.getId(), actual.getChannels().get(0).getId());
}
public void 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", NotificationManager.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", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
mNotificationManager.createNotificationChannel(channel);
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
assertNull(mNotificationManager.getNotificationChannel(channel.getId()));
assertEquals(0, mNotificationManager.getNotificationChannelGroups().size());
}
public void testCreateChannel() throws Exception {
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("bananas");
channel.enableVibration(true);
channel.setVibrationPattern(new long[] {5, 8, 2, 1});
channel.setSound(new Uri.Builder().scheme("test").build(),
new AudioAttributes.Builder().setUsage(
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED).build());
channel.enableLights(true);
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel createdChannel =
mNotificationManager.getNotificationChannel(mId);
compareChannels(channel, createdChannel);
// Lockscreen Visibility and canBypassDnd no longer settable.
assertTrue(createdChannel.getLockscreenVisibility() != Notification.VISIBILITY_SECRET);
assertFalse(createdChannel.canBypassDnd());
}
public void testCreateChannel_rename() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
channel.setName("new name");
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel createdChannel =
mNotificationManager.getNotificationChannel(mId);
compareChannels(channel, createdChannel);
channel.setImportance(NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
assertEquals(NotificationManager.IMPORTANCE_DEFAULT,
mNotificationManager.getNotificationChannel(mId).getImportance());
}
public void testCreateChannel_addToGroup() throws Exception {
String oldGroup = null;
String newGroup = "new group";
mNotificationManager.createNotificationChannelGroup(
new NotificationChannelGroup(newGroup, newGroup));
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(oldGroup);
mNotificationManager.createNotificationChannel(channel);
channel.setGroup(newGroup);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel updatedChannel =
mNotificationManager.getNotificationChannel(mId);
assertEquals("Failed to add non-grouped channel to a group on update ",
newGroup, updatedChannel.getGroup());
}
public void testCreateChannel_cannotChangeGroup() throws Exception {
String oldGroup = "old group";
String newGroup = "new group";
mNotificationManager.createNotificationChannelGroup(
new NotificationChannelGroup(oldGroup, oldGroup));
mNotificationManager.createNotificationChannelGroup(
new NotificationChannelGroup(newGroup, newGroup));
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(oldGroup);
mNotificationManager.createNotificationChannel(channel);
channel.setGroup(newGroup);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel updatedChannel =
mNotificationManager.getNotificationChannel(mId);
assertEquals("Channels should not be allowed to change groups",
oldGroup, updatedChannel.getGroup());
}
public void testCreateSameChannelDoesNotUpdate() throws Exception {
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
final NotificationChannel channelDupe =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channelDupe);
final NotificationChannel createdChannel =
mNotificationManager.getNotificationChannel(mId);
compareChannels(channel, createdChannel);
}
public void testCreateChannelAlreadyExistsNoOp() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationManager.createNotificationChannel(channel);
NotificationChannel channelDupe =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channelDupe);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
}
public void testCreateChannelWithGroup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("g", "n");
mNotificationManager.createNotificationChannelGroup(ncg);
try {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannel(channel);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
} finally {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
}
public void testCreateChannelWithBadGroup() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup("garbage");
try {
mNotificationManager.createNotificationChannel(channel);
fail("Created notification with bad group");
} catch (IllegalArgumentException e) {}
}
public void testCreateChannelInvalidImportance() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_UNSPECIFIED);
try {
mNotificationManager.createNotificationChannel(channel);
} catch (IllegalArgumentException e) {
//success
}
}
public void testDeleteChannel() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_LOW);
mNotificationManager.createNotificationChannel(channel);
compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
mNotificationManager.deleteNotificationChannel(channel.getId());
assertNull(mNotificationManager.getNotificationChannel(channel.getId()));
}
public void testCannotDeleteDefaultChannel() throws Exception {
try {
mNotificationManager.deleteNotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID);
fail("Deleted default channel");
} catch (IllegalArgumentException e) {
//success
}
}
public void testGetChannel() throws Exception {
NotificationChannel channel1 =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel channel2 =
new NotificationChannel(
UUID.randomUUID().toString(), "name2", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel(
UUID.randomUUID().toString(), "name3", NotificationManager.IMPORTANCE_LOW);
NotificationChannel channel4 =
new NotificationChannel(
UUID.randomUUID().toString(), "name4", NotificationManager.IMPORTANCE_MIN);
mNotificationManager.createNotificationChannel(channel1);
mNotificationManager.createNotificationChannel(channel2);
mNotificationManager.createNotificationChannel(channel3);
mNotificationManager.createNotificationChannel(channel4);
compareChannels(channel2,
mNotificationManager.getNotificationChannel(channel2.getId()));
compareChannels(channel3,
mNotificationManager.getNotificationChannel(channel3.getId()));
compareChannels(channel1,
mNotificationManager.getNotificationChannel(channel1.getId()));
compareChannels(channel4,
mNotificationManager.getNotificationChannel(channel4.getId()));
}
public void testGetChannels() throws Exception {
NotificationChannel channel1 =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel channel2 =
new NotificationChannel(
UUID.randomUUID().toString(), "name2", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel(
UUID.randomUUID().toString(), "name3", NotificationManager.IMPORTANCE_LOW);
NotificationChannel channel4 =
new NotificationChannel(
UUID.randomUUID().toString(), "name4", NotificationManager.IMPORTANCE_MIN);
Map<String, NotificationChannel> channelMap = new HashMap<>();
channelMap.put(channel1.getId(), channel1);
channelMap.put(channel2.getId(), channel2);
channelMap.put(channel3.getId(), channel3);
channelMap.put(channel4.getId(), channel4);
mNotificationManager.createNotificationChannel(channel1);
mNotificationManager.createNotificationChannel(channel2);
mNotificationManager.createNotificationChannel(channel3);
mNotificationManager.createNotificationChannel(channel4);
mNotificationManager.deleteNotificationChannel(channel3.getId());
List<NotificationChannel> channels = mNotificationManager.getNotificationChannels();
for (NotificationChannel nc : channels) {
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
continue;
}
if (NOTIFICATION_CHANNEL_ID.equals(nc.getId())) {
continue;
}
assertFalse(channel3.getId().equals(nc.getId()));
if (!channelMap.containsKey(nc.getId())) {
// failed cleanup from prior test run; ignore
continue;
}
compareChannels(channelMap.get(nc.getId()), nc);
}
}
public void testRecreateDeletedChannel() throws Exception {
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(true);
NotificationChannel newChannel = new NotificationChannel(
channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
mNotificationManager.deleteNotificationChannel(channel.getId());
mNotificationManager.createNotificationChannel(newChannel);
compareChannels(channel,
mNotificationManager.getNotificationChannel(newChannel.getId()));
}
public void testNotify() throws Exception {
mNotificationManager.cancelAll();
final int id = 1;
sendNotification(id, R.drawable.black);
// test updating the same notification
sendNotification(id, R.drawable.blue);
sendNotification(id, R.drawable.yellow);
// assume that sendNotification tested to make sure individual notifications were present
StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
if (sbn.getId() != id) {
fail("we got back other notifications besides the one we posted: "
+ sbn.getKey());
}
}
}
public void testSuspendPackage() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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() && !mPackageManager.hasSystemFeature(FEATURE_WATCH))
|| isAutomotive()) {
return;
}
// turn on bubbles globally
toggleBubbleSetting(true);
assertEquals(1, Settings.Secure.getInt(
mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BUBBLES));
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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);
// by default everything can bubble
assertTrue(outRanking.canBubble());
}
}
// turn off bubbles globally
toggleBubbleSetting(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();
} finally {
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testShowBadging_ranking() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
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(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
try {
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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(
NotificationManager.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);
}
}
public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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);
// 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", NotificationManager.IMPORTANCE_NONE);
mNotificationManager.createNotificationChannel(channel);
int id = 1;
final Notification notification =
new Notification.Builder(mContext, mId)
.setSmallIcon(R.drawable.black)
.setWhen(System.currentTimeMillis())
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("found unexpected notification id=" + id);
}
}
public void testNotify_blockedChannelGroup() throws Exception {
mNotificationManager.cancelAll();
NotificationChannelGroup group = new NotificationChannelGroup(mId, "group name");
group.setBlocked(true);
mNotificationManager.createNotificationChannelGroup(group);
NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(mId);
mNotificationManager.createNotificationChannel(channel);
int id = 1;
final Notification notification =
new Notification.Builder(mContext, mId)
.setSmallIcon(R.drawable.black)
.setWhen(System.currentTimeMillis())
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("found unexpected notification id=" + id);
}
}
public void testCancel() throws Exception {
final int id = 9;
sendNotification(id, R.drawable.black);
mNotificationManager.cancel(id);
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("canceled notification was still alive, id=" + id);
}
}
public void testCancelAll() throws Exception {
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
if (DEBUG) {
Log.d(TAG, "posted 3 notifications, here they are: ");
StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
Log.d(TAG, " " + sbn);
}
Log.d(TAG, "about to cancel...");
}
mNotificationManager.cancelAll();
for (int id = 1; id <= 3; id++) {
if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
fail("Failed to cancel notification id=" + id);
}
}
}
public void testNotifyWithTimeout() throws Exception {
mNotificationManager.cancelAll();
final int id = 128;
final long timeout = 1000;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.setTimeoutAfter(timeout)
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
try {
Thread.sleep(timeout);
} catch (InterruptedException ex) {
// pass
}
checkNotificationExistence(id, false);
}
public void 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() throws Exception {
Notification.MediaStyle style = new Notification.MediaStyle();
assertNotNull(style);
}
public void testMediaStyle() throws Exception {
mNotificationManager.cancelAll();
final int id = 99;
MediaSession session = new MediaSession(getContext(), "media");
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"play", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"pause", getPendingIntent()).build())
.setStyle(new Notification.MediaStyle()
.setShowActionsInCompactView(0, 1)
.setMediaSession(session.getSessionToken()))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testInboxStyle() throws Exception {
final int id = 100;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"a1", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"a2", getPendingIntent()).build())
.setStyle(new Notification.InboxStyle().addLine("line")
.setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testBigTextStyle() throws Exception {
final int id = 101;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"a1", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"a2", getPendingIntent()).build())
.setStyle(new Notification.BigTextStyle()
.setBigContentTitle("big title")
.bigText("big text")
.setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testBigPictureStyle() throws Exception {
final int id = 102;
final Notification notification =
new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.black)
.setContentTitle("notify#" + id)
.setContentText("This is #" + id + "notification ")
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_black),
"a1", getPendingIntent()).build())
.addAction(new Notification.Action.Builder(
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"a2", getPendingIntent()).build())
.setStyle(new Notification.BigPictureStyle()
.setBigContentTitle("title")
.bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
.bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
.setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
fail("couldn't find posted notification id=" + id);
}
}
public void testAutogrouping() throws Exception {
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
sendNotification(4, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception {
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
sendNotification(4, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
for (int i = 4; i > 1; i--) {
cancelAndPoll(i);
assertNotificationCount(i);
assertAllPostedNotificationsAutogrouped();
}
cancelAndPoll(1);
assertNotificationCount(0);
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()
throws Exception {
String newGroup = "new!";
sendNotification(1, R.drawable.black);
sendNotification(2, R.drawable.blue);
sendNotification(3, R.drawable.yellow);
sendNotification(4, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
postedIds.add(1);
postedIds.add(2);
postedIds.add(3);
postedIds.add(4);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
for (int i = 4; i > 1; i--) {
sendNotification(i, newGroup, R.drawable.blue);
postedIds.remove(postedIds.size() - 1);
assertNotificationCount(5);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
sendNotification(1, newGroup, R.drawable.blue);
assertNotificationCount(4); // no more autogroup summary
postedIds.remove(0);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()
throws Exception {
String newGroup = "new!";
sendNotification(10, R.drawable.black);
sendNotification(20, R.drawable.blue);
sendNotification(30, R.drawable.yellow);
sendNotification(40, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
postedIds.add(10);
postedIds.add(20);
postedIds.add(30);
postedIds.add(40);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// regroup all but one of the children
for (int i = postedIds.size() - 1; i > 0; i--) {
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
// pass
}
int id = postedIds.remove(i);
sendNotification(id, newGroup, R.drawable.blue);
assertNotificationCount(5);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
// send a new non-grouped notification. since the autogroup summary still exists,
// the notification should be added to it
sendNotification(50, R.drawable.blue);
postedIds.add(50);
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
// pass
}
assertOnlySomeNotificationsAutogrouped(postedIds);
}
public void testAddAutomaticZenRule_configActivity() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
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 {
if (mActivityManager.isLowRamDevice()) {
return;
}
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 {
if (mActivityManager.isLowRamDevice()) {
return;
}
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 {
if (mActivityManager.isLowRamDevice()) {
return;
}
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 {
if (mActivityManager.isLowRamDevice()) {
return;
}
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 {
if (mActivityManager.isLowRamDevice()) {
return;
}
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);
}
@FlakyTest
public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
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(NotificationManager.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 {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF);
mNotificationManager.setNotificationPolicy(appPolicy);
int expected = SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF
| SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
}
}
public void testSetNotificationPolicy_P_setNewFields() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
mNotificationManager.setNotificationPolicy(appPolicy);
int expected = SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_SCREEN_OFF
| SUPPRESSED_EFFECT_AMBIENT | SUPPRESSED_EFFECT_LIGHTS
| SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
}
}
public void testSetNotificationPolicy_P_setOldNewFields() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR);
mNotificationManager.setNotificationPolicy(appPolicy);
int expected = SUPPRESSED_EFFECT_STATUS_BAR;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
appPolicy = new NotificationManager.Policy(0, 0, 0,
SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
mNotificationManager.setNotificationPolicy(appPolicy);
expected = SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
assertEquals(expected,
mNotificationManager.getNotificationPolicy().suppressedVisualEffects);
}
}
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);
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);
findPostedNotification(0);
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() {
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);
assertNotNull(n);
}
public void testShouldHideSilentStatusIcons() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
try {
mNotificationManager.shouldHideSilentStatusBarIcons();
fail("Non-privileged apps should not get this information");
} catch (SecurityException e) {
// pass
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
// no exception this time
mNotificationManager.shouldHideSilentStatusBarIcons();
}
public void testMatchesCallFilter() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
}
// allow all callers
toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
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)));
}
/* 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();
}
public void testNotificationListener_setNotificationsShown() throws Exception {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
final int notificationId1 = 1;
final int notificationId2 = 2;
sendNotification(notificationId1, R.drawable.black);
sendNotification(notificationId2, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
StatusBarNotification sbn1 = findPostedNotification(notificationId1);
StatusBarNotification sbn2 = findPostedNotification(notificationId2);
mListener.setNotificationsShown(new String[]{ sbn1.getKey() });
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
Thread.sleep(500); // wait for listener to be allowed
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
final int notificationId1 = 1;
final int notificationId2 = 2;
sendNotification(notificationId1, R.drawable.black);
sendNotification(notificationId2, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
StatusBarNotification sbn1 = findPostedNotification(notificationId1);
StatusBarNotification sbn2 = findPostedNotification(notificationId2);
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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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 {
if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
return;
}
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), 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
StatusBarNotification sbn = findPostedNotification(notificationId);
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", oneString);
}
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_flagForMessage_failsNoRemoteInput()
throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
try {
// turn on bubbles globally
toggleBubbleSetting(true);
Person person = new Person.Builder()
.setName("bubblebot")
.build();
Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle("foo")
.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);
sendAndVerifyBubble(1, nb, null /* use default metadata */, false);
} finally {
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_flagForMessage_succeeds()
throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
try {
// turn on bubbles globally
toggleBubbleSetting(true);
Person person = new Person.Builder()
.setName("bubblebot")
.build();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel(
"reply").build();
PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
inputIntent).addRemoteInput(remoteInput)
.build();
Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle("foo")
.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)
)
.setActions(replyAction)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
sendAndVerifyBubble(1, nb, null /* use default metadata */, shouldBeBubble);
} finally {
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_flagForPhonecall() throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_SUCCESS);
try {
// turn on bubbles globally
toggleBubbleSetting(true);
mContext.startService(serviceIntent);
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, shouldBeBubble)) {
fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
}
} finally {
mContext.stopService(serviceIntent);
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoPerson()
throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_PERSON);
try {
// turn on bubbles globally
toggleBubbleSetting(true);
mContext.startService(serviceIntent);
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, false /* shouldBeBubble */)) {
fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+ " or it was a bubble when it shouldn't be");
}
} finally {
mContext.stopService(serviceIntent);
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoForeground()
throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
try {
// turn on bubbles globally
toggleBubbleSetting(true);
Person person = new Person.Builder()
.setName("bubblebot")
.build();
Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle("foo")
.setCategory(CATEGORY_CALL)
.addPerson(person)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
sendAndVerifyBubble(1, nb, null /* use default metadata */, false /* shouldBeBubble */);
} finally {
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoCategory()
throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_CATEGORY);
try {
// turn on bubbles globally
toggleBubbleSetting(true);
mContext.startService(serviceIntent);
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, false /* shouldBeBubble */)) {
fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+ " or it was a bubble when it shouldn't be");
}
} finally {
mContext.stopService(serviceIntent);
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_flagForPhonecallFailsNoMetadata()
throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_NO_BUBBLE_METADATA);
try {
// turn on bubbles globally
toggleBubbleSetting(true);
mContext.startService(serviceIntent);
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, false /* shouldBeBubble */)) {
fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+ " or it was a bubble when it shouldn't be");
}
} finally {
mContext.stopService(serviceIntent);
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_noFlagForAppNotForeground()
throws InterruptedException {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
try {
// turn on bubbles globally
toggleBubbleSetting(true);
sendAndVerifyBubble(1, null /* use default notif */, null /* use default metadata */,
false /* shouldBeBubble */);
} finally {
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_flagForAppForeground() throws Exception {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
try {
// turn on bubbles globally
toggleBubbleSetting(true);
final CountDownLatch latch = new CountDownLatch(2);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
latch.countDown();
}
};
IntentFilter filter = new IntentFilter(BubblesTestActivity.BUBBLE_ACTIVITY_OPENED);
mContext.registerReceiver(receiver, filter);
// Start & get the activity
BubblesTestActivity a = (BubblesTestActivity) launchSendBubbleActivity();
// Make sure device is unlocked
KeyguardManager keyguardManager =
(KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
keyguardManager.requestDismissKeyguard(a, new KeyguardManager.KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() {
latch.countDown();
}
});
try {
latch.await(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Should be foreground now
a.sendBubble(1);
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, shouldBeBubble)) {
fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
}
// Make ourselves not foreground
HomeHelper homeHelper = new HomeHelper();
homeHelper.goHome();
// The notif should be allowed to update as a bubble
a.sendBubble(2);
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, shouldBeBubble)) {
fail("couldn't find posted notification bubble with id=" + BUBBLE_NOTIF_ID);
}
// Cancel the notif
cancelAndPoll(BUBBLE_NOTIF_ID);
// Send it again when not foreground, this should not be a bubble & just be a notif
a.sendBubble(3);
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, false /* shouldBeBubble */)) {
fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
+ " or it was a bubble when it shouldn't be");
}
mContext.unregisterReceiver(receiver);
homeHelper.close();
} finally {
// turn off bubbles globally
toggleBubbleSetting(false);
}
}
public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() throws Exception {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
Person person = new Person.Builder()
.setName("bubblebot")
.build();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
inputIntent).addRemoteInput(remoteInput)
.build();
Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle("foo")
.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)
)
.setActions(replyAction)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
final Intent intent = new Intent(mContext, BubblesTestNotEmbeddableActivity.class);
final PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, intent, 0);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder()
.setIntent(pendingIntent)
.setIcon(Icon.createWithResource(mContext, R.drawable.black));
sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
}
public void testNotificationManagerBubblePolicy_noFlag_notDocumentLaunchModeAlways() throws Exception {
if (isAutomotive()) {
// Automotive does not support notification bubbles.
return;
}
Person person = new Person.Builder()
.setName("bubblebot")
.build();
RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
inputIntent).addRemoteInput(remoteInput)
.build();
Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle("foo")
.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)
)
.setActions(replyAction)
.setSmallIcon(android.R.drawable.sym_def_app_icon);
final Intent intent = new Intent(mContext, BubblesTestNotDocumentLaunchModeActivity.class);
final PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, intent, 0);
Notification.BubbleMetadata.Builder metadataBuilder =
new Notification.BubbleMetadata.Builder()
.setIntent(pendingIntent)
.setIcon(Icon.createWithResource(mContext, R.drawable.black));
sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
}
private boolean isAutomotive() {
return mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
}