Loading services/core/java/com/android/server/notification/ManagedServices.java +54 −12 Original line number Diff line number Diff line Loading @@ -21,6 +21,9 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT; 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; Loading Loading @@ -106,7 +109,8 @@ abstract public class ManagedServices { 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"; Loading Loading @@ -875,7 +879,21 @@ abstract public class ManagedServices { 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); Loading Loading @@ -973,7 +991,7 @@ abstract public class ManagedServices { || isPackageOrComponentAllowed(component.getPackageName(), userId))) { return false; } return componentHasBindPermission(component, userId); return isValidService(component, userId); } private boolean componentHasBindPermission(ComponentName component, int userId) { Loading Loading @@ -1220,12 +1238,21 @@ abstract public class ManagedServices { 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); Loading Loading @@ -1325,11 +1352,12 @@ abstract public class ManagedServices { 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); } } Loading @@ -1348,6 +1376,15 @@ abstract public class ManagedServices { } } 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); } Loading Loading @@ -1505,23 +1542,27 @@ abstract public class ManagedServices { * 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)) Loading Loading @@ -1716,6 +1757,7 @@ abstract public class ManagedServices { 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 Loading services/core/java/com/android/server/notification/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -194,3 +194,13 @@ flag { 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 } } services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +289 −9 File changed.Preview size limit exceeded, changes collapsed. Show changes services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static junit.framework.Assert.assertNotNull; 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; Loading Loading @@ -197,6 +198,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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, Loading Loading @@ -432,6 +435,10 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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( Loading Loading @@ -577,6 +584,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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); Loading @@ -600,6 +608,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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); Loading @@ -623,6 +632,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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); Loading Loading
services/core/java/com/android/server/notification/ManagedServices.java +54 −12 Original line number Diff line number Diff line Loading @@ -21,6 +21,9 @@ import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT; 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; Loading Loading @@ -106,7 +109,8 @@ abstract public class ManagedServices { 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"; Loading Loading @@ -875,7 +879,21 @@ abstract public class ManagedServices { 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); Loading Loading @@ -973,7 +991,7 @@ abstract public class ManagedServices { || isPackageOrComponentAllowed(component.getPackageName(), userId))) { return false; } return componentHasBindPermission(component, userId); return isValidService(component, userId); } private boolean componentHasBindPermission(ComponentName component, int userId) { Loading Loading @@ -1220,12 +1238,21 @@ abstract public class ManagedServices { 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); Loading Loading @@ -1325,11 +1352,12 @@ abstract public class ManagedServices { 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); } } Loading @@ -1348,6 +1376,15 @@ abstract public class ManagedServices { } } 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); } Loading Loading @@ -1505,23 +1542,27 @@ abstract public class ManagedServices { * 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)) Loading Loading @@ -1716,6 +1757,7 @@ abstract public class ManagedServices { 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 Loading
services/core/java/com/android/server/notification/flags.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -194,3 +194,13 @@ flag { 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 } }
services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +289 −9 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static junit.framework.Assert.assertNotNull; 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; Loading Loading @@ -197,6 +198,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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, Loading Loading @@ -432,6 +435,10 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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( Loading Loading @@ -577,6 +584,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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); Loading @@ -600,6 +608,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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); Loading @@ -623,6 +632,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { 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); Loading