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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8c672a1..76132e4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -914,6 +914,10 @@
@Override
public void onActivityLaunchFinished(byte[] finalActivity) {
}
+
+ @Override
+ public void onReportFullyDrawn(byte[] finalActivity, long timestampNanos) {
+ }
};
/**
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);
+ }
+ }
}