Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19eb2d59 authored by Jordan Jozwiak's avatar Jordan Jozwiak
Browse files

Match any user pkgs for headless sys user mode

In headless system user mode a package might not be installed in the
system user. Since the config list is only loaded and matched against
packages for the system user, we need to handle the case where a package
might exist on the system but not be installed for the system user.

Bug: 241469882
Test: atest NotificationListenersTest
Change-Id: I3e225047846658479d00d4089b8864860ef0c274
parent 71cb768e
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELECOM;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
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.PERMISSION_GRANTED;
@@ -10693,10 +10694,18 @@ public class NotificationManagerService extends SystemService {
        private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
        ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
                mRequestedNotificationListeners = new ArrayMap<>();
        private final boolean mIsHeadlessSystemUserMode;
        public NotificationListeners(Context context, Object lock, UserProfiles userProfiles,
                IPackageManager pm) {
            this(context, lock, userProfiles, pm, UserManager.isHeadlessSystemUserMode());
        }
        @VisibleForTesting
        public NotificationListeners(Context context, Object lock, UserProfiles userProfiles,
                IPackageManager pm, boolean isHeadlessSystemUserMode) {
            super(context, lock, userProfiles, pm);
            this.mIsHeadlessSystemUserMode = isHeadlessSystemUserMode;
        }
        @Override
@@ -10721,10 +10730,16 @@ public class NotificationManagerService extends SystemService {
                    if (TextUtils.isEmpty(listeners[i])) {
                        continue;
                    }
                    int packageQueryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
                    // In the headless system user mode, packages might not be installed for the
                    // system user. Match packages for any user since apps can be installed only for
                    // non-system users and would be considering uninstalled for the system user.
                    if (mIsHeadlessSystemUserMode) {
                        packageQueryFlags += MATCH_ANY_USER;
                    }
                    ArraySet<ComponentName> approvedListeners =
                            this.queryPackageForServices(listeners[i],
                                    MATCH_DIRECT_BOOT_AWARE
                                            | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
                            this.queryPackageForServices(listeners[i], packageQueryFlags,
                                    USER_SYSTEM);
                    for (int k = 0; k < approvedListeners.size(); k++) {
                        ComponentName cn = approvedListeners.valueAt(k);
                        addDefaultComponentOrPackage(cn.flattenToString());
+56 −3
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.server.notification;

import static android.content.pm.PackageManager.MATCH_ANY_USER;
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;
@@ -30,9 +31,11 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -49,6 +52,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.VersionedPackage;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationListenerFilter;
@@ -69,6 +73,7 @@ import com.google.common.collect.ImmutableList;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.internal.util.reflection.FieldSetter;
@@ -77,6 +82,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.List;

public class NotificationListenersTest extends UiServiceTestCase {
@@ -85,6 +91,8 @@ public class NotificationListenersTest extends UiServiceTestCase {
    private PackageManager mPm;
    @Mock
    private IPackageManager miPm;
    @Mock
    private Resources mResources;

    @Mock
    NotificationManagerService mNm;
@@ -96,7 +104,8 @@ public class NotificationListenersTest extends UiServiceTestCase {

    private ComponentName mCn1 = new ComponentName("pkg", "pkg.cmp");
    private ComponentName mCn2 = new ComponentName("pkg2", "pkg2.cmp2");

    private ComponentName mUninstalledComponent = new ComponentName("pkg3",
            "pkg3.NotificationListenerService");

    @Before
    public void setUp() throws Exception {
@@ -130,6 +139,50 @@ public class NotificationListenersTest extends UiServiceTestCase {
        validateListenersFromXml();
    }

    @Test
    public void loadDefaultsFromConfig_forHeadlessSystemUser_loadUninstalled() throws Exception {
        // setup with headless system user mode
        mListeners = spy(mNm.new NotificationListeners(
                mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm,
                /* isHeadlessSystemUserMode= */ true));
        mockDefaultListenerConfigForUninstalledComponent(mUninstalledComponent);

        mListeners.loadDefaultsFromConfig();

        assertThat(mListeners.getDefaultComponents()).contains(mUninstalledComponent);
    }

    @Test
    public void loadDefaultsFromConfig_forNonHeadlessSystemUser_ignoreUninstalled()
            throws Exception {
        // setup without headless system user mode
        mListeners = spy(mNm.new NotificationListeners(
                mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm,
                /* isHeadlessSystemUserMode= */ false));
        mockDefaultListenerConfigForUninstalledComponent(mUninstalledComponent);

        mListeners.loadDefaultsFromConfig();

        assertThat(mListeners.getDefaultComponents()).doesNotContain(mUninstalledComponent);
    }

    private void mockDefaultListenerConfigForUninstalledComponent(ComponentName componentName) {
        ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(componentName));
        when(mResources
                .getString(
                        com.android.internal.R.string.config_defaultListenerAccessPackages))
                .thenReturn(componentName.getPackageName());
        when(mContext.getResources()).thenReturn(mResources);
        doReturn(components).when(mListeners).queryPackageForServices(
                eq(componentName.getPackageName()),
                intThat(hasIntBitFlag(MATCH_ANY_USER)),
                anyInt());
    }

    public static ArgumentMatcher<Integer> hasIntBitFlag(int flag) {
        return arg -> arg != null && ((arg & flag) == flag);
    }

    @Test
    public void testWriteExtraTag() throws Exception {
        NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());