diff options
9 files changed, 166 insertions, 115 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java index 22cdd580b01c..d4fadcf2b319 100644 --- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java @@ -33,7 +33,7 @@ public class NotificationIntrusivenessExtractor implements NotificationSignalExt the top of the ranking order, before it falls back to its natural position. */ private static final long HANG_TIME_MS = 10000; - public void initialize(Context ctx) { + public void initialize(Context ctx, NotificationUsageStats usageStats) { if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 90e912d03e60..9c288beadb70 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -856,8 +856,10 @@ public class NotificationManagerService extends SystemService { } catch (Resources.NotFoundException e) { extractorNames = new String[0]; } + mUsageStats = new NotificationUsageStats(getContext()); mRankingHelper = new RankingHelper(getContext(), new RankingWorkerHandler(mRankingThread.getLooper()), + mUsageStats, extractorNames); mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles); mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders); @@ -882,7 +884,6 @@ public class NotificationManagerService extends SystemService { }); final File systemDir = new File(Environment.getDataDirectory(), "system"); mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); - mUsageStats = new NotificationUsageStats(getContext()); importOldBlockDb(); @@ -2071,6 +2072,7 @@ public class NotificationManagerService extends SystemService { r.score = JUNK_SCORE; Slog.e(TAG, "Suppressing notification from package " + pkg + " by user request."); + mUsageStats.registerBlocked(r); } } @@ -2736,12 +2738,6 @@ public class NotificationManagerService extends SystemService { case REASON_NOMAN_CANCEL_ALL: mUsageStats.registerRemovedByApp(r); break; - case REASON_DELEGATE_CLICK: - mUsageStats.registerCancelDueToClick(r); - break; - default: - mUsageStats.registerCancelUnknown(r); - break; } mNotificationsByKey.remove(r.sbn.getKey()); diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java index 43d05d024230..31f8b2703c0e 100644 --- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java @@ -26,7 +26,7 @@ import android.content.Context; public interface NotificationSignalExtractor { /** One-time initialization. */ - public void initialize(Context context); + public void initialize(Context context, NotificationUsageStats usageStats); /** * Called once per notification that is posted or updated. diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index 4696771e3fea..2d5c1998c1e2 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -25,13 +25,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.SystemClock; -import android.service.notification.StatusBarNotification; import android.util.Log; import com.android.internal.logging.MetricsLogger; import com.android.server.notification.NotificationManagerService.DumpFilter; import java.io.PrintWriter; +import java.util.ArrayDeque; import java.util.HashMap; import java.util.Map; @@ -47,21 +47,45 @@ import java.util.Map; * {@hide} */ public class NotificationUsageStats { - // WARNING: Aggregated stats can grow unboundedly with pkg+id+tag. - // Don't enable on production builds. - private static final boolean ENABLE_AGGREGATED_IN_MEMORY_STATS = false; - private static final boolean ENABLE_SQLITE_LOG = true; + private static final String TAG = "NotificationUsageStats"; + private static final boolean ENABLE_AGGREGATED_IN_MEMORY_STATS = true; + private static final boolean ENABLE_SQLITE_LOG = true; private static final AggregatedStats[] EMPTY_AGGREGATED_STATS = new AggregatedStats[0]; + private static final String DEVICE_GLOBAL_STATS = "__global"; // packages start with letters + private static final int MSG_EMIT = 1; + + private static final boolean DEBUG = false; + public static final int TEN_SECONDS = 1000 * 10; + public static final int ONE_HOUR = 1000 * 60 * 60; + private static final long EMIT_PERIOD = DEBUG ? TEN_SECONDS : ONE_HOUR; // Guarded by synchronized(this). - private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>(); + private final Map<String, AggregatedStats> mStats = new HashMap<>(); + private final ArrayDeque<AggregatedStats[]> mStatsArrays = new ArrayDeque<>(); private final SQLiteLog mSQLiteLog; private final Context mContext; + private final Handler mHandler; + private long mLastEmitTime; public NotificationUsageStats(Context context) { mContext = context; + mLastEmitTime = SystemClock.elapsedRealtime(); mSQLiteLog = ENABLE_SQLITE_LOG ? new SQLiteLog(context) : null; + mHandler = new Handler(mContext.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_EMIT: + emit(); + break; + default: + Log.wtf(TAG, "Unknown message type: " + msg.what); + break; + } + } + }; + mHandler.sendEmptyMessageDelayed(MSG_EMIT, EMIT_PERIOD); } /** @@ -70,9 +94,12 @@ public class NotificationUsageStats { public synchronized void registerPostedByApp(NotificationRecord notification) { notification.stats = new SingleNotificationStats(); notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime(); - for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { + + AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); + for (AggregatedStats stats : aggregatedStatsArray) { stats.numPostedByApp++; } + releaseAggregatedStatsLocked(aggregatedStatsArray); if (ENABLE_SQLITE_LOG) { mSQLiteLog.logPosted(notification); } @@ -83,9 +110,11 @@ public class NotificationUsageStats { */ public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) { notification.stats = old.stats; - for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { + AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); + for (AggregatedStats stats : aggregatedStatsArray) { stats.numUpdatedByApp++; } + releaseAggregatedStatsLocked(aggregatedStatsArray); } /** @@ -93,10 +122,11 @@ public class NotificationUsageStats { */ public synchronized void registerRemovedByApp(NotificationRecord notification) { notification.stats.onRemoved(); - for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { + AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); + for (AggregatedStats stats : aggregatedStatsArray) { stats.numRemovedByApp++; - stats.collect(notification.stats); } + releaseAggregatedStatsLocked(aggregatedStatsArray); if (ENABLE_SQLITE_LOG) { mSQLiteLog.logRemoved(notification); } @@ -109,10 +139,6 @@ public class NotificationUsageStats { MetricsLogger.histogram(mContext, "note_dismiss_longevity", (int) (System.currentTimeMillis() - notification.getRankingTimeMs()) / (60 * 1000)); notification.stats.onDismiss(); - for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { - stats.numDismissedByUser++; - stats.collect(notification.stats); - } if (ENABLE_SQLITE_LOG) { mSQLiteLog.logDismissed(notification); } @@ -125,36 +151,36 @@ public class NotificationUsageStats { MetricsLogger.histogram(mContext, "note_click_longevity", (int) (System.currentTimeMillis() - notification.getRankingTimeMs()) / (60 * 1000)); notification.stats.onClick(); - for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { - stats.numClickedByUser++; - } if (ENABLE_SQLITE_LOG) { mSQLiteLog.logClicked(notification); } } - /** - * Called when the notification is canceled because the user clicked it. - * - * <p>Called after {@link #registerClickedByUser(NotificationRecord)}.</p> - */ - public synchronized void registerCancelDueToClick(NotificationRecord notification) { - notification.stats.onCancel(); - for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { - stats.collect(notification.stats); + public synchronized void registerPeopleAffinity(NotificationRecord notification, boolean valid, + boolean starred, boolean cached) { + AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); + for (AggregatedStats stats : aggregatedStatsArray) { + if (valid) { + stats.numWithValidPeople++; + } + if (starred) { + stats.numWithStaredPeople++; + } + if (cached) { + stats.numPeopleCacheHit++; + } else { + stats.numPeopleCacheMiss++; + } } + releaseAggregatedStatsLocked(aggregatedStatsArray); } - /** - * Called when the notification is canceled due to unknown reasons. - * - * <p>Called for notifications of apps being uninstalled, for example.</p> - */ - public synchronized void registerCancelUnknown(NotificationRecord notification) { - notification.stats.onCancel(); - for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { - stats.collect(notification.stats); + public synchronized void registerBlocked(NotificationRecord notification) { + AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); + for (AggregatedStats stats : aggregatedStatsArray) { + stats.numBlocked++; } + releaseAggregatedStatsLocked(aggregatedStatsArray); } // Locked by this. @@ -163,24 +189,28 @@ public class NotificationUsageStats { return EMPTY_AGGREGATED_STATS; } - StatusBarNotification n = record.sbn; - - String user = String.valueOf(n.getUserId()); - String userPackage = user + ":" + n.getPackageName(); + // TODO: expand to package-level counts in the future. + AggregatedStats[] array = mStatsArrays.poll(); + if (array == null) { + array = new AggregatedStats[1]; + } + array[0] = getOrCreateAggregatedStatsLocked(DEVICE_GLOBAL_STATS); + return array; + } - // TODO: Use pool of arrays. - return new AggregatedStats[] { - getOrCreateAggregatedStatsLocked(user), - getOrCreateAggregatedStatsLocked(userPackage), - getOrCreateAggregatedStatsLocked(n.getKey()), - }; + // Locked by this. + private void releaseAggregatedStatsLocked(AggregatedStats[] array) { + for(int i = 0; i < array.length; i++) { + array[i] = null; + } + mStatsArrays.offer(array); } // Locked by this. private AggregatedStats getOrCreateAggregatedStatsLocked(String key) { AggregatedStats result = mStats.get(key); if (result == null) { - result = new AggregatedStats(key); + result = new AggregatedStats(mContext, key); mStats.put(key, result); } return result; @@ -193,64 +223,74 @@ public class NotificationUsageStats { continue; as.dump(pw, indent); } + pw.println(indent + "mStatsArrays.size(): " + mStatsArrays.size()); } if (ENABLE_SQLITE_LOG) { mSQLiteLog.dump(pw, indent, filter); } } + public synchronized void emit() { + // TODO: expand to package-level counts in the future. + AggregatedStats stats = getOrCreateAggregatedStatsLocked(DEVICE_GLOBAL_STATS); + stats.emit(); + mLastEmitTime = SystemClock.elapsedRealtime(); + mHandler.removeMessages(MSG_EMIT); + mHandler.sendEmptyMessageDelayed(MSG_EMIT, EMIT_PERIOD); + } + /** * Aggregated notification stats. */ private static class AggregatedStats { + + private final Context mContext; public final String key; // ---- Updated as the respective events occur. public int numPostedByApp; public int numUpdatedByApp; public int numRemovedByApp; - public int numClickedByUser; - public int numDismissedByUser; - - // ---- Updated when a notification is canceled. - public final Aggregate posttimeMs = new Aggregate(); - public final Aggregate posttimeToDismissMs = new Aggregate(); - public final Aggregate posttimeToFirstClickMs = new Aggregate(); - public final Aggregate airtimeCount = new Aggregate(); - public final Aggregate airtimeMs = new Aggregate(); - public final Aggregate posttimeToFirstAirtimeMs = new Aggregate(); - public final Aggregate userExpansionCount = new Aggregate(); - public final Aggregate airtimeExpandedMs = new Aggregate(); - public final Aggregate posttimeToFirstVisibleExpansionMs = new Aggregate(); - - public AggregatedStats(String key) { + public int numPeopleCacheHit; + public int numPeopleCacheMiss;; + public int numWithStaredPeople; + public int numWithValidPeople; + public int numBlocked; + + private AggregatedStats mPrevious; + + public AggregatedStats(Context context, String key) { this.key = key; + mContext = context; } - public void collect(SingleNotificationStats singleNotificationStats) { - posttimeMs.addSample( - SystemClock.elapsedRealtime() - singleNotificationStats.posttimeElapsedMs); - if (singleNotificationStats.posttimeToDismissMs >= 0) { - posttimeToDismissMs.addSample(singleNotificationStats.posttimeToDismissMs); - } - if (singleNotificationStats.posttimeToFirstClickMs >= 0) { - posttimeToFirstClickMs.addSample(singleNotificationStats.posttimeToFirstClickMs); + public void emit() { + if (mPrevious == null) { + mPrevious = new AggregatedStats(null, key); } - airtimeCount.addSample(singleNotificationStats.airtimeCount); - if (singleNotificationStats.airtimeMs >= 0) { - airtimeMs.addSample(singleNotificationStats.airtimeMs); - } - if (singleNotificationStats.posttimeToFirstAirtimeMs >= 0) { - posttimeToFirstAirtimeMs.addSample( - singleNotificationStats.posttimeToFirstAirtimeMs); - } - if (singleNotificationStats.posttimeToFirstVisibleExpansionMs >= 0) { - posttimeToFirstVisibleExpansionMs.addSample( - singleNotificationStats.posttimeToFirstVisibleExpansionMs); - } - userExpansionCount.addSample(singleNotificationStats.userExpansionCount); - if (singleNotificationStats.airtimeExpandedMs >= 0) { - airtimeExpandedMs.addSample(singleNotificationStats.airtimeExpandedMs); + + maybeCount("note_post", (numPostedByApp - mPrevious.numPostedByApp)); + maybeCount("note_update", (numUpdatedByApp - mPrevious.numUpdatedByApp)); + maybeCount("note_remove", (numRemovedByApp - mPrevious.numRemovedByApp)); + maybeCount("note_with_people", (numWithValidPeople - mPrevious.numWithValidPeople)); + maybeCount("note_with_stars", (numWithStaredPeople - mPrevious.numWithStaredPeople)); + maybeCount("people_cache_hit", (numPeopleCacheHit - mPrevious.numPeopleCacheHit)); + maybeCount("people_cache_miss", (numPeopleCacheMiss - mPrevious.numPeopleCacheMiss)); + maybeCount("note_blocked", (numBlocked - mPrevious.numBlocked)); + + mPrevious.numPostedByApp = numPostedByApp; + mPrevious.numUpdatedByApp = numUpdatedByApp; + mPrevious.numRemovedByApp = numRemovedByApp; + mPrevious.numPeopleCacheHit = numPeopleCacheHit; + mPrevious.numPeopleCacheMiss = numPeopleCacheMiss; + mPrevious.numWithStaredPeople = numWithStaredPeople; + mPrevious.numWithValidPeople = numWithValidPeople; + mPrevious.numBlocked = numBlocked; + } + + void maybeCount(String name, int value) { + if (value > 0) { + MetricsLogger.count(mContext, name, value); } } @@ -269,17 +309,11 @@ public class NotificationUsageStats { indent + " numPostedByApp=" + numPostedByApp + ",\n" + indent + " numUpdatedByApp=" + numUpdatedByApp + ",\n" + indent + " numRemovedByApp=" + numRemovedByApp + ",\n" + - indent + " numClickedByUser=" + numClickedByUser + ",\n" + - indent + " numDismissedByUser=" + numDismissedByUser + ",\n" + - indent + " posttimeMs=" + posttimeMs + ",\n" + - indent + " posttimeToDismissMs=" + posttimeToDismissMs + ",\n" + - indent + " posttimeToFirstClickMs=" + posttimeToFirstClickMs + ",\n" + - indent + " airtimeCount=" + airtimeCount + ",\n" + - indent + " airtimeMs=" + airtimeMs + ",\n" + - indent + " posttimeToFirstAirtimeMs=" + posttimeToFirstAirtimeMs + ",\n" + - indent + " userExpansionCount=" + userExpansionCount + ",\n" + - indent + " airtimeExpandedMs=" + airtimeExpandedMs + ",\n" + - indent + " posttimeToFVEMs=" + posttimeToFirstVisibleExpansionMs + ",\n" + + indent + " numPeopleCacheHit=" + numPeopleCacheHit + ",\n" + + indent + " numWithStaredPeople=" + numWithStaredPeople + ",\n" + + indent + " numWithValidPeople=" + numWithValidPeople + ",\n" + + indent + " numPeopleCacheMiss=" + numPeopleCacheMiss + ",\n" + + indent + " numBlocked=" + numBlocked + ",\n" + indent + "}"; } } diff --git a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java b/services/core/java/com/android/server/notification/PackagePriorityExtractor.java index a13e54abbe21..6beed9c3614e 100644 --- a/services/core/java/com/android/server/notification/PackagePriorityExtractor.java +++ b/services/core/java/com/android/server/notification/PackagePriorityExtractor.java @@ -24,7 +24,7 @@ public class PackagePriorityExtractor implements NotificationSignalExtractor { private RankingConfig mConfig; - public void initialize(Context ctx) { + public void initialize(Context ctx, NotificationUsageStats usageStats) { if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); } diff --git a/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java b/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java index f720f9f3fb45..af99db7688e5 100644 --- a/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java +++ b/services/core/java/com/android/server/notification/PackageVisibilityExtractor.java @@ -24,7 +24,7 @@ public class PackageVisibilityExtractor implements NotificationSignalExtractor { private RankingConfig mConfig; - public void initialize(Context ctx) { + public void initialize(Context ctx, NotificationUsageStats usageStats) { if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); } diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index e503ac856a5f..a089518c53b8 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -68,7 +68,8 @@ public class RankingHelper implements RankingConfig { private final Context mContext; private final Handler mRankingHandler; - public RankingHelper(Context context, Handler rankingHandler, String[] extractorNames) { + public RankingHelper(Context context, Handler rankingHandler, NotificationUsageStats usageStats, + String[] extractorNames) { mContext = context; mRankingHandler = rankingHandler; @@ -79,7 +80,7 @@ public class RankingHelper implements RankingConfig { Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]); NotificationSignalExtractor extractor = (NotificationSignalExtractor) extractorClass.newInstance(); - extractor.initialize(mContext); + extractor.initialize(mContext, usageStats); extractor.setConfig(this); mSignalExtractors[i] = extractor; } catch (ClassNotFoundException e) { diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index 4af7d4ea9452..04202696f78b 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -34,7 +34,6 @@ import android.util.ArrayMap; import android.util.Log; import android.util.LruCache; import android.util.Slog; -import com.android.internal.logging.MetricsLogger; import java.util.ArrayList; import java.util.LinkedList; @@ -85,11 +84,13 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private Handler mHandler; private ContentObserver mObserver; private int mEvictionCount; + private NotificationUsageStats mUsageStats; - public void initialize(Context context) { + public void initialize(Context context, NotificationUsageStats usageStats) { if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); mUserToContextMap = new ArrayMap<>(); mBaseContext = context; + mUsageStats = usageStats; mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt( mBaseContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1); @@ -203,8 +204,15 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { final String key = record.getKey(); final Bundle extras = record.getNotification().extras; final float[] affinityOut = new float[1]; - final RankingReconsideration rr = validatePeople(context, key, extras, affinityOut); - record.setContactAffinity(affinityOut[0]); + final PeopleRankingReconsideration rr = validatePeople(context, key, extras, affinityOut); + final float affinity = affinityOut[0]; + record.setContactAffinity(affinity); + if (rr == null) { + mUsageStats.registerPeopleAffinity(record, affinity > NONE, affinity == STARRED_CONTACT, + true /* cached */); + } else { + rr.setRecord(record); + } return rr; } @@ -245,7 +253,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { if (pendingLookups.isEmpty()) { if (VERBOSE) Slog.i(TAG, "final affinity: " + affinity); - if (affinity != NONE) MetricsLogger.count(mBaseContext, "note_with_people", 1); return null; } @@ -413,6 +420,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private final Context mContext; private float mContactAffinity = NONE; + private NotificationRecord mRecord; private PeopleRankingReconsideration(Context context, String key, LinkedList<String> pendingLookups) { super(key); @@ -456,7 +464,10 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { "ms"); } - if (mContactAffinity != NONE) MetricsLogger.count(mBaseContext, "note_with_people", 1); + if (mRecord != null) { + mUsageStats.registerPeopleAffinity(mRecord, mContactAffinity > NONE, + mContactAffinity == STARRED_CONTACT, false /* cached */); + } } @Override @@ -469,6 +480,10 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { public float getContactAffinity() { return mContactAffinity; } + + public void setRecord(NotificationRecord record) { + mRecord = record; + } } } diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java index 3cc04e8d9156..b40fd068e58a 100644 --- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java @@ -15,6 +15,9 @@ */ package com.android.server.notification; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + import android.app.Notification; import android.os.UserHandle; import android.service.notification.StatusBarNotification; @@ -24,6 +27,7 @@ import android.test.suitebuilder.annotation.SmallTest; import java.util.ArrayList; public class RankingHelperTest extends AndroidTestCase { + @Mock NotificationUsageStats mUsageStats; private Notification mNotiGroupGSortA; private Notification mNotiGroupGSortB; @@ -39,9 +43,10 @@ public class RankingHelperTest extends AndroidTestCase { @Override public void setUp() { + MockitoAnnotations.initMocks(this); UserHandle user = UserHandle.ALL; - mHelper = new RankingHelper(getContext(), null, new String[0]); + mHelper = new RankingHelper(getContext(), null, mUsageStats, new String[0]); mNotiGroupGSortA = new Notification.Builder(getContext()) .setContentTitle("A") |