Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9eac5ecd authored by Yan Wang's avatar Yan Wang
Browse files

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
parent 2173ac60
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -914,6 +914,10 @@ public class ActivityManagerService extends IActivityManager.Stub
        @Override
        public void onActivityLaunchFinished(byte[] finalActivity) {
        }
        @Override
        public void onReportFullyDrawn(byte[] finalActivity, long timestampNanos) {
        }
    };
    /**
+32 −11
Original line number Diff line number Diff line
@@ -39,8 +39,13 @@ import java.lang.annotation.RetentionPolicy;
 * 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 @@ import java.lang.annotation.RetentionPolicy;
 * 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 @@ public interface ActivityMetricsLaunchObserver {
     *          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);

}
+18 −1
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ import com.android.internal.logging.MetricsLogger;
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 @@ class ActivityMetricsLogger {
        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 @@ class ActivityMetricsLogger {
        final WindowingModeTransitionInfoSnapshot infoSnapshot =
                new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
        BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));

        // Notify reportFullyDrawn event.
        launchObserverNotifyReportFullyDrawn(r, currentTimestampNs);

        return infoSnapshot;
    }

@@ -1048,6 +1055,16 @@ class ActivityMetricsLogger {
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }

    /**
     * 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.
+18 −0
Original line number Diff line number Diff line
@@ -104,6 +104,15 @@ class LaunchObserverRegistryImpl implements
                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 @@ class LaunchObserverRegistryImpl implements
            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);
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMor
import static com.google.common.truth.Truth.assertWithMessage;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.timeout;
@@ -185,6 +186,16 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
       verifyNoMoreInteractions(mLaunchObserver);
    }

    @Test
    public void testOnReportFullyDrawn() throws Exception {
        testOnActivityLaunched();

        mActivityMetricsLogger.logAppTransitionReportedDrawn(mActivityRecord, false);

        verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mActivityRecord), anyLong());
        verifyNoMoreInteractions(mLaunchObserver);
    }

    @Test
    public void testOnActivityLaunchedTrampoline() throws Exception {
        testOnIntentStarted();