UsageStats tracking of screen usage.

1. Add UsageStats Event types:
ACTIVITY_RESUMED is synonym to existing MOVE_TO_FOREGROUND.
ACTIVITY_PAUSED is synonym to existing MOVE_TO_BACKGROUND.
ACTIVITY_STOPPED when an activity becomes invisible on the UI.
2. In UsageStats.java, add API getLastTimeVisible() to report last time the
app is visible (ACTIVITY_RESUMED or ACTIVITY_PAUSED), add API getTotalTimeVisible()
to report total time the app is visible.
The existing API getLastTimeUsed() can report last time the app is in
foreground (AKA have focus).
The existing API getTotalTimeInForeground() can report total time the
app is in foreground (AKA have focus).
3. UsageStats.getTotalTimeVisible() can report screen usage for
split-screen mode and picture-in-picture mode.
4. Because in the same package, activity can be instantiated multiple times,
In UsageEvents.Event class, add a member mInstaceId for activity's
instance ID, add interface getInstanceId() to retrieve the instance ID.

Bug: 112002260
Test: frameworks/base/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
atest frameworks/base/core/tests/coretests/src/android/app/usage/UsageStatsTest.java

Change-Id: Ibcef2488e9620804c9f9220b027f976e8fa0c98b
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 40da881..57bf4a3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2724,47 +2724,86 @@
         return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
     }
 
-    void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+    /**
+     * Update battery stats on the activity' usage.
+     * @param activity
+     * @param uid
+     * @param userId
+     * @param resumed
+     */
+    void updateBatteryStats(ComponentName activity, int uid, int userId, boolean resumed) {
         if (DEBUG_SWITCH) {
             Slog.d(TAG_SWITCH,
-                    "updateUsageStats: comp=" + activity + "res=" + resumed);
+                    "updateBatteryStats: comp=" + activity + "res=" + resumed);
         }
         final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
-            uid, activity.getPackageName(),
-            activity.getShortClassName(), resumed ?
-                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
+                uid, activity.getPackageName(), activity.getShortClassName(),
+                resumed ? StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
                         StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
-        if (resumed) {
-            if (mUsageStatsService != null) {
-                mUsageStatsService.reportEvent(activity, userId,
-                        UsageEvents.Event.MOVE_TO_FOREGROUND);
-
-            }
-            synchronized (stats) {
+        synchronized (stats) {
+            if (resumed) {
                 stats.noteActivityResumedLocked(uid);
-            }
-        } else {
-            if (mUsageStatsService != null) {
-                mUsageStatsService.reportEvent(activity, userId,
-                        UsageEvents.Event.MOVE_TO_BACKGROUND);
-            }
-            synchronized (stats) {
+            } else {
                 stats.noteActivityPausedLocked(uid);
             }
         }
     }
 
+    /**
+     * Update UsageStas on the activity's usage.
+     * @param activity
+     * @param userId
+     * @param event
+     * @param appToken ActivityRecord's appToken.
+     */
+    public void updateActivityUsageStats(ComponentName activity, int userId, int event,
+            IBinder appToken) {
+        if (DEBUG_SWITCH) {
+            Slog.d(TAG_SWITCH, "updateActivityUsageStats: comp="
+                    + activity + " hash=" + appToken.hashCode() + " event=" + event);
+        }
+        synchronized (this) {
+            if (mUsageStatsService != null) {
+                mUsageStatsService.reportEvent(activity, userId, event, appToken.hashCode());
+            }
+        }
+    }
+
+    /**
+     * Update UsageStats on this package's usage.
+     * @param packageName
+     * @param userId
+     * @param event
+     */
+    public void updateActivityUsageStats(String packageName, int userId, int event) {
+        if (DEBUG_SWITCH) {
+            Slog.d(TAG_SWITCH, "updateActivityUsageStats: package="
+                    + packageName + " event=" + event);
+        }
+        synchronized (this) {
+            if (mUsageStatsService != null) {
+                mUsageStatsService.reportEvent(packageName, userId, event);
+            }
+        }
+    }
+
+    /**
+     * Update Usages on this foreground service's usage.
+     * @param service
+     * @param userId
+     * @param started
+     */
     void updateForegroundServiceUsageStats(ComponentName service, int userId, boolean started) {
         if (DEBUG_SWITCH) {
             Slog.d(TAG_SWITCH, "updateForegroundServiceUsageStats: comp="
-                    + service + "started=" + started);
+                    + service + " started=" + started);
         }
         synchronized (this) {
             if (mUsageStatsService != null) {
                 mUsageStatsService.reportEvent(service, userId,
                         started ? UsageEvents.Event.FOREGROUND_SERVICE_START
-                                : UsageEvents.Event.FOREGROUND_SERVICE_STOP);
+                                : UsageEvents.Event.FOREGROUND_SERVICE_STOP, 0);
             }
         }
     }
@@ -19041,9 +19080,19 @@
         }
 
         @Override
-        public void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
+        public void updateBatteryStats(ComponentName activity, int uid, int userId,
+                boolean resumed) {
             synchronized (ActivityManagerService.this) {
-                ActivityManagerService.this.updateUsageStats(activity, uid, userId, resumed);
+                ActivityManagerService.this.updateBatteryStats(activity, uid, userId, resumed);
+            }
+        }
+
+        @Override
+        public void updateActivityUsageStats(ComponentName activity, int userId, int event,
+                IBinder appToken) {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.updateActivityUsageStats(activity, userId, event,
+                        appToken);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4e9c5ab..2bc0edb 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -96,6 +96,7 @@
 import static com.android.server.am.ActivityRecordProto.VISIBLE;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
@@ -122,8 +123,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService
-        .RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -157,6 +157,7 @@
 import android.app.servertransaction.PipModeChangeItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.WindowVisibilityItem;
+import android.app.usage.UsageEvents.Event;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -1801,6 +1802,18 @@
             }
             mAppWindowToken.detachChildren();
         }
+
+        if (state == RESUMED) {
+            mAtmService.updateBatteryStats(this, true);
+            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED);
+        } else if (state == PAUSED) {
+            mAtmService.updateBatteryStats(this, false);
+            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED);
+        } else if (state == STOPPED) {
+            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
+        } else if (state == DESTROYED) {
+            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED);
+        }
     }
 
     ActivityState getState() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 7683172..94a5b27 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1617,7 +1617,6 @@
             try {
                 EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
                         prev.shortComponentName, "userLeaving=" + userLeaving);
-                mService.updateUsageStats(prev, false);
 
                 mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
                         prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
