Add API to track usage time of apps.

This adds a new ActivityOption for the caller to ask the
system to track the time the user is in the app it launches,
delivering the result when they are done.

The time interval tracked is from when the app launches the
activity until the user leaves that app's flow.  They are
considered to stay in the flow as long as new activities
are being launched or returned to from the original flow,
even if they cross package or task boundaries.  For example,
if the originator starts an activity to view an image, and
while there the user selects to share, which launches gmail
in a new task, and they complete the share, the time during
that entire operation will be included.

The user is considered to complete the operation once they
switch to another activity that is not part of the tracked
flow.  For example, use the notification shade, launcher, or
recents to launch or switch to another app.  Simply going
in to these navigation elements does not break the flow
(although the launcher and recents stops time tracking of
the session), it is the act of going somewhere else that
completes the tracking.

The data is delivered to the app through a PendingIntent,
which includes the total time the app was in the flow along
with a time break-down by app package.

Change-Id: If1cf8892d422c52ec5042eba0e15a8e7e8f83abf
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5eee34f..ff70629 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1567,6 +1567,12 @@
             outActivity[0] = r;
         }
 
+        if (r.appTimeTracker == null && sourceRecord != null) {
+            // If the caller didn't specify an explicit time tracker, we want to continue
+            // tracking under any it has.
+            r.appTimeTracker = sourceRecord.appTimeTracker;
+        }
+
         final ActivityStack stack = mFocusedStack;
         if (voiceSession == null && (stack.mResumedActivity == null
                 || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
@@ -1950,7 +1956,7 @@
                             }
                             movedHome = true;
                             targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
-                                    options, "bringingFoundTaskToFront");
+                                    options, r.appTimeTracker, "bringingFoundTaskToFront");
                             movedToFront = true;
                             if ((launchFlags &
                                     (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
@@ -2192,7 +2198,7 @@
             final TaskRecord topTask = targetStack.topTask();
             if (topTask != sourceTask) {
                 targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options,
-                        "sourceTaskToFront");
+                        r.appTimeTracker, "sourceTaskToFront");
             }
             if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
                 // In this case, we are adding the activity to an existing
@@ -2239,14 +2245,15 @@
                     + " in existing task " + r.task + " from source " + sourceRecord);
 
         } else if (inTask != null) {
-            // The calling is asking that the new activity be started in an explicit
+            // The caller is asking that the new activity be started in an explicit
             // task it has provided to us.
             if (isLockTaskModeViolation(inTask)) {
                 Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
                 return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
             targetStack = inTask.stack;
-            targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, "inTaskToFront");
+            targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, r.appTimeTracker,
+                    "inTaskToFront");
 
             // Check whether we should actually launch the new activity in to the task,
             // or just reuse the current activity on top.
@@ -2628,7 +2635,9 @@
                     + task + " to front. Stack is null");
             return;
         }
-        task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options, reason);
+        task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+                task.getTopActivity() == null ? null : task.getTopActivity().appTimeTracker,
+                reason);
         if (DEBUG_STACK) Slog.d(TAG_STACK,
                 "findTaskToMoveToFront: moved to front of stack=" + task.stack);
     }
@@ -3143,6 +3152,17 @@
         }
     }
 
+    void clearOtherAppTimeTrackers(AppTimeTracker except) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+            final int topStackNdx = stacks.size() - 1;
+            for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                stack.clearOtherAppTimeTrackers(except);
+            }
+        }
+    }
+
     void scheduleDestroyAllActivities(ProcessRecord app, String reason) {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;