Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.server.notification; |
| 18 | |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 19 | import static android.service.notification.NotificationListenerService.REASON_CANCEL; |
| 20 | import static android.service.notification.NotificationListenerService.REASON_CLICK; |
| 21 | import static android.service.notification.NotificationListenerService.REASON_TIMEOUT; |
| 22 | |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 23 | import android.annotation.Nullable; |
| 24 | import android.app.Notification; |
| 25 | import android.app.Person; |
| 26 | import android.os.Bundle; |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 27 | import android.service.notification.NotificationListenerService; |
| 28 | import android.service.notification.NotificationStats; |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 29 | |
| 30 | import com.android.internal.logging.UiEvent; |
| 31 | import com.android.internal.logging.UiEventLogger; |
| 32 | |
| 33 | import java.util.ArrayList; |
| 34 | import java.util.Objects; |
| 35 | |
| 36 | /** |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 37 | * Interface for writing NotificationReported atoms to statsd log. Use NotificationRecordLoggerImpl |
| 38 | * in production. Use NotificationRecordLoggerFake for testing. |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 39 | * @hide |
| 40 | */ |
| 41 | public interface NotificationRecordLogger { |
| 42 | |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 43 | // The high-level interface used by clients. |
| 44 | |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 45 | /** |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 46 | * May log a NotificationReported atom reflecting the posting or update of a notification. |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 47 | * @param r The new NotificationRecord. If null, no action is taken. |
| 48 | * @param old The previous NotificationRecord. Null if there was no previous record. |
| 49 | * @param position The position at which this notification is ranked. |
| 50 | * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user. |
| 51 | */ |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 52 | void maybeLogNotificationPosted(@Nullable NotificationRecord r, |
| 53 | @Nullable NotificationRecord old, |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 54 | int position, int buzzBeepBlink); |
| 55 | |
| 56 | /** |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 57 | * Logs a notification cancel / dismiss event using UiEventReported (event ids from the |
| 58 | * NotificationCancelledEvents enum). |
| 59 | * @param r The NotificationRecord. If null, no action is taken. |
| 60 | * @param reason The reason the notification was canceled. |
| 61 | * @param dismissalSurface The surface the notification was dismissed from. |
| 62 | */ |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 63 | default void logNotificationCancelled(@Nullable NotificationRecord r, |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 64 | @NotificationListenerService.NotificationCancelReason int reason, |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 65 | @NotificationStats.DismissalSurface int dismissalSurface) { |
| 66 | log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r); |
| 67 | } |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 68 | |
| 69 | /** |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 70 | * Logs a notification visibility change event using UiEventReported (event ids from the |
| 71 | * NotificationEvents enum). |
| 72 | * @param r The NotificationRecord. If null, no action is taken. |
| 73 | * @param visible True if the notification became visible. |
| 74 | */ |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 75 | default void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible) { |
| 76 | log(NotificationEvent.fromVisibility(visible), r); |
| 77 | } |
| 78 | |
| 79 | // The UiEventReported logging methods are implemented in terms of this lower-level interface. |
| 80 | |
| 81 | /** Logs a UiEventReported event for the given notification. */ |
| 82 | void log(UiEventLogger.UiEventEnum event, NotificationRecord r); |
| 83 | |
| 84 | /** Logs a UiEventReported event that is not associated with any notification. */ |
| 85 | void log(UiEventLogger.UiEventEnum event); |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 86 | |
| 87 | /** |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 88 | * The UiEvent enums that this class can log. |
| 89 | */ |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 90 | enum NotificationReportedEvent implements UiEventLogger.UiEventEnum { |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 91 | @UiEvent(doc = "New notification enqueued to post") |
| 92 | NOTIFICATION_POSTED(162), |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 93 | @UiEvent(doc = "Notification substantially updated, or alerted again.") |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 94 | NOTIFICATION_UPDATED(163); |
| 95 | |
| 96 | private final int mId; |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 97 | NotificationReportedEvent(int id) { |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 98 | mId = id; |
| 99 | } |
| 100 | @Override public int getId() { |
| 101 | return mId; |
| 102 | } |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 103 | |
| 104 | public static NotificationReportedEvent fromRecordPair(NotificationRecordPair p) { |
| 105 | return (p.old != null) ? NotificationReportedEvent.NOTIFICATION_UPDATED : |
| 106 | NotificationReportedEvent.NOTIFICATION_POSTED; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | enum NotificationCancelledEvent implements UiEventLogger.UiEventEnum { |
| 111 | INVALID(0), |
| 112 | @UiEvent(doc = "Notification was canceled due to a notification click.") |
| 113 | NOTIFICATION_CANCEL_CLICK(164), |
| 114 | @UiEvent(doc = "Notification was canceled due to a user dismissal, surface not specified.") |
| 115 | NOTIFICATION_CANCEL_USER_OTHER(165), |
| 116 | @UiEvent(doc = "Notification was canceled due to a user dismiss-all (from the notification" |
| 117 | + " shade).") |
| 118 | NOTIFICATION_CANCEL_USER_CANCEL_ALL(166), |
| 119 | @UiEvent(doc = "Notification was canceled due to an inflation error.") |
| 120 | NOTIFICATION_CANCEL_ERROR(167), |
| 121 | @UiEvent(doc = "Notification was canceled by the package manager modifying the package.") |
| 122 | NOTIFICATION_CANCEL_PACKAGE_CHANGED(168), |
| 123 | @UiEvent(doc = "Notification was canceled by the owning user context being stopped.") |
| 124 | NOTIFICATION_CANCEL_USER_STOPPED(169), |
| 125 | @UiEvent(doc = "Notification was canceled by the user banning the package.") |
| 126 | NOTIFICATION_CANCEL_PACKAGE_BANNED(170), |
| 127 | @UiEvent(doc = "Notification was canceled by the app canceling this specific notification.") |
| 128 | NOTIFICATION_CANCEL_APP_CANCEL(171), |
| 129 | @UiEvent(doc = "Notification was canceled by the app cancelling all its notifications.") |
| 130 | NOTIFICATION_CANCEL_APP_CANCEL_ALL(172), |
| 131 | @UiEvent(doc = "Notification was canceled by a listener reporting a user dismissal.") |
| 132 | NOTIFICATION_CANCEL_LISTENER_CANCEL(173), |
| 133 | @UiEvent(doc = "Notification was canceled by a listener reporting a user dismiss all.") |
| 134 | NOTIFICATION_CANCEL_LISTENER_CANCEL_ALL(174), |
| 135 | @UiEvent(doc = "Notification was canceled because it was a member of a canceled group.") |
| 136 | NOTIFICATION_CANCEL_GROUP_SUMMARY_CANCELED(175), |
| 137 | @UiEvent(doc = "Notification was canceled because it was an invisible member of a group.") |
| 138 | NOTIFICATION_CANCEL_GROUP_OPTIMIZATION(176), |
| 139 | @UiEvent(doc = "Notification was canceled by the device administrator suspending the " |
| 140 | + "package.") |
| 141 | NOTIFICATION_CANCEL_PACKAGE_SUSPENDED(177), |
| 142 | @UiEvent(doc = "Notification was canceled by the owning managed profile being turned off.") |
| 143 | NOTIFICATION_CANCEL_PROFILE_TURNED_OFF(178), |
| 144 | @UiEvent(doc = "Autobundled summary notification was canceled because its group was " |
| 145 | + "unbundled") |
| 146 | NOTIFICATION_CANCEL_UNAUTOBUNDLED(179), |
| 147 | @UiEvent(doc = "Notification was canceled by the user banning the channel.") |
| 148 | NOTIFICATION_CANCEL_CHANNEL_BANNED(180), |
| 149 | @UiEvent(doc = "Notification was snoozed.") |
| 150 | NOTIFICATION_CANCEL_SNOOZED(181), |
| 151 | @UiEvent(doc = "Notification was canceled due to timeout") |
| 152 | NOTIFICATION_CANCEL_TIMEOUT(182), |
| 153 | // Values 183-189 reserved for future system dismissal reasons |
| 154 | @UiEvent(doc = "Notification was canceled due to user dismissal of a peeking notification.") |
| 155 | NOTIFICATION_CANCEL_USER_PEEK(190), |
| 156 | @UiEvent(doc = "Notification was canceled due to user dismissal from the always-on display") |
| 157 | NOTIFICATION_CANCEL_USER_AOD(191), |
| 158 | @UiEvent(doc = "Notification was canceled due to user dismissal from the notification" |
| 159 | + " shade.") |
| 160 | NOTIFICATION_CANCEL_USER_SHADE(192), |
| 161 | @UiEvent(doc = "Notification was canceled due to user dismissal from the lockscreen") |
| 162 | NOTIFICATION_CANCEL_USER_LOCKSCREEN(193); |
| 163 | |
| 164 | private final int mId; |
| 165 | NotificationCancelledEvent(int id) { |
| 166 | mId = id; |
| 167 | } |
| 168 | @Override public int getId() { |
| 169 | return mId; |
| 170 | } |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 171 | |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 172 | public static NotificationCancelledEvent fromCancelReason( |
| 173 | @NotificationListenerService.NotificationCancelReason int reason, |
| 174 | @NotificationStats.DismissalSurface int surface) { |
| 175 | // Shouldn't be possible to get a non-dismissed notification here. |
| 176 | if (surface == NotificationStats.DISMISSAL_NOT_DISMISSED) { |
| 177 | if (NotificationManagerService.DBG) { |
| 178 | throw new IllegalArgumentException("Unexpected surface " + surface); |
| 179 | } |
| 180 | return INVALID; |
| 181 | } |
| 182 | // Most cancel reasons do not have a meaningful surface. Reason codes map directly |
| 183 | // to NotificationCancelledEvent codes. |
| 184 | if (surface == NotificationStats.DISMISSAL_OTHER) { |
| 185 | if ((REASON_CLICK <= reason) && (reason <= REASON_TIMEOUT)) { |
| 186 | return NotificationCancelledEvent.values()[reason]; |
| 187 | } |
| 188 | if (NotificationManagerService.DBG) { |
| 189 | throw new IllegalArgumentException("Unexpected cancel reason " + reason); |
| 190 | } |
| 191 | return INVALID; |
| 192 | } |
| 193 | // User cancels have a meaningful surface, which we differentiate by. See b/149038335 |
| 194 | // for caveats. |
| 195 | if (reason != REASON_CANCEL) { |
| 196 | if (NotificationManagerService.DBG) { |
| 197 | throw new IllegalArgumentException("Unexpected cancel with surface " + reason); |
| 198 | } |
| 199 | return INVALID; |
| 200 | } |
| 201 | switch (surface) { |
| 202 | case NotificationStats.DISMISSAL_PEEK: |
| 203 | return NOTIFICATION_CANCEL_USER_PEEK; |
| 204 | case NotificationStats.DISMISSAL_AOD: |
| 205 | return NOTIFICATION_CANCEL_USER_AOD; |
| 206 | case NotificationStats.DISMISSAL_SHADE: |
| 207 | return NOTIFICATION_CANCEL_USER_SHADE; |
| 208 | default: |
| 209 | if (NotificationManagerService.DBG) { |
| 210 | throw new IllegalArgumentException("Unexpected surface for user-dismiss " |
| 211 | + reason); |
| 212 | } |
| 213 | return INVALID; |
| 214 | } |
| 215 | } |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 216 | } |
| 217 | |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 218 | enum NotificationEvent implements UiEventLogger.UiEventEnum { |
| 219 | @UiEvent(doc = "Notification became visible.") |
| 220 | NOTIFICATION_OPEN(197), |
| 221 | @UiEvent(doc = "Notification stopped being visible.") |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 222 | NOTIFICATION_CLOSE(198), |
| 223 | @UiEvent(doc = "Notification was snoozed.") |
| 224 | NOTIFICATION_SNOOZED(317), |
| 225 | @UiEvent(doc = "Notification was not posted because its app is snoozed.") |
| 226 | NOTIFICATION_NOT_POSTED_SNOOZED(319), |
| 227 | @UiEvent(doc = "Notification was clicked.") |
| 228 | NOTIFICATION_CLICKED(320), |
| 229 | @UiEvent(doc = "Notification action was clicked.") |
| 230 | NOTIFICATION_ACTION_CLICKED(321), |
| 231 | @UiEvent(doc = "Notification detail was expanded due to non-user action.") |
| 232 | NOTIFICATION_DETAIL_OPEN_SYSTEM(327), |
| 233 | @UiEvent(doc = "Notification detail was collapsed due to non-user action.") |
| 234 | NOTIFICATION_DETAIL_CLOSE_SYSTEM(328), |
| 235 | @UiEvent(doc = "Notification detail was expanded due to user action.") |
| 236 | NOTIFICATION_DETAIL_OPEN_USER(329), |
| 237 | @UiEvent(doc = "Notification detail was collapsed due to user action.") |
| 238 | NOTIFICATION_DETAIL_CLOSE_USER(330), |
| 239 | @UiEvent(doc = "Notification direct reply action was used.") |
| 240 | NOTIFICATION_DIRECT_REPLIED(331), |
| 241 | @UiEvent(doc = "Notification smart reply action was used.") |
| 242 | NOTIFICATION_SMART_REPLIED(332), |
| 243 | @UiEvent(doc = "Notification smart reply action was visible.") |
| 244 | NOTIFICATION_SMART_REPLY_VISIBLE(333), |
| 245 | ; |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 246 | |
| 247 | private final int mId; |
| 248 | NotificationEvent(int id) { |
| 249 | mId = id; |
| 250 | } |
| 251 | @Override public int getId() { |
| 252 | return mId; |
| 253 | } |
| 254 | |
| 255 | public static NotificationEvent fromVisibility(boolean visible) { |
| 256 | return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE; |
| 257 | } |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 258 | public static NotificationEvent fromExpanded(boolean expanded, boolean userAction) { |
| 259 | if (userAction) { |
| 260 | return expanded ? NOTIFICATION_DETAIL_OPEN_USER : NOTIFICATION_DETAIL_CLOSE_USER; |
| 261 | } |
| 262 | return expanded ? NOTIFICATION_DETAIL_OPEN_SYSTEM : NOTIFICATION_DETAIL_CLOSE_SYSTEM; |
| 263 | } |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 264 | } |
Will Brockman | 9918db9 | 2020-03-06 16:48:39 -0500 | [diff] [blame] | 265 | |
| 266 | enum NotificationPanelEvent implements UiEventLogger.UiEventEnum { |
| 267 | @UiEvent(doc = "Notification panel became visible.") |
| 268 | NOTIFICATION_PANEL_OPEN(325), |
| 269 | @UiEvent(doc = "Notification panel stopped being visible.") |
| 270 | NOTIFICATION_PANEL_CLOSE(326); |
| 271 | |
| 272 | private final int mId; |
| 273 | NotificationPanelEvent(int id) { |
| 274 | mId = id; |
| 275 | } |
| 276 | @Override public int getId() { |
| 277 | return mId; |
| 278 | } |
| 279 | } |
| 280 | |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 281 | /** |
| 282 | * A helper for extracting logging information from one or two NotificationRecords. |
| 283 | */ |
| 284 | class NotificationRecordPair { |
| 285 | public final NotificationRecord r, old; |
| 286 | /** |
| 287 | * Construct from one or two NotificationRecords. |
| 288 | * @param r The new NotificationRecord. If null, only shouldLog() method is usable. |
| 289 | * @param old The previous NotificationRecord. Null if there was no previous record. |
| 290 | */ |
| 291 | NotificationRecordPair(@Nullable NotificationRecord r, @Nullable NotificationRecord old) { |
| 292 | this.r = r; |
| 293 | this.old = old; |
| 294 | } |
| 295 | |
| 296 | /** |
| 297 | * @return True if old is null, alerted, or important logged fields have changed. |
| 298 | */ |
Will Brockman | d3d4933 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 299 | boolean shouldLogReported(int buzzBeepBlink) { |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 300 | if (r == null) { |
| 301 | return false; |
| 302 | } |
| 303 | if ((old == null) || (buzzBeepBlink > 0)) { |
| 304 | return true; |
| 305 | } |
| 306 | |
Will Brockman | 75c6057 | 2020-01-31 10:30:27 -0500 | [diff] [blame] | 307 | return !(Objects.equals(r.getSbn().getChannelIdLogTag(), |
| 308 | old.getSbn().getChannelIdLogTag()) |
Julia Reynolds | 24edc00 | 2020-01-29 16:35:32 -0500 | [diff] [blame] | 309 | && Objects.equals(r.getSbn().getGroupLogTag(), old.getSbn().getGroupLogTag()) |
| 310 | && (r.getSbn().getNotification().isGroupSummary() |
| 311 | == old.getSbn().getNotification().isGroupSummary()) |
| 312 | && Objects.equals(r.getSbn().getNotification().category, |
| 313 | old.getSbn().getNotification().category) |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 314 | && (r.getImportance() == old.getImportance())); |
| 315 | } |
| 316 | |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 317 | /** |
| 318 | * @return hash code for the notification style class, or 0 if none exists. |
| 319 | */ |
| 320 | public int getStyle() { |
Julia Reynolds | 24edc00 | 2020-01-29 16:35:32 -0500 | [diff] [blame] | 321 | return getStyle(r.getSbn().getNotification().extras); |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 322 | } |
| 323 | |
| 324 | private int getStyle(@Nullable Bundle extras) { |
| 325 | if (extras != null) { |
| 326 | String template = extras.getString(Notification.EXTRA_TEMPLATE); |
| 327 | if (template != null && !template.isEmpty()) { |
| 328 | return template.hashCode(); |
| 329 | } |
| 330 | } |
| 331 | return 0; |
| 332 | } |
| 333 | |
| 334 | int getNumPeople() { |
Julia Reynolds | 24edc00 | 2020-01-29 16:35:32 -0500 | [diff] [blame] | 335 | return getNumPeople(r.getSbn().getNotification().extras); |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 336 | } |
| 337 | |
| 338 | private int getNumPeople(@Nullable Bundle extras) { |
| 339 | if (extras != null) { |
| 340 | ArrayList<Person> people = extras.getParcelableArrayList( |
| 341 | Notification.EXTRA_PEOPLE_LIST); |
| 342 | if (people != null && !people.isEmpty()) { |
| 343 | return people.size(); |
| 344 | } |
| 345 | } |
| 346 | return 0; |
| 347 | } |
| 348 | |
| 349 | int getAssistantHash() { |
| 350 | String assistant = r.getAdjustmentIssuer(); |
| 351 | return (assistant == null) ? 0 : assistant.hashCode(); |
| 352 | } |
Will Brockman | cfd9830 | 2020-01-29 15:57:30 -0500 | [diff] [blame] | 353 | |
| 354 | int getInstanceId() { |
Julia Reynolds | 24edc00 | 2020-01-29 16:35:32 -0500 | [diff] [blame] | 355 | return (r.getSbn().getInstanceId() == null ? 0 : r.getSbn().getInstanceId().getId()); |
Will Brockman | cfd9830 | 2020-01-29 15:57:30 -0500 | [diff] [blame] | 356 | } |
Will Brockman | 7614f696 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 357 | |
| 358 | /** |
| 359 | * @return Small hash of the notification ID, and tag (if present). |
| 360 | */ |
| 361 | int getNotificationIdHash() { |
Will Brockman | a83de9e | 2020-03-26 13:42:18 -0400 | [diff] [blame^] | 362 | return SmallHash.hash(Objects.hashCode(r.getSbn().getTag()) ^ r.getSbn().getId()); |
Will Brockman | 7614f696 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 363 | } |
| 364 | |
| 365 | /** |
| 366 | * @return Small hash of the channel ID, if present, or 0 otherwise. |
| 367 | */ |
| 368 | int getChannelIdHash() { |
Will Brockman | a83de9e | 2020-03-26 13:42:18 -0400 | [diff] [blame^] | 369 | return SmallHash.hash(r.getSbn().getNotification().getChannelId()); |
Will Brockman | 7614f696 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 370 | } |
| 371 | |
| 372 | /** |
| 373 | * @return Small hash of the group ID, respecting group override if present. 0 otherwise. |
| 374 | */ |
| 375 | int getGroupIdHash() { |
Will Brockman | a83de9e | 2020-03-26 13:42:18 -0400 | [diff] [blame^] | 376 | return SmallHash.hash(r.getSbn().getGroup()); |
Will Brockman | 7614f696 | 2020-02-10 19:43:03 -0500 | [diff] [blame] | 377 | } |
| 378 | |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 379 | } |
Will Brockman | 23db6d4 | 2020-02-28 09:51:12 -0500 | [diff] [blame] | 380 | |
Will Brockman | 23db6d4 | 2020-02-28 09:51:12 -0500 | [diff] [blame] | 381 | |
Will Brockman | 2b6959e | 2020-01-22 09:59:50 -0500 | [diff] [blame] | 382 | } |