diff options
| author | 2021-02-10 13:06:43 -0500 | |
|---|---|---|
| committer | 2021-02-12 12:55:02 -0500 | |
| commit | b6c718c9b8ebfa19d9ee90973111e21353f94e5c (patch) | |
| tree | eddc39fbd0631936e8a7faeb7eb6c91ba8a8673d | |
| parent | 98039ded901181b14c3b79edc79d1b9a71bbfbbb (diff) | |
Allow NLSes to supply a default notif type filter
Test: atest, cts verifier
Bug: 173052211
Change-Id: I06a110679c1c9c1de8d0e3f45a2b69c040e538f6
8 files changed, 141 insertions, 69 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 1296dd19cc7e..dd57b6a9fd02 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -38076,6 +38076,10 @@ package android.service.notification { method public final void setNotificationsShown(String[]); method public final void snoozeNotification(String, long); method public final void updateNotificationChannel(@NonNull String, @NonNull android.os.UserHandle, @NonNull android.app.NotificationChannel); + field public static final int FLAG_FILTER_TYPE_ALERTING = 2; // 0x2 + field public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1; // 0x1 + field public static final int FLAG_FILTER_TYPE_ONGOING = 8; // 0x8 + field public static final int FLAG_FILTER_TYPE_SILENT = 4; // 0x4 field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4 field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1 field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2 @@ -38084,6 +38088,7 @@ package android.service.notification { field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3 field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2 field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 + field public static final String META_DATA_DEFAULT_FILTER_TYPES = "android.service.notification.default_filter_types"; field public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; // 0x1 field public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; // 0x3 field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2 diff --git a/core/java/android/service/notification/NotificationListenerFilter.java b/core/java/android/service/notification/NotificationListenerFilter.java index 053fb2b1b4eb..9de75cac159a 100644 --- a/core/java/android/service/notification/NotificationListenerFilter.java +++ b/core/java/android/service/notification/NotificationListenerFilter.java @@ -32,6 +32,7 @@ import android.util.ArraySet; */ public class NotificationListenerFilter implements Parcelable { private int mAllowedNotificationTypes; + // VersionedPackage is holding the pkg name and pkg uid private ArraySet<VersionedPackage> mDisallowedPackages; public NotificationListenerFilter() { diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 64cddc35d2bb..f66f85b9e8cc 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -80,6 +80,10 @@ import java.util.Objects; * <intent-filter> * <action android:name="android.service.notification.NotificationListenerService" /> * </intent-filter> + * <meta-data + * android:name="android.service.notification.default_filter_types" + * android:value="1,2"> + * </meta-data> * </service></pre> * * <p>The service should wait for the {@link #onListenerConnected()} event @@ -103,6 +107,21 @@ public abstract class NotificationListenerService extends Service { private final String TAG = getClass().getSimpleName(); /** + * The name of the {@code meta-data} tag containing a comma separated list of default + * integer notification types that should be provided to this listener. See + * {@link #FLAG_FILTER_TYPE_ONGOING}, + * {@link #FLAG_FILTER_TYPE_CONVERSATIONS}, {@link #FLAG_FILTER_TYPE_ALERTING), + * and {@link #FLAG_FILTER_TYPE_SILENT}. + * <p>This value will only be read if the app has not previously specified a default type list, + * and if the user has not overridden the allowed types.</p> + * <p>An absent value means 'allow all types'. + * A present but empty value means 'allow no types'.</p> + * + */ + public static final String META_DATA_DEFAULT_FILTER_TYPES + = "android.service.notification.default_filter_types"; + + /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Normal interruption filter. */ @@ -254,23 +273,19 @@ public abstract class NotificationListenerService extends Service { /** * 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; /** * A flag value indicating that this notification listener can see important * ( > {@link NotificationManager#IMPORTANCE_MIN}) ongoing type notifications. - * @hide */ public static final int FLAG_FILTER_TYPE_ONGOING = 8; diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 769b781b1ae9..78c1a95a891d 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -21,6 +21,7 @@ import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; +import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.IBinder; import android.os.IInterface; @@ -197,6 +198,11 @@ public class ConditionProviders extends ManagedServices { } @Override + protected void ensureFilters(ServiceInfo si, int userId) { + // nothing to filter + } + + @Override protected void loadDefaultsFromConfig() { String defaultDndAccess = mContext.getResources().getString( R.string.config_defaultDndAccessPackages); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 21537e6aaa22..bbdcac28fb02 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -189,6 +189,8 @@ abstract public class ManagedServices { abstract protected void onServiceAdded(ManagedServiceInfo info); + abstract protected void ensureFilters(ServiceInfo si, int userId); + protected List<ManagedServiceInfo> getServices() { synchronized (mMutex) { List<ManagedServiceInfo> services = new ArrayList<>(mServices); @@ -1342,8 +1344,10 @@ abstract public class ManagedServices { for (ComponentName component : add) { try { ServiceInfo info = mPm.getServiceInfo(component, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + PackageManager.GET_META_DATA + | PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + userId); if (info == null) { Slog.w(TAG, "Not binding " + getCaption() + " service " + component + ": service not found"); @@ -1356,7 +1360,7 @@ abstract public class ManagedServices { } Slog.v(TAG, "enabling " + getCaption() + " for " + userId + ": " + component); - registerService(component, userId); + registerService(info, userId); } catch (RemoteException e) { e.rethrowFromSystemServer(); } @@ -1368,9 +1372,15 @@ abstract public class ManagedServices { * Version of registerService that takes the name of a service component to bind to. */ @VisibleForTesting - void registerService(final ComponentName name, final int userid) { + void registerService(final ServiceInfo si, final int userId) { + ensureFilters(si, userId); + registerService(si.getComponentName(), userId); + } + + @VisibleForTesting + void registerService(final ComponentName cn, final int userId) { synchronized (mMutex) { - registerServiceLocked(name, userid); + registerServiceLocked(cn, userId); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 1591e999e893..917be29243ad 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -215,6 +215,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.Condition; +import android.service.notification.ConditionProviderService; import android.service.notification.ConversationChannelWrapper; import android.service.notification.IConditionProvider; import android.service.notification.INotificationListener; @@ -9134,6 +9135,12 @@ public class NotificationManagerService extends SystemService { } @Override + protected void ensureFilters(ServiceInfo si, int userId) { + // nothing to filter; no user visible settings for types/packages like other + // listeners + } + + @Override @GuardedBy("mNotificationLock") protected void onServiceRemovedLocked(ManagedServiceInfo removed) { mListeners.unregisterService(removed.service, removed.userid); @@ -9578,7 +9585,7 @@ 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 = "req_listeners"; + static final String TAG_REQUESTED_LISTENERS = "request_listeners"; static final String TAG_REQUESTED_LISTENER = "listener"; static final String ATT_COMPONENT = "component"; static final String ATT_TYPES = "types"; @@ -9701,28 +9708,6 @@ public class NotificationManagerService extends SystemService { } @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); @@ -9842,6 +9827,38 @@ public class NotificationManagerService extends SystemService { mRequestedNotificationListeners.put(pair, nlf); } + @Override + protected void ensureFilters(ServiceInfo si, int userId) { + Pair listener = Pair.create(si.getComponentName(), userId); + NotificationListenerFilter existingNlf = + mRequestedNotificationListeners.get(listener); + if (existingNlf == null) { + // no stored filters for this listener; see if they provided a default + if (si.metaData != null) { + String typeList = si.metaData.getString( + NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES); + if (typeList != null) { + int types = 0; + String[] typeStrings = typeList.split(XML_SEPARATOR); + for (int i = 0; i < typeStrings.length; i++) { + if (TextUtils.isEmpty(typeStrings[i])) { + continue; + } + try { + types |= Integer.parseInt(typeStrings[i]); + } catch (NumberFormatException e) { + // skip + } + } + + NotificationListenerFilter nlf = + new NotificationListenerFilter(types, new ArraySet<>()); + mRequestedNotificationListeners.put(listener, nlf); + } + } + } + } + @GuardedBy("mNotificationLock") public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { if (trim == TRIM_LIGHT) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index ec28baf53d3c..07475e955785 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -1686,6 +1686,11 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Override + protected void ensureFilters(ServiceInfo si, int userId) { + + } + + @Override protected void loadDefaultsFromConfig() { mDefaultComponents.addAll(mDefaults); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index b13f80b6f490..80a046a1e8bb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -15,6 +15,11 @@ */ package com.android.server.notification; +import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; +import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; + +import static com.android.server.notification.NotificationManagerService.NotificationListeners.TAG_REQUESTED_LISTENERS; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -25,14 +30,16 @@ import static org.mockito.Mockito.when; import android.app.INotificationManager; import android.content.ComponentName; -import android.content.pm.VersionedPackage; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.VersionedPackage; +import android.os.Bundle; import android.service.notification.NotificationListenerFilter; +import android.service.notification.NotificationListenerService; import android.util.ArraySet; import android.util.Pair; +import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -48,8 +55,6 @@ 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 { @@ -81,7 +86,7 @@ public class NotificationListenersTest extends UiServiceTestCase { @Test public void testReadExtraTag() throws Exception { - String xml = "<req_listeners>" + String xml = "<" + TAG_REQUESTED_LISTENERS+ ">" + "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">" + "<allowed types=\"7\" />" + "</listener>" @@ -89,13 +94,13 @@ public class NotificationListenersTest extends UiServiceTestCase { + "<allowed types=\"4\" />" + "<disallowed pkg=\"pkg1\" uid=\"243\"/>" + "</listener>" - + "</req_listeners>"; + + "</" + TAG_REQUESTED_LISTENERS + ">"; TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); - mListeners.readExtraTag("req_listeners", parser); + mListeners.readExtraTag(TAG_REQUESTED_LISTENERS, parser); validateListenersFromXml(); } @@ -158,52 +163,60 @@ public class NotificationListenersTest extends UiServiceTestCase { } @Test - public void testOnUserUnlocked() { + public void testEnsureFilters_newServiceNoMetadata() { + ServiceInfo si = new ServiceInfo(); + si.packageName = "new2"; + si.name = "comp2"; + + mListeners.ensureFilters(si, 0); + + assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isNull(); + } + + @Test + public void testEnsureFilters_preExisting() { // one exists already, say from xml VersionedPackage a1 = new VersionedPackage("pkg1", 243); NotificationListenerFilter nlf = new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf); + ServiceInfo siOld = new ServiceInfo(); + siOld.packageName = mCn2.getPackageName(); + siOld.name = mCn2.getClassName(); + + mListeners.ensureFilters(siOld, 0); - // new service exists or backfilling on upgrade to S + assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isEqualTo(nlf); + } + + @Test + public void testEnsureFilters_newServiceWithMetadata() { 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"; + si.metaData = new Bundle(); + si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2"); - List<ResolveInfo> ris = new ArrayList<>(); - ris.add(ri); - ris.add(ri2); + mListeners.ensureFilters(si, 0); - when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(ris); + assertThat(mListeners.getNotificationListenerFilter( + Pair.create(si.getComponentName(), 0)).getTypes()) + .isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING); + } - mListeners.onUserUnlocked(0); + @Test + public void testEnsureFilters_newServiceWithEmptyMetadata() { + ServiceInfo si = new ServiceInfo(); + si.packageName = "new"; + si.name = "comp"; + si.metaData = new Bundle(); + si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, ""); - assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes()) - .isEqualTo(4); - assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)) - .getDisallowedPackages()) - .contains(a1); + mListeners.ensureFilters(si, 0); assertThat(mListeners.getNotificationListenerFilter( Pair.create(si.getComponentName(), 0)).getTypes()) - .isEqualTo(15); - assertThat(mListeners.getNotificationListenerFilter(Pair.create(si.getComponentName(), 0)) - .getDisallowedPackages()) - .isEmpty(); - - assertThat(mListeners.getNotificationListenerFilter(Pair.create(si2.getComponentName(), 0))) - .isNull(); - + .isEqualTo(0); } @Test |