NotificationReported atom: Hash free-string ids.
Removed & renumbered old fields, as this atom is not yet being mapped.
Added test for NotificationRecordLogger.
Test: atest NotificationManagerServiceTest NotificiationRecordLoggerTest
Bug: 146488473
Change-Id: Ic5e429aedc38da881987343ba6d5a9f83813b966
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0bee44f..bb78eee 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3474,27 +3474,25 @@
// A small system-assigned identifier for the notification.
// Locally probably-unique, but expect collisions across users and/or days.
optional int32 instance_id = 4;
- // The app-assigned notification ID and tag
- optional int32 notification_id = 5;
- optional string notification_tag = 6;
- optional string channel_id = 7; // App-assigned channel ID
+ optional int32 notification_id_hash = 5; // Small hash of the app-assigned notif ID + tag
+ optional int32 channel_id_hash = 6; // Small hash of app-assigned channel ID
// Grouping information
- optional string group_id = 8; // Group the notification currently belongs to
- optional int32 group_instance_id = 9; // Instance_id of the group-summary notification
- optional bool is_group_summary = 10; // Tags the group-summary notification
+ optional int32 group_id_hash = 7; // Small hash of the group ID of the notification
+ optional int32 group_instance_id = 8; // Instance_id of the group-summary notification
+ optional bool is_group_summary = 9; // Tags the group-summary notification
// Attributes
- optional string category = 11; // App-assigned notification category (API-defined strings)
- optional int32 style = 12; // App-assigned notification style
- optional int32 num_people = 13; // Number of Person records attached to the notification
+ optional string category = 10; // App-assigned notification category (API-defined strings)
+ optional int32 style = 11; // App-assigned notification style
+ optional int32 num_people = 12; // Number of Person records attached to the notification
// Ordering, importance and interruptiveness
- optional int32 position = 14; // Position in NotificationManager's list
+ optional int32 position = 13; // Position in NotificationManager's list
- optional android.stats.sysui.NotificationImportance importance = 15;
- optional int32 alerting = 16; // Bitfield, 1=buzz 2=beep 4=blink
+ optional android.stats.sysui.NotificationImportance importance = 14;
+ optional int32 alerting = 15; // Bitfield, 1=buzz 2=beep 4=blink
enum NotificationImportanceExplanation {
IMPORTANCE_EXPLANATION_UNKNOWN = 0;
@@ -3506,12 +3504,12 @@
IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5;
}
- optional NotificationImportanceExplanation importance_source = 17;
- optional android.stats.sysui.NotificationImportance importance_initial = 18;
- optional NotificationImportanceExplanation importance_initial_source = 19;
- optional android.stats.sysui.NotificationImportance importance_asst = 20;
- optional int32 assistant_hash = 21;
- optional float assistant_ranking_score = 22;
+ optional NotificationImportanceExplanation importance_source = 16;
+ optional android.stats.sysui.NotificationImportance importance_initial = 17;
+ optional NotificationImportanceExplanation importance_initial_source = 18;
+ optional android.stats.sysui.NotificationImportance importance_asst = 19;
+ optional int32 assistant_hash = 20;
+ optional float assistant_ranking_score = 21;
}
message Notification {
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index fc2d9e7..2f78542 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -27,6 +27,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -266,5 +267,39 @@
int getInstanceId() {
return (r.getSbn().getInstanceId() == null ? 0 : r.getSbn().getInstanceId().getId());
}
+
+ /**
+ * @return Small hash of the notification ID, and tag (if present).
+ */
+ int getNotificationIdHash() {
+ return smallHash(Objects.hashCode(r.getSbn().getTag()) ^ r.getSbn().getId());
+ }
+
+ /**
+ * @return Small hash of the channel ID, if present, or 0 otherwise.
+ */
+ int getChannelIdHash() {
+ return smallHash(Objects.hashCode(r.getSbn().getNotification().getChannelId()));
+ }
+
+ /**
+ * @return Small hash of the group ID, respecting group override if present. 0 otherwise.
+ */
+ int getGroupIdHash() {
+ return smallHash(Objects.hashCode(r.getSbn().getGroup()));
+ }
+
+ // "Small" hashes will be in the range [0, MAX_HASH).
+ static final int MAX_HASH = (1 << 13);
+
+ /**
+ * Maps in to the range [0, MAX_HASH), keeping similar values distinct.
+ * @param in An arbitrary integer.
+ * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH).
+ */
+ @VisibleForTesting
+ static int smallHash(int in) {
+ return Math.floorMod(in, MAX_HASH);
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 015d280..bb23d1e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -40,28 +40,27 @@
/* int32 uid = 2 */ r.getUid(),
/* string package_name = 3 */ r.getSbn().getPackageName(),
/* int32 instance_id = 4 */ p.getInstanceId(),
- /* int32 notification_id = 5 */ r.getSbn().getId(),
- /* string notification_tag = 6 */ r.getSbn().getTag(),
- /* string channel_id = 7 */ r.getSbn().getChannelIdLogTag(),
- /* string group_id = 8 */ r.getSbn().getGroupLogTag(),
- /* int32 group_instance_id = 9 */ 0, // TODO generate and fill instance ids
- /* bool is_group_summary = 10 */ r.getSbn().getNotification().isGroupSummary(),
- /* string category = 11 */ r.getSbn().getNotification().category,
- /* int32 style = 12 */ p.getStyle(),
- /* int32 num_people = 13 */ p.getNumPeople(),
- /* int32 position = 14 */ position,
- /* android.stats.sysui.NotificationImportance importance = 15 */ r.getImportance(),
- /* int32 alerting = 16 */ buzzBeepBlink,
- /* NotificationImportanceExplanation importance_source = 17 */
+ /* int32 notification_id_hash = 5 */ p.getNotificationIdHash(),
+ /* int32 channel_id_hash = 6 */ p.getChannelIdHash(),
+ /* string group_id_hash = 7 */ p.getGroupIdHash(),
+ /* int32 group_instance_id = 8 */ 0, // TODO generate and fill instance ids
+ /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(),
+ /* string category = 10 */ r.getSbn().getNotification().category,
+ /* int32 style = 11 */ p.getStyle(),
+ /* int32 num_people = 12 */ p.getNumPeople(),
+ /* int32 position = 13 */ position,
+ /* android.stats.sysui.NotificationImportance importance = 14 */ r.getImportance(),
+ /* int32 alerting = 15 */ buzzBeepBlink,
+ /* NotificationImportanceExplanation importance_source = 16 */
r.getImportanceExplanationCode(),
- /* android.stats.sysui.NotificationImportance importance_initial = 18 */
+ /* android.stats.sysui.NotificationImportance importance_initial = 17 */
r.getInitialImportance(),
- /* NotificationImportanceExplanation importance_initial_source = 19 */
+ /* NotificationImportanceExplanation importance_initial_source = 18 */
r.getInitialImportanceExplanationCode(),
- /* android.stats.sysui.NotificationImportance importance_asst = 20 */
+ /* android.stats.sysui.NotificationImportance importance_asst = 19 */
r.getAssistantImportance(),
- /* int32 assistant_hash = 21 */ p.getAssistantHash(),
- /* float assistant_ranking_score = 22 */ 0 // TODO connect up ranking score
+ /* int32 assistant_hash = 20 */ p.getAssistantHash(),
+ /* float assistant_ranking_score = 21 */ 0 // TODO connect up ranking score
);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
new file mode 100644
index 0000000..f051fa4
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 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 android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationRecordLoggerTest extends UiServiceTestCase {
+ private static final int UID = 9999;
+ private static final String CHANNEL_ID = "NotificationRecordLoggerTestChannelId";
+
+ private NotificationRecord getNotification(int id, String tag) {
+ final String packageName = mContext.getPackageName();
+ NotificationChannel channel = new NotificationChannel(
+ CHANNEL_ID, CHANNEL_ID, IMPORTANCE_DEFAULT);
+ Notification.Builder nb = new Notification.Builder(mContext, channel.getId());
+ StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, id, tag,
+ UID, 0, nb.build(), new UserHandle(UID), null,
+ 0);
+ return new NotificationRecord(mContext, sbn, channel);
+ }
+
+ private NotificationRecordLogger.NotificationRecordPair getNotificationRecordPair(int id,
+ String tag) {
+ return new NotificationRecordLogger.NotificationRecordPair(getNotification(id, tag),
+ null);
+ }
+
+ @Test
+ public void testSmallHash() {
+ assertEquals(0, NotificationRecordLogger.NotificationRecordPair.smallHash(0));
+ final int maxHash = NotificationRecordLogger.NotificationRecordPair.MAX_HASH;
+ assertEquals(0,
+ NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash));
+ assertEquals(0,
+ NotificationRecordLogger.NotificationRecordPair.smallHash(17 * maxHash));
+ assertEquals(maxHash - 1,
+ NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash - 1));
+ assertEquals(maxHash - 1,
+ NotificationRecordLogger.NotificationRecordPair.smallHash(-1));
+ }
+
+ @Test
+ public void testGetNotificationIdHash() {
+ assertEquals(0,
+ getNotificationRecordPair(0, null).getNotificationIdHash());
+ assertEquals(1,
+ getNotificationRecordPair(1, null).getNotificationIdHash());
+ assertEquals(NotificationRecordLogger.NotificationRecordPair.MAX_HASH - 1,
+ getNotificationRecordPair(-1, null).getNotificationIdHash());
+ final String tag = "someTag";
+ final int hash = NotificationRecordLogger.NotificationRecordPair.smallHash(tag.hashCode());
+ assertEquals(hash, getNotificationRecordPair(0, tag).getNotificationIdHash());
+ // We xor the tag and hashcode together before compressing the range. The order of
+ // operations doesn't matter if id is small.
+ assertEquals(1 ^ hash,
+ getNotificationRecordPair(1, tag).getNotificationIdHash());
+ // But it does matter for an id with more 1 bits than fit in the small hash.
+ assertEquals(
+ NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode()),
+ getNotificationRecordPair(-1, tag).getNotificationIdHash());
+ assertNotEquals(-1 ^ hash,
+ NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode()));
+ }
+
+ @Test
+ public void testGetChannelIdHash() {
+ assertEquals(
+ NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()),
+ getNotificationRecordPair(0, null).getChannelIdHash());
+ assertNotEquals(
+ NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()),
+ CHANNEL_ID.hashCode());
+ }
+
+ @Test
+ public void testGetGroupIdHash() {
+ NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair(
+ 0, null);
+ assertEquals(0, p.getGroupIdHash());
+ final String group = "someGroup";
+ p.r.setOverrideGroupKey(group);
+ assertEquals(
+ NotificationRecordLogger.NotificationRecordPair.smallHash(group.hashCode()),
+ p.getGroupIdHash());
+ }
+}