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