Merge "Check for NLS service intent filter when rebinding services" into main
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 93482e7..122836e 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -21,6 +21,9 @@
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_INSTANT;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
@@ -106,7 +109,8 @@
     protected final String TAG = getClass().getSimpleName().replace('$', '.');
     protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
+    protected static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
+    protected static final int ON_BINDING_DIED_REBIND_MSG = 1234;
     protected static final String ENABLED_SERVICES_SEPARATOR = ":";
     private static final String DB_VERSION_1 = "1";
     private static final String DB_VERSION_2 = "2";
@@ -875,7 +879,21 @@
             String approvedItem = getApprovedValue(pkgOrComponent);
 
             if (approvedItem != null) {
+                final ComponentName component = ComponentName.unflattenFromString(approvedItem);
                 if (enabled) {
+                    if (Flags.notificationNlsRebind()) {
+                        if (component != null && !isValidService(component, userId)) {
+                            // Only fail if package is available
+                            // If not, it will be validated again in onPackagesChanged
+                            final PackageManager pm = mContext.getPackageManager();
+                            if (pm.isPackageAvailable(component.getPackageName())) {
+                                Slog.w(TAG, "Skip allowing " + mConfig.caption
+                                        + " " + pkgOrComponent + " (userSet: " + userSet
+                                        + ") for invalid service");
+                                return;
+                            }
+                        }
+                    }
                     approved.add(approvedItem);
                 } else {
                     approved.remove(approvedItem);
@@ -973,7 +991,7 @@
                 || isPackageOrComponentAllowed(component.getPackageName(), userId))) {
             return false;
         }
-        return componentHasBindPermission(component, userId);
+        return isValidService(component, userId);
     }
 
     private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1220,12 +1238,21 @@
         if (!TextUtils.isEmpty(packageName)) {
             queryIntent.setPackage(packageName);
         }
+
+        if (Flags.notificationNlsRebind()) {
+            // Expand the package query
+            extraFlags |= MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+            extraFlags |= MATCH_INSTANT;
+        }
+
         List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
                 queryIntent,
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA | extraFlags,
                 userId);
