Merge "Track NotificationEntry creation time" into rvc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
index 362014f..e17d4e6 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender.java
@@ -25,6 +25,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.util.time.SystemClock;
 
 /**
  * Extends the lifetime of foreground notification services such that they show for at least
@@ -39,8 +40,10 @@
     private NotificationSafeToRemoveCallback mNotificationSafeToRemoveCallback;
     private ArraySet<NotificationEntry> mManagedEntries = new ArraySet<>();
     private Handler mHandler = new Handler(Looper.getMainLooper());
+    private final SystemClock mSystemClock;
 
-    public ForegroundServiceLifetimeExtender() {
+    public ForegroundServiceLifetimeExtender(SystemClock systemClock) {
+        mSystemClock = systemClock;
     }
 
     @Override
@@ -55,8 +58,8 @@
             return false;
         }
 
-        long currentTime = System.currentTimeMillis();
-        return currentTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
+        long currentTime = mSystemClock.uptimeMillis();
+        return currentTime - entry.getCreationTime() < MIN_FGS_TIME_MS;
     }
 
     @Override
@@ -84,7 +87,7 @@
             }
         };
         long delayAmt = MIN_FGS_TIME_MS
-                - (System.currentTimeMillis() - entry.getSbn().getPostTime());
+                - (mSystemClock.uptimeMillis() - entry.getCreationTime());
         mHandler.postDelayed(r, delayAmt);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index f01fa81..ef1f4e0 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -30,6 +30,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.util.time.SystemClock;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -49,7 +50,8 @@
     public ForegroundServiceNotificationListener(Context context,
             ForegroundServiceController foregroundServiceController,
             NotificationEntryManager notificationEntryManager,
-            NotifPipeline notifPipeline) {
+            NotifPipeline notifPipeline,
+            SystemClock systemClock) {
         mContext = context;
         mForegroundServiceController = foregroundServiceController;
 
@@ -76,7 +78,8 @@
                 removeNotification(entry.getSbn());
             }
         });
-        mEntryManager.addNotificationLifetimeExtender(new ForegroundServiceLifetimeExtender());
+        mEntryManager.addNotificationLifetimeExtender(
+                new ForegroundServiceLifetimeExtender(systemClock));
 
         notifPipeline.addCollectionListener(new NotifCollectionListener() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 295adae..9324b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,6 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -555,8 +556,8 @@
         NotificationEntry entry = new NotificationEntry(
                 notification,
                 ranking,
-                mFgsFeatureController.isForegroundServiceDismissalEnabled());
-        mAllNotifications.add(entry);
+                mFgsFeatureController.isForegroundServiceDismissalEnabled(),
+                SystemClock.uptimeMillis());
 
         mLeakDetector.trackInstance(entry);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 9c2cac71..365862b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -47,6 +47,7 @@
 import android.annotation.UserIdInt;
 import android.app.Notification;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -387,7 +388,7 @@
 
         if (entry == null) {
             // A new notification!
-            entry = new NotificationEntry(sbn, ranking);
+            entry = new NotificationEntry(sbn, ranking, SystemClock.uptimeMillis());
             mNotificationSet.put(sbn.getKey(), entry);
 
             mLogger.logNotifPosted(sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index c1ba26d..68ec34e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -35,6 +35,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.CurrentTimeMillisLong;
 import android.app.Notification;
 import android.app.Notification.MessagingStyle.Message;
 import android.app.NotificationChannel;
@@ -93,6 +94,7 @@
     private final String mKey;
     private StatusBarNotification mSbn;
     private Ranking mRanking;
+    private long mCreationTime;
 
     /*
      * Bookkeeping members
@@ -171,21 +173,29 @@
     private boolean mAllowFgsDismissal;
     private int mBucket = BUCKET_ALERTING;
 
+    /**
+     * @param sbn the StatusBarNotification from system server
+     * @param ranking also from system server
+     * @param creationTime SystemClock.uptimeMillis of when we were created
+     */
     public NotificationEntry(
             @NonNull StatusBarNotification sbn,
-            @NonNull Ranking ranking) {
-        this(sbn, ranking, false);
+            @NonNull Ranking ranking,
+            long creationTime) {
+        this(sbn, ranking, false, creationTime);
     }
 
     public NotificationEntry(
             @NonNull StatusBarNotification sbn,
             @NonNull Ranking ranking,
-            boolean allowFgsDismissal
+            boolean allowFgsDismissal,
+            long creationTime
     ) {
         super(requireNonNull(Objects.requireNonNull(sbn).getKey()));
 
         requireNonNull(ranking);
 
+        mCreationTime = creationTime;
         mKey = sbn.getKey();
         setSbn(sbn);
         setRanking(ranking);
@@ -238,6 +248,21 @@
     }
 
     /**
+     * A timestamp of SystemClock.uptimeMillis() of when this entry was first created, regardless
+     * of any changes to the data presented. It is set once on creation and will never change, and
+     * allows us to know exactly how long this notification has been alive for in our listener
+     * service. It is entirely unrelated to the information inside of the notification.
+     *
+     * This is different to Notification#when because it persists throughout updates, whereas
+     * system server treats every single call to notify() as a new notification and we handle
+     * updates to NotificationEntry locally.
+     */
+    @CurrentTimeMillisLong
+    public long getCreationTime() {
+        return mCreationTime;
+    }
+
+    /**
      * Should only be called by NotificationEntryManager and friends.
      * TODO: Make this package-private
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 69e933e..45f9437 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import junit.framework.Assert;
 
@@ -69,6 +70,7 @@
     private ForegroundServiceController mFsc;
     private ForegroundServiceNotificationListener mListener;
     private NotificationEntryListener mEntryListener;
+    private final FakeSystemClock mClock = new FakeSystemClock();
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private AppOpsController mAppOpsController;
     @Mock private Handler mMainHandler;
@@ -80,9 +82,10 @@
         allowTestableLooperAsMainThread();
 
         MockitoAnnotations.initMocks(this);
-        mFsc = new ForegroundServiceController(mEntryManager, mAppOpsController, mMainHandler);
+        mFsc = new ForegroundServiceController(
+                mEntryManager, mAppOpsController, mMainHandler);
         mListener = new ForegroundServiceNotificationListener(
-                mContext, mFsc, mEntryManager, mNotifPipeline);
+                mContext, mFsc, mEntryManager, mNotifPipeline, mClock);
         ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
                 ArgumentCaptor.forClass(NotificationEntryListener.class);
         verify(mEntryManager).addNotificationEntryListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
index 46a473b..bca8dee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceNotificationListenerTest.java
@@ -17,7 +17,6 @@
 package com.android.systemui;
 
 import static com.android.systemui.ForegroundServiceLifetimeExtender.MIN_FGS_TIME_MS;
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -29,6 +28,7 @@
 
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -37,12 +37,15 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ForegroundServiceNotificationListenerTest extends SysuiTestCase {
-    private ForegroundServiceLifetimeExtender mExtender = new ForegroundServiceLifetimeExtender();
+    private ForegroundServiceLifetimeExtender mExtender;
     private NotificationEntry mEntry;
     private Notification mNotif;
+    private final FakeSystemClock mClock = new FakeSystemClock();
 
     @Before
     public void setup() {
+        mExtender = new ForegroundServiceLifetimeExtender(mClock);
+
         mNotif = new Notification.Builder(mContext, "")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
@@ -50,6 +53,7 @@
                 .build();
 
         mEntry = new NotificationEntryBuilder()
+                .setCreationTime(mClock.uptimeMillis())
                 .setNotification(mNotif)
                 .build();
     }
@@ -62,27 +66,26 @@
         // Extend the lifetime of a FGS notification iff it has not been visible
         // for the minimum time
         mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        modifySbn(mEntry)
-                .setPostTime(System.currentTimeMillis())
-                .build();
+
+        // No time has elapsed, keep showing
         assertTrue(mExtender.shouldExtendLifetime(mEntry));
     }
 
     @Test
     public void testShouldExtendLifetime_shouldNot_foreground() {
         mNotif.flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        modifySbn(mEntry)
-                .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
-                .build();
+
+        // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
+        mClock.advanceTime(MIN_FGS_TIME_MS + 1);
         assertFalse(mExtender.shouldExtendLifetime(mEntry));
     }
 
     @Test
     public void testShouldExtendLifetime_shouldNot_notForeground() {
         mNotif.flags = 0;
-        modifySbn(mEntry)
-                .setPostTime(System.currentTimeMillis() - MIN_FGS_TIME_MS - 1)
-                .build();
+
+        // Entry was created at mClock.uptimeMillis(), advance it MIN_FGS_TIME_MS + 1
+        mClock.advanceTime(MIN_FGS_TIME_MS + 1);
         assertFalse(mExtender.shouldExtendLifetime(mEntry));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 43cf83f..81f9546 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -29,6 +29,7 @@
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import java.util.ArrayList;
 
@@ -44,16 +45,22 @@
 public class NotificationEntryBuilder {
     private final SbnBuilder mSbnBuilder = new SbnBuilder();
     private final RankingBuilder mRankingBuilder = new RankingBuilder();
+    private final FakeSystemClock mClock = new FakeSystemClock();
     private StatusBarNotification mSbn = null;
 
     /* ListEntry properties */
     private GroupEntry mParent;
     private int mSection = -1;
 
