Add filtering to notifications sent to NLSes
Test: atest Bug: 173052211 Change-Id: Id1b3440ff375543b546f2a599b791a70e28d8bb8
This commit is contained in:
@@ -35,6 +35,7 @@ import android.service.notification.Condition;
|
||||
import android.service.notification.IConditionListener;
|
||||
import android.service.notification.IConditionProvider;
|
||||
import android.service.notification.INotificationListener;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.app.AutomaticZenRule;
|
||||
import android.service.notification.ZenModeConfig;
|
||||
@@ -224,4 +225,7 @@ interface INotificationManager
|
||||
boolean getPrivateNotificationsAllowed();
|
||||
|
||||
long pullStats(long startNs, int report, boolean doAgg, out List<ParcelFileDescriptor> stats);
|
||||
|
||||
NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId);
|
||||
void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.service.notification;
|
||||
|
||||
parcelable NotificationListenerFilter;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2020, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, 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.service.notification;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.ArraySet;
|
||||
|
||||
/**
|
||||
* Specifies a filter for what types of notifications should be bridged to notification listeners.
|
||||
* Each requested listener will have their own filter instance.
|
||||
* @hide
|
||||
*/
|
||||
public class NotificationListenerFilter implements Parcelable {
|
||||
private int mAllowedNotificationTypes;
|
||||
private ArraySet<String> mDisallowedPackages;
|
||||
|
||||
public NotificationListenerFilter() {
|
||||
mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS
|
||||
| FLAG_FILTER_TYPE_ALERTING
|
||||
| FLAG_FILTER_TYPE_SILENT;
|
||||
mDisallowedPackages = new ArraySet<>();
|
||||
}
|
||||
|
||||
public NotificationListenerFilter(int types, ArraySet<String> pkgs) {
|
||||
mAllowedNotificationTypes = types;
|
||||
mDisallowedPackages = pkgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected NotificationListenerFilter(Parcel in) {
|
||||
mAllowedNotificationTypes = in.readInt();
|
||||
mDisallowedPackages = (ArraySet<String>) in.readArraySet(String.class.getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mAllowedNotificationTypes);
|
||||
dest.writeArraySet(mDisallowedPackages);
|
||||
}
|
||||
|
||||
public static final Creator<NotificationListenerFilter> CREATOR =
|
||||
new Creator<NotificationListenerFilter>() {
|
||||
@Override
|
||||
public NotificationListenerFilter createFromParcel(Parcel in) {
|
||||
return new NotificationListenerFilter(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationListenerFilter[] newArray(int size) {
|
||||
return new NotificationListenerFilter[size];
|
||||
}
|
||||
};
|
||||
|
||||
public boolean isTypeAllowed(int type) {
|
||||
return (mAllowedNotificationTypes & type) != 0;
|
||||
}
|
||||
|
||||
public boolean isPackageAllowed(String pkg) {
|
||||
return !mDisallowedPackages.contains(pkg);
|
||||
}
|
||||
|
||||
public int getTypes() {
|
||||
return mAllowedNotificationTypes;
|
||||
}
|
||||
|
||||
public ArraySet<String> getDisallowedPackages() {
|
||||
return mDisallowedPackages;
|
||||
}
|
||||
|
||||
public void setTypes(int types) {
|
||||
mAllowedNotificationTypes = types;
|
||||
}
|
||||
|
||||
public void setDisallowedPackages(ArraySet<String> pkgs) {
|
||||
mDisallowedPackages = pkgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -241,6 +241,23 @@ public abstract class NotificationListenerService extends Service {
|
||||
})
|
||||
public @interface NotificationCancelReason{};
|
||||
|
||||
/**
|
||||
* A flag value indicating that this notification listener can see conversation type
|
||||
* notifications.
|
||||
* @hide
|
||||
*/
|
||||
public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1;
|
||||
/**
|
||||
* A flag value indicating that this notification listener can see altering type notifications.
|
||||
* @hide
|
||||
*/
|
||||
public static final int FLAG_FILTER_TYPE_ALERTING = 2;
|
||||
/**
|
||||
* A flag value indicating that this notification listener can see silent type notifications.
|
||||
* @hide
|
||||
*/
|
||||
public static final int FLAG_FILTER_TYPE_SILENT = 4;
|
||||
|
||||
/**
|
||||
* The full trim of the StatusBarNotification including all its features.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.service.notification;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.os.Parcel;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class NotificationListenerFilterTest {
|
||||
|
||||
@Test
|
||||
public void testEmptyConstructor() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter();
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isTrue();
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
|
||||
assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
|
||||
| FLAG_FILTER_TYPE_ALERTING
|
||||
| FLAG_FILTER_TYPE_SILENT);
|
||||
|
||||
assertThat(nlf.getDisallowedPackages()).isEmpty();
|
||||
assertThat(nlf.isPackageAllowed("pkg1")).isTrue();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testConstructor() {
|
||||
ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
|
||||
NotificationListenerFilter nlf =
|
||||
new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
|
||||
assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
|
||||
|
||||
assertThat(nlf.getDisallowedPackages()).contains("pkg1");
|
||||
assertThat(nlf.getDisallowedPackages()).contains("pkg2");
|
||||
assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
|
||||
assertThat(nlf.isPackageAllowed("pkg2")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetDisallowedPackages() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter();
|
||||
|
||||
ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1"});
|
||||
nlf.setDisallowedPackages(pkgs);
|
||||
|
||||
assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTypes() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter();
|
||||
|
||||
nlf.setTypes(FLAG_FILTER_TYPE_ALERTING | FLAG_FILTER_TYPE_SILENT);
|
||||
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
|
||||
assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
|
||||
assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING
|
||||
| FLAG_FILTER_TYPE_SILENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDescribeContents() {
|
||||
final int expected = 0;
|
||||
ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
|
||||
NotificationListenerFilter nlf =
|
||||
new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
|
||||
assertThat(nlf.describeContents()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParceling() {
|
||||
ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
|
||||
NotificationListenerFilter nlf =
|
||||
new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
|
||||
|
||||
Parcel parcel = Parcel.obtain();
|
||||
nlf.writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
NotificationListenerFilter nlf1 =
|
||||
NotificationListenerFilter.CREATOR.createFromParcel(parcel);
|
||||
assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
|
||||
assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
|
||||
assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
|
||||
assertThat(nlf1.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
|
||||
|
||||
assertThat(nlf1.getDisallowedPackages()).contains("pkg1");
|
||||
assertThat(nlf1.getDisallowedPackages()).contains("pkg2");
|
||||
assertThat(nlf1.isPackageAllowed("pkg1")).isFalse();
|
||||
assertThat(nlf1.isPackageAllowed("pkg2")).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,6 @@ import com.android.server.utils.TimingsTraceAndSlog;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
@@ -544,7 +543,8 @@ abstract public class ManagedServices {
|
||||
/**
|
||||
* This is called to process tags other than {@link #TAG_MANAGED_SERVICES}.
|
||||
*/
|
||||
protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {}
|
||||
protected void readExtraTag(String tag, TypedXmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {}
|
||||
|
||||
protected final void migrateToXml() {
|
||||
for (UserInfo user : mUm.getUsers()) {
|
||||
@@ -1613,6 +1613,7 @@ abstract public class ManagedServices {
|
||||
public boolean isSystem;
|
||||
public ServiceConnection connection;
|
||||
public int targetSdkVersion;
|
||||
public Pair<ComponentName, Integer> mKey;
|
||||
|
||||
public ManagedServiceInfo(IInterface service, ComponentName component,
|
||||
int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) {
|
||||
@@ -1622,6 +1623,7 @@ abstract public class ManagedServices {
|
||||
this.isSystem = isSystem;
|
||||
this.connection = connection;
|
||||
this.targetSdkVersion = targetSdkVersion;
|
||||
mKey = Pair.create(component, userid);
|
||||
}
|
||||
|
||||
public boolean isGuest(ManagedServices host) {
|
||||
|
||||
@@ -66,6 +66,9 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
|
||||
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
|
||||
import static android.os.UserHandle.USER_NULL;
|
||||
import static android.os.UserHandle.USER_SYSTEM;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
|
||||
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
|
||||
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
|
||||
@@ -163,6 +166,8 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
import android.content.pm.UserInfo;
|
||||
@@ -209,6 +214,7 @@ import android.service.notification.INotificationListener;
|
||||
import android.service.notification.IStatusBarNotificationHolder;
|
||||
import android.service.notification.ListenersDisablingEffectsProto;
|
||||
import android.service.notification.NotificationAssistantService;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.NotificationRankingUpdate;
|
||||
import android.service.notification.NotificationRecordProto;
|
||||
@@ -285,6 +291,7 @@ import libcore.io.IoUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -301,6 +308,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
@@ -1021,7 +1029,7 @@ public class NotificationManagerService extends SystemService {
|
||||
nv.recycle();
|
||||
}
|
||||
reportUserInteraction(r);
|
||||
mAssistants.notifyAssistantActionClicked(r.getSbn(), action, generatedByAssistant);
|
||||
mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1110,7 +1118,7 @@ public class NotificationManagerService extends SystemService {
|
||||
reportSeen(r);
|
||||
}
|
||||
r.setVisibility(true, nv.rank, nv.count, mNotificationRecordLogger);
|
||||
mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true);
|
||||
mAssistants.notifyAssistantVisibilityChangedLocked(r, true);
|
||||
boolean isHun = (nv.location
|
||||
== NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
|
||||
// hasBeenVisiblyExpanded must be called after updating the expansion state of
|
||||
@@ -1129,7 +1137,7 @@ public class NotificationManagerService extends SystemService {
|
||||
NotificationRecord r = mNotificationsByKey.get(nv.key);
|
||||
if (r == null) continue;
|
||||
r.setVisibility(false, nv.rank, nv.count, mNotificationRecordLogger);
|
||||
mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false);
|
||||
mAssistants.notifyAssistantVisibilityChangedLocked(r, false);
|
||||
nv.recycle();
|
||||
}
|
||||
}
|
||||
@@ -1161,7 +1169,7 @@ public class NotificationManagerService extends SystemService {
|
||||
reportUserInteraction(r);
|
||||
}
|
||||
mAssistants.notifyAssistantExpansionChangedLocked(
|
||||
r.getSbn(), userAction, expanded);
|
||||
r.getSbn(), r.getNotificationType(), userAction, expanded);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1180,7 +1188,7 @@ public class NotificationManagerService extends SystemService {
|
||||
NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
|
||||
r);
|
||||
reportUserInteraction(r);
|
||||
mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
|
||||
mAssistants.notifyAssistantNotificationDirectReplyLocked(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1227,7 +1235,8 @@ public class NotificationManagerService extends SystemService {
|
||||
// Treat clicking on a smart reply as a user interaction.
|
||||
reportUserInteraction(r);
|
||||
mAssistants.notifyAssistantSuggestedReplySent(
|
||||
r.getSbn(), reply, r.getSuggestionsGeneratedByAssistant());
|
||||
r.getSbn(), r.getNotificationType(), reply,
|
||||
r.getSuggestionsGeneratedByAssistant());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2241,7 +2250,8 @@ public class NotificationManagerService extends SystemService {
|
||||
init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),
|
||||
AppGlobals.getPackageManager(), getContext().getPackageManager(),
|
||||
getLocalService(LightsManager.class),
|
||||
new NotificationListeners(AppGlobals.getPackageManager()),
|
||||
new NotificationListeners(getContext(), mNotificationLock, mUserProfiles,
|
||||
AppGlobals.getPackageManager()),
|
||||
new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
|
||||
AppGlobals.getPackageManager()),
|
||||
new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
|
||||
@@ -3252,6 +3262,21 @@ public class NotificationManagerService extends SystemService {
|
||||
mHistoryManager.deleteNotificationHistoryItem(pkg, uid, postedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationListenerFilter getListenerFilter(ComponentName cn, int userId) {
|
||||
checkCallerIsSystem();
|
||||
return mListeners.getNotificationListenerFilter(Pair.create(cn, userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListenerFilter(ComponentName cn, int userId,
|
||||
NotificationListenerFilter nlf) {
|
||||
checkCallerIsSystem();
|
||||
mListeners.setNotificationListenerFilter(Pair.create(cn, userId), nlf);
|
||||
// TODO (b/173052211): cancel notifications for listeners that can no longer see them
|
||||
handleSavePolicyFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPackageImportance(String pkg) {
|
||||
checkCallerIsSystemOrSameApp(pkg);
|
||||
@@ -4268,7 +4293,7 @@ public class NotificationManagerService extends SystemService {
|
||||
: mNotificationList.get(i);
|
||||
if (r == null) continue;
|
||||
StatusBarNotification sbn = r.getSbn();
|
||||
if (!isVisibleToListener(sbn, info)) continue;
|
||||
if (!isVisibleToListener(sbn, r.getNotificationType(), info)) continue;
|
||||
StatusBarNotification sbnToSend =
|
||||
(trim == TRIM_FULL) ? sbn : sbn.cloneLight();
|
||||
list.add(sbnToSend);
|
||||
@@ -4298,7 +4323,7 @@ public class NotificationManagerService extends SystemService {
|
||||
final NotificationRecord r = snoozedRecords.get(i);
|
||||
if (r == null) continue;
|
||||
StatusBarNotification sbn = r.getSbn();
|
||||
if (!isVisibleToListener(sbn, info)) continue;
|
||||
if (!isVisibleToListener(sbn, r.getNotificationType(), info)) continue;
|
||||
StatusBarNotification sbnToSend =
|
||||
(trim == TRIM_FULL) ? sbn : sbn.cloneLight();
|
||||
list.add(sbnToSend);
|
||||
@@ -6339,7 +6364,7 @@ public class NotificationManagerService extends SystemService {
|
||||
cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
|
||||
updateLightsLocked();
|
||||
if (mSnoozeCriterionId != null) {
|
||||
mAssistants.notifyAssistantSnoozedLocked(r.getSbn(), mSnoozeCriterionId);
|
||||
mAssistants.notifyAssistantSnoozedLocked(r, mSnoozeCriterionId);
|
||||
mSnoozeHelper.snooze(r, mSnoozeCriterionId);
|
||||
} else {
|
||||
mSnoozeHelper.snooze(r, mDuration);
|
||||
@@ -8812,7 +8837,7 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
NotificationRecord record = mNotificationList.get(i);
|
||||
if (!isVisibleToListener(record.getSbn(), info)) {
|
||||
if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) {
|
||||
continue;
|
||||
}
|
||||
final String key = record.getSbn().getKey();
|
||||
@@ -8886,11 +8911,21 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
|
||||
boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
|
||||
ManagedServiceInfo listener) {
|
||||
if (!listener.enabledAndUserMatches(sbn.getUserId())) {
|
||||
return false;
|
||||
}
|
||||
return isInteractionVisibleToListener(listener, sbn.getUserId());
|
||||
if (!isInteractionVisibleToListener(listener, sbn.getUserId())) {
|
||||
return false;
|
||||
}
|
||||
NotificationListenerFilter nls = mListeners.getNotificationListenerFilter(listener.mKey);
|
||||
if (nls != null
|
||||
&& (!nls.isTypeAllowed(notificationType)
|
||||
|| !nls.isPackageAllowed(sbn.getPackageName()))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9126,7 +9161,8 @@ public class NotificationManagerService extends SystemService {
|
||||
for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
|
||||
ArrayList<String> keys = new ArrayList<>(records.size());
|
||||
for (NotificationRecord r : records) {
|
||||
boolean sbnVisible = isVisibleToListener(r.getSbn(), info)
|
||||
boolean sbnVisible = isVisibleToListener(
|
||||
r.getSbn(), r.getNotificationType(), info)
|
||||
&& info.isSameUser(r.getUserId());
|
||||
if (sbnVisible) {
|
||||
keys.add(r.getKey());
|
||||
@@ -9241,6 +9277,7 @@ public class NotificationManagerService extends SystemService {
|
||||
final StatusBarNotification sbn = r.getSbn();
|
||||
notifyAssistantLocked(
|
||||
sbn,
|
||||
r.getNotificationType(),
|
||||
true /* sameUserOnly */,
|
||||
(assistant, sbnHolder) -> {
|
||||
try {
|
||||
@@ -9257,14 +9294,15 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
@GuardedBy("mNotificationLock")
|
||||
void notifyAssistantVisibilityChangedLocked(
|
||||
final StatusBarNotification sbn,
|
||||
final NotificationRecord r,
|
||||
final boolean isVisible) {
|
||||
final String key = sbn.getKey();
|
||||
final String key = r.getSbn().getKey();
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "notifyAssistantVisibilityChangedLocked: " + key);
|
||||
}
|
||||
notifyAssistantLocked(
|
||||
sbn,
|
||||
r.getSbn(),
|
||||
r.getNotificationType(),
|
||||
true /* sameUserOnly */,
|
||||
(assistant, sbnHolder) -> {
|
||||
try {
|
||||
@@ -9278,11 +9316,13 @@ public class NotificationManagerService extends SystemService {
|
||||
@GuardedBy("mNotificationLock")
|
||||
void notifyAssistantExpansionChangedLocked(
|
||||
final StatusBarNotification sbn,
|
||||
final int notificationType,
|
||||
final boolean isUserAction,
|
||||
final boolean isExpanded) {
|
||||
final String key = sbn.getKey();
|
||||
notifyAssistantLocked(
|
||||
sbn,
|
||||
notificationType,
|
||||
true /* sameUserOnly */,
|
||||
(assistant, sbnHolder) -> {
|
||||
try {
|
||||
@@ -9295,10 +9335,11 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
@GuardedBy("mNotificationLock")
|
||||
void notifyAssistantNotificationDirectReplyLocked(
|
||||
final StatusBarNotification sbn) {
|
||||
final String key = sbn.getKey();
|
||||
final NotificationRecord r) {
|
||||
final String key = r.getKey();
|
||||
notifyAssistantLocked(
|
||||
sbn,
|
||||
r.getSbn(),
|
||||
r.getNotificationType(),
|
||||
true /* sameUserOnly */,
|
||||
(assistant, sbnHolder) -> {
|
||||
try {
|
||||
@@ -9311,10 +9352,12 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
@GuardedBy("mNotificationLock")
|
||||
void notifyAssistantSuggestedReplySent(
|
||||
final StatusBarNotification sbn, CharSequence reply, boolean generatedByAssistant) {
|
||||
final StatusBarNotification sbn, int notificationType,
|
||||
CharSequence reply, boolean generatedByAssistant) {
|
||||
final String key = sbn.getKey();
|
||||
notifyAssistantLocked(
|
||||
sbn,
|
||||
notificationType,
|
||||
true /* sameUserOnly */,
|
||||
(assistant, sbnHolder) -> {
|
||||
try {
|
||||
@@ -9332,11 +9375,12 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
@GuardedBy("mNotificationLock")
|
||||
void notifyAssistantActionClicked(
|
||||
final StatusBarNotification sbn, Notification.Action action,
|
||||
final NotificationRecord r, Notification.Action action,
|
||||
boolean generatedByAssistant) {
|
||||
final String key = sbn.getKey();
|
||||
final String key = r.getSbn().getKey();
|
||||
notifyAssistantLocked(
|
||||
sbn,
|
||||
r.getSbn(),
|
||||
r.getNotificationType(),
|
||||
true /* sameUserOnly */,
|
||||
(assistant, sbnHolder) -> {
|
||||
try {
|
||||
@@ -9358,9 +9402,10 @@ public class NotificationManagerService extends SystemService {
|
||||
*/
|
||||
@GuardedBy("mNotificationLock")
|
||||
private void notifyAssistantSnoozedLocked(
|
||||
final StatusBarNotification sbn, final String snoozeCriterionId) {
|
||||
final NotificationRecord r, final String snoozeCriterionId) {
|
||||
notifyAssistantLocked(
|
||||
sbn,
|
||||
r.getSbn(),
|
||||
r.getNotificationType(),
|
||||
true /* sameUserOnly */,
|
||||
(assistant, sbnHolder) -> {
|
||||
try {
|
||||
@@ -9384,6 +9429,7 @@ public class NotificationManagerService extends SystemService {
|
||||
@GuardedBy("mNotificationLock")
|
||||
private void notifyAssistantLocked(
|
||||
final StatusBarNotification sbn,
|
||||
int notificationType,
|
||||
boolean sameUserOnly,
|
||||
BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
|
||||
TrimCache trimCache = new TrimCache(sbn);
|
||||
@@ -9397,7 +9443,7 @@ public class NotificationManagerService extends SystemService {
|
||||
+ sameUserOnly + "], callback = [" + callback + "]");
|
||||
}
|
||||
for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
|
||||
boolean sbnVisible = isVisibleToListener(sbn, info)
|
||||
boolean sbnVisible = isVisibleToListener(sbn, notificationType, info)
|
||||
&& (!sameUserOnly || info.isSameUser(sbn.getUserId()));
|
||||
if (debug) {
|
||||
Slog.v(TAG, "notifyAssistantLocked info=" + info + " snbVisible=" + sbnVisible);
|
||||
@@ -9451,11 +9497,22 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
public class NotificationListeners extends ManagedServices {
|
||||
static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
|
||||
static final String TAG_REQUESTED_LISTENERS = "requested_listeners";
|
||||
static final String TAG_REQUESTED_LISTENER = "listener";
|
||||
static final String ATT_COMPONENT = "component";
|
||||
static final String ATT_TYPES = "types";
|
||||
static final String ATT_PKGS = "pkgs";
|
||||
static final String TAG_APPROVED = "allowed";
|
||||
static final String TAG_DISALLOWED= "disallowed";
|
||||
static final String XML_SEPARATOR = ",";
|
||||
|
||||
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
|
||||
ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
|
||||
mRequestedNotificationListeners = new ArrayMap<>();
|
||||
|
||||
public NotificationListeners(IPackageManager pm) {
|
||||
super(getContext(), mNotificationLock, mUserProfiles, pm);
|
||||
public NotificationListeners(Context context, Object lock, UserProfiles userProfiles,
|
||||
IPackageManager pm) {
|
||||
super(context, lock, userProfiles, pm);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -9551,6 +9608,59 @@ public class NotificationManagerService extends SystemService {
|
||||
mLightTrimListeners.remove(removed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserRemoved(int user) {
|
||||
super.onUserRemoved(user);
|
||||
for (int i = mRequestedNotificationListeners.size() - 1; i >= 0; i--) {
|
||||
if (mRequestedNotificationListeners.keyAt(i).second == user) {
|
||||
mRequestedNotificationListeners.removeAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserUnlocked(int user) {
|
||||
int flags = PackageManager.GET_SERVICES | PackageManager.GET_META_DATA;
|
||||
|
||||
final PackageManager pmWrapper = mContext.getPackageManager();
|
||||
List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser(
|
||||
new Intent(getConfig().serviceInterface), flags, user);
|
||||
|
||||
for (ResolveInfo resolveInfo : installedServices) {
|
||||
ServiceInfo info = resolveInfo.serviceInfo;
|
||||
|
||||
if (!getConfig().bindPermission.equals(info.permission)) {
|
||||
continue;
|
||||
}
|
||||
Pair key = Pair.create(info.getComponentName(), user);
|
||||
if (!mRequestedNotificationListeners.containsKey(key)) {
|
||||
mRequestedNotificationListeners.put(key, new NotificationListenerFilter());
|
||||
}
|
||||
}
|
||||
super.onUserUnlocked(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
|
||||
super.onPackagesChanged(removingPackage, pkgList, uidList);
|
||||
|
||||
// Since the default behavior is to allow everything, we don't need to explicitly
|
||||
// handle package add or update. they will be added to the xml file on next boot or
|
||||
// when the user tries to change the settings.
|
||||
if (removingPackage) {
|
||||
for (int i = 0; i < pkgList.length; i++) {
|
||||
String pkg = pkgList[i];
|
||||
int userId = UserHandle.getUserId(uidList[i]);
|
||||
for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
|
||||
Pair<ComponentName, Integer> key = mRequestedNotificationListeners.keyAt(j);
|
||||
if (key.second == userId && key.first.getPackageName().equals(pkg)) {
|
||||
mRequestedNotificationListeners.removeAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRequiredPermission() {
|
||||
return null;
|
||||
@@ -9563,6 +9673,75 @@ public class NotificationManagerService extends SystemService {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readExtraTag(String tag, TypedXmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
if (TAG_REQUESTED_LISTENERS.equals(tag)) {
|
||||
final int listenersOuterDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, listenersOuterDepth)) {
|
||||
if (!TAG_REQUESTED_LISTENER.equals(parser.getName())) {
|
||||
continue;
|
||||
}
|
||||
final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID);
|
||||
final ComponentName cn = ComponentName.unflattenFromString(
|
||||
XmlUtils.readStringAttribute(parser, ATT_COMPONENT));
|
||||
int approved = FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING
|
||||
| FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
ArraySet<String> disallowedPkgs = new ArraySet<>();
|
||||
final int listenerOuterDepth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, listenerOuterDepth)) {
|
||||
if (TAG_APPROVED.equals(parser.getName())) {
|
||||
approved = XmlUtils.readIntAttribute(parser, ATT_TYPES);
|
||||
} else if (TAG_DISALLOWED.equals(parser.getName())) {
|
||||
String pkgs = XmlUtils.readStringAttribute(parser, ATT_PKGS);
|
||||
if (!TextUtils.isEmpty(pkgs)) {
|
||||
disallowedPkgs = new ArraySet<>(pkgs.split(XML_SEPARATOR));
|
||||
}
|
||||
}
|
||||
}
|
||||
NotificationListenerFilter nlf =
|
||||
new NotificationListenerFilter(approved, disallowedPkgs);
|
||||
mRequestedNotificationListeners.put(Pair.create(cn, userId), nlf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
|
||||
out.startTag(null, TAG_REQUESTED_LISTENERS);
|
||||
for (Pair<ComponentName, Integer> listener : mRequestedNotificationListeners.keySet()) {
|
||||
NotificationListenerFilter nlf = mRequestedNotificationListeners.get(listener);
|
||||
out.startTag(null, TAG_REQUESTED_LISTENER);
|
||||
XmlUtils.writeStringAttribute(
|
||||
out, ATT_COMPONENT, listener.first.flattenToString());
|
||||
XmlUtils.writeIntAttribute(out, ATT_USER_ID, listener.second);
|
||||
|
||||
out.startTag(null, TAG_APPROVED);
|
||||
XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
|
||||
out.endTag(null, TAG_APPROVED);
|
||||
|
||||
out.startTag(null, TAG_DISALLOWED);
|
||||
XmlUtils.writeStringAttribute(
|
||||
out, ATT_PKGS, String.join(XML_SEPARATOR, nlf.getDisallowedPackages()));
|
||||
out.endTag(null, TAG_DISALLOWED);
|
||||
|
||||
out.endTag(null, TAG_REQUESTED_LISTENER);
|
||||
}
|
||||
|
||||
out.endTag(null, TAG_REQUESTED_LISTENERS);
|
||||
}
|
||||
|
||||
protected @Nullable NotificationListenerFilter getNotificationListenerFilter(
|
||||
Pair<ComponentName, Integer> pair) {
|
||||
return mRequestedNotificationListeners.get(pair);
|
||||
}
|
||||
|
||||
protected void setNotificationListenerFilter(Pair<ComponentName, Integer> pair,
|
||||
NotificationListenerFilter nlf) {
|
||||
mRequestedNotificationListeners.put(pair, nlf);
|
||||
}
|
||||
|
||||
@GuardedBy("mNotificationLock")
|
||||
public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
|
||||
if (trim == TRIM_LIGHT) {
|
||||
@@ -9618,8 +9797,9 @@ public class NotificationManagerService extends SystemService {
|
||||
TrimCache trimCache = new TrimCache(sbn);
|
||||
|
||||
for (final ManagedServiceInfo info : getServices()) {
|
||||
boolean sbnVisible = isVisibleToListener(sbn, info);
|
||||
boolean oldSbnVisible = (oldSbn != null) && isVisibleToListener(oldSbn, info);
|
||||
boolean sbnVisible = isVisibleToListener(sbn, r. getNotificationType(), info);
|
||||
boolean oldSbnVisible = (oldSbn != null)
|
||||
&& isVisibleToListener(oldSbn, old.getNotificationType(), info);
|
||||
// This notification hasn't been and still isn't visible -> ignore.
|
||||
if (!oldSbnVisible && !sbnVisible) {
|
||||
continue;
|
||||
@@ -9672,7 +9852,7 @@ public class NotificationManagerService extends SystemService {
|
||||
for (final NotificationRecord r : mNotificationList) {
|
||||
// When granting permissions, ignore notifications which are invisible.
|
||||
// When revoking permissions, all notifications are invisible, so process all.
|
||||
if (grant && !isVisibleToListener(r.getSbn(), info)) {
|
||||
if (grant && !isVisibleToListener(r.getSbn(), r.getNotificationType(), info)) {
|
||||
continue;
|
||||
}
|
||||
// If the notification is hidden, permissions are not required by the listener.
|
||||
@@ -9714,7 +9894,7 @@ public class NotificationManagerService extends SystemService {
|
||||
// notification
|
||||
final StatusBarNotification sbnLight = sbn.cloneLight();
|
||||
for (final ManagedServiceInfo info : getServices()) {
|
||||
if (!isVisibleToListener(sbn, info)) {
|
||||
if (!isVisibleToListener(sbn, r.getNotificationType(), info)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -9754,7 +9934,8 @@ public class NotificationManagerService extends SystemService {
|
||||
public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
|
||||
boolean isHiddenRankingUpdate = changedHiddenNotifications != null
|
||||
&& changedHiddenNotifications.size() > 0;
|
||||
|
||||
// TODO (b/73052211): if the ranking update changed the notification type,
|
||||
// cancel notifications for NLSes that can't see them anymore
|
||||
for (final ManagedServiceInfo serviceInfo : getServices()) {
|
||||
if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
|
||||
serviceInfo, ActivityManager.getCurrentUser())) {
|
||||
@@ -9765,7 +9946,8 @@ public class NotificationManagerService extends SystemService {
|
||||
if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >=
|
||||
Build.VERSION_CODES.P) {
|
||||
for (NotificationRecord rec : changedHiddenNotifications) {
|
||||
if (isVisibleToListener(rec.getSbn(), serviceInfo)) {
|
||||
if (isVisibleToListener(
|
||||
rec.getSbn(), rec.getNotificationType(), serviceInfo)) {
|
||||
notifyThisListener = true;
|
||||
break;
|
||||
}
|
||||
@@ -9979,6 +10161,7 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RoleObserver implements OnRoleHoldersChangedListener {
|
||||
// Role name : user id : list of approved packages
|
||||
private ArrayMap<String, ArrayMap<Integer, ArraySet<String>>> mNonBlockableDefaultApps;
|
||||
|
||||
@@ -1257,6 +1257,16 @@ public final class NotificationRecord {
|
||||
return !Objects.equals(getSbn().getPackageName(), getSbn().getOpPkg());
|
||||
}
|
||||
|
||||
public int getNotificationType() {
|
||||
if (isConversation()) {
|
||||
return NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
} else if (getImportance() >= IMPORTANCE_DEFAULT) {
|
||||
return NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
|
||||
} else {
|
||||
return NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all {@link Uri} that should have permission granted to whoever
|
||||
* will be rendering it. This list has already been vetted to only
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.notification;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.INotificationManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Pair;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.server.UiServiceTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NotificationListenersTest extends UiServiceTestCase {
|
||||
|
||||
@Mock
|
||||
private PackageManager mPm;
|
||||
@Mock
|
||||
private IPackageManager miPm;
|
||||
|
||||
@Mock
|
||||
NotificationManagerService mNm;
|
||||
@Mock
|
||||
private INotificationManager mINm;
|
||||
|
||||
NotificationManagerService.NotificationListeners mListeners;
|
||||
|
||||
private ComponentName mCn1 = new ComponentName("pkg", "pkg.cmp");
|
||||
private ComponentName mCn2 = new ComponentName("pkg2", "pkg2.cmp2");
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
getContext().setMockPackageManager(mPm);
|
||||
|
||||
mListeners = spy(mNm.new NotificationListeners(
|
||||
mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm));
|
||||
when(mNm.getBinderService()).thenReturn(mINm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadExtraTag() throws Exception {
|
||||
String xml = "<requested_listeners>"
|
||||
+ "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">"
|
||||
+ "<allowed types=\"7\" />"
|
||||
+ "<disallowed pkgs=\"\" />"
|
||||
+ "</listener>"
|
||||
+ "<listener component=\"" + mCn2.flattenToString() + "\" user=\"10\">"
|
||||
+ "<allowed types=\"4\" />"
|
||||
+ "<disallowed pkgs=\"something\" />"
|
||||
+ "</listener>"
|
||||
+ "</requested_listeners>";
|
||||
|
||||
TypedXmlPullParser parser = Xml.newFastPullParser();
|
||||
parser.setInput(new BufferedInputStream(
|
||||
new ByteArrayInputStream(xml.getBytes())), null);
|
||||
parser.nextTag();
|
||||
mListeners.readExtraTag("requested_listeners", parser);
|
||||
|
||||
validateListenersFromXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteExtraTag() throws Exception {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
|
||||
NotificationListenerFilter nlf2 =
|
||||
new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
|
||||
|
||||
TypedXmlSerializer serializer = Xml.newFastSerializer();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
|
||||
serializer.startDocument(null, true);
|
||||
mListeners.writeExtraXmlTags(serializer);
|
||||
serializer.endDocument();
|
||||
serializer.flush();
|
||||
|
||||
TypedXmlPullParser parser = Xml.newFastPullParser();
|
||||
parser.setInput(new BufferedInputStream(
|
||||
new ByteArrayInputStream(baos.toByteArray())), null);
|
||||
parser.nextTag();
|
||||
mListeners.readExtraTag("requested_listeners", parser);
|
||||
|
||||
validateListenersFromXml();
|
||||
}
|
||||
|
||||
private void validateListenersFromXml() {
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)).getTypes())
|
||||
.isEqualTo(7);
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))
|
||||
.getDisallowedPackages())
|
||||
.isEmpty();
|
||||
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
|
||||
.isEqualTo(4);
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10))
|
||||
.getDisallowedPackages())
|
||||
.contains("something");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUserRemoved() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
|
||||
NotificationListenerFilter nlf2 =
|
||||
new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
|
||||
|
||||
mListeners.onUserRemoved(0);
|
||||
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))).isNull();
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
|
||||
.isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUserUnlocked() {
|
||||
// one exists already, say from xml
|
||||
NotificationListenerFilter nlf =
|
||||
new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf);
|
||||
|
||||
// new service exists or backfilling on upgrade to S
|
||||
ServiceInfo si = new ServiceInfo();
|
||||
si.permission = mListeners.getConfig().bindPermission;
|
||||
si.packageName = "new";
|
||||
si.name = "comp";
|
||||
ResolveInfo ri = new ResolveInfo();
|
||||
ri.serviceInfo = si;
|
||||
|
||||
// incorrect service
|
||||
ServiceInfo si2 = new ServiceInfo();
|
||||
ResolveInfo ri2 = new ResolveInfo();
|
||||
ri2.serviceInfo = si2;
|
||||
si2.packageName = "new2";
|
||||
si2.name = "comp2";
|
||||
|
||||
List<ResolveInfo> ris = new ArrayList<>();
|
||||
ris.add(ri);
|
||||
ris.add(ri2);
|
||||
|
||||
when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(ris);
|
||||
|
||||
mListeners.onUserUnlocked(0);
|
||||
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes())
|
||||
.isEqualTo(4);
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
|
||||
.getDisallowedPackages())
|
||||
.contains("something");
|
||||
|
||||
assertThat(mListeners.getNotificationListenerFilter(
|
||||
Pair.create(si.getComponentName(), 0)).getTypes())
|
||||
.isEqualTo(7);
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(si.getComponentName(), 0))
|
||||
.getDisallowedPackages())
|
||||
.isEmpty();
|
||||
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(si2.getComponentName(), 0)))
|
||||
.isNull();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPackageChanged() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
|
||||
NotificationListenerFilter nlf2 =
|
||||
new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
|
||||
|
||||
String[] pkgs = new String[] {mCn1.getPackageName()};
|
||||
int[] uids = new int[] {1};
|
||||
mListeners.onPackagesChanged(false, pkgs, uids);
|
||||
|
||||
// not removing; no change
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)).getTypes())
|
||||
.isEqualTo(7);
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
|
||||
.isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPackageChanged_removing() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
|
||||
NotificationListenerFilter nlf2 =
|
||||
new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
|
||||
mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
|
||||
|
||||
String[] pkgs = new String[] {mCn1.getPackageName()};
|
||||
int[] uids = new int[] {1};
|
||||
mListeners.onPackagesChanged(true, pkgs, uids);
|
||||
|
||||
// only mCn1 removed
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))).isNull();
|
||||
assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes())
|
||||
.isEqualTo(4);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,6 +55,7 @@ import static android.os.Build.VERSION_CODES.P;
|
||||
import static android.os.UserHandle.USER_SYSTEM;
|
||||
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
|
||||
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
|
||||
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
|
||||
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
|
||||
|
||||
@@ -143,6 +144,7 @@ import android.provider.MediaStore;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.Adjustment;
|
||||
import android.service.notification.ConversationChannelWrapper;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.NotificationStats;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
@@ -269,6 +271,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
|
||||
@Mock
|
||||
private NotificationListeners mListeners;
|
||||
@Mock
|
||||
private NotificationListenerFilter mNlf;
|
||||
@Mock private NotificationAssistants mAssistants;
|
||||
@Mock private ConditionProviders mConditionProviders;
|
||||
private ManagedServices.ManagedServiceInfo mListener;
|
||||
@@ -459,6 +463,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
mPolicyFile.finishWrite(fos);
|
||||
|
||||
// Setup managed services
|
||||
when(mNlf.isTypeAllowed(anyInt())).thenReturn(true);
|
||||
when(mNlf.isPackageAllowed(anyString())).thenReturn(true);
|
||||
when(mNlf.isPackageAllowed(null)).thenReturn(true);
|
||||
when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf);
|
||||
mListener = mListeners.new ManagedServiceInfo(
|
||||
null, new ComponentName(PKG, "test_class"),
|
||||
UserHandle.getUserId(mUid), true, null, 0);
|
||||
@@ -3596,7 +3604,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
|
||||
mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
|
||||
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
|
||||
verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.getSbn()));
|
||||
verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r));
|
||||
|
||||
assertEquals(1, mNotificationRecordLogger.numCalls());
|
||||
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
|
||||
@@ -3610,14 +3618,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
|
||||
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true,
|
||||
NOTIFICATION_LOCATION_UNKNOWN);
|
||||
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
|
||||
eq((true)));
|
||||
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()),
|
||||
eq(FLAG_FILTER_TYPE_ALERTING), eq(true), eq((true)));
|
||||
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
|
||||
|
||||
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false,
|
||||
NOTIFICATION_LOCATION_UNKNOWN);
|
||||
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
|
||||
eq((false)));
|
||||
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()),
|
||||
eq(FLAG_FILTER_TYPE_ALERTING), eq(true), eq((false)));
|
||||
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
|
||||
|
||||
assertEquals(2, mNotificationRecordLogger.numCalls());
|
||||
@@ -3635,14 +3643,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true,
|
||||
NOTIFICATION_LOCATION_UNKNOWN);
|
||||
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
|
||||
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(false),
|
||||
eq((true)));
|
||||
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()),
|
||||
eq(FLAG_FILTER_TYPE_ALERTING), eq(false), eq((true)));
|
||||
|
||||
mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false,
|
||||
NOTIFICATION_LOCATION_UNKNOWN);
|
||||
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
|
||||
verify(mAssistants).notifyAssistantExpansionChangedLocked(
|
||||
eq(r.getSbn()), eq(false), eq((false)));
|
||||
eq(r.getSbn()), eq(FLAG_FILTER_TYPE_ALERTING), eq(false), eq((false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -3662,11 +3670,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, 2, true);
|
||||
mService.mNotificationDelegate.onNotificationVisibilityChanged(
|
||||
new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
|
||||
verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(true));
|
||||
verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r), eq(true));
|
||||
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
|
||||
mService.mNotificationDelegate.onNotificationVisibilityChanged(
|
||||
new NotificationVisibility[] {}, new NotificationVisibility[]{nv});
|
||||
verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(false));
|
||||
verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r), eq(false));
|
||||
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
|
||||
}
|
||||
|
||||
@@ -5324,7 +5332,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
r.getKey(), replyIndex, reply, NOTIFICATION_LOCATION_UNKNOWN,
|
||||
modifiedBeforeSending);
|
||||
verify(mAssistants).notifyAssistantSuggestedReplySent(
|
||||
eq(r.getSbn()), eq(reply), eq(generatedByAssistant));
|
||||
eq(r.getSbn()), eq(FLAG_FILTER_TYPE_ALERTING), eq(reply), eq(generatedByAssistant));
|
||||
assertEquals(1, mNotificationRecordLogger.numCalls());
|
||||
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
|
||||
mNotificationRecordLogger.event(0));
|
||||
@@ -5346,7 +5354,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
10, 10, r.getKey(), actionIndex, action, notificationVisibility,
|
||||
generatedByAssistant);
|
||||
verify(mAssistants).notifyAssistantActionClicked(
|
||||
eq(r.getSbn()), eq(action), eq(generatedByAssistant));
|
||||
eq(r), eq(action), eq(generatedByAssistant));
|
||||
|
||||
assertEquals(1, mNotificationRecordLogger.numCalls());
|
||||
assertEquals(
|
||||
@@ -5370,7 +5378,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
10, 10, r.getKey(), actionIndex, action, notificationVisibility,
|
||||
generatedByAssistant);
|
||||
verify(mAssistants).notifyAssistantActionClicked(
|
||||
eq(r.getSbn()), eq(action), eq(generatedByAssistant));
|
||||
eq(r), eq(action), eq(generatedByAssistant));
|
||||
|
||||
assertEquals(1, mNotificationRecordLogger.numCalls());
|
||||
assertEquals(
|
||||
@@ -7249,7 +7257,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
when(info.enabledAndUserMatches(info.userid)).thenReturn(false);
|
||||
when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
|
||||
|
||||
assertFalse(mService.isVisibleToListener(sbn, info));
|
||||
assertFalse(mService.isVisibleToListener(sbn, 0, info));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -7262,7 +7270,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
|
||||
when(mAssistants.checkServiceTokenLocked(any())).thenReturn(null);
|
||||
|
||||
assertTrue(mService.isVisibleToListener(sbn, info));
|
||||
assertTrue(mService.isVisibleToListener(sbn, 0, info));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -7277,7 +7285,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
|
||||
when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
|
||||
|
||||
assertFalse(mService.isVisibleToListener(sbn, info));
|
||||
assertFalse(mService.isVisibleToListener(sbn, 0, info));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -7292,7 +7300,42 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
|
||||
when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
|
||||
|
||||
assertTrue(mService.isVisibleToListener(sbn, info));
|
||||
assertTrue(mService.isVisibleToListener(sbn, 0, info));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsVisibleToListener_mismatchedType() {
|
||||
when(mNlf.isTypeAllowed(anyInt())).thenReturn(false);
|
||||
|
||||
StatusBarNotification sbn = mock(StatusBarNotification.class);
|
||||
when(sbn.getUserId()).thenReturn(10);
|
||||
ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
|
||||
ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class);
|
||||
info.userid = 10;
|
||||
when(info.isSameUser(anyInt())).thenReturn(true);
|
||||
when(assistant.isSameUser(anyInt())).thenReturn(true);
|
||||
when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
|
||||
when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
|
||||
|
||||
assertFalse(mService.isVisibleToListener(sbn, 0, info));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsVisibleToListener_disallowedPackage() {
|
||||
when(mNlf.isPackageAllowed(null)).thenReturn(false);
|
||||
|
||||
StatusBarNotification sbn = mock(StatusBarNotification.class);
|
||||
when(sbn.getUserId()).thenReturn(10);
|
||||
ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
|
||||
ManagedServices.ManagedServiceInfo assistant =
|
||||
mock(ManagedServices.ManagedServiceInfo.class);
|
||||
info.userid = 10;
|
||||
when(info.isSameUser(anyInt())).thenReturn(true);
|
||||
when(assistant.isSameUser(anyInt())).thenReturn(true);
|
||||
when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
|
||||
when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
|
||||
|
||||
assertFalse(mService.isVisibleToListener(sbn, 0, info));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -21,6 +21,9 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
|
||||
import static android.service.notification.Adjustment.KEY_NOT_CONVERSATION;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
|
||||
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
|
||||
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
|
||||
@@ -910,11 +913,13 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
||||
record.setAssistantImportance(IMPORTANCE_LOW);
|
||||
record.calculateImportance();
|
||||
assertEquals(IMPORTANCE_LOW, record.getImportance());
|
||||
assertEquals(FLAG_FILTER_TYPE_SILENT, record.getNotificationType());
|
||||
|
||||
record.updateNotificationChannel(
|
||||
new NotificationChannel(channelId, "", IMPORTANCE_DEFAULT));
|
||||
|
||||
assertEquals(IMPORTANCE_LOW, record.getImportance());
|
||||
assertEquals(FLAG_FILTER_TYPE_SILENT, record.getNotificationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1125,6 +1130,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
||||
record.setShortcutInfo(mock(ShortcutInfo.class));
|
||||
|
||||
assertTrue(record.isConversation());
|
||||
assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS, record.getNotificationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1134,6 +1140,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
||||
record.setShortcutInfo(null);
|
||||
|
||||
assertTrue(record.isConversation());
|
||||
assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS, record.getNotificationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1144,6 +1151,7 @@ public class NotificationRecordTest extends UiServiceTestCase {
|
||||
record.setHasSentValidMsg(true);
|
||||
|
||||
assertFalse(record.isConversation());
|
||||
assertEquals(FLAG_FILTER_TYPE_ALERTING, record.getNotificationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user