@@ -4649,9 +4648,6 @@
                                     r.mUserId, System.identityHashCode(r),
                                     r.getTaskRecord().taskId, r.shortComponentName,
                                     "proc died without state saved");
-                            if (r.getState() == RESUMED) {
-                                mService.updateUsageStats(r, false);
-                            }
                         }
                     } else {
                         // We have the current state for this activity, so
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index e761ad8..234dba8 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2040,9 +2040,6 @@
         mStoppingActivities.remove(r);
 
         final ActivityStack stack = r.getActivityStack();
-        if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
-            mService.updateUsageStats(r, true);
-        }
         if (stack.getDisplay().allResumedActivitiesComplete()) {
             mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
             // Make sure activity & window visibility should be identical
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 182d1a0..46ee08f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5234,13 +5234,20 @@
         mH.post(mAmInternal::updateCpuStats);
     }
 
-    void updateUsageStats(ActivityRecord component, boolean resumed) {
-        final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
+    void updateBatteryStats(ActivityRecord component, boolean resumed) {
+        final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateBatteryStats,
                 mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId,
                 resumed);
         mH.sendMessage(m);
     }
 
+    void updateActivityUsageStats(ActivityRecord activity, int event) {
+        final Message m = PooledLambda.obtainMessage(
+                ActivityManagerInternal::updateActivityUsageStats, mAmInternal,
+                activity.mActivityComponent, activity.mUserId, event, activity.appToken);
+        mH.sendMessage(m);
+    }
+
     void setBooting(boolean booting) {
         mAmInternal.setBooting(booting);
     }
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index bf7f53d..860656b 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageEvents.Event.MAX_EVENT_TYPE;
+
 import static junit.framework.TestCase.fail;
 
 import static org.testng.Assert.assertEquals;
@@ -136,9 +138,11 @@
                 event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
             }
 
-            event.mClass = ".fake.class.name" + i % 11;
+            final int instanceId = i % 11;
+            event.mClass = ".fake.class.name" + instanceId;
             event.mTimeStamp = time;
-            event.mEventType = i % 19; //"random" event type
+            event.mEventType = i % (MAX_EVENT_TYPE + 1); //"random" event type
+            event.mInstanceId = instanceId;
 
             switch (event.mEventType) {
                 case Event.CONFIGURATION_CHANGE:
@@ -160,7 +164,8 @@
             }
 
             mIntervalStats.events.insert(event);
-            mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType);
+            mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType,
+                    event.mInstanceId);
 
             time += timeProgression; // Arbitrary progression of time
         }
@@ -219,7 +224,9 @@
         // mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking
         // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
         assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+        assertEquals(us1.mLastTimeVisible, us2.mLastTimeVisible);
         assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+        assertEquals(us1.mTotalTimeVisible, us2.mTotalTimeVisible);
         assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
         assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
         // mLaunchCount not persisted, so skipped
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index f553c35..2c6cb28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -447,7 +447,11 @@
         }
 
         @Override