+    /* If set, use this creation time instead of mClock.uptimeMillis */
+    private long mCreationTime = -1;
+
     public NotificationEntry build() {
         StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
         mRankingBuilder.setKey(sbn.getKey());
-        final NotificationEntry entry = new NotificationEntry(sbn, mRankingBuilder.build());
+        long creationTime = mCreationTime != -1 ? mCreationTime : mClock.uptimeMillis();
+        final NotificationEntry entry = new NotificationEntry(
+                sbn, mRankingBuilder.build(), mClock.uptimeMillis());
 
         /* ListEntry properties */
         entry.setParent(mParent);
@@ -86,6 +93,14 @@
         return this;
     }
 
+    /**
+     * Set the creation time
+     */
+    public NotificationEntryBuilder setCreationTime(long creationTime) {
+        mCreationTime = creationTime;
+        return this;
+    }
+
     /* Delegated to SbnBuilder */
 
     public NotificationEntryBuilder setPkg(String pkg) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 5b0b668..1a022ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -52,6 +52,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -71,6 +72,7 @@
     private int mId;
 
     private NotificationEntry mEntry;
+    private final FakeSystemClock mClock = new FakeSystemClock();
 
     @Before
     public void setup() {
@@ -187,7 +189,7 @@
                 .build();
 
         NotificationEntry entry =
-                new NotificationEntry(sbn, ranking);
+                new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
 
         assertEquals(systemGeneratedSmartActions, entry.getSmartActions());
         assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());