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

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

Log notification channels and groups.

Test: manual, runtest systemui-notification
Change-Id: I326a35ae87e7a3dc4b2587271056b7dd5cd11f8d
parent a33ae2d8
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -3495,6 +3495,28 @@ message MetricsEvent {
    // FIELD: The new value of preference when it is changed in Settings
    FIELD_SETTINGS_PREFERENCE_CHANGE_VALUE = 855;

    // OPEN: Notification channel created. CLOSE: Notification channel deleted. UPDATE: notification
    // channel updated
    // PACKAGE: the package the channel belongs too
    // CATEGORY: NOTIFICATION
    // OS: O
    ACTION_NOTIFICATION_CHANNEL = 856;

    // Tagged data for notification channel. String.
    FIELD_NOTIFICATION_CHANNEL_ID = 857;

    // Tagged data for notification channel. int.
    FIELD_NOTIFICATION_CHANNEL_IMPORTANCE = 858;

    // OPEN: Notification channel group created.
    // PACKAGE: the package the group belongs to
    // CATEGORY: NOTIFICATION
    // OS: O
    ACTION_NOTIFICATION_CHANNEL_GROUP = 859;

    // Tagged data for notification channel group. String.
    FIELD_NOTIFICATION_CHANNEL_GROUP_ID = 860;

    // ---- End O Constants, all O constants go above this line ----

    // Add new aosp constants above this line.
+1 −0
Original line number Diff line number Diff line
@@ -2792,6 +2792,7 @@ public class NotificationManagerService extends SystemService {
            dump.put("bans", mRankingHelper.dumpBansJson(filter));
            dump.put("ranking", mRankingHelper.dumpJson(filter));
            dump.put("stats", mUsageStats.dumpJson(filter));
            dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
        } catch (JSONException e) {
            e.printStackTrace();
        }
+74 −1
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ import static android.app.NotificationManager.IMPORTANCE_NONE;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.Preconditions;

import android.app.Notification;
@@ -30,6 +32,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.metrics.LogMaker;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
@@ -485,6 +488,12 @@ public class RankingHelper implements RankingConfig {
        if (r == null) {
            throw new IllegalArgumentException("Invalid package");
        }
        LogMaker lm = new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
                .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
                        group.getId())
                .setPackageName(pkg);
        MetricsLogger.action(lm);
        r.groups.put(group.getId(), group);
        updateConfig();
    }
@@ -516,8 +525,11 @@ public class RankingHelper implements RankingConfig {
        if (existing != null) {
            if (existing.isDeleted()) {
                existing.setDeleted(false);
                updateConfig();
            }

            MetricsLogger.action(getChannelLog(channel, pkg));

            updateConfig();
            return;
        }
        if (channel.getImportance() < NotificationManager.IMPORTANCE_NONE
@@ -541,6 +553,8 @@ public class RankingHelper implements RankingConfig {
                    Notification.AUDIO_ATTRIBUTES_DEFAULT);
        }
        r.channels.put(channel.getId(), channel);
        MetricsLogger.action(getChannelLog(channel, pkg).setType(
                MetricsProto.MetricsEvent.TYPE_OPEN));
        updateConfig();
    }

@@ -568,6 +582,8 @@ public class RankingHelper implements RankingConfig {
            updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
        }
        r.channels.put(updatedChannel.getId(), updatedChannel);

        MetricsLogger.action(getChannelLog(updatedChannel, pkg));
        updateConfig();
    }

@@ -615,6 +631,7 @@ public class RankingHelper implements RankingConfig {
        }
        // Assistant cannot change the group

        MetricsLogger.action(getChannelLog(channel, pkg));
        r.channels.put(channel.getId(), channel);
        updateConfig();
    }
@@ -664,6 +681,9 @@ public class RankingHelper implements RankingConfig {
        if (channel != null) {
            channel.setDeleted(true);
        }
        LogMaker lm = getChannelLog(channel, pkg);
        lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
        MetricsLogger.action(lm);
    }

    @Override
