Loading services/core/java/com/android/server/notification/NotificationChannelLogger.java 0 → 100644 +213 −0 Original line number Diff line number Diff line /* * 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 android.annotation.NonNull; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.FrameworkStatsLog; /** * Interface for logging NotificationChannelModified statsd atoms. Provided as an interface to * enable unit-testing - use standard implementation NotificationChannelLoggerImpl in production. */ public interface NotificationChannelLogger { // The logging interface. Not anticipating a need to override these high-level methods, which by // default forward to a lower-level interface. /** * Log the creation of a notification channel. * @param channel The channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. */ default void logNotificationChannelCreated(@NonNull NotificationChannel channel, int uid, String pkg) { logNotificationChannel( NotificationChannelEvent.getCreated(channel), channel, uid, pkg, 0, 0); } /** * Log the deletion of a notification channel. * @param channel The channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. */ default void logNotificationChannelDeleted(@NonNull NotificationChannel channel, int uid, String pkg) { logNotificationChannel( NotificationChannelEvent.getDeleted(channel), channel, uid, pkg, 0, 0); } /** * Log the modification of a notification channel. * @param channel The channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param oldImportance Previous importance level of the channel. * @param byUser True if the modification was user-specified. */ default void logNotificationChannelModified(@NonNull NotificationChannel channel, int uid, String pkg, int oldImportance, boolean byUser) { logNotificationChannel(NotificationChannelEvent.getUpdated(byUser), channel, uid, pkg, oldImportance, channel.getImportance()); } /** * Log the creation or modification of a notification channel group. * @param channelGroup The notification channel group. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param isNew True if this is a creation of a new group. * @param wasBlocked */ default void logNotificationChannelGroup(@NonNull NotificationChannelGroup channelGroup, int uid, String pkg, boolean isNew, boolean wasBlocked) { logNotificationChannelGroup(NotificationChannelEvent.getGroupUpdated(isNew), channelGroup, uid, pkg, wasBlocked); } /** * Log the deletion of a notification channel group. * @param channelGroup The notification channel group. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. */ default void logNotificationChannelGroupDeleted(@NonNull NotificationChannelGroup channelGroup, int uid, String pkg) { logNotificationChannelGroup(NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_DELETED, channelGroup, uid, pkg, false); } /** * Low-level interface for logging events, to be implemented. * @param event Event to log. * @param channel Notification channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param oldImportance Old importance of the channel, if applicable (0 otherwise). * @param newImportance New importance of the channel, if applicable (0 otherwise). */ void logNotificationChannel(@NonNull NotificationChannelEvent event, @NonNull NotificationChannel channel, int uid, String pkg, int oldImportance, int newImportance); /** * Low-level interface for logging channel group events, to be implemented. * @param event Event to log. * @param channelGroup Notification channel group. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param wasBlocked True if the channel is being modified and was previously blocked. */ void logNotificationChannelGroup(@NonNull NotificationChannelEvent event, @NonNull NotificationChannelGroup channelGroup, int uid, String pkg, boolean wasBlocked); /** * The UiEvent enums that this class can log. */ enum NotificationChannelEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "App created a new notification channel") NOTIFICATION_CHANNEL_CREATED(219), @UiEvent(doc = "App modified an existing notification channel") NOTIFICATION_CHANNEL_UPDATED(220), @UiEvent(doc = "User modified a new notification channel") NOTIFICATION_CHANNEL_UPDATED_BY_USER(221), @UiEvent(doc = "App deleted an existing notification channel") NOTIFICATION_CHANNEL_DELETED(222), @UiEvent(doc = "App created a new notification channel group") NOTIFICATION_CHANNEL_GROUP_CREATED(223), @UiEvent(doc = "App modified an existing notification channel group") NOTIFICATION_CHANNEL_GROUP_UPDATED(224), @UiEvent(doc = "App deleted an existing notification channel group") NOTIFICATION_CHANNEL_GROUP_DELETED(226), @UiEvent(doc = "System created a new conversation (sub-channel in a notification channel)") NOTIFICATION_CHANNEL_CONVERSATION_CREATED(272), @UiEvent(doc = "System deleted a new conversation (sub-channel in a notification channel)") NOTIFICATION_CHANNEL_CONVERSATION_DELETED(274); private final int mId; NotificationChannelEvent(int id) { mId = id; } @Override public int getId() { return mId; } public static NotificationChannelEvent getUpdated(boolean byUser) { return byUser ? NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER : NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED; } public static NotificationChannelEvent getCreated(@NonNull NotificationChannel channel) { return channel.getConversationId() != null ? NotificationChannelEvent.NOTIFICATION_CHANNEL_CONVERSATION_CREATED : NotificationChannelEvent.NOTIFICATION_CHANNEL_CREATED; } public static NotificationChannelEvent getDeleted(@NonNull NotificationChannel channel) { return channel.getConversationId() != null ? NotificationChannelEvent.NOTIFICATION_CHANNEL_CONVERSATION_DELETED : NotificationChannelEvent.NOTIFICATION_CHANNEL_DELETED; } public static NotificationChannelEvent getGroupUpdated(boolean isNew) { return isNew ? NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_CREATED : NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_DELETED; } } /** * @return Small hash of the channel ID, if present, or 0 otherwise. */ static int getIdHash(@NonNull NotificationChannel channel) { return NotificationRecordLogger.smallHash(channel.getId()); } /** * @return Small hash of the channel ID, if present, or 0 otherwise. */ static int getIdHash(@NonNull NotificationChannelGroup group) { return NotificationRecordLogger.smallHash(group.getId()); } /** * @return "Importance" for a channel group */ static int getImportance(@NonNull NotificationChannelGroup channelGroup) { return getImportance(channelGroup.isBlocked()); } /** * @return "Importance" for a channel group, from its blocked status */ static int getImportance(boolean isBlocked) { return isBlocked ? FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED__IMPORTANCE__IMPORTANCE_NONE : FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED__IMPORTANCE__IMPORTANCE_DEFAULT; } } services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * 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 android.app.NotificationChannel; import android.app.NotificationChannelGroup; import com.android.internal.util.FrameworkStatsLog; /** * Standard implementation of NotificationChannelLogger, which passes data through to StatsLog. * This layer is as skinny as possible, to maximize code coverage of unit tests. Nontrivial code * should live in the interface so it can be tested. */ public class NotificationChannelLoggerImpl implements NotificationChannelLogger { @Override public void logNotificationChannel(NotificationChannelEvent event, NotificationChannel channel, int uid, String pkg, int oldImportance, int newImportance) { FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED, /* int event_id*/ event.getId(), /* int uid*/ uid, /* String package_name */ pkg, /* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channel), /* int old_importance*/ oldImportance, /* int importance*/ newImportance); } @Override public void logNotificationChannelGroup(NotificationChannelEvent event, NotificationChannelGroup channelGroup, int uid, String pkg, boolean wasBlocked) { FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED, /* int event_id*/ event.getId(), /* int uid*/ uid, /* String package_name */ pkg, /* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channelGroup), /* int old_importance*/ NotificationChannelLogger.getImportance(wasBlocked), /* int importance*/ NotificationChannelLogger.getImportance(channelGroup)); } } services/core/java/com/android/server/notification/NotificationManagerService.java +2 −1 Original line number Diff line number Diff line Loading @@ -1979,7 +1979,8 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper = new PreferencesHelper(getContext(), mPackageManagerClient, mRankingHandler, mZenModeHelper); mZenModeHelper, new NotificationChannelLoggerImpl()); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper, Loading services/core/java/com/android/server/notification/NotificationRecordLogger.java +22 −14 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ 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; Loading Loading @@ -307,27 +306,36 @@ public interface NotificationRecordLogger { * @return Small hash of the channel ID, if present, or 0 otherwise. */ int getChannelIdHash() { return smallHash(Objects.hashCode(r.getSbn().getNotification().getChannelId())); return smallHash(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())); return smallHash(r.getSbn().getGroup()); } } // "Small" hashes will be in the range [0, MAX_HASH). static final int MAX_HASH = (1 << 13); 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); } /** * @return Small hash of the string, if non-null, or 0 otherwise. */ static int smallHash(@Nullable String in) { return smallHash(Objects.hashCode(in)); } } services/core/java/com/android/server/notification/PreferencesHelper.java +43 −16 Original line number Diff line number Diff line Loading @@ -145,6 +145,7 @@ public class PreferencesHelper implements RankingConfig { private final PackageManager mPm; private final RankingHandler mRankingHandler; private final ZenModeHelper mZenModeHelper; private final NotificationChannelLogger mNotificationChannelLogger; private SparseBooleanArray mBadgingEnabled; private boolean mBubblesEnabled = DEFAULT_ALLOW_BUBBLE; Loading @@ -161,11 +162,12 @@ public class PreferencesHelper implements RankingConfig { } public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper) { ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger) { mContext = context; mZenModeHelper = zenHelper; mRankingHandler = rankingHandler; mPm = pm; mNotificationChannelLogger = notificationChannelLogger; // STOPSHIP (b/142218092) this should be removed before ship if (!wasBadgingForcedTrue(context)) { Loading Loading @@ -654,10 +656,6 @@ public class PreferencesHelper implements RankingConfig { throw new IllegalArgumentException("Invalid package"); } final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); if (!group.equals(oldGroup)) { // will log for new entries as well as name/description changes MetricsLogger.action(getChannelGroupLog(group.getId(), pkg)); } if (oldGroup != null) { group.setChannels(oldGroup.getChannels()); Loading @@ -674,6 +672,13 @@ public class PreferencesHelper implements RankingConfig { } } } if (!group.equals(oldGroup)) { // will log for new entries as well as name/description changes MetricsLogger.action(getChannelGroupLog(group.getId(), pkg)); mNotificationChannelLogger.logNotificationChannelGroup(group, uid, pkg, oldGroup == null, (oldGroup != null) && oldGroup.isBlocked()); } r.groups.put(group.getId(), group); } } Loading @@ -685,7 +690,7 @@ public class PreferencesHelper implements RankingConfig { Objects.requireNonNull(channel); Objects.requireNonNull(channel.getId()); Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName())); boolean needsPolicyFileChange = false; boolean needsPolicyFileChange = false, wasUndeleted = false; synchronized (mPackagePreferences) { PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); if (r == null) { Loading @@ -698,15 +703,18 @@ public class PreferencesHelper implements RankingConfig { throw new IllegalArgumentException("Reserved id"); } NotificationChannel existing = r.channels.get(channel.getId()); // Keep most of the existing settings if (existing != null && fromTargetApp) { // Actually modifying an existing channel - keep most of the existing settings if (existing.isDeleted()) { // The existing channel was deleted - undelete it. existing.setDeleted(false); needsPolicyFileChange = true; wasUndeleted = true; // log a resurrected channel as if it's new again MetricsLogger.action(getChannelLog(channel, pkg).setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg); } if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) { Loading Loading @@ -756,6 +764,10 @@ public class PreferencesHelper implements RankingConfig { } updateConfig(); if (needsPolicyFileChange && !wasUndeleted) { mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg, previousExistingImportance, false); } return needsPolicyFileChange; } Loading Loading @@ -806,6 +818,7 @@ public class PreferencesHelper implements RankingConfig { } MetricsLogger.action(getChannelLog(channel, pkg).setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg); } return needsPolicyFileChange; Loading Loading @@ -867,6 +880,8 @@ public class PreferencesHelper implements RankingConfig { // only log if there are real changes MetricsLogger.action(getChannelLog(updatedChannel, pkg) .setSubtype(fromUser ? 1 : 0)); mNotificationChannelLogger.logNotificationChannelModified(updatedChannel, uid, pkg, channel.getImportance(), fromUser); } if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd Loading Loading @@ -954,17 +969,24 @@ public class PreferencesHelper implements RankingConfig { } NotificationChannel channel = r.channels.get(channelId); if (channel != null) { deleteNotificationChannelLocked(channel, pkg, uid); } } } private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) { if (!channel.isDeleted()) { channel.setDeleted(true); LogMaker lm = getChannelLog(channel, pkg); lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); MetricsLogger.action(lm); mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg); if (mAreChannelsBypassingDnd && channel.canBypassDnd()) { updateChannelsBypassingDnd(mContext.getUserId()); } } } } @Override @VisibleForTesting Loading Loading @@ -1158,13 +1180,17 @@ public class PreferencesHelper implements RankingConfig { return deletedChannels; } r.groups.remove(groupId); NotificationChannelGroup channelGroup = r.groups.remove(groupId); if (channelGroup != null) { mNotificationChannelLogger.logNotificationChannelGroupDeleted(channelGroup, uid, pkg); } int N = r.channels.size(); for (int i = 0; i < N; i++) { final NotificationChannel nc = r.channels.valueAt(i); if (groupId.equals(nc.getGroup())) { nc.setDeleted(true); deleteNotificationChannelLocked(nc, pkg, uid); deletedChannels.add(nc); } } Loading Loading @@ -1279,6 +1305,7 @@ public class PreferencesHelper implements RankingConfig { lm.setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); MetricsLogger.action(lm); mNotificationChannelLogger.logNotificationChannelDeleted(nc, uid, pkg); deletedChannelIds.add(nc.getId()); } Loading Loading
services/core/java/com/android/server/notification/NotificationChannelLogger.java 0 → 100644 +213 −0 Original line number Diff line number Diff line /* * 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 android.annotation.NonNull; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.FrameworkStatsLog; /** * Interface for logging NotificationChannelModified statsd atoms. Provided as an interface to * enable unit-testing - use standard implementation NotificationChannelLoggerImpl in production. */ public interface NotificationChannelLogger { // The logging interface. Not anticipating a need to override these high-level methods, which by // default forward to a lower-level interface. /** * Log the creation of a notification channel. * @param channel The channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. */ default void logNotificationChannelCreated(@NonNull NotificationChannel channel, int uid, String pkg) { logNotificationChannel( NotificationChannelEvent.getCreated(channel), channel, uid, pkg, 0, 0); } /** * Log the deletion of a notification channel. * @param channel The channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. */ default void logNotificationChannelDeleted(@NonNull NotificationChannel channel, int uid, String pkg) { logNotificationChannel( NotificationChannelEvent.getDeleted(channel), channel, uid, pkg, 0, 0); } /** * Log the modification of a notification channel. * @param channel The channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param oldImportance Previous importance level of the channel. * @param byUser True if the modification was user-specified. */ default void logNotificationChannelModified(@NonNull NotificationChannel channel, int uid, String pkg, int oldImportance, boolean byUser) { logNotificationChannel(NotificationChannelEvent.getUpdated(byUser), channel, uid, pkg, oldImportance, channel.getImportance()); } /** * Log the creation or modification of a notification channel group. * @param channelGroup The notification channel group. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param isNew True if this is a creation of a new group. * @param wasBlocked */ default void logNotificationChannelGroup(@NonNull NotificationChannelGroup channelGroup, int uid, String pkg, boolean isNew, boolean wasBlocked) { logNotificationChannelGroup(NotificationChannelEvent.getGroupUpdated(isNew), channelGroup, uid, pkg, wasBlocked); } /** * Log the deletion of a notification channel group. * @param channelGroup The notification channel group. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. */ default void logNotificationChannelGroupDeleted(@NonNull NotificationChannelGroup channelGroup, int uid, String pkg) { logNotificationChannelGroup(NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_DELETED, channelGroup, uid, pkg, false); } /** * Low-level interface for logging events, to be implemented. * @param event Event to log. * @param channel Notification channel. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param oldImportance Old importance of the channel, if applicable (0 otherwise). * @param newImportance New importance of the channel, if applicable (0 otherwise). */ void logNotificationChannel(@NonNull NotificationChannelEvent event, @NonNull NotificationChannel channel, int uid, String pkg, int oldImportance, int newImportance); /** * Low-level interface for logging channel group events, to be implemented. * @param event Event to log. * @param channelGroup Notification channel group. * @param uid UID of app that owns the channel. * @param pkg Package of app that owns the channel. * @param wasBlocked True if the channel is being modified and was previously blocked. */ void logNotificationChannelGroup(@NonNull NotificationChannelEvent event, @NonNull NotificationChannelGroup channelGroup, int uid, String pkg, boolean wasBlocked); /** * The UiEvent enums that this class can log. */ enum NotificationChannelEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "App created a new notification channel") NOTIFICATION_CHANNEL_CREATED(219), @UiEvent(doc = "App modified an existing notification channel") NOTIFICATION_CHANNEL_UPDATED(220), @UiEvent(doc = "User modified a new notification channel") NOTIFICATION_CHANNEL_UPDATED_BY_USER(221), @UiEvent(doc = "App deleted an existing notification channel") NOTIFICATION_CHANNEL_DELETED(222), @UiEvent(doc = "App created a new notification channel group") NOTIFICATION_CHANNEL_GROUP_CREATED(223), @UiEvent(doc = "App modified an existing notification channel group") NOTIFICATION_CHANNEL_GROUP_UPDATED(224), @UiEvent(doc = "App deleted an existing notification channel group") NOTIFICATION_CHANNEL_GROUP_DELETED(226), @UiEvent(doc = "System created a new conversation (sub-channel in a notification channel)") NOTIFICATION_CHANNEL_CONVERSATION_CREATED(272), @UiEvent(doc = "System deleted a new conversation (sub-channel in a notification channel)") NOTIFICATION_CHANNEL_CONVERSATION_DELETED(274); private final int mId; NotificationChannelEvent(int id) { mId = id; } @Override public int getId() { return mId; } public static NotificationChannelEvent getUpdated(boolean byUser) { return byUser ? NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER : NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED; } public static NotificationChannelEvent getCreated(@NonNull NotificationChannel channel) { return channel.getConversationId() != null ? NotificationChannelEvent.NOTIFICATION_CHANNEL_CONVERSATION_CREATED : NotificationChannelEvent.NOTIFICATION_CHANNEL_CREATED; } public static NotificationChannelEvent getDeleted(@NonNull NotificationChannel channel) { return channel.getConversationId() != null ? NotificationChannelEvent.NOTIFICATION_CHANNEL_CONVERSATION_DELETED : NotificationChannelEvent.NOTIFICATION_CHANNEL_DELETED; } public static NotificationChannelEvent getGroupUpdated(boolean isNew) { return isNew ? NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_CREATED : NotificationChannelEvent.NOTIFICATION_CHANNEL_GROUP_DELETED; } } /** * @return Small hash of the channel ID, if present, or 0 otherwise. */ static int getIdHash(@NonNull NotificationChannel channel) { return NotificationRecordLogger.smallHash(channel.getId()); } /** * @return Small hash of the channel ID, if present, or 0 otherwise. */ static int getIdHash(@NonNull NotificationChannelGroup group) { return NotificationRecordLogger.smallHash(group.getId()); } /** * @return "Importance" for a channel group */ static int getImportance(@NonNull NotificationChannelGroup channelGroup) { return getImportance(channelGroup.isBlocked()); } /** * @return "Importance" for a channel group, from its blocked status */ static int getImportance(boolean isBlocked) { return isBlocked ? FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED__IMPORTANCE__IMPORTANCE_NONE : FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED__IMPORTANCE__IMPORTANCE_DEFAULT; } }
services/core/java/com/android/server/notification/NotificationChannelLoggerImpl.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * 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 android.app.NotificationChannel; import android.app.NotificationChannelGroup; import com.android.internal.util.FrameworkStatsLog; /** * Standard implementation of NotificationChannelLogger, which passes data through to StatsLog. * This layer is as skinny as possible, to maximize code coverage of unit tests. Nontrivial code * should live in the interface so it can be tested. */ public class NotificationChannelLoggerImpl implements NotificationChannelLogger { @Override public void logNotificationChannel(NotificationChannelEvent event, NotificationChannel channel, int uid, String pkg, int oldImportance, int newImportance) { FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED, /* int event_id*/ event.getId(), /* int uid*/ uid, /* String package_name */ pkg, /* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channel), /* int old_importance*/ oldImportance, /* int importance*/ newImportance); } @Override public void logNotificationChannelGroup(NotificationChannelEvent event, NotificationChannelGroup channelGroup, int uid, String pkg, boolean wasBlocked) { FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_CHANNEL_MODIFIED, /* int event_id*/ event.getId(), /* int uid*/ uid, /* String package_name */ pkg, /* int32 channel_id_hash */ NotificationChannelLogger.getIdHash(channelGroup), /* int old_importance*/ NotificationChannelLogger.getImportance(wasBlocked), /* int importance*/ NotificationChannelLogger.getImportance(channelGroup)); } }
services/core/java/com/android/server/notification/NotificationManagerService.java +2 −1 Original line number Diff line number Diff line Loading @@ -1979,7 +1979,8 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper = new PreferencesHelper(getContext(), mPackageManagerClient, mRankingHandler, mZenModeHelper); mZenModeHelper, new NotificationChannelLoggerImpl()); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper, Loading
services/core/java/com/android/server/notification/NotificationRecordLogger.java +22 −14 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ 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; Loading Loading @@ -307,27 +306,36 @@ public interface NotificationRecordLogger { * @return Small hash of the channel ID, if present, or 0 otherwise. */ int getChannelIdHash() { return smallHash(Objects.hashCode(r.getSbn().getNotification().getChannelId())); return smallHash(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())); return smallHash(r.getSbn().getGroup()); } } // "Small" hashes will be in the range [0, MAX_HASH). static final int MAX_HASH = (1 << 13); 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); } /** * @return Small hash of the string, if non-null, or 0 otherwise. */ static int smallHash(@Nullable String in) { return smallHash(Objects.hashCode(in)); } }
services/core/java/com/android/server/notification/PreferencesHelper.java +43 −16 Original line number Diff line number Diff line Loading @@ -145,6 +145,7 @@ public class PreferencesHelper implements RankingConfig { private final PackageManager mPm; private final RankingHandler mRankingHandler; private final ZenModeHelper mZenModeHelper; private final NotificationChannelLogger mNotificationChannelLogger; private SparseBooleanArray mBadgingEnabled; private boolean mBubblesEnabled = DEFAULT_ALLOW_BUBBLE; Loading @@ -161,11 +162,12 @@ public class PreferencesHelper implements RankingConfig { } public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper) { ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger) { mContext = context; mZenModeHelper = zenHelper; mRankingHandler = rankingHandler; mPm = pm; mNotificationChannelLogger = notificationChannelLogger; // STOPSHIP (b/142218092) this should be removed before ship if (!wasBadgingForcedTrue(context)) { Loading Loading @@ -654,10 +656,6 @@ public class PreferencesHelper implements RankingConfig { throw new IllegalArgumentException("Invalid package"); } final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); if (!group.equals(oldGroup)) { // will log for new entries as well as name/description changes MetricsLogger.action(getChannelGroupLog(group.getId(), pkg)); } if (oldGroup != null) { group.setChannels(oldGroup.getChannels()); Loading @@ -674,6 +672,13 @@ public class PreferencesHelper implements RankingConfig { } } } if (!group.equals(oldGroup)) { // will log for new entries as well as name/description changes MetricsLogger.action(getChannelGroupLog(group.getId(), pkg)); mNotificationChannelLogger.logNotificationChannelGroup(group, uid, pkg, oldGroup == null, (oldGroup != null) && oldGroup.isBlocked()); } r.groups.put(group.getId(), group); } } Loading @@ -685,7 +690,7 @@ public class PreferencesHelper implements RankingConfig { Objects.requireNonNull(channel); Objects.requireNonNull(channel.getId()); Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName())); boolean needsPolicyFileChange = false; boolean needsPolicyFileChange = false, wasUndeleted = false; synchronized (mPackagePreferences) { PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); if (r == null) { Loading @@ -698,15 +703,18 @@ public class PreferencesHelper implements RankingConfig { throw new IllegalArgumentException("Reserved id"); } NotificationChannel existing = r.channels.get(channel.getId()); // Keep most of the existing settings if (existing != null && fromTargetApp) { // Actually modifying an existing channel - keep most of the existing settings if (existing.isDeleted()) { // The existing channel was deleted - undelete it. existing.setDeleted(false); needsPolicyFileChange = true; wasUndeleted = true; // log a resurrected channel as if it's new again MetricsLogger.action(getChannelLog(channel, pkg).setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg); } if (!Objects.equals(channel.getName().toString(), existing.getName().toString())) { Loading Loading @@ -756,6 +764,10 @@ public class PreferencesHelper implements RankingConfig { } updateConfig(); if (needsPolicyFileChange && !wasUndeleted) { mNotificationChannelLogger.logNotificationChannelModified(existing, uid, pkg, previousExistingImportance, false); } return needsPolicyFileChange; } Loading Loading @@ -806,6 +818,7 @@ public class PreferencesHelper implements RankingConfig { } MetricsLogger.action(getChannelLog(channel, pkg).setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); mNotificationChannelLogger.logNotificationChannelCreated(channel, uid, pkg); } return needsPolicyFileChange; Loading Loading @@ -867,6 +880,8 @@ public class PreferencesHelper implements RankingConfig { // only log if there are real changes MetricsLogger.action(getChannelLog(updatedChannel, pkg) .setSubtype(fromUser ? 1 : 0)); mNotificationChannelLogger.logNotificationChannelModified(updatedChannel, uid, pkg, channel.getImportance(), fromUser); } if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd Loading Loading @@ -954,17 +969,24 @@ public class PreferencesHelper implements RankingConfig { } NotificationChannel channel = r.channels.get(channelId); if (channel != null) { deleteNotificationChannelLocked(channel, pkg, uid); } } } private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) { if (!channel.isDeleted()) { channel.setDeleted(true); LogMaker lm = getChannelLog(channel, pkg); lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); MetricsLogger.action(lm); mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg); if (mAreChannelsBypassingDnd && channel.canBypassDnd()) { updateChannelsBypassingDnd(mContext.getUserId()); } } } } @Override @VisibleForTesting Loading Loading @@ -1158,13 +1180,17 @@ public class PreferencesHelper implements RankingConfig { return deletedChannels; } r.groups.remove(groupId); NotificationChannelGroup channelGroup = r.groups.remove(groupId); if (channelGroup != null) { mNotificationChannelLogger.logNotificationChannelGroupDeleted(channelGroup, uid, pkg); } int N = r.channels.size(); for (int i = 0; i < N; i++) { final NotificationChannel nc = r.channels.valueAt(i); if (groupId.equals(nc.getGroup())) { nc.setDeleted(true); deleteNotificationChannelLocked(nc, pkg, uid); deletedChannels.add(nc); } } Loading Loading @@ -1279,6 +1305,7 @@ public class PreferencesHelper implements RankingConfig { lm.setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE); MetricsLogger.action(lm); mNotificationChannelLogger.logNotificationChannelDeleted(nc, uid, pkg); deletedChannelIds.add(nc.getId()); } Loading