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

Commit a46a7694 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Better handle notification uid resolution for missing pkgs

system_process can post and cancel notifications on behalf
of apps (e.g. AccountManagerService posting a 'please
log into this account' notification).  The cancellation
signal for these notifications can happen after the app
has been removed (indeed, the app removal might be the reason
the system tries to cancel the notification). If the system
tries to cancel a notification for a package that does not
exist, log and return instead of throwing a SecurityException. A
SecurityException will still be thrown if an app tries to post
a notification for an app that doesn't exist.

Test: atest
Fixes: 138922329
Change-Id: Id315ebe7ae86f877ba62cfbe07efd14ac354a4d1
parent c6958aa6
Loading
Loading
Loading
Loading
+17 −7
Original line number Diff line number Diff line
@@ -315,7 +315,7 @@ public class NotificationManagerService extends SystemService {

    static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps

    static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
    static final int INVALID_UID = -1;

    static final boolean ENABLE_BLOCKED_TOASTS = true;

@@ -4699,6 +4699,12 @@ public class NotificationManagerService extends SystemService {
        // ensure opPkg is delegate if does not match pkg
        int uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);

        if (uid == INVALID_UID) {
            Slog.w(TAG, opPkg + ":" + callingUid + " trying to cancel notification "
                    + "for nonexistent pkg " + pkg + " in user " + userId);
            return;
        }

        // if opPkg is not the same as pkg, make sure the notification given was posted
        // by opPkg
        if (!Objects.equals(pkg, opPkg)) {
@@ -4743,6 +4749,11 @@ public class NotificationManagerService extends SystemService {
        // as "pkg"
        final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);

        if (notificationUid == INVALID_UID) {
            throw new SecurityException("Caller " + opPkg + ":" + callingUid
                    + " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
        }

        checkRestrictedCategories(notification);

        // Fix the notification as best we can.
@@ -5063,8 +5074,7 @@ public class NotificationManagerService extends SystemService {
    }

    @VisibleForTesting
    int resolveNotificationUid(String callingPkg, String targetPkg,
            int callingUid, int userId) {
    int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) {
        if (userId == UserHandle.USER_ALL) {
            userId = USER_SYSTEM;
        }
@@ -5075,16 +5085,16 @@ public class NotificationManagerService extends SystemService {
            return callingUid;
        }

        int targetUid = -1;
        int targetUid = INVALID_UID;
        try {
            targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
        } catch (NameNotFoundException e) {
            /* ignore */
            /* ignore, handled by caller */
        }
        // posted from app A on behalf of app B
        if (targetUid != -1 && (isCallerAndroid(callingPkg, callingUid)
        if (isCallerAndroid(callingPkg, callingUid)
                || mPreferencesHelper.isDelegateAllowed(
                        targetPkg, targetUid, callingPkg, callingUid))) {
                        targetPkg, targetUid, callingPkg, callingUid)) {
            return targetUid;
        }

+35 −6
Original line number Diff line number Diff line
@@ -181,12 +181,6 @@ import java.util.function.Consumer;
@RunWithLooper
public class NotificationManagerServiceTest extends UiServiceTestCase {
    private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
    private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
            "device_config delete " + DeviceConfig.NAMESPACE_SYSTEMUI + " "
                    + SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE;
    private static final String SET_DEFAULT_ASSISTANT_DEVICE_CONFIG_CMD =
            "device_config put " + DeviceConfig.NAMESPACE_SYSTEMUI + " "
                    + SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE;

    private final int mUid = Binder.getCallingUid();
    private TestableNotificationManagerService mService;
@@ -4039,6 +4033,41 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(expectedUid, mService.resolveNotificationUid("android", "target", 0, 0));
    }

    @Test
    public void testPostFromAndroidForNonExistentPackage() throws Exception {
        final String notReal = "NOT REAL";
        when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt())).thenThrow(
                PackageManager.NameNotFoundException.class);
        ApplicationInfo ai = new ApplicationInfo();
        ai.uid = -1;
        when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);

        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
        try {
            mInternalService.enqueueNotification(notReal, "android", 0, 0, "tag",
                    sbn.getId(), sbn.getNotification(), sbn.getUserId());
            fail("can't post notifications for nonexistent packages, even if you exist");
        } catch (SecurityException e) {
            // yay
        }
    }

    @Test
    public void testCancelFromAndroidForNonExistentPackage() throws Exception {
        final String notReal = "NOT REAL";
        when(mPackageManagerClient.getPackageUidAsUser(eq(notReal), anyInt())).thenThrow(
                PackageManager.NameNotFoundException.class);
        ApplicationInfo ai = new ApplicationInfo();
        ai.uid = -1;
        when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);

        // unlike the post case, ignore instead of throwing
        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;

        mInternalService.cancelNotification(notReal, "android", 0, 0, "tag",
                sbn.getId(), sbn.getUserId());
    }

    @Test
    public void testResolveNotificationUid_delegateNotAllowed() throws Exception {
        when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(123);