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