-        void updateUsageStats(ActivityRecord component, boolean resumed) {
+        void updateBatteryStats(ActivityRecord component, boolean resumed) {
+        }
+
+        @Override
+        void updateActivityUsageStats(ActivityRecord activity, int event) {
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4f573a4..65ed85d 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -842,8 +842,8 @@
             final boolean previouslyIdle = mAppIdleHistory.isIdle(
                     event.mPackage, userId, elapsedRealtime);
             // Inform listeners if necessary
-            if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
-                    || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+            if ((event.mEventType == UsageEvents.Event.ACTIVITY_RESUMED
+                    || event.mEventType == UsageEvents.Event.ACTIVITY_PAUSED
                     || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
                     || event.mEventType == UsageEvents.Event.USER_INTERACTION
                     || event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -894,8 +894,8 @@
 
     private int usageEventToSubReason(int eventType) {
         switch (eventType) {
-            case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
-            case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+            case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+            case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
             case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
             case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
             case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 8405267..94cc650 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -15,10 +15,31 @@
  */
 package com.android.server.usage;
 
+import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED;
+import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
+import static android.app.usage.UsageEvents.Event.KEYGUARD_HIDDEN;
+import static android.app.usage.UsageEvents.Event.KEYGUARD_SHOWN;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.SCREEN_INTERACTIVE;
+import static android.app.usage.UsageEvents.Event.SCREEN_NON_INTERACTIVE;
+import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+import static android.app.usage.UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+import static android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION;
+
 import android.app.usage.ConfigurationStats;
 import android.app.usage.EventList;
 import android.app.usage.EventStats;
-import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
 import android.util.ArrayMap;
@@ -125,8 +146,8 @@
     /**
      * Builds a UsageEvents.Event, but does not add it internally.
      */
-    UsageEvents.Event buildEvent(String packageName, String className) {
-        UsageEvents.Event event = new UsageEvents.Event();
+    Event buildEvent(String packageName, String className) {
+        Event event = new Event();
         event.mPackage = getCachedStringRef(packageName);
         if (className != null) {
             event.mClass = getCachedStringRef(className);
@@ -138,9 +159,9 @@
      * Builds a UsageEvents.Event from a proto, but does not add it internally.
      * Built here to take advantage of the cached String Refs
      */
-    UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool)
+    Event buildEvent(ProtoInputStream parser, List<String> stringPool)
             throws IOException {
-        final UsageEvents.Event event = new UsageEvents.Event();
+        final Event event = new Event();
         while (true) {
             switch (parser.nextField()) {
                 case (int) IntervalStatsProto.Event.PACKAGE:
@@ -190,20 +211,23 @@
                             parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX)
                                     - 1));
                     break;
+                case (int) IntervalStatsProto.Event.INSTANCE_ID:
+                    event.mInstanceId = parser.readInt(IntervalStatsProto.Event.INSTANCE_ID);
+                    break;
                 case ProtoInputStream.NO_MORE_FIELDS:
                     // Handle default values for certain events types
                     switch (event.mEventType) {
-                        case UsageEvents.Event.CONFIGURATION_CHANGE:
+                        case CONFIGURATION_CHANGE:
                             if (event.mConfiguration == null) {
                                 event.mConfiguration = new Configuration();
                             }
                             break;
-                        case UsageEvents.Event.SHORTCUT_INVOCATION:
+                        case SHORTCUT_INVOCATION:
                             if (event.mShortcutId == null) {
                                 event.mShortcutId = "";
                             }
                             break;
-                        case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                        case NOTIFICATION_INTERRUPTION:
                             if (event.mNotificationChannelId == null) {
                                 event.mNotificationChannelId = "";
                             }
@@ -220,14 +244,15 @@
 
     private boolean isStatefulEvent(int eventType) {
         switch (eventType) {
-            case UsageEvents.Event.MOVE_TO_FOREGROUND:
-            case UsageEvents.Event.MOVE_TO_BACKGROUND:
-            case UsageEvents.Event.FOREGROUND_SERVICE_START:
-            case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
-            case UsageEvents.Event.END_OF_DAY:
-            case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
-            case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
-            case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
+            case ACTIVITY_RESUMED:
+            case ACTIVITY_PAUSED:
+            case ACTIVITY_STOPPED:
+            case FOREGROUND_SERVICE_START:
+            case FOREGROUND_SERVICE_STOP:
+            case END_OF_DAY:
+            case ROLLOVER_FOREGROUND_SERVICE:
+            case CONTINUE_PREVIOUS_DAY:
+            case CONTINUING_FOREGROUND_SERVICE:
                 return true;
         }
         return false;
@@ -238,17 +263,56 @@
      * interaction. Excludes those that are internally generated.
      */
     private boolean isUserVisibleEvent(int eventType) {
-        return eventType != UsageEvents.Event.SYSTEM_INTERACTION
-                && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+        return eventType != SYSTEM_INTERACTION
+                && eventType != STANDBY_BUCKET_CHANGED;
     }
 
     /**
+     * Update the IntervalStats by a activity or foreground service event.
+     * @param packageName package name of this event. Is null if event targets to all packages.
+     * @param className class name of a activity or foreground service, could be null to if this
+     *                  is sent to all activities/services in this package.
+     * @param timeStamp Epoch timestamp in milliseconds.
+     * @param eventType event type as in {@link Event}
+     * @param instanceId if className is an activity, the hashCode of ActivityRecord's appToken.
+     *                 if className is not an activity, instanceId is not used.
      * @hide
      */
     @VisibleForTesting
-    public void update(String packageName, String className, long timeStamp, int eventType) {
-        UsageStats usageStats = getOrCreateUsageStats(packageName);
-        usageStats.update(className, timeStamp, eventType);
+    public void update(String packageName, String className, long timeStamp, int eventType,
+            int instanceId) {
+        if (eventType == FLUSH_TO_DISK) {
+            // FLUSH_TO_DISK are sent to all packages.
+            final int size = packageStats.size();
+            for (int i = 0; i < size; i++) {
+                UsageStats usageStats = packageStats.valueAt(i);
+                usageStats.update(null, timeStamp, eventType, instanceId);
+            }
+        } else if (eventType == ACTIVITY_DESTROYED) {
+            UsageStats usageStats = packageStats.get(packageName);
+            if (usageStats != null) {
+                // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED
+                // to ACTIVITY_STOPPED and add to event list.
+                // Otherwise do not add anything to event list. (Because we want to save space
+                // and we do not want a ACTIVITY_STOPPED followed by
+                // ACTIVITY_DESTROYED in event list).
+                final int index = usageStats.mActivities.indexOfKey(instanceId);
+                if (index >= 0) {
+                    final int type = usageStats.mActivities.valueAt(index);
+                    if (type != ACTIVITY_STOPPED) {
+                        Event event = new Event(ACTIVITY_STOPPED, timeStamp);
+                        event.mPackage = packageName;
+                        event.mClass = className;
+                        event.mInstanceId = instanceId;
+                        addEvent(event);
+                    }
+                }
+                usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId);
+            }
+        } else {
+            UsageStats usageStats = getOrCreateUsageStats(packageName);
+            usageStats.update(className, timeStamp, eventType, instanceId);
+        }
         endTime = timeStamp;
     }
 
@@ -256,7 +320,7 @@
      * @hide
      */
     @VisibleForTesting
-    public void addEvent(UsageEvents.Event event) {
+    public void addEvent(Event event) {
         if (events == null) {
             events = new EventList();
         }
@@ -265,7 +329,7 @@
         if (event.mClass != null) {
             event.mClass = getCachedStringRef(event.mClass);
         }
-        if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+        if (event.mEventType == NOTIFICATION_INTERRUPTION) {
             event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId);
         }
         events.insert(event);
@@ -338,13 +402,13 @@
     }
 
     void addEventStatsTo(List<EventStats> out) {
-        interactiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE,
+        interactiveTracker.addToEventStats(out, SCREEN_INTERACTIVE,
                 beginTime, endTime);
-        nonInteractiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE,
+        nonInteractiveTracker.addToEventStats(out, SCREEN_NON_INTERACTIVE,
                 beginTime, endTime);
-        keyguardShownTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_SHOWN,
+        keyguardShownTracker.addToEventStats(out, KEYGUARD_SHOWN,
                 beginTime, endTime);
-        keyguardHiddenTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_HIDDEN,
+        keyguardHiddenTracker.addToEventStats(out, KEYGUARD_HIDDEN,
                 beginTime, endTime);
     }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 8e1c060..d706537 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -135,6 +135,15 @@
                     stats.mTotalTimeForegroundServiceUsed = proto.readLong(
                             IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS);
                     break;
+                case (int) IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS:
+                    // Time attributes stored is an offset of the beginTime.
+                    stats.mLastTimeVisible = statsOut.beginTime + proto.readLong(
+                            IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS);
+                    break;
+                case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS:
+                    stats.mTotalTimeVisible = proto.readLong(
+                            IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS);
+                    break;
             }
         }
         if (stats.mLastTimeUsed == 0) {
@@ -337,6 +346,11 @@
                 usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
         proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS,
                 usageStats.mTotalTimeForegroundServiceUsed);
+        // Time attributes stored as an offset of the beginTime.
+        proto.write(IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS,
+                usageStats.mLastTimeVisible - stats.beginTime);
+        proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
+                usageStats.mTotalTimeVisible);
         proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
         writeChooserCounts(proto, usageStats);
         proto.end(token);
@@ -427,6 +441,7 @@
         proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
         proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
         proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
+        proto.write(IntervalStatsProto.Event.INSTANCE_ID, event.mInstanceId);
         switch (event.mEventType) {
             case UsageEvents.Event.CONFIGURATION_CHANGE:
                 if (event.mConfiguration != null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2621252..57dc08f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -16,6 +16,12 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageEvents.Event.CHOOSER_ACTION;
+import static android.app.usage.UsageEvents.Event.CONFIGURATION_CHANGE;
+import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
+import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
@@ -28,10 +34,10 @@
 import android.app.usage.EventStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManager;
-import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -75,9 +81,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
 /**
  * A service that collects, aggregates, and persists application usage data.
@@ -106,6 +110,7 @@
     static final int MSG_FLUSH_TO_DISK = 1;
     static final int MSG_REMOVE_USER = 2;
     static final int MSG_UID_STATE_CHANGED = 3;
+    static final int MSG_REPORT_EVENT_TO_ALL_USERID = 4;
 
     private final Object mLock = new Object();
     Handler mHandler;
@@ -135,12 +140,10 @@
                 @Override
                 public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
                         int bucket, int reason) {
-                    Event event = new Event();
-                    event.mEventType = Event.STANDBY_BUCKET_CHANGED;
+                    Event event = new Event(Event.STANDBY_BUCKET_CHANGED,
+                            SystemClock.elapsedRealtime());
                     event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
                     event.mPackage = packageName;
-                    // This will later be converted to system time.
-                    event.mTimeStamp = SystemClock.elapsedRealtime();
                     mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
                 }
 
@@ -397,7 +400,7 @@
      * Assuming the event's timestamp is measured in milliseconds since boot,
      * convert it to a system wall time.
      */
-    private void convertToSystemTimeLocked(UsageEvents.Event event) {
+    private void convertToSystemTimeLocked(Event event) {
         event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
     }
 
@@ -406,7 +409,6 @@
      */
     void shutdown() {
         synchronized (mLock) {
-            mHandler.removeMessages(MSG_REPORT_EVENT);
             flushToDiskLocked();
         }
     }
@@ -414,7 +416,7 @@
     /**
      * Called by the Binder stub.
      */
-    void reportEvent(UsageEvents.Event event, int userId) {
+    void reportEvent(Event event, int userId) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
             final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -431,14 +433,14 @@
 
             mAppStandby.reportEvent(event, elapsedRealtime, userId);
             switch (event.mEventType) {
-                case Event.MOVE_TO_FOREGROUND:
+                case Event.ACTIVITY_RESUMED:
                     try {
                         mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
                     } catch (IllegalArgumentException iae) {
                         Slog.e(TAG, "Failed to note usage start", iae);
                     }
                     break;
-                case Event.MOVE_TO_BACKGROUND:
+                case Event.ACTIVITY_PAUSED:
                     try {
                         mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
                     } catch (IllegalArgumentException iae) {
@@ -450,10 +452,31 @@
     }
 
     /**
-     * Called by the Binder stub.
+     * Some events like FLUSH_TO_DISK need to be sent to all userId.
+     * @param event
+     */
+    void reportEventToAllUserId(Event event) {
+        synchronized (mLock) {
+            final int userCount = mUserState.size();
+            for (int i = 0; i < userCount; i++) {
+                Event copy = new Event(event);
+                reportEvent(copy, mUserState.keyAt(i));
+            }
+        }
+    }
+
+    /**
+     * Called by the Handler for message MSG_FLUSH_TO_DISK.
      */
     void flushToDisk() {
         synchronized (mLock) {
+            // Before flush to disk, report FLUSH_TO_DISK event to signal UsageStats to update app
+            // usage. In case of abrupt power shutdown like battery drain or cold temperature,
+            // all UsageStats has correct data up to last flush to disk.
+            // The FLUSH_TO_DISK event is an internal event, it will not show up in IntervalStats'
+            // EventList.
+            Event event = new Event(FLUSH_TO_DISK, SystemClock.elapsedRealtime());
+            reportEventToAllUserId(event);
             flushToDiskLocked();
         }
     }
@@ -656,9 +679,11 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_REPORT_EVENT:
-                    reportEvent((UsageEvents.Event) msg.obj, msg.arg1);
+                    reportEvent((Event) msg.obj, msg.arg1);
                     break;
-
+                case MSG_REPORT_EVENT_TO_ALL_USERID:
+                    reportEventToAllUserId((Event) msg.obj);
+                    break;
                 case MSG_FLUSH_TO_DISK:
                     flushToDisk();
                     break;
@@ -1120,20 +1145,11 @@
                 return;
             }
 
-            UsageEvents.Event event = new UsageEvents.Event();
+            Event event = new Event(CHOOSER_ACTION, SystemClock.elapsedRealtime());
             event.mPackage = packageName;
-
-            // This will later be converted to system time.
-            event.mTimeStamp = SystemClock.elapsedRealtime();
-
-            event.mEventType = Event.CHOOSER_ACTION;
-
             event.mAction = action;
-
             event.mContentType = contentType;
-
             event.mContentAnnotations = annotations;
-
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
 
@@ -1251,37 +1267,29 @@
     private final class LocalService extends UsageStatsManagerInternal {
 
         @Override
-        public void reportEvent(ComponentName component, int userId, int eventType) {
+        public void reportEvent(ComponentName component, int userId, int eventType,
+                int instanceId) {
             if (component == null) {
                 Slog.w(TAG, "Event reported without a component name");
                 return;
             }
 
-            UsageEvents.Event event = new UsageEvents.Event();
+            Event event = new Event(eventType, SystemClock.elapsedRealtime());
             event.mPackage = component.getPackageName();
             event.mClass = component.getClassName();
-
-            // This will later be converted to system time.
-            event.mTimeStamp = SystemClock.elapsedRealtime();
-
-            event.mEventType = eventType;
+            event.mInstanceId = instanceId;
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
 
         @Override
         public void reportEvent(String packageName, int userId, int eventType) {
             if (packageName == null) {
-                Slog.w(TAG, "Event reported without a package name");
+                Slog.w(TAG, "Event reported without a package name, eventType:" + eventType);
                 return;
             }
 
-            UsageEvents.Event event = new UsageEvents.Event();
+            Event event = new Event(eventType, SystemClock.elapsedRealtime());
             event.mPackage = packageName;
-
-            // This will later be converted to system time.
-            event.mTimeStamp = SystemClock.elapsedRealtime();
-
-            event.mEventType = eventType;
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
 
@@ -1292,13 +1300,8 @@
                 return;
             }
 
-            UsageEvents.Event event = new UsageEvents.Event();
+            Event event = new Event(CONFIGURATION_CHANGE, SystemClock.elapsedRealtime());
             event.mPackage = "android";
-
-            // This will later be converted to system time.
-            event.mTimeStamp = SystemClock.elapsedRealtime();
-
-            event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
             event.mConfiguration = new Configuration(config);
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
@@ -1311,14 +1314,9 @@
                 return;
             }
 
-            UsageEvents.Event event = new UsageEvents.Event();
+            Event event = new Event(NOTIFICATION_INTERRUPTION, SystemClock.elapsedRealtime());
             event.mPackage = packageName.intern();
             event.mNotificationChannelId = channelId.intern();
-
-            // This will later be converted to system time.
-            event.mTimeStamp = SystemClock.elapsedRealtime();
-
-            event.mEventType = Event.NOTIFICATION_INTERRUPTION;
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
 
@@ -1329,14 +1327,9 @@
                 return;
             }
 
-            UsageEvents.Event event = new UsageEvents.Event();
+            Event event = new Event(SHORTCUT_INVOCATION, SystemClock.elapsedRealtime());
             event.mPackage = packageName.intern();
             event.mShortcutId = shortcutId.intern();
-
-            // This will later be converted to system time.
-            event.mTimeStamp = SystemClock.elapsedRealtime();
-
-            event.mEventType = Event.SHORTCUT_INVOCATION;
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
 
@@ -1372,7 +1365,7 @@
             // This method *WILL* do IO work, but we must block until it is finished or else
             // we might not shutdown cleanly. This is ok to do with the 'am' lock held, because
             // we are shutting down.
-            shutdown();
+            UsageStatsService.this.shutdown();
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 01e566c..ec475bf 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -61,11 +61,13 @@
     private static final String FLAGS_ATTR = "flags";
     private static final String CLASS_ATTR = "class";
     private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
+    private static final String TOTAL_TIME_VISIBLE_ATTR = "timeVisible";
     private static final String TOTAL_TIME_SERVICE_USED_ATTR = "timeServiceUsed";
     private static final String COUNT_ATTR = "count";
     private static final String ACTIVE_ATTR = "active";
     private static final String LAST_EVENT_ATTR = "lastEvent";
     private static final String TYPE_ATTR = "type";
+    private static final String INSTANCE_ID_ATTR = "instanceId";
     private static final String SHORTCUT_ID_ATTR = "shortcutId";
     private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
     private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount";
@@ -75,6 +77,7 @@
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+    private static final String LAST_TIME_VISIBLE_ATTR = "lastTimeVisible";
     private static final String LAST_TIME_SERVICE_USED_ATTR = "lastTimeServiceUsed";
     private static final String END_TIME_ATTR = "endTime";
     private static final String TIME_ATTR = "time";
@@ -92,6 +95,13 @@
                 parser, LAST_TIME_ACTIVE_ATTR);
 
         try {
+            stats.mLastTimeVisible = statsOut.beginTime + XmlUtils.readLongAttribute(
+                    parser, LAST_TIME_VISIBLE_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse mLastTimeVisible", e);
+        }
+
+        try {
             stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                     parser, LAST_TIME_SERVICE_USED_ATTR);
         } catch (IOException e) {
@@ -101,8 +111,14 @@
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
 
         try {
+            stats.mTotalTimeVisible = XmlUtils.readLongAttribute(parser, TOTAL_TIME_VISIBLE_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse mTotalTimeVisible", e);
+        }
+
+        try {
             stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
-                TOTAL_TIME_SERVICE_USED_ATTR);
+                    TOTAL_TIME_SERVICE_USED_ATTR);
         } catch (IOException e) {
             Log.e(TAG, "Failed to parse mTotalTimeForegroundServiceUsed", e);
         }
@@ -199,6 +215,13 @@
         event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
 
         event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
+
+        try {
+            event.mInstanceId = XmlUtils.readIntAttribute(parser, INSTANCE_ID_ATTR);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to parse mInstanceId", e);
+        }
+
         switch (event.mEventType) {
             case UsageEvents.Event.CONFIGURATION_CHANGE:
                 event.mConfiguration = new Configuration();
@@ -227,10 +250,13 @@
         // Write the time offset.
         XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
                 usageStats.mLastTimeUsed - stats.beginTime);
+        XmlUtils.writeLongAttribute(xml, LAST_TIME_VISIBLE_ATTR,
+                usageStats.mLastTimeVisible - stats.beginTime);
         XmlUtils.writeLongAttribute(xml, LAST_TIME_SERVICE_USED_ATTR,
                 usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
+        XmlUtils.writeLongAttribute(xml, TOTAL_TIME_VISIBLE_ATTR, usageStats.mTotalTimeVisible);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_SERVICE_USED_ATTR,
                 usageStats.mTotalTimeForegroundServiceUsed);
         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
@@ -317,6 +343,7 @@
         }
         XmlUtils.writeIntAttribute(xml, FLAGS_ATTR, event.mFlags);
         XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
+        XmlUtils.writeIntAttribute(xml, INSTANCE_ID_ATTR, event.mInstanceId);
 
         switch (event.mEventType) {
             case UsageEvents.Event.CONFIGURATION_CHANGE:
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 94d7dbb..5128ae1 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -16,10 +16,18 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
+import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
+import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
+import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY;
+import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY;
+import static android.app.usage.UsageStatsManager.INTERVAL_YEARLY;
+
 import android.app.usage.ConfigurationStats;
 import android.app.usage.EventList;
 import android.app.usage.EventStats;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
@@ -29,6 +37,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.SparseIntArray;
 
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.usage.UsageStatsDatabase.StatCombiner;
@@ -65,7 +74,7 @@
     private final int mUserId;
 
     // STOPSHIP: Temporary member variable for debugging b/110930764.
-    private UsageEvents.Event mLastEvent;
+    private Event mLastEvent;
 
     private static final long[] INTERVAL_LENGTH = new long[] {
             UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
@@ -87,7 +96,7 @@
         mContext = context;
         mDailyExpiryDate = new UnixCalendar(0);
         mDatabase = new UsageStatsDatabase(usageStatsDir);
-        mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
+        mCurrentStats = new IntervalStats[INTERVAL_COUNT];
         mListener = listener;
         mLogPrefix = "User[" + Integer.toString(userId) + "] ";
         mUserId = userId;
@@ -125,28 +134,6 @@
             updateRolloverDeadline();
         }
 
-        // Now close off any events that were open at the time this was saved.
-        for (IntervalStats stat : mCurrentStats) {
-            final int pkgCount = stat.packageStats.size();
-            for (int i = 0; i < pkgCount; i++) {
-                final UsageStats pkgStats = stat.packageStats.valueAt(i);
-                if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
-                        || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
-                    if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
-                        stat.update(pkgStats.mPackageName, null, stat.lastTimeSaved,
-                                UsageEvents.Event.END_OF_DAY);
-                    }
-                    if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
-                        stat.update(pkgStats.mPackageName, null , stat.lastTimeSaved,
-                                UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
-                    }
-                    notifyStatsChanged();
-                }
-            }
-
-            stat.updateConfigurationStats(null, stat.lastTimeSaved);
-        }
-
         if (mDatabase.isNewUpdate()) {
             notifyNewUpdate();
         }
@@ -158,40 +145,46 @@
         loadActiveStats(newTime);
     }
 
-    void reportEvent(UsageEvents.Event event) {
+    void reportEvent(Event event) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
                     + "[" + event.mTimeStamp + "]: "
                     + eventToString(event.mEventType));
         }
 
-        mLastEvent = new UsageEvents.Event(event);
+        mLastEvent = new Event(event);
 
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
             rolloverStats(event.mTimeStamp);
         }
 
-        final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
+        final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
 
         final Configuration newFullConfig = event.mConfiguration;
-        if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
-                currentDailyStats.activeConfiguration != null) {
+        if (event.mEventType == Event.CONFIGURATION_CHANGE
+                && currentDailyStats.activeConfiguration != null) {
             // Make the event configuration a delta.
             event.mConfiguration = Configuration.generateDelta(
                     currentDailyStats.activeConfiguration, newFullConfig);
         }
 
-        if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
+        if (event.mEventType != Event.SYSTEM_INTERACTION
+                // ACTIVITY_DESTROYED is a private event. If there is preceding ACTIVITY_STOPPED
+                // ACTIVITY_DESTROYED will be dropped. Otherwise it will be converted to
+                // ACTIVITY_STOPPED.
+                && event.mEventType != Event.ACTIVITY_DESTROYED
+                // FLUSH_TO_DISK is a private event.
+                && event.mEventType != Event.FLUSH_TO_DISK) {
             currentDailyStats.addEvent(event);
         }
 
         boolean incrementAppLaunch = false;
-        if (event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+        if (event.mEventType == Event.ACTIVITY_RESUMED) {
             if (event.mPackage != null && !event.mPackage.equals(mLastBackgroundedPackage)) {
                 incrementAppLaunch = true;
             }
-        } else if (event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND) {
+        } else if (event.mEventType == Event.ACTIVITY_PAUSED) {
             if (event.mPackage != null) {
                 mLastBackgroundedPackage = event.mPackage;
             }
@@ -199,10 +192,10 @@
 
         for (IntervalStats stats : mCurrentStats) {
             switch (event.mEventType) {
-                case UsageEvents.Event.CONFIGURATION_CHANGE: {
+                case Event.CONFIGURATION_CHANGE: {
                     stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
                 } break;
-                case UsageEvents.Event.CHOOSER_ACTION: {
+                case Event.CHOOSER_ACTION: {
                     stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
                     String[] annotations = event.mContentAnnotations;
                     if (annotations != null) {
@@ -211,21 +204,21 @@
                         }
                     }
                 } break;
-                case UsageEvents.Event.SCREEN_INTERACTIVE: {
+                case Event.SCREEN_INTERACTIVE: {
                     stats.updateScreenInteractive(event.mTimeStamp);
                 } break;
-                case UsageEvents.Event.SCREEN_NON_INTERACTIVE: {
+                case Event.SCREEN_NON_INTERACTIVE: {
                     stats.updateScreenNonInteractive(event.mTimeStamp);
                 } break;
-                case UsageEvents.Event.KEYGUARD_SHOWN: {
+                case Event.KEYGUARD_SHOWN: {
                     stats.updateKeyguardShown(event.mTimeStamp);
                 } break;
-                case UsageEvents.Event.KEYGUARD_HIDDEN: {
+                case Event.KEYGUARD_HIDDEN: {
                     stats.updateKeyguardHidden(event.mTimeStamp);
                 } break;
                 default: {
                     stats.update(event.mPackage, event.getClassName(),
-                            event.mTimeStamp, event.mEventType);
+                            event.mTimeStamp, event.mEventType, event.mInstanceId);
                     if (incrementAppLaunch) {
                         stats.incrementAppLaunchCount(event.mPackage);
                     }
@@ -286,12 +279,12 @@
      */
     private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
             StatCombiner<T> combiner) {
-        if (intervalType == UsageStatsManager.INTERVAL_BEST) {
+        if (intervalType == INTERVAL_BEST) {
             intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
             if (intervalType < 0) {
                 // Nothing saved to disk yet, so every stat is just as equal (no rollover has
                 // occurred.
-                intervalType = UsageStatsManager.INTERVAL_DAILY;
+                intervalType = INTERVAL_DAILY;
             }
         }
 
@@ -316,10 +309,10 @@
             }
 
             // STOPSHIP: Temporary logging for b/110930764.
-            if (intervalType == UsageStatsManager.INTERVAL_DAILY
+            if (intervalType == INTERVAL_DAILY
                     && mLastEvent != null && mLastEvent.mTimeStamp >= beginTime) {
                 final IntervalStats diskStats = mDatabase.getLatestUsageStats(
-                        UsageStatsManager.INTERVAL_DAILY);
+                        INTERVAL_DAILY);
                 StringBuilder sb = new StringBuilder(256);
                 sb.append("Last 24 hours of UsageStats missing! timeRange : ");
                 sb.append(beginTime);
@@ -395,11 +388,11 @@
     UsageEvents queryEvents(final long beginTime, final long endTime,
             boolean obfuscateInstantApps) {
         final ArraySet<String> names = new ArraySet<>();
-        List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
-                beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+        List<Event> results = queryStats(INTERVAL_DAILY,
+                beginTime, endTime, new StatCombiner<Event>() {
                     @Override
                     public void combine(IntervalStats stats, boolean mutable,
-                            List<UsageEvents.Event> accumulatedResult) {
+                            List<Event> accumulatedResult) {
                         if (stats.events == null) {
                             return;
                         }
@@ -411,11 +404,13 @@
                                 return;
                             }
 
-                            UsageEvents.Event event = stats.events.get(i);
+                            Event event = stats.events.get(i);
                             if (obfuscateInstantApps) {
                                 event = event.getObfuscatedIfInstantApp();
                             }
-                            names.add(event.mPackage);
+                            if (event.mPackage != null) {
+                                names.add(event.mPackage);
+                            }
                             if (event.mClass != null) {
                                 names.add(event.mClass);
                             }
@@ -437,7 +432,7 @@
             final String packageName) {
         final ArraySet<String> names = new ArraySet<>();
         names.add(packageName);
-        final List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
+        final List<Event> results = queryStats(INTERVAL_DAILY,
                 beginTime, endTime, (stats, mutable, accumulatedResult) -> {
                     if (stats.events == null) {
                         return;
@@ -450,7 +445,7 @@
                             return;
                         }
 
-                        final UsageEvents.Event event = stats.events.get(i);
+                        final Event event = stats.events.get(i);
                         if (!packageName.equals(event.mPackage)) {
                             continue;
                         }
@@ -492,33 +487,33 @@
         // Make a note of which components need a new CONTINUE_PREVIOUS_DAY or
         // CONTINUING_FOREGROUND_SERVICE entry.
         final Configuration previousConfig =
-                mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
-        ArraySet<String> continuePreviousDay = new ArraySet<>();
-        ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundActivity =
+                mCurrentStats[INTERVAL_DAILY].activeConfiguration;
+        ArraySet<String> continuePkgs = new ArraySet<>();
+        ArrayMap<String, SparseIntArray> continueActivity =
                 new ArrayMap<>();
-        ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundService =
+        ArrayMap<String, ArrayMap<String, Integer>> continueForegroundService =
                 new ArrayMap<>();
         for (IntervalStats stat : mCurrentStats) {
             final int pkgCount = stat.packageStats.size();
             for (int i = 0; i < pkgCount; i++) {
                 final UsageStats pkgStats = stat.packageStats.valueAt(i);
-                if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
-                        || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
-                    if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
-                        continuePreviousDayForegroundActivity.put(pkgStats.mPackageName,
-                                pkgStats.mLastForegroundActivityEventMap);
+                if (pkgStats.mActivities.size() > 0
+                        || !pkgStats.mForegroundServices.isEmpty()) {
+                    if (pkgStats.mActivities.size() > 0) {
+                        continueActivity.put(pkgStats.mPackageName,
+                                pkgStats.mActivities);
                         stat.update(pkgStats.mPackageName, null,
                                 mDailyExpiryDate.getTimeInMillis() - 1,
-                                UsageEvents.Event.END_OF_DAY);
+                                Event.END_OF_DAY, 0);
                     }
-                    if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
-                        continuePreviousDayForegroundService.put(pkgStats.mPackageName,
-                                pkgStats.mLastForegroundServiceEventMap);
+                    if (!pkgStats.mForegroundServices.isEmpty()) {
+                        continueForegroundService.put(pkgStats.mPackageName,
+                                pkgStats.mForegroundServices);
                         stat.update(pkgStats.mPackageName, null,
                                 mDailyExpiryDate.getTimeInMillis() - 1,
-                                UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
+                                Event.ROLLOVER_FOREGROUND_SERVICE, 0);
                     }
-                    continuePreviousDay.add(pkgStats.mPackageName);
+                    continuePkgs.add(pkgStats.mPackageName);
                     notifyStatsChanged();
                 }
             }
@@ -532,27 +527,27 @@
         mDatabase.prune(currentTimeMillis);
         loadActiveStats(currentTimeMillis);
 
-        final int continueCount = continuePreviousDay.size();
+        final int continueCount = continuePkgs.size();
         for (int i = 0; i < continueCount; i++) {
-            String pkgName = continuePreviousDay.valueAt(i);
-            final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
+            String pkgName = continuePkgs.valueAt(i);
+            final long beginTime = mCurrentStats[INTERVAL_DAILY].beginTime;
             for (IntervalStats stat : mCurrentStats) {
-                if (continuePreviousDayForegroundActivity.containsKey(pkgName)) {
-                    final ArrayMap<String, Integer> foregroundActivityEventMap =
-                            continuePreviousDayForegroundActivity.get(pkgName);
-                    final int size = foregroundActivityEventMap.size();
+                if (continueActivity.containsKey(pkgName)) {
+                    final SparseIntArray eventMap =
+                            continueActivity.get(pkgName);
+                    final int size = eventMap.size();
                     for (int j = 0; j < size; j++) {
-                        stat.update(pkgName, foregroundActivityEventMap.keyAt(j), beginTime,
-                                UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+                        stat.update(pkgName, null, beginTime,
+                                eventMap.valueAt(j), eventMap.keyAt(j));
                     }
                 }
-                if (continuePreviousDayForegroundService.containsKey(pkgName)) {
-                    final ArrayMap<String, Integer> foregroundServiceEventMap =
-                            continuePreviousDayForegroundService.get(pkgName);
-                    final int size = foregroundServiceEventMap.size();
+                if (continueForegroundService.containsKey(pkgName)) {
+                    final ArrayMap<String, Integer> eventMap =
+                            continueForegroundService.get(pkgName);
+                    final int size = eventMap.size();
                     for (int j = 0; j < size; j++) {
-                        stat.update(pkgName, foregroundServiceEventMap.keyAt(j), beginTime,
-                                UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE);
+                        stat.update(pkgName, eventMap.keyAt(j), beginTime,
+                                eventMap.valueAt(j), 0);
                     }
                 }
                 stat.updateConfigurationStats(previousConfig, beginTime);
@@ -611,7 +606,7 @@
 
     private void updateRolloverDeadline() {
         mDailyExpiryDate.setTimeInMillis(
-                mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
+                mCurrentStats[INTERVAL_DAILY].beginTime);
         mDailyExpiryDate.addDays(1);
         Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
                 sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
@@ -660,7 +655,7 @@
     }
 
 
-    void printEvent(IndentingPrintWriter pw, UsageEvents.Event event, boolean prettyDates) {
+    void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
         pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
         pw.printPair("type", eventToString(event.mEventType));
         pw.printPair("package", event.mPackage);
@@ -673,10 +668,15 @@
         if (event.mShortcutId != null) {
             pw.printPair("shortcutId", event.mShortcutId);
         }
-        if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
+        if (event.mEventType == Event.STANDBY_BUCKET_CHANGED) {
             pw.printPair("standbyBucket", event.getStandbyBucket());
             pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
+        } else if (event.mEventType == Event.ACTIVITY_RESUMED
+                || event.mEventType == Event.ACTIVITY_PAUSED
+                || event.mEventType == Event.ACTIVITY_STOPPED) {
+            pw.printPair("instanceId", event.getInstanceId());
         }
+
         if (event.mNotificationChannelId != null) {
             pw.printPair("channelId", event.mNotificationChannelId);
         }
@@ -691,11 +691,11 @@
 
         final long beginTime = yesterday.getTimeInMillis();
 
-        List<UsageEvents.Event> events = queryStats(UsageStatsManager.INTERVAL_DAILY,
-                beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
+        List<Event> events = queryStats(INTERVAL_DAILY,
+                beginTime, endTime, new StatCombiner<Event>() {
                     @Override
                     public void combine(IntervalStats stats, boolean mutable,
-                            List<UsageEvents.Event> accumulatedResult) {
+                            List<Event> accumulatedResult) {
                         if (stats.events == null) {
                             return;
                         }
@@ -707,7 +707,7 @@
                                 return;
                             }
 
-                            UsageEvents.Event event = stats.events.get(i);
+                            Event event = stats.events.get(i);
                             if (pkg != null && !pkg.equals(event.mPackage)) {
                                 continue;
                             }
@@ -727,7 +727,7 @@
         pw.println(")");
         if (events != null) {
             pw.increaseIndent();
-            for (UsageEvents.Event event : events) {
+            for (Event event : events) {
                 printEvent(pw, event, prettyDates);
             }
             pw.decreaseIndent();
@@ -772,9 +772,17 @@
                 continue;
             }
             pw.printPair("package", usageStats.mPackageName);
-            pw.printPair("totalTime",
+            pw.printPair("totalTimeUsed",
                     formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
-            pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+            pw.printPair("lastTimeUsed", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+            pw.printPair("totalTimeVisible",
+                    formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates));
+            pw.printPair("lastTimeVisible",
+                    formatDateTime(usageStats.mLastTimeVisible, prettyDates));
+            pw.printPair("totalTimeFS",
+                    formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates));
+            pw.printPair("lastTimeFS",
+                    formatDateTime(usageStats.mLastTimeForegroundServiceUsed, prettyDates));
             pw.printPair("appLaunchCount", usageStats.mAppLaunchCount);
             pw.println();
         }
@@ -845,7 +853,7 @@
             final EventList events = stats.events;
             final int eventCount = events != null ? events.size() : 0;
             for (int i = 0; i < eventCount; i++) {
-                final UsageEvents.Event event = events.get(i);
+                final Event event = events.get(i);
                 if (pkg != null && !pkg.equals(event.mPackage)) {
                     continue;
                 }
@@ -858,13 +866,13 @@
 
     private static String intervalToString(int interval) {
         switch (interval) {
-            case UsageStatsManager.INTERVAL_DAILY:
+            case INTERVAL_DAILY:
                 return "daily";
-            case UsageStatsManager.INTERVAL_WEEKLY:
+            case INTERVAL_WEEKLY:
                 return "weekly";
-            case UsageStatsManager.INTERVAL_MONTHLY:
+            case INTERVAL_MONTHLY:
                 return "monthly";
-            case UsageStatsManager.INTERVAL_YEARLY:
+            case INTERVAL_YEARLY:
                 return "yearly";
             default:
                 return "?";
@@ -873,47 +881,49 @@
 
     private static String eventToString(int eventType) {
         switch (eventType) {
-            case UsageEvents.Event.NONE:
+            case Event.NONE:
                 return "NONE";
-            case UsageEvents.Event.MOVE_TO_BACKGROUND:
-                return "MOVE_TO_BACKGROUND";
-            case UsageEvents.Event.MOVE_TO_FOREGROUND:
-                return "MOVE_TO_FOREGROUND";
-            case UsageEvents.Event.FOREGROUND_SERVICE_START:
+            case Event.ACTIVITY_PAUSED:
+                return "ACTIVITY_PAUSED";
+            case Event.ACTIVITY_RESUMED:
+                return "ACTIVITY_RESUMED";
+            case Event.FOREGROUND_SERVICE_START:
                 return "FOREGROUND_SERVICE_START";
-            case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
+            case Event.FOREGROUND_SERVICE_STOP:
                 return "FOREGROUND_SERVICE_STOP";
-            case UsageEvents.Event.END_OF_DAY:
+            case Event.ACTIVITY_STOPPED:
+                return "ACTIVITY_STOPPED";
+            case Event.END_OF_DAY:
                 return "END_OF_DAY";
-            case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
+            case Event.ROLLOVER_FOREGROUND_SERVICE:
                 return "ROLLOVER_FOREGROUND_SERVICE";
-            case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
+            case Event.CONTINUE_PREVIOUS_DAY:
                 return "CONTINUE_PREVIOUS_DAY";
-            case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
+            case Event.CONTINUING_FOREGROUND_SERVICE:
                 return "CONTINUING_FOREGROUND_SERVICE";
-            case UsageEvents.Event.CONFIGURATION_CHANGE:
+            case Event.CONFIGURATION_CHANGE:
                 return "CONFIGURATION_CHANGE";
-            case UsageEvents.Event.SYSTEM_INTERACTION:
+            case Event.SYSTEM_INTERACTION:
                 return "SYSTEM_INTERACTION";
-            case UsageEvents.Event.USER_INTERACTION:
+            case Event.USER_INTERACTION:
                 return "USER_INTERACTION";
-            case UsageEvents.Event.SHORTCUT_INVOCATION:
+            case Event.SHORTCUT_INVOCATION:
                 return "SHORTCUT_INVOCATION";
-            case UsageEvents.Event.CHOOSER_ACTION:
+            case Event.CHOOSER_ACTION:
                 return "CHOOSER_ACTION";
-            case UsageEvents.Event.NOTIFICATION_SEEN:
+            case Event.NOTIFICATION_SEEN:
                 return "NOTIFICATION_SEEN";
-            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+            case Event.STANDBY_BUCKET_CHANGED:
                 return "STANDBY_BUCKET_CHANGED";
-            case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+            case Event.NOTIFICATION_INTERRUPTION:
                 return "NOTIFICATION_INTERRUPTION";
-            case UsageEvents.Event.SLICE_PINNED:
+            case Event.SLICE_PINNED:
                 return "SLICE_PINNED";
-            case UsageEvents.Event.SLICE_PINNED_PRIV:
+            case Event.SLICE_PINNED_PRIV:
                 return "SLICE_PINNED_PRIV";
-            case UsageEvents.Event.SCREEN_INTERACTIVE:
+            case Event.SCREEN_INTERACTIVE:
                 return "SCREEN_INTERACTIVE";
-            case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
+            case Event.SCREEN_NON_INTERACTIVE:
                 return "SCREEN_NON_INTERACTIVE";
             case UsageEvents.Event.KEYGUARD_SHOWN:
                 return "KEYGUARD_SHOWN";