diff options
4 files changed, 188 insertions, 37 deletions
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 0bee44f32bfc..bb78eee2205f 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3474,27 +3474,25 @@ message NotificationReported { // 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 @@ message NotificationReported { 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 fc2d9e775149..2f7854226c5c 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.os.Bundle; 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 @@ public interface NotificationRecordLogger { 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 015d280535e6..bb23d1e876dc 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 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { /* 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 000000000000..f051fa4c2290 --- /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()); + } +} |