blob: c02c091713016d341784e8bb59f06f31ce5d2930 [file] [log] [blame]
/*
* Copyright (C) 2019 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.notification.legacy29.cts;
import static android.service.notification.NotificationAssistantService.FEEDBACK_RATING;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Person;
import android.app.StatusBarManager;
import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.provider.Telephony;
import android.service.notification.Adjustment;
import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class NotificationAssistantServiceTest {
final String TAG = "NotAsstServiceTest";
final String NOTIFICATION_CHANNEL_ID = "NotificationAssistantServiceTest";
final int ICON_ID = android.R.drawable.sym_def_app_icon;
final long SLEEP_TIME = 1000; // milliseconds
private TestNotificationAssistant mNotificationAssistantService;
private TestNotificationListener mNotificationListenerService;
private NotificationManager mNotificationManager;
private StatusBarManager mStatusBarManager;
private Context mContext;
private UiAutomation mUi;
private boolean isWatch() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
}
@Before
public void setUp() throws IOException {
mUi = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mContext = InstrumentationRegistry.getContext();
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
mStatusBarManager = (StatusBarManager) mContext.getSystemService(
Context.STATUS_BAR_SERVICE);
}
@After
public void tearDown() throws IOException {
if (mNotificationListenerService != null) mNotificationListenerService.resetData();
toggleListenerAccess(false);
toggleAssistantAccess(false);
mUi.dropShellPermissionIdentity();
}
@Test
public void testOnNotificationEnqueued() throws Exception {
toggleListenerAccess(true);
Thread.sleep(SLEEP_TIME);
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_USER_SENTIMENT);
mUi.dropShellPermissionIdentity();
mNotificationListenerService = TestNotificationListener.getInstance();
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
// No modification because the Notification Assistant is not enabled
assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL,
out.getUserSentiment());
mNotificationListenerService.resetData();
toggleAssistantAccess(true);
Thread.sleep(SLEEP_TIME); // wait for listener and assistant to be allowed
mNotificationAssistantService = TestNotificationAssistant.getInstance();
sendNotification(1, ICON_ID);
sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
// Assistant gets correct rank
assertTrue(mNotificationAssistantService.notificationRank >= 0);
// Assistant modifies notification
assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE,
out.getUserSentiment());
}
@Test
public void testAdjustNotification_userSentimentKey() throws Exception {
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_USER_SENTIMENT);
mUi.dropShellPermissionIdentity();
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE,
out.getUserSentiment());
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT,
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
sbn.getUser());
mNotificationAssistantService.adjustNotification(adjustment);
Thread.sleep(SLEEP_TIME); // wait for adjustment to be processed
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
assertEquals(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE,
out.getUserSentiment());
}
@Test
public void testAdjustNotification_importanceKey() throws Exception {
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_IMPORTANCE);
mUi.dropShellPermissionIdentity();
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
int currentImportance = out.getImportance();
int newImportance = currentImportance == NotificationManager.IMPORTANCE_DEFAULT
? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT;
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_IMPORTANCE, newImportance);
Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
sbn.getUser());
mNotificationAssistantService.adjustNotification(adjustment);
Thread.sleep(SLEEP_TIME); // wait for adjustment to be processed
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
assertEquals(newImportance, out.getImportance());
}
@Test
public void testAdjustNotifications_rankingScoreKey() throws Exception {
setUpListeners();
try {
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_RANKING_SCORE);
mUi.dropShellPermissionIdentity();
sendNotification(1, ICON_ID);
StatusBarNotification sbn1 = getFirstNotificationFromPackage(
TestNotificationListener.PKG);
NotificationListenerService.Ranking out1 = new NotificationListenerService.Ranking();
sendNotification(2, ICON_ID);
StatusBarNotification sbn2 = getFirstNotificationFromPackage(
TestNotificationListener.PKG);
NotificationListenerService.Ranking out2 = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn1.getKey(), out1);
mNotificationListenerService.mRankingMap.getRanking(sbn2.getKey(), out2);
int currentRank1 = out1.getRank();
int currentRank2 = out2.getRank();
float rankingScore1 = (currentRank1 > currentRank2) ? 1f : 0;
float rankingScore2 = (currentRank1 > currentRank2) ? 0 : 1f;
Bundle signals = new Bundle();
signals.putFloat(Adjustment.KEY_RANKING_SCORE, rankingScore1);
Adjustment adjustment = new Adjustment(sbn1.getPackageName(), sbn1.getKey(), signals, "",
sbn1.getUser());
Bundle signals2 = new Bundle();
signals2.putFloat(Adjustment.KEY_RANKING_SCORE, rankingScore2);
Adjustment adjustment2 = new Adjustment(sbn2.getPackageName(), sbn2.getKey(), signals2, "",
sbn2.getUser());
mNotificationAssistantService.adjustNotifications(List.of(adjustment, adjustment2));
Thread.sleep(SLEEP_TIME); // wait for adjustments to be processed
mNotificationListenerService.mRankingMap.getRanking(sbn1.getKey(), out1);
mNotificationListenerService.mRankingMap.getRanking(sbn2.getKey(), out2);
// verify the relative ordering changed
int newRank1 = out1.getRank();
int newRank2 = out2.getRank();
if (currentRank1 > currentRank2) {
assertTrue(newRank1 < newRank2);
} else {
assertTrue(newRank1 > newRank2);
}
} finally {
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.disallowAssistantAdjustment(Adjustment.KEY_RANKING_SCORE);
mUi.dropShellPermissionIdentity();
}
}
@Test
public void testAdjustNotification_smartActionKey() throws Exception {
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_CONTEXTUAL_ACTIONS);
mUi.dropShellPermissionIdentity();
PendingIntent sendIntent = PendingIntent.getActivity(mContext, 0,
new Intent(Intent.ACTION_SEND), PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.Action sendAction = new Notification.Action.Builder(ICON_ID, "SEND",
sendIntent).build();
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
List<Notification.Action> smartActions = out.getSmartActions();
if (smartActions != null) {
for (int i = 0; i < smartActions.size(); i++) {
Notification.Action action = smartActions.get(i);
assertNotEquals(sendIntent, action.actionIntent);
}
}
ArrayList<Notification.Action> extraAction = new ArrayList<>();
extraAction.add(sendAction);
Bundle signals = new Bundle();
signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
sbn.getUser());
mNotificationAssistantService.adjustNotification(adjustment);
Thread.sleep(SLEEP_TIME); //wait for adjustment to be processed
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
boolean actionFound = false;
smartActions = out.getSmartActions();
for (int i = 0; i < smartActions.size(); i++) {
Notification.Action action = smartActions.get(i);
actionFound = actionFound || action.actionIntent.equals(sendIntent);
}
assertTrue(actionFound);
}
@Test
public void testAdjustNotification_smartReplyKey() throws Exception {
setUpListeners();
CharSequence smartReply = "Smart Reply!";
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_TEXT_REPLIES);
mUi.dropShellPermissionIdentity();
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
List<CharSequence> smartReplies = out.getSmartReplies();
if (smartReplies != null) {
for (int i = 0; i < smartReplies.size(); i++) {
CharSequence reply = smartReplies.get(i);
assertNotEquals(smartReply, reply);
}
}
ArrayList<CharSequence> extraReply = new ArrayList<>();
extraReply.add(smartReply);
Bundle signals = new Bundle();
signals.putCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES, extraReply);
Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
sbn.getUser());
mNotificationAssistantService.adjustNotification(adjustment);
Thread.sleep(SLEEP_TIME); //wait for adjustment to be processed
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
boolean replyFound = false;
smartReplies = out.getSmartReplies();
for (int i = 0; i < smartReplies.size(); i++) {
CharSequence reply = smartReplies.get(i);
replyFound = replyFound || reply.equals(smartReply);
}
assertTrue(replyFound);
}
@Test
public void testAdjustNotification_importanceKey_notAllowed() throws Exception {
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.disallowAssistantAdjustment(Adjustment.KEY_IMPORTANCE);
mUi.dropShellPermissionIdentity();
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(
TestNotificationListener.PKG);
NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
int currentImportance = out.getImportance();
int newImportance = currentImportance == NotificationManager.IMPORTANCE_DEFAULT
? NotificationManager.IMPORTANCE_HIGH : NotificationManager.IMPORTANCE_DEFAULT;
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_IMPORTANCE, newImportance);
Adjustment adjustment = new Adjustment(sbn.getPackageName(), sbn.getKey(), signals, "",
sbn.getUser());
mNotificationAssistantService.adjustNotification(adjustment);
Thread.sleep(SLEEP_TIME); // wait for adjustment to be processed
mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
assertEquals(currentImportance, out.getImportance());
}
@Test
public void testAdjustNotification_rankingScoreKey_notAllowed() throws Exception {
setUpListeners();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.cancelAll();
mNotificationManager.disallowAssistantAdjustment(Adjustment.KEY_RANKING_SCORE);
mUi.dropShellPermissionIdentity();
sendNotification(1, ICON_ID);
StatusBarNotification sbn1 = getFirstNotificationFromPackage(TestNotificationListener.PKG);
NotificationListenerService.Ranking out1 = new NotificationListenerService.Ranking();
sendNotification(2, ICON_ID);
StatusBarNotification sbn2 = getFirstNotificationFromPackage(TestNotificationListener.PKG);
NotificationListenerService.Ranking out2 = new NotificationListenerService.Ranking();
mNotificationListenerService.mRankingMap.getRanking(sbn1.getKey(), out1);
mNotificationListenerService.mRankingMap.getRanking(sbn2.getKey(), out2);
int currentRank1 = out1.getRank();
int currentRank2 = out2.getRank();
float rankingScore1 = (currentRank1 > currentRank2) ? 1f: 0;
float rankingScore2 = (currentRank1 > currentRank2) ? 0: 1f;
Bundle signals = new Bundle();
signals.putFloat(Adjustment.KEY_RANKING_SCORE, rankingScore1);
Adjustment adjustment = new Adjustment(sbn1.getPackageName(), sbn1.getKey(), signals, "",
sbn1.getUser());
mNotificationAssistantService.adjustNotification(adjustment);
signals = new Bundle();
signals.putFloat(Adjustment.KEY_RANKING_SCORE, rankingScore2);
adjustment = new Adjustment(sbn2.getPackageName(), sbn2.getKey(), signals, "",
sbn2.getUser());
mNotificationAssistantService.adjustNotification(adjustment);
Thread.sleep(SLEEP_TIME); // wait for adjustments to be processed
mNotificationListenerService.mRankingMap.getRanking(sbn1.getKey(), out1);
mNotificationListenerService.mRankingMap.getRanking(sbn2.getKey(), out2);
// verify the relative ordering remains the same
int newRank1 = out1.getRank();
int newRank2 = out2.getRank();
if (currentRank1 > currentRank2) {
assertTrue(newRank1 > newRank2);
} else {
assertTrue(newRank1 < newRank2);
}
}
@Test
public void testGetAllowedAssistantCapabilities_permission() throws Exception {
toggleAssistantAccess(false);
try {
mNotificationManager.getAllowedAssistantAdjustments();
fail(" Non assistants cannot call this method");
} catch (SecurityException e) {
//pass
}
}
@Test
public void testGetAllowedAssistantCapabilities() throws Exception {
toggleAssistantAccess(true);
Thread.sleep(SLEEP_TIME); // wait for assistant to be allowed
mNotificationAssistantService = TestNotificationAssistant.getInstance();
mNotificationAssistantService.onAllowedAdjustmentsChanged();
assertNotNull(mNotificationAssistantService.currentCapabilities);
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE");
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_SNOOZE_CRITERIA);
Thread.sleep(SLEEP_TIME);
assertTrue(mNotificationAssistantService.currentCapabilities.contains(
Adjustment.KEY_SNOOZE_CRITERIA));
mNotificationManager.disallowAssistantAdjustment(Adjustment.KEY_SNOOZE_CRITERIA);
Thread.sleep(SLEEP_TIME);
assertFalse(mNotificationAssistantService.currentCapabilities.contains(
Adjustment.KEY_SNOOZE_CRITERIA));
// just in case KEY_SNOOZE_CRITERIA was included in the original set, test adding again
mNotificationManager.allowAssistantAdjustment(Adjustment.KEY_SNOOZE_CRITERIA);
Thread.sleep(SLEEP_TIME);
assertTrue(mNotificationAssistantService.currentCapabilities.contains(
Adjustment.KEY_SNOOZE_CRITERIA));
mUi.dropShellPermissionIdentity();
}
@Test
public void testOnNotificationSnoozedUntilContext() throws Exception {
final String snoozeContext = "@SnoozeContext1@";
setUpListeners(); // also enables assistant
sendNotification(1001, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
// simulate the user snoozing the notification
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
runCommand(String.format("cmd notification snooze --condition %s %s", snoozeContext,
sbn.getKey()), instrumentation);
Thread.sleep(SLEEP_TIME);
assertTrue(String.format("snoozed notification <%s> was not removed", sbn.getKey()),
mNotificationListenerService.checkRemovedKey(sbn.getKey()));
assertEquals(String.format("snoozed notification <%s> was not observed by NAS", sbn.getKey()),
sbn.getKey(), mNotificationAssistantService.snoozedKey);
assertEquals(snoozeContext, mNotificationAssistantService.snoozedUntilContext);
}
@Test
public void testUnsnoozeFromNAS() throws Exception {
final String snoozeContext = "@SnoozeContext2@";
setUpListeners(); // also enables assistant
sendNotification(1002, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
// simulate the user snoozing the notification
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
runCommand(String.format("cmd notification snooze --context %s %s", snoozeContext,
sbn.getKey()), instrumentation);
Thread.sleep(SLEEP_TIME);
// unsnooze from listener
mNotificationAssistantService = TestNotificationAssistant.getInstance();
android.util.Log.v(TAG, "unsnoozing from listener: " + sbn.getKey());
mNotificationAssistantService.unsnoozeNotification(sbn.getKey());
Thread.sleep(SLEEP_TIME);
NotificationListenerService.Ranking out = new NotificationListenerService.Ranking();
boolean found = mNotificationListenerService.mRankingMap.getRanking(sbn.getKey(), out);
assertTrue("notification <" + sbn.getKey()
+ "> was not restored when unsnoozed from listener",
found);
}
@Test
public void testOnActionInvoked_methodExists() throws Exception {
setUpListeners();
final Intent intent = new Intent(Intent.ACTION_MAIN, Telephony.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, PendingIntent.FLAG_MUTABLE_UNAUDITED);
Notification.Action action = new Notification.Action.Builder(null, "",
pendingIntent).build();
// This method has to exist and the call cannot fail
mNotificationAssistantService.onActionInvoked("", action,
NotificationAssistantService.SOURCE_FROM_APP);
}
@Test
public void testOnNotificationDirectReplied_methodExists() throws Exception {
setUpListeners();
// This method has to exist and the call cannot fail
mNotificationAssistantService.onNotificationDirectReplied("");
}
@Test
public void testOnNotificationExpansionChanged_methodExists() throws Exception {
setUpListeners();
// This method has to exist and the call cannot fail
mNotificationAssistantService.onNotificationExpansionChanged("", true, true);
}
@Test
public void testOnNotificationVisibilityChanged() throws Exception {
if (isTelevision()) {
return;
}
assumeFalse("Status bar service not supported", isWatch());
setUpListeners();
turnScreenOn();
mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
sendConversationNotification(mNotificationAssistantService.notificationId);
Thread.sleep(SLEEP_TIME * 2);
// Initialize as closed
mStatusBarManager.collapsePanels();
Thread.sleep(SLEEP_TIME * 2);
mStatusBarManager.expandNotificationsPanel();
Thread.sleep(SLEEP_TIME * 2);
assertTrue(mNotificationAssistantService.notificationVisible);
mStatusBarManager.collapsePanels();
Thread.sleep(SLEEP_TIME * 2);
assertFalse(mNotificationAssistantService.notificationVisible);
mUi.dropShellPermissionIdentity();
}
@Test
public void testOnNotificationsSeen() throws Exception {
if (isTelevision()) {
return;
}
assumeFalse("Status bar service not supported", isWatch());
setUpListeners();
turnScreenOn();
mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
mNotificationAssistantService.resetNotificationVisibilityCounts();
// Initialize as closed
mStatusBarManager.collapsePanels();
sendNotification(1, ICON_ID);
assertEquals(0, mNotificationAssistantService.notificationSeenCount);
mStatusBarManager.expandNotificationsPanel();
Thread.sleep(SLEEP_TIME * 2);
assertTrue(mNotificationAssistantService.notificationSeenCount > 0);
mStatusBarManager.collapsePanels();
mUi.dropShellPermissionIdentity();
}
@Test
public void testOnPanelRevealedAndHidden() throws Exception {
if (isTelevision()) {
return;
}
assumeFalse("Status bar service not supported", isWatch());
setUpListeners();
turnScreenOn();
mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
// Initialize as closed
mStatusBarManager.collapsePanels();
assertFalse(mNotificationAssistantService.isPanelOpen);
mStatusBarManager.expandNotificationsPanel();
Thread.sleep(SLEEP_TIME * 2);
assertTrue(mNotificationAssistantService.isPanelOpen);
mStatusBarManager.collapsePanels();
Thread.sleep(SLEEP_TIME * 2);
assertFalse(mNotificationAssistantService.isPanelOpen);
mUi.dropShellPermissionIdentity();
}
@Test
public void testOnSuggestedReplySent_methodExists() throws Exception {
setUpListeners();
// This method has to exist and the call cannot fail
mNotificationAssistantService.onSuggestedReplySent("", "",
NotificationAssistantService.SOURCE_FROM_APP);
}
@Test
public void testOnNotificationClicked() throws Exception {
if (isTelevision()) {
return;
}
setUpListeners();
turnScreenOn();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE", "android.permission.EXPAND_STATUS_BAR");
mNotificationAssistantService.resetNotificationClickCount();
// Initialize as closed
mStatusBarManager.collapsePanels();
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
mStatusBarManager.expandNotificationsPanel();
Thread.sleep(SLEEP_TIME * 2);
mStatusBarManager.clickNotification(sbn.getKey(), 1, 1, true);
Thread.sleep(SLEEP_TIME * 2);
assertEquals(1, mNotificationAssistantService.notificationClickCount);
mStatusBarManager.collapsePanels();
mUi.dropShellPermissionIdentity();
}
@Test
public void testOnNotificationFeedbackReceived() throws Exception {
setUpListeners(); // also enables assistant
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE", "android.permission.EXPAND_STATUS_BAR");
sendNotification(1, ICON_ID);
StatusBarNotification sbn = getFirstNotificationFromPackage(TestNotificationListener.PKG);
Bundle feedback = new Bundle();
feedback.putInt(FEEDBACK_RATING, 1);
mStatusBarManager.sendNotificationFeedback(sbn.getKey(), feedback);
Thread.sleep(SLEEP_TIME * 2);
assertEquals(1, mNotificationAssistantService.notificationFeedback);
mUi.dropShellPermissionIdentity();
}
private StatusBarNotification getFirstNotificationFromPackage(String PKG)
throws InterruptedException {
StatusBarNotification sbn = mNotificationListenerService.mPosted.poll(SLEEP_TIME,
TimeUnit.MILLISECONDS);
assertNotNull(sbn);
while (!sbn.getPackageName().equals(PKG)) {
sbn = mNotificationListenerService.mPosted.poll(SLEEP_TIME, TimeUnit.MILLISECONDS);
}
assertNotNull(sbn);
return sbn;
}
private void setUpListeners() throws Exception {
toggleListenerAccess(true);
toggleAssistantAccess(true);
Thread.sleep(2 * SLEEP_TIME); // wait for listener and assistant to be allowed
mNotificationListenerService = TestNotificationListener.getInstance();
mNotificationAssistantService = TestNotificationAssistant.getInstance();
assertNotNull(mNotificationListenerService);
assertNotNull(mNotificationAssistantService);
}
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, Telephony.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, PendingIntent.FLAG_MUTABLE_UNAUDITED);
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);
}
private void sendConversationNotification(final int id) {
Person person = new Person.Builder()
.setName("test")
.build();
final Notification notification = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setContentTitle("foo")
.setShortcutId("shareShortcut")
.setStyle(new Notification.MessagingStyle(person)
.setConversationTitle("Test Chat")
.addMessage("Hello?",
SystemClock.currentThreadTimeMillis() - 300000, person)
.addMessage("Is it me you're looking for?",
SystemClock.currentThreadTimeMillis(), person)
)
.setSmallIcon(ICON_ID)
.build();
mNotificationManager.notify(id, notification);
}
private void turnScreenOn() throws IOException {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
runCommand("input keyevent KEYCODE_WAKEUP", instrumentation);
runCommand("wm dismiss-keyguard", instrumentation);
}
private boolean isTelevision() {
PackageManager packageManager = mContext.getPackageManager();
return packageManager != null
&& (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|| packageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION));
}
private void toggleListenerAccess(boolean on) throws IOException {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
String componentName = TestNotificationListener.getId();
String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
+ componentName;
runCommand(command, instrumentation);
final ComponentName listenerComponent = TestNotificationListener.getComponentName();
final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
Assert.assertTrue(listenerComponent + " has not been " + (on ? "allowed" : "disallowed"),
nm.isNotificationListenerAccessGranted(listenerComponent) == on);
}
private void toggleAssistantAccess(boolean on) {
final ComponentName assistantComponent = TestNotificationAssistant.getComponentName();
mUi.adoptShellPermissionIdentity("android.permission.STATUS_BAR_SERVICE",
"android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE");
mNotificationManager.setNotificationAssistantAccessGranted(assistantComponent, on);
assertTrue(assistantComponent + " has not been " + (on ? "allowed" : "disallowed"),
mNotificationManager.isNotificationAssistantAccessGranted(assistantComponent)
== on);
if (on) {
assertEquals(assistantComponent,
mNotificationManager.getAllowedNotificationAssistant());
} else {
assertNotEquals(assistantComponent,
mNotificationManager.getAllowedNotificationAssistant());
}
mUi.dropShellPermissionIdentity();
}
private void runCommand(String command, Instrumentation instrumentation) throws IOException {
UiAutomation uiAutomation = instrumentation.getUiAutomation();
// Execute command
System.out.println("runCommand: <<<" + command + ">>>");
try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
assertNotNull("Failed to execute shell command: " + command, fd);
// Wait for the command to finish by reading until EOF
try (BufferedReader in = new BufferedReader(new FileReader(fd.getFileDescriptor()))) {
String line;
while (null != (line = in.readLine())) {
android.util.Log.v(TAG, "runCommand: output: " + line);
}
} catch (IOException e) {
throw new IOException("Could not read stdout of command: " + command, e);
}
}
}
}