@@ -935,6 +955,49 @@ public class RankingHelper implements RankingConfig {
        return packageBans;
    }

    /**
     * Dump only the channel information as structured JSON for the stats collector.
     *
     * This is intentionally redundant with {#link dumpJson} because the old
     * scraper will expect this format.
     *
     * @param filter
     * @return
     */
    public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
        JSONArray channels = new JSONArray();
        Map<String, Integer> packageChannels = getPackageChannels();
        for(Entry<String, Integer> channelCount : packageChannels.entrySet()) {
            final String packageName = channelCount.getKey();
            if (filter == null || filter.matches(packageName)) {
                JSONObject channelCountJson = new JSONObject();
                try {
                    channelCountJson.put("packageName", packageName);
                    channelCountJson.put("channelCount", channelCount.getValue());
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                channels.put(channelCountJson);
            }
        }
        return channels;
    }

    private Map<String, Integer> getPackageChannels() {
        ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
        for (int i = 0; i < mRecords.size(); i++) {
            final Record r = mRecords.valueAt(i);
            int channelCount = 0;
            for (int j = 0; j < r.channels.size();j++) {
                if (!r.channels.valueAt(j).isDeleted()) {
                    channelCount++;
                }
            }
            packageChannels.put(r.pkg, channelCount);
        }
        return packageChannels;
    }

    public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
            int[] uidList) {
        if (pkgList == null || pkgList.length == 0) {
@@ -982,6 +1045,16 @@ public class RankingHelper implements RankingConfig {
        }
    }

    private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
        return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL)
                .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
                .setPackageName(pkg)
                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID,
                        channel.getId())
                .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
                        channel.getImportance());
    }

    private static class Record {
        static int UNKNOWN_UID = UserHandle.USER_NULL;

+56 −1
Original line number Diff line number Diff line
@@ -15,12 +15,15 @@
 */
package com.android.server.notification;

import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;

import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;

import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,6 +51,7 @@ import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import android.util.Xml;

import java.io.BufferedInputStream;
@@ -60,12 +64,14 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;

@@ -991,4 +997,53 @@ public class RankingHelperTest {
            }
        }
    }

    @Test
    public void testCreateChannel_updateNameResId() throws Exception {
        NotificationChannel nc = new NotificationChannel("id", 1, IMPORTANCE_DEFAULT);
        mHelper.createNotificationChannel(pkg, uid, nc, true);

        nc = new NotificationChannel("id", 2, IMPORTANCE_DEFAULT);
        mHelper.createNotificationChannel(pkg, uid, nc, true);

        assertEquals(2, mHelper.getNotificationChannel(pkg, uid, "id", false).getNameResId());
    }

    @Test
    public void testDumpChannelsJson() throws Exception {
        final ApplicationInfo upgrade = new ApplicationInfo();
        upgrade.targetSdkVersion = Build.VERSION_CODES.O;
        try {
            when(mPm.getApplicationInfoAsUser(
                    anyString(), anyInt(), anyInt())).thenReturn(upgrade);
        } catch (PackageManager.NameNotFoundException e) {
        }
        ArrayMap<String, Integer> expectedChannels = new ArrayMap<>();
        int numPackages = ThreadLocalRandom.current().nextInt(1, 5);
        for (int i = 0; i < numPackages; i++) {
            String pkgName = "pkg" + i;
            int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
            for (int j = 0; j < numChannels; j++) {
                mHelper.createNotificationChannel(pkgName, uid,
                        new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true);
            }
            expectedChannels.put(pkgName, numChannels);
        }

        // delete the first channel of the first package
        String pkg = expectedChannels.keyAt(0);
        mHelper.deleteNotificationChannel("pkg" + 0, uid, "0");
        // dump should not include deleted channels
        int count = expectedChannels.get(pkg);
        expectedChannels.put(pkg, count - 1);

        JSONArray actual = mHelper.dumpChannelsJson(new NotificationManagerService.DumpFilter());
        assertEquals(numPackages, actual.length());
        for (int i = 0; i < numPackages; i++) {
            JSONObject object = actual.getJSONObject(i);
            assertTrue(expectedChannels.containsKey(object.get("packageName")));
            assertEquals(expectedChannels.get(object.get("packageName")).intValue() + 1,
                    object.getInt("channelCount"));
        }
    }
}