-        if (DEBUG)
-            Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
+        if (DEBUG) {
+            Slog.v(TAG, mConfig.serviceInterface + " pkg: " + packageName + " services: "
+                    + installedServices);
+        }
         if (installedServices != null) {
             for (int i = 0, count = installedServices.size(); i < count; i++) {
                 ResolveInfo resolveInfo = installedServices.get(i);
@@ -1325,11 +1352,12 @@
                     if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
                         final ComponentName component = ComponentName.unflattenFromString(
                                 approvedPackageOrComponent);
-                        if (component != null && !componentHasBindPermission(component, userId)) {
+                        if (component != null && !isValidService(component, userId)) {
                             approved.removeAt(j);
                             if (DEBUG) {
                                 Slog.v(TAG, "Removing " + approvedPackageOrComponent
-                                        + " from approved list; no bind permission found "
+                                        + " from approved list; no bind permission or "
+                                        + "service interface filter found "
                                         + mConfig.bindPermission);
                             }
                         }
@@ -1348,6 +1376,15 @@
         }
     }
 
+    protected boolean isValidService(ComponentName component, int userId) {
+        if (Flags.notificationNlsRebind()) {
+            return componentHasBindPermission(component, userId) && queryPackageForServices(
+                    component.getPackageName(), userId).contains(component);
+        } else {
+            return componentHasBindPermission(component, userId);
+        }
+    }
+
     protected boolean isValidEntry(String packageOrComponent, int userId) {
         return hasMatchingServices(packageOrComponent, userId);
     }
@@ -1505,23 +1542,27 @@
      * Called when user switched to unbind all services from other users.
      */
     @VisibleForTesting
-    void unbindOtherUserServices(int currentUser) {
+    void unbindOtherUserServices(int switchedToUser) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
-        t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
-        unbindServicesImpl(currentUser, true /* allExceptUser */);
+        t.traceBegin("ManagedServices.unbindOtherUserServices_current" + switchedToUser);
+        unbindServicesImpl(switchedToUser, true /* allExceptUser */);
         t.traceEnd();
     }
 
-    void unbindUserServices(int user) {
+    void unbindUserServices(int removedUser) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
-        t.traceBegin("ManagedServices.unbindUserServices" + user);
-        unbindServicesImpl(user, false /* allExceptUser */);
+        t.traceBegin("ManagedServices.unbindUserServices" + removedUser);
+        unbindServicesImpl(removedUser, false /* allExceptUser */);
         t.traceEnd();
     }
 
     void unbindServicesImpl(int user, boolean allExceptUser) {
         final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
         synchronized (mMutex) {
+            if (Flags.notificationNlsRebind()) {
+                // Remove enqueued rebinds to avoid rebinding services for a switched user
+                mHandler.removeMessages(ON_BINDING_DIED_REBIND_MSG);
+            }
             final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
             for (ManagedServiceInfo info : removableBoundServices) {
                 if ((allExceptUser && (info.userid != user))
@@ -1716,6 +1757,7 @@
                             mServicesRebinding.add(servicesBindingTag);
                             mHandler.postDelayed(() ->
                                     reregisterService(name, userid),
+                                    ON_BINDING_DIED_REBIND_MSG,
                                     ON_BINDING_DIED_REBIND_DELAY_MS);
                         } else {
                             Slog.v(TAG, getCaption() + " not rebinding in user " + userid
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index f79d9ef..c479acf 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -194,3 +194,13 @@
   description: "Enables sound uri with vibration source in notification channel"
   bug: "351975435"
 }
+
+flag {
+  name: "notification_nls_rebind"
+  namespace: "systemui"
+  description: "Check for NLS service intent filter when rebinding services"
+  bug: "347674739"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
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 48bc9d7..b5724b5c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -21,8 +21,10 @@
 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND;
 
+import static com.android.server.notification.Flags.FLAG_NOTIFICATION_NLS_REBIND;
 import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT;
 import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE;
 import static com.android.server.notification.NotificationManagerService.privateSpaceFlagsEnabled;
@@ -63,11 +65,14 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
+import android.testing.TestableLooper;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -82,7 +87,9 @@
 
 import com.google.android.collect.Lists;
 
+import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -103,7 +110,10 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 
+
 public class ManagedServicesTest extends UiServiceTestCase {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
     @Mock
     private IPackageManager mIpm;
@@ -115,6 +125,7 @@
     private ManagedServices.UserProfiles mUserProfiles;
     @Mock private DevicePolicyManager mDpm;
     Object mLock = new Object();
+    private TestableLooper mTestableLooper;
 
     UserInfo mZero = new UserInfo(0, "zero", 0);
     UserInfo mTen = new UserInfo(10, "ten", 0);
@@ -142,6 +153,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mTestableLooper = new TestableLooper(Looper.getMainLooper());
 
         mContext.setMockPackageManager(mPm);
         mContext.addMockSystemService(Context.USER_SERVICE, mUm);
@@ -199,6 +211,11 @@
                 mIpm, APPROVAL_BY_COMPONENT);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        mTestableLooper.destroy();
+    }
+
     @Test
     public void testBackupAndRestore_migration() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
@@ -888,7 +905,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -919,7 +936,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -950,7 +967,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a/a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -981,7 +998,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
         service.addApprovedList("a/a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -1053,6 +1070,78 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
+    public void registerService_bindingDied_rebindIsClearedOnUserSwitch() throws Exception {
+        Context context = mock(Context.class);
+        PackageManager pm = mock(PackageManager.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        when(context.getPackageName()).thenReturn(mPkg);
+        when(context.getUserId()).thenReturn(mUser.getIdentifier());
+        when(context.getPackageManager()).thenReturn(pm);
+        when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_PACKAGE);
+        service = spy(service);
+        ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+        // Trigger onBindingDied for component when registering
+        //  => will schedule a rebind in 10 seconds
+        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            ServiceConnection sc = (ServiceConnection) args[1];
+            sc.onBindingDied(cn);
+            return true;
+        });
+        service.registerService(cn, 0);
+        assertThat(service.isBound(cn, 0)).isFalse();
+
+        // Switch to user 10
+        service.onUserSwitched(10);
+
+        // Check that the scheduled rebind for user 0 was cleared
+        mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
+        mTestableLooper.processAllMessages();
+        verify(service, never()).reregisterService(any(), anyInt());
+    }
+
+    @Test
+    public void registerService_bindingDied_rebindIsExecutedAfterTimeout() throws Exception {
+        Context context = mock(Context.class);
+        PackageManager pm = mock(PackageManager.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        when(context.getPackageName()).thenReturn(mPkg);
+        when(context.getUserId()).thenReturn(mUser.getIdentifier());
+        when(context.getPackageManager()).thenReturn(pm);
+        when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_PACKAGE);
+        service = spy(service);
+        ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+        // Trigger onBindingDied for component when registering
+        //  => will schedule a rebind in 10 seconds
+        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            ServiceConnection sc = (ServiceConnection) args[1];
+            sc.onBindingDied(cn);
+            return true;
+        });
+        service.registerService(cn, 0);
+        assertThat(service.isBound(cn, 0)).isFalse();
+
+        // Check that the scheduled rebind is run
+        mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
+        mTestableLooper.processAllMessages();
+        verify(service, times(1)).reregisterService(eq(cn), eq(0));
+    }
+
+    @Test
     public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1211,6 +1300,65 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
+    public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
+        Context context = spy(getContext());
+        doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+
+        List<String> packages = new ArrayList<>();
+        packages.add("package");
+        addExpectedServices(service, packages, 0);
+
+        final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
+        final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
+
+        // Both components are approved initially
+        mExpectedPrimaryComponentNames.clear();
+        mExpectedPrimaryPackages.clear();
+        mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+        mExpectedSecondaryComponentNames.clear();
+        mExpectedSecondaryPackages.clear();
+
+        loadXml(service);
+
+        //Component package/C1 loses serviceInterface intent filter
+        ManagedServices.Config config = service.getConfig();
+        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+                .thenAnswer(new Answer<List<ResolveInfo>>() {
+                    @Override
+                    public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+                            throws Throwable {
+                        Object[] args = invocationOnMock.getArguments();
+                        Intent invocationIntent = (Intent) args[0];
+                        if (invocationIntent != null) {
+                            if (invocationIntent.getAction().equals(config.serviceInterface)
+                                    && packages.contains(invocationIntent.getPackage())) {
+                                List<ResolveInfo> dummyServices = new ArrayList<>();
+                                ResolveInfo resolveInfo = new ResolveInfo();
+                                ServiceInfo serviceInfo = new ServiceInfo();
+                                serviceInfo.packageName = invocationIntent.getPackage();
+                                serviceInfo.name = approvedComponent.getClassName();
+                                serviceInfo.permission = service.getConfig().bindPermission;
+                                resolveInfo.serviceInfo = serviceInfo;
+                                dummyServices.add(resolveInfo);
+                                return dummyServices;
+                            }
+                        }
+                        return new ArrayList<>();
+                    }
+                });
+
+        // Trigger package update
+        service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+        assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
+        assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
+    }
+
+    @Test
     public void testSetPackageOrComponentEnabled() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1223,6 +1371,21 @@
                             "user10package1/K", "user10.3/Component", "user10package2/L",
                             "user10.4/Component"}));
 
