NotificationRecord.java revision 3ad4e3a45bbe44129b14c4d391431e44f1e04f0c
1/* 2 * Copyright (C) 2014 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 */ 16package com.android.server.notification; 17 18import android.app.Notification; 19import android.content.Context; 20import android.content.pm.PackageManager.NameNotFoundException; 21import android.content.res.Resources; 22import android.graphics.Bitmap; 23import android.media.AudioAttributes; 24import android.service.notification.StatusBarNotification; 25 26import com.android.internal.annotations.VisibleForTesting; 27 28import java.io.PrintWriter; 29import java.lang.reflect.Array; 30import java.util.Arrays; 31import java.util.Objects; 32 33/** 34 * Holds data about notifications that should not be shared with the 35 * {@link android.service.notification.NotificationListenerService}s. 36 * 37 * <p>These objects should not be mutated unless the code is synchronized 38 * on {@link NotificationManagerService#mNotificationList}, and any 39 * modification should be followed by a sorting of that list.</p> 40 * 41 * <p>Is sortable by {@link NotificationComparator}.</p> 42 * 43 * {@hide} 44 */ 45public final class NotificationRecord { 46 final StatusBarNotification sbn; 47 NotificationUsageStats.SingleNotificationStats stats; 48 boolean isCanceled; 49 int score; 50 51 // These members are used by NotificationSignalExtractors 52 // to communicate with the ranking module. 53 private float mContactAffinity; 54 private boolean mRecentlyIntrusive; 55 56 // is this notification currently being intercepted by Zen Mode? 57 private boolean mIntercept; 58 59 // The timestamp used for ranking. 60 private long mRankingTimeMs; 61 62 // Is this record an update of an old record? 63 public boolean isUpdate; 64 private int mPackagePriority; 65 66 private int mAuthoritativeRank; 67 private String mGlobalSortKey; 68 private int mPackageVisibility; 69 70 @VisibleForTesting 71 public NotificationRecord(StatusBarNotification sbn, int score) 72 { 73 this.sbn = sbn; 74 this.score = score; 75 mRankingTimeMs = calculateRankingTimeMs(0L); 76 } 77 78 // copy any notes that the ranking system may have made before the update 79 public void copyRankingInformation(NotificationRecord previous) { 80 mContactAffinity = previous.mContactAffinity; 81 mRecentlyIntrusive = previous.mRecentlyIntrusive; 82 mPackagePriority = previous.mPackagePriority; 83 mPackageVisibility = previous.mPackageVisibility; 84 mIntercept = previous.mIntercept; 85 mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs()); 86 // Don't copy mGlobalSortKey, recompute it. 87 } 88 89 public Notification getNotification() { return sbn.getNotification(); } 90 public int getFlags() { return sbn.getNotification().flags; } 91 public int getUserId() { return sbn.getUserId(); } 92 public String getKey() { return sbn.getKey(); } 93 94 void dump(PrintWriter pw, String prefix, Context baseContext) { 95 final Notification notification = sbn.getNotification(); 96 pw.println(prefix + this); 97 pw.println(prefix + " uid=" + sbn.getUid() + " userId=" + sbn.getUserId()); 98 pw.println(prefix + " icon=0x" + Integer.toHexString(notification.icon) 99 + " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon)); 100 pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore()); 101 pw.println(prefix + " key=" + sbn.getKey()); 102 pw.println(prefix + " groupKey=" + getGroupKey()); 103 pw.println(prefix + " contentIntent=" + notification.contentIntent); 104 pw.println(prefix + " deleteIntent=" + notification.deleteIntent); 105 pw.println(prefix + " tickerText=" + notification.tickerText); 106 pw.println(prefix + " contentView=" + notification.contentView); 107 pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x", 108 notification.defaults, notification.flags)); 109 pw.println(prefix + " sound=" + notification.sound); 110 pw.println(prefix + " audioStreamType=" + notification.audioStreamType); 111 pw.println(prefix + " audioAttributes=" + notification.audioAttributes); 112 pw.println(prefix + String.format(" color=0x%08x", notification.color)); 113 pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); 114 pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", 115 notification.ledARGB, notification.ledOnMS, notification.ledOffMS)); 116 if (notification.actions != null && notification.actions.length > 0) { 117 pw.println(prefix + " actions={"); 118 final int N = notification.actions.length; 119 for (int i=0; i<N; i++) { 120 final Notification.Action action = notification.actions[i]; 121 pw.println(String.format("%s [%d] \"%s\" -> %s", 122 prefix, 123 i, 124 action.title, 125 action.actionIntent.toString() 126 )); 127 } 128 pw.println(prefix + " }"); 129 } 130 if (notification.extras != null && notification.extras.size() > 0) { 131 pw.println(prefix + " extras={"); 132 for (String key : notification.extras.keySet()) { 133 pw.print(prefix + " " + key + "="); 134 Object val = notification.extras.get(key); 135 if (val == null) { 136 pw.println("null"); 137 } else { 138 pw.print(val.getClass().getSimpleName()); 139 if (val instanceof CharSequence || val instanceof String) { 140 // redact contents from bugreports 141 } else if (val instanceof Bitmap) { 142 pw.print(String.format(" (%dx%d)", 143 ((Bitmap) val).getWidth(), 144 ((Bitmap) val).getHeight())); 145 } else if (val.getClass().isArray()) { 146 final int N = Array.getLength(val); 147 pw.println(" (" + N + ")"); 148 } else { 149 pw.print(" (" + String.valueOf(val) + ")"); 150 } 151 pw.println(); 152 } 153 } 154 pw.println(prefix + " }"); 155 } 156 pw.println(prefix + " stats=" + stats.toString()); 157 pw.println(prefix + " mContactAffinity=" + mContactAffinity); 158 pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive); 159 pw.println(prefix + " mPackagePriority=" + mPackagePriority); 160 pw.println(prefix + " mPackageVisibility=" + mPackageVisibility); 161 pw.println(prefix + " mIntercept=" + mIntercept); 162 pw.println(prefix + " mGlobalSortKey=" + mGlobalSortKey); 163 pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs); 164 } 165 166 167 static String idDebugString(Context baseContext, String packageName, int id) { 168 Context c; 169 170 if (packageName != null) { 171 try { 172 c = baseContext.createPackageContext(packageName, 0); 173 } catch (NameNotFoundException e) { 174 c = baseContext; 175 } 176 } else { 177 c = baseContext; 178 } 179 180 Resources r = c.getResources(); 181 try { 182 return r.getResourceName(id); 183 } catch (Resources.NotFoundException e) { 184 return "<name unknown>"; 185 } 186 } 187 188 @Override 189 public final String toString() { 190 return String.format( 191 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)", 192 System.identityHashCode(this), 193 this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), 194 this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(), 195 this.sbn.getNotification()); 196 } 197 198 public void setContactAffinity(float contactAffinity) { 199 mContactAffinity = contactAffinity; 200 } 201 202 public float getContactAffinity() { 203 return mContactAffinity; 204 } 205 206 public void setRecentlyIntusive(boolean recentlyIntrusive) { 207 mRecentlyIntrusive = recentlyIntrusive; 208 } 209 210 public boolean isRecentlyIntrusive() { 211 return mRecentlyIntrusive; 212 } 213 214 public void setPackagePriority(int packagePriority) { 215 mPackagePriority = packagePriority; 216 } 217 218 public int getPackagePriority() { 219 return mPackagePriority; 220 } 221 222 public void setPackageVisibilityOverride(int packageVisibility) { 223 mPackageVisibility = packageVisibility; 224 } 225 226 public int getPackageVisibilityOverride() { 227 return mPackageVisibility; 228 } 229 230 public boolean setIntercepted(boolean intercept) { 231 mIntercept = intercept; 232 return mIntercept; 233 } 234 235 public boolean isIntercepted() { 236 return mIntercept; 237 } 238 239 public boolean isCategory(String category) { 240 return Objects.equals(getNotification().category, category); 241 } 242 243 public boolean isAudioStream(int stream) { 244 return getNotification().audioStreamType == stream; 245 } 246 247 public boolean isAudioAttributesUsage(int usage) { 248 final AudioAttributes attributes = getNotification().audioAttributes; 249 return attributes != null && attributes.getUsage() == usage; 250 } 251 252 /** 253 * Returns the timestamp to use for time-based sorting in the ranker. 254 */ 255 public long getRankingTimeMs() { 256 return mRankingTimeMs; 257 } 258 259 /** 260 * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()} 261 * of the previous notification record, 0 otherwise 262 */ 263 private long calculateRankingTimeMs(long previousRankingTimeMs) { 264 Notification n = getNotification(); 265 // Take developer provided 'when', unless it's in the future. 266 if (n.when != 0 && n.when <= sbn.getPostTime()) { 267 return n.when; 268 } 269 // If we've ranked a previous instance with a timestamp, inherit it. This case is 270 // important in order to have ranking stability for updating notifications. 271 if (previousRankingTimeMs > 0) { 272 return previousRankingTimeMs; 273 } 274 return sbn.getPostTime(); 275 } 276 277 public void setGlobalSortKey(String globalSortKey) { 278 mGlobalSortKey = globalSortKey; 279 } 280 281 public String getGlobalSortKey() { 282 return mGlobalSortKey; 283 } 284 285 public void setAuthoritativeRank(int authoritativeRank) { 286 mAuthoritativeRank = authoritativeRank; 287 } 288 289 public int getAuthoritativeRank() { 290 return mAuthoritativeRank; 291 } 292 293 public String getGroupKey() { 294 return sbn.getGroupKey(); 295 } 296} 297