Loading proto/src/metrics_constants.proto +22 −0 Original line number Diff line number Diff line Loading @@ -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. Loading services/core/java/com/android/server/notification/NotificationManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -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(); } Loading services/core/java/com/android/server/notification/RankingHelper.java +74 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -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 Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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; Loading services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +56 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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")); } } } Loading
proto/src/metrics_constants.proto +22 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -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(); } Loading
services/core/java/com/android/server/notification/RankingHelper.java +74 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); } Loading Loading @@ -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 Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -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(); } Loading Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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; Loading
services/tests/notification/src/com/android/server/notification/RankingHelperTest.java +56 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -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")); } } }