+            // mock permissions for services
+            PackageManager pm = mock(PackageManager.class);
+            when(getContext().getPackageManager()).thenReturn(pm);
+            List<ComponentName> enabledComponents = List.of(
+                    ComponentName.unflattenFromString("package/Comp"),
+                    ComponentName.unflattenFromString("package/C2"),
+                    ComponentName.unflattenFromString("again/M4"),
+                    ComponentName.unflattenFromString("user10package/B"),
+                    ComponentName.unflattenFromString("user10/Component"),
+                    ComponentName.unflattenFromString("user10package1/K"),
+                    ComponentName.unflattenFromString("user10.3/Component"),
+                    ComponentName.unflattenFromString("user10package2/L"),
+                    ComponentName.unflattenFromString("user10.4/Component"));
+            mockServiceInfoWithMetaData(enabledComponents, service, pm, new ArrayMap<>());
+
             for (int userId : expectedEnabled.keySet()) {
                 ArrayList<String> expectedForUser = expectedEnabled.get(userId);
                 for (int i = 0; i < expectedForUser.size(); i++) {
@@ -1284,6 +1447,90 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
+    public void testSetPackageOrComponentEnabled_pkgInstalledAfterEnabling() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+
+        final int userId = 0;
+        final String validComponent = "again/M4";
+        ArrayList<String> expectedEnabled = Lists.newArrayList("package/Comp", "package/C2",
+                validComponent);
+
+        PackageManager pm = mock(PackageManager.class);
+        when(getContext().getPackageManager()).thenReturn(pm);
+        service = spy(service);
+
+        // Component again/M4 is a valid service and the package is available
+        doReturn(true).when(service)
+                .isValidService(ComponentName.unflattenFromString(validComponent), userId);
+        when(pm.isPackageAvailable("again")).thenReturn(true);
+
+        // "package" is not available and its services are not valid
+        doReturn(false).when(service)
+                .isValidService(ComponentName.unflattenFromString("package/Comp"), userId);
+        doReturn(false).when(service)
+                .isValidService(ComponentName.unflattenFromString("package/C2"), userId);
+        when(pm.isPackageAvailable("package")).thenReturn(false);
+
+        // Enable all components
+        for (String component: expectedEnabled) {
+            service.setPackageOrComponentEnabled(component, userId, true, true);
+        }
+
+        // Verify everything added is approved
+        for (String component: expectedEnabled) {
+            assertTrue("Not allowed: user: " + userId + " entry: " + component
+                    + " for approval level " + APPROVAL_BY_COMPONENT,
+                    service.isPackageOrComponentAllowed(component, userId));
+        }
+
+        // Add missing package "package"
+        service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+        // Check that component of "package" are not enabled
+        assertFalse(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString("package/Comp")));
+        assertFalse(service.isPackageOrComponentAllowed("package/Comp", userId));
+
+        assertFalse(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString("package/C2")));
+        assertFalse(service.isPackageOrComponentAllowed("package/C2", userId));
+
+        // Check that the valid components are still enabled
+        assertTrue(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString(validComponent)));
+        assertTrue(service.isPackageOrComponentAllowed(validComponent, userId));
+    }
+
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_NLS_REBIND)
+    public void testSetPackageOrComponentEnabled_invalidComponent() throws Exception {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+                mIpm, APPROVAL_BY_COMPONENT);
+
+        final int userId = 0;
+        final String invalidComponent = "package/Comp";
+
+        PackageManager pm = mock(PackageManager.class);
+        when(getContext().getPackageManager()).thenReturn(pm);
+        service = spy(service);
+
+        // Component is an invalid service and the package is available
+        doReturn(false).when(service)
+                .isValidService(ComponentName.unflattenFromString(invalidComponent), userId);
+        when(pm.isPackageAvailable("package")).thenReturn(true);
+        service.setPackageOrComponentEnabled(invalidComponent, userId, true, true);
+
+        // Verify that the component was not enabled
+        assertFalse("Not allowed: user: " + userId + " entry: " + invalidComponent
+                    + " for approval level " + APPROVAL_BY_COMPONENT,
+                service.isPackageOrComponentAllowed(invalidComponent, userId));
+        assertFalse(service.isComponentEnabledForCurrentProfiles(
+                ComponentName.unflattenFromString(invalidComponent)));
+    }
+
+    @Test
     public void testGetAllowedPackages_byUser() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1944,7 +2191,7 @@
         metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
         metaDatas.put(cn_allowed, metaDataAutobindAllow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_allowed.flattenToString(), 0, true);
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -1989,7 +2236,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -2028,7 +2275,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -2099,8 +2346,8 @@
     }
 
     private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
