startop: Add reportFullyDrawn event support in framework.

Bug: 142128772
Test: make
Test: run on a crosshatch device and check the reportFullyDrawn event in the logcat.
Test: atest CtsWindowManagerDeviceTestCases:ActivityMetricsLoggerTests

Change-Id: Idbf8f2f476bc8998185ab04a346d1119404a4b87
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index eff0f75..11a6598 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -39,8 +39,13 @@
  * If an activity is successfully started, the launch sequence's state will transition into
  * {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
  *
- * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
- * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled},
+ * which is a terminal state or into {@code FINISHED} with {@link #onActivityLaunchFinished}.
+ *
+ * The {@code FINISHED} with {@link #onActivityLaunchFinished} then may transition to
+ * {@code FULLY_DRAWN} with {@link #onReportFullyDrawn}, which is a terminal state.
+ * Note this transition may not happen if the reportFullyDrawn event is not receivied,
+ * in which case {@code FINISHED} is terminal.
  *
  * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
  * necessarily the same within a single launch sequence: it is only the top-most activity at the
@@ -51,15 +56,15 @@
  * until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
  *
  * <pre>
- *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ╔══════════════════════════╗
- *    ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮     ACTIVITY_LAUNCHED     ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║
- *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     ╚══════════════════════════╝
- *          :                      :
- *          :                      :
- *          ▼                      ▼
- *        ╔════════════════╗     ╔═══════════════════════════╗
- *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║
- *        ╚════════════════╝     ╚═══════════════════════════╝
+ *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌--------------------------┐
+ *    ╴╴▶  INTENT_STARTED    ──▶      ACTIVITY_LAUNCHED        ──▶   ACTIVITY_LAUNCH_FINISHED
+ *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └--------------------------┘
+ *          :                      :                                 :
+ *          :                      :                                 :
+ *          ▼                      ▼                                 ▼
+ *        ╔════════════════╗     ╔═══════════════════════════╗     ╔═══════════════════════════╗
+ *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║     ║    REPORT_FULLY_DRAWN     ║
+ *        ╚════════════════╝     ╚═══════════════════════════╝     ╚═══════════════════════════╝
  * </pre>
  */
 public interface ActivityMetricsLaunchObserver {
@@ -187,4 +192,20 @@
      *          is reported here.
      */
     public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
+
+    /**
+     * Notifies the observer that the application self-reported itself as being fully drawn.
+     *
+     * @param activity the activity that triggers the ReportFullyDrawn event.
+     * @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds.
+     *        To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted}
+     *        from {@code timestampNanos}.
+     *
+     * @apiNote The behavior of ReportFullyDrawn mostly depends on the app.
+     *          It is used as an accurate estimate of meanfully app startup time.
+     *          This event may be missing for many apps.
+     */
+    public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+        long timestampNanos);
+
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a0a2967..b6c6261 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -92,6 +92,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
 import com.android.server.LocalServices;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
@@ -811,7 +812,9 @@
         final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
         builder.setPackageName(r.packageName);
         builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
-        long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime;
+        long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
+        long startupTimeMs =
+            TimeUnit.NANOSECONDS.toMillis(currentTimestampNs) - mLastTransitionStartTime;
         builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
         builder.setType(restoredFromBundle
                 ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
@@ -837,6 +840,10 @@
         final WindowingModeTransitionInfoSnapshot infoSnapshot =
                 new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
         BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+
+        // Notify reportFullyDrawn event.
+        launchObserverNotifyReportFullyDrawn(r, currentTimestampNs);
+
         return infoSnapshot;
     }
 
@@ -1049,6 +1056,16 @@
     }
 
     /**
+     * Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event.
+     */
+    private void launchObserverNotifyReportFullyDrawn(ActivityRecord r, long timestampNanos) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+            "MetricsLogger:launchObserverNotifyReportFullyDrawn");
+        mLaunchObserver.onReportFullyDrawn(convertActivityRecordToProto(r), timestampNanos);
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /**
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
      * cancelled.
      */
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
index 93e2d8d..e34626d 100644
--- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -104,6 +104,15 @@
                 LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
     }
 
+    @Override
+    public void onReportFullyDrawn(@ActivityRecordProto byte[] activity, long timestampNanos) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+            LaunchObserverRegistryImpl::handleOnReportFullyDrawn,
+            this,
+            activity,
+            timestampNanos));
+    }
+
     // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
     // unbound (i.e. not capture any variables explicitly or implicitly) to fulfill the
     // singleton-lambda requirement.
@@ -159,4 +168,13 @@
             o.onActivityLaunchFinished(activity);
         }
     }
+
+    private void handleOnReportFullyDrawn(
+            @ActivityRecordProto byte[] activity, long timestampNanos) {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+            ActivityMetricsLaunchObserver o = mList.get(i);
+            o.onReportFullyDrawn(activity, timestampNanos);
+        }
+    }
 }