summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt1
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/service/notification/NotificationListenerFilter.java14
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java23
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java45
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java108
6 files changed, 187 insertions, 5 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index c6b4ea8e803e..3d00bda551bc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -38532,6 +38532,7 @@ package android.service.notification {
method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(@NonNull String, @NonNull android.os.UserHandle);
method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(@NonNull String, @NonNull android.os.UserHandle);
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
+ method public final void migrateNotificationFilter(int, @Nullable java.util.List<java.lang.String>);
method public android.os.IBinder onBind(android.content.Intent);
method public void onInterruptionFilterChanged(int);
method public void onListenerConnected();
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index d0e17f00e990..dab5aff5c9a8 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -228,6 +228,7 @@ interface INotificationManager
NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId);
void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf);
+ void migrateNotificationFilter(in INotificationListener token, int defaultTypes, in List<String> disallowedPkgs);
void setToastRateLimitingEnabled(boolean enable);
}
diff --git a/core/java/android/service/notification/NotificationListenerFilter.java b/core/java/android/service/notification/NotificationListenerFilter.java
index 9de75cac159a..a69d33c17e9d 100644
--- a/core/java/android/service/notification/NotificationListenerFilter.java
+++ b/core/java/android/service/notification/NotificationListenerFilter.java
@@ -31,15 +31,17 @@ import android.util.ArraySet;
* @hide
*/
public class NotificationListenerFilter implements Parcelable {
+
+ private static final int DEFAULT_TYPES = FLAG_FILTER_TYPE_CONVERSATIONS
+ | FLAG_FILTER_TYPE_ALERTING
+ | FLAG_FILTER_TYPE_SILENT
+ | FLAG_FILTER_TYPE_ONGOING;
private int mAllowedNotificationTypes;
// VersionedPackage is holding the pkg name and pkg uid
private ArraySet<VersionedPackage> mDisallowedPackages;
public NotificationListenerFilter() {
- mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS
- | FLAG_FILTER_TYPE_ALERTING
- | FLAG_FILTER_TYPE_SILENT
- | FLAG_FILTER_TYPE_ONGOING;
+ mAllowedNotificationTypes = DEFAULT_TYPES;
mDisallowedPackages = new ArraySet<>();
}
@@ -80,6 +82,10 @@ public class NotificationListenerFilter implements Parcelable {
return (mAllowedNotificationTypes & type) != 0;
}
+ public boolean areAllTypesAllowed() {
+ return DEFAULT_TYPES == mAllowedNotificationTypes;
+ }
+
public boolean isPackageAllowed(VersionedPackage pkg) {
return !mDisallowedPackages.contains(pkg);
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 09c3b2effcb0..6c775852fcee 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -792,6 +792,29 @@ public abstract class NotificationListenerService extends Service {
}
}
+ /**
+ * Lets an app migrate notification filters from its app into the OS.
+ *
+ * <p>This call will be ignored if the app has already migrated these settings or the user
+ * has set filters in the UI. This method is intended for user specific settings; if an app has
+ * already specified defaults types in its manifest with
+ * {@link #META_DATA_DEFAULT_FILTER_TYPES}, the defaultTypes option will be ignored.</p>
+ * @param defaultTypes A value representing the types of notifications that this listener should
+ * receive by default
+ * @param disallowedPkgs A list of package names whose notifications should not be seen by this
+ * listener, by default, because the listener does not process or display them, or because a
+ * user had previously disallowed these packages in the listener app's UI
+ */
+ public final void migrateNotificationFilter(@NotificationFilterTypes int defaultTypes,
+ @Nullable List<String> disallowedPkgs) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().migrateNotificationFilter(
+ mWrapper, defaultTypes, disallowedPkgs);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
/**
* Inform the notification manager that these notifications have been viewed by the
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4ee90d6e0956..50b91766735e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4342,6 +4342,49 @@ public class NotificationManagerService extends SystemService {
}
/**
+ * Allows an app to set an initial notification listener filter
+ *
+ * @param token The binder for the listener, to check that the caller is allowed
+ */
+ @Override
+ public void migrateNotificationFilter(INotificationListener token, int defaultTypes,
+ List<String> disallowedApps) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationLock) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+
+ Pair key = Pair.create(info.component, info.userid);
+
+ NotificationListenerFilter nlf = mListeners.getNotificationListenerFilter(key);
+ if (nlf == null) {
+ nlf = new NotificationListenerFilter();
+ }
+ if (nlf.getDisallowedPackages().isEmpty() && disallowedApps != null) {
+ for (String pkg : disallowedApps) {
+ // block the current user's version and any work profile versions
+ for (int userId : mUm.getProfileIds(info.userid, false)) {
+ try {
+ int uid = getUidForPackageAndUser(pkg, UserHandle.of(userId));
+ VersionedPackage vp = new VersionedPackage(pkg, uid);
+ nlf.addPackage(vp);
+ } catch (Exception e) {
+ // pkg doesn't exist on that user; skip
+ }
+ }
+ }
+ }
+ if (nlf.areAllTypesAllowed()) {
+ nlf.setTypes(defaultTypes);
+ }
+ mListeners.setNotificationListenerFilter(key, nlf);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Allow an INotificationListener to simulate clearing (dismissing) a single notification.
*
* {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
@@ -5239,7 +5282,7 @@ public class NotificationManagerService extends SystemService {
}
private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
- int uid = 0;
+ int uid = INVALID_UID;
final long identity = Binder.clearCallingIdentity();
try {
uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9a63782a4079..c8c8c8288bd5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -56,6 +56,9 @@ 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.FLAG_FILTER_TYPE_CONVERSATIONS;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+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;
@@ -124,6 +127,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
+import android.content.pm.VersionedPackage;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Icon;
@@ -189,6 +193,8 @@ import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.collect.ImmutableList;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -7585,4 +7591,106 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
r.getSbn().getTag(), r,false);
}
+
+ @Test
+ public void testMigrateNotificationFilter_migrationAllAllowed() throws Exception {
+ int uid = 9000;
+ int[] userIds = new int[] {UserHandle.getUserId(mUid), 1000};
+ when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
+ List<String> disallowedApps = ImmutableList.of("apples", "bananas", "cherries");
+ for (int userId : userIds) {
+ for (String pkg : disallowedApps) {
+ when(mPackageManager.getPackageUid(pkg, 0, userId)).thenReturn(uid++);
+ }
+ }
+
+ when(mListeners.getNotificationListenerFilter(any())).thenReturn(
+ new NotificationListenerFilter());
+
+ mBinderService.migrateNotificationFilter(null,
+ FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ONGOING,
+ disallowedApps);
+
+ ArgumentCaptor<NotificationListenerFilter> captor =
+ ArgumentCaptor.forClass(NotificationListenerFilter.class);
+ verify(mListeners).setNotificationListenerFilter(any(), captor.capture());
+
+ assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ONGOING,
+ captor.getValue().getTypes());
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("apples", 9000)));
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("cherries", 9002)));
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("apples", 9003)));
+
+ // hypothetical other user untouched
+ assertTrue(captor.getValue().isPackageAllowed(new VersionedPackage("apples", 10000)));
+ }
+
+ @Test
+ public void testMigrateNotificationFilter_noPreexistingFilter() throws Exception {
+ int[] userIds = new int[] {UserHandle.getUserId(mUid)};
+ when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
+ List<String> disallowedApps = ImmutableList.of("apples");
+ when(mPackageManager.getPackageUid("apples", 0, UserHandle.getUserId(mUid)))
+ .thenReturn(1001);
+
+ when(mListeners.getNotificationListenerFilter(any())).thenReturn(null);
+
+ mBinderService.migrateNotificationFilter(null, FLAG_FILTER_TYPE_ONGOING,
+ disallowedApps);
+
+ ArgumentCaptor<NotificationListenerFilter> captor =
+ ArgumentCaptor.forClass(NotificationListenerFilter.class);
+ verify(mListeners).setNotificationListenerFilter(any(), captor.capture());
+
+ assertEquals(FLAG_FILTER_TYPE_ONGOING, captor.getValue().getTypes());
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("apples", 1001)));
+ }
+
+ @Test
+ public void testMigrateNotificationFilter_existingTypeFilter() throws Exception {
+ int[] userIds = new int[] {UserHandle.getUserId(mUid)};
+ when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
+ List<String> disallowedApps = ImmutableList.of("apples");
+ when(mPackageManager.getPackageUid("apples", 0, UserHandle.getUserId(mUid)))
+ .thenReturn(1001);
+
+ when(mListeners.getNotificationListenerFilter(any())).thenReturn(
+ new NotificationListenerFilter(FLAG_FILTER_TYPE_CONVERSATIONS, new ArraySet<>()));
+
+ mBinderService.migrateNotificationFilter(null, FLAG_FILTER_TYPE_ONGOING,
+ disallowedApps);
+
+ ArgumentCaptor<NotificationListenerFilter> captor =
+ ArgumentCaptor.forClass(NotificationListenerFilter.class);
+ verify(mListeners).setNotificationListenerFilter(any(), captor.capture());
+
+ // type isn't saved but pkg list is
+ assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS, captor.getValue().getTypes());
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("apples", 1001)));
+ }
+
+ @Test
+ public void testMigrateNotificationFilter_existingPkgFilter() throws Exception {
+ int[] userIds = new int[] {UserHandle.getUserId(mUid)};
+ when(mUm.getProfileIds(anyInt(), anyBoolean())).thenReturn(userIds);
+ List<String> disallowedApps = ImmutableList.of("apples");
+ when(mPackageManager.getPackageUid("apples", 0, UserHandle.getUserId(mUid)))
+ .thenReturn(1001);
+
+ NotificationListenerFilter preexisting = new NotificationListenerFilter();
+ preexisting.addPackage(new VersionedPackage("test", 1002));
+ when(mListeners.getNotificationListenerFilter(any())).thenReturn(preexisting);
+
+ mBinderService.migrateNotificationFilter(null, FLAG_FILTER_TYPE_ONGOING,
+ disallowedApps);
+
+ ArgumentCaptor<NotificationListenerFilter> captor =
+ ArgumentCaptor.forClass(NotificationListenerFilter.class);
+ verify(mListeners).setNotificationListenerFilter(any(), captor.capture());
+
+ // type is saved but pkg list isn't
+ assertEquals(FLAG_FILTER_TYPE_ONGOING, captor.getValue().getTypes());
+ assertTrue(captor.getValue().isPackageAllowed(new VersionedPackage("apples", 1001)));
+ assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("test", 1002)));
+ }
}