-            ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
-            throws RemoteException {
+            ManagedServices service, PackageManager packageManager,
+            ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
         when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
                 (Answer<ServiceInfo>) invocation -> {
                     ComponentName invocationCn = invocation.getArgument(0);
@@ -2115,6 +2362,39 @@
                     return null;
                 }
         );
+
+        // add components to queryIntentServicesAsUser response
+        final List<String> packages = new ArrayList<>();
+        for (ComponentName cn: componentNames) {
+            packages.add(cn.getPackageName());
+        }
+        ManagedServices.Config config = service.getConfig();
+        when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
+                thenAnswer(new Answer<List<ResolveInfo>>() {
+                @Override
+                public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+                    throws Throwable {
+                    Object[] args = invocationOnMock.getArguments();
+                    Intent invocationIntent = (Intent) args[0];
+                    if (invocationIntent != null) {
+                        if (invocationIntent.getAction().equals(config.serviceInterface)
+                            && packages.contains(invocationIntent.getPackage())) {
+                            List<ResolveInfo> dummyServices = new ArrayList<>();
+                            for (ComponentName cn: componentNames) {
+                                ResolveInfo resolveInfo = new ResolveInfo();
+                                ServiceInfo serviceInfo = new ServiceInfo();
+                                serviceInfo.packageName = invocationIntent.getPackage();
+                                serviceInfo.name = cn.getClassName();
+                                serviceInfo.permission = service.getConfig().bindPermission;
+                                resolveInfo.serviceInfo = serviceInfo;
+                                dummyServices.add(resolveInfo);
+                            }
+                            return dummyServices;
+                        }
+                    }
+                    return new ArrayList<>();
+                }
+            });
     }
 
     private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 0f7de7d..2c645e0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -28,6 +28,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertNull;
+
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.any;
@@ -197,6 +198,8 @@
     public void testWriteXml_userTurnedOffNAS() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
+
         mAssistants.loadDefaultsFromConfig(true);
 
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
@@ -432,6 +435,10 @@
     public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
         ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
         ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
+
+        doReturn(true).when(mAssistants).isValidService(eq(component1), eq(mZero.id));
+        doReturn(true).when(mAssistants).isValidService(eq(component2), eq(mZero.id));
+
         mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
                 true, true);
         verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal(
@@ -577,6 +584,7 @@
     public void testSetAdjustmentTypeSupportedState() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
         mAssistants.loadDefaultsFromConfig(true);
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
                 true, true);
@@ -600,6 +608,7 @@
     public void testSetAdjustmentTypeSupportedState_readWriteXml_entries() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
         mAssistants.loadDefaultsFromConfig(true);
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
                 true, true);
@@ -623,6 +632,7 @@
     public void testSetAdjustmentTypeSupportedState_readWriteXml_empty() throws Exception {
         int userId = ActivityManager.getCurrentUser();
 
+        doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
         mAssistants.loadDefaultsFromConfig(true);
         mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
                 true, true);