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

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

Lock all the things

Fix some instances of .*Locked() methods being called without
holding mNotificationLock.

Also, if this happens again, don't crash system server

Change-Id: I0a51310a6f6c8b07266092176e189e7cd08778cc
Fixes: 37444864
Test: runtest systemui-notification
parent 2ed861d5
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -15,19 +15,25 @@
 */
package com.android.server.notification;

import android.util.Slog;

import java.util.Comparator;

/**
 * Sorts notifications by their global sort key.
 */
public class GlobalSortKeyComparator implements Comparator<NotificationRecord> {
    private final static String TAG = "GlobalSortComp";

    @Override
    public int compare(NotificationRecord left, NotificationRecord right) {
        if (left.getGlobalSortKey() == null) {
            throw new IllegalStateException("Missing left global sort key: " + left);
            Slog.wtf(TAG, "Missing left global sort key: " + left);
            return 1;
        }
        if (right.getGlobalSortKey() == null) {
            throw new IllegalStateException("Missing right global sort key: " + right);
            Slog.wtf(TAG, "Missing right global sort key: " + right);
            return  -1;
        }
        return left.getGlobalSortKey().compareTo(right.getGlobalSortKey());
    }
+30 −19
Original line number Diff line number Diff line
@@ -1874,10 +1874,9 @@ public class NotificationManagerService extends SystemService {
            int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                    Binder.getCallingUid(), incomingUserId, true, false,
                    "getAppActiveNotifications", pkg);
            synchronized (mNotificationLock) {
                final ArrayMap<String, StatusBarNotification> map
                        = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());

            synchronized (mNotificationLock) {
                final int N = mNotificationList.size();
                for (int i = 0; i < N; i++) {
                    StatusBarNotification sbn = sanitizeSbn(pkg, userId,
@@ -1900,12 +1899,11 @@ public class NotificationManagerService extends SystemService {
                        map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
                    }
                }
            }

                final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
                list.addAll(map.values());
                return new ParceledListSlice<StatusBarNotification>(list);
            }
        }

        private StatusBarNotification sanitizeSbn(String pkg, int userId,
                StatusBarNotification sbn) {
@@ -2036,8 +2034,10 @@ public class NotificationManagerService extends SystemService {
            long identity = Binder.clearCallingIdentity();
            try {
                // allow bound services to disable themselves
                synchronized (mNotificationLock) {
                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                    info.getOwner().setComponentState(info.component, false);
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -2101,8 +2101,10 @@ public class NotificationManagerService extends SystemService {
                String key, String snoozeCriterionId) {
            long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mNotificationLock) {
                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                    snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -2118,8 +2120,10 @@ public class NotificationManagerService extends SystemService {
                long duration) {
            long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mNotificationLock) {
                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                    snoozeNotificationInt(key, duration, null, info);
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -2134,9 +2138,11 @@ public class NotificationManagerService extends SystemService {
        public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
            long identity = Binder.clearCallingIdentity();
            try {
                synchronized (mNotificationLock) {
                    final ManagedServiceInfo info =
                            mNotificationAssistants.checkServiceTokenLocked(token);
                    unsnoozeNotificationInt(key, info);
                }
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
@@ -2734,7 +2740,10 @@ public class NotificationManagerService extends SystemService {
        }

        private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
            ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
            ManagedServiceInfo info;
            synchronized (mNotificationLock) {
                info = mListeners.checkServiceTokenLocked(token);
            }
            if (!hasCompanionDevice(info)) {
                throw new SecurityException(info + " does not have access");
            }
@@ -3099,8 +3108,10 @@ public class NotificationManagerService extends SystemService {
                @Override
                public void run() {
                    synchronized (mNotificationLock) {
                        removeForegroundServiceFlagByListLocked(mEnqueuedNotifications, pkg, notificationId, userId);
                        removeForegroundServiceFlagByListLocked(mNotificationList, pkg, notificationId, userId);
                        removeForegroundServiceFlagByListLocked(
                                mEnqueuedNotifications, pkg, notificationId, userId);
                        removeForegroundServiceFlagByListLocked(
                                mNotificationList, pkg, notificationId, userId);
                    }
                }
            });
+153 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.notification;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;

import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class GlobalSortKeyComparatorTest {

    private final String PKG = "PKG";
    private final int UID = 1111111;
    private static final String TEST_CHANNEL_ID = "test_channel_id";

    @Test
    public void testComparator() throws Exception {
        Notification n = new Notification.Builder(
                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
                .build();
        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
                new StatusBarNotification(PKG,
                        PKG, 1, "media", UID, UID, n,
                        new UserHandle(UserHandle.myUserId()),
                        "", 1499), getDefaultChannel());
        left.setGlobalSortKey("first");

        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
                new StatusBarNotification(PKG,
                        PKG, 1, "media", UID, UID, n,
                        new UserHandle(UserHandle.myUserId()),
                        "", 1499), getDefaultChannel());
        right.setGlobalSortKey("second");

        NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
                new StatusBarNotification(PKG,
                        PKG, 1, "media", UID, UID, n,
                        new UserHandle(UserHandle.myUserId()),
                        "", 1499), getDefaultChannel());


        final List<NotificationRecord> expected = new ArrayList<>();
        expected.add(left);
        expected.add(right);
        expected.add(last);

        List<NotificationRecord> actual = new ArrayList<>();
        actual.addAll(expected);
        Collections.shuffle(actual);

        Collections.sort(actual, new GlobalSortKeyComparator());

        assertEquals(expected, actual);
    }

    @Test
    public void testNoCrash_leftNull() throws Exception {
        Notification n = new Notification.Builder(
                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
                .build();
        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
                new StatusBarNotification(PKG,
                        PKG, 1, "media", UID, UID, n,
                        new UserHandle(UserHandle.myUserId()),
                        "", 1499), getDefaultChannel());

        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
                new StatusBarNotification(PKG,
                        PKG, 1, "media", UID, UID, n,
                        new UserHandle(UserHandle.myUserId()),
                        "", 1499), getDefaultChannel());
        right.setGlobalSortKey("not null");

        final List<NotificationRecord> expected = new ArrayList<>();
        expected.add(right);
        expected.add(left);

        List<NotificationRecord> actual = new ArrayList<>();
        actual.addAll(expected);
        Collections.shuffle(actual);

        Collections.sort(actual, new GlobalSortKeyComparator());

        assertEquals(expected, actual);
    }

    @Test
    public void testNoCrash_rightNull() throws Exception {
        Notification n = new Notification.Builder(
                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
                .build();
        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
                new StatusBarNotification(PKG,
                        PKG, 1, "media", UID, UID, n,
                        new UserHandle(UserHandle.myUserId()),
                        "", 1499), getDefaultChannel());
        left.setGlobalSortKey("not null");

        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
                new StatusBarNotification(PKG,
                        PKG, 1, "media", UID, UID, n,
                        new UserHandle(UserHandle.myUserId()),
                        "", 1499), getDefaultChannel());

        final List<NotificationRecord> expected = new ArrayList<>();
        expected.add(left);
        expected.add(right);

        List<NotificationRecord> actual = new ArrayList<>();
        actual.addAll(expected);
        Collections.shuffle(actual);

        Collections.sort(actual, new GlobalSortKeyComparator());

        assertEquals(expected, actual);
    }

    private NotificationChannel getDefaultChannel() {
        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
                NotificationManager.IMPORTANCE_LOW);
    }
}