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

Commit 9b72caed authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Add app start source information to statsd

Add more information about how the activity is launched
with the event time which is closer to user interaction.

Bug: 166614700
Test: ActivityMetricsLaunchObserverTests#testOnReportFullyDrawn
Test: Enable statsd log: "adb shell cmd stats print-logs"
      Check the logcat output contains the given string.

Change-Id: If153f0b9e8d8969a2122e5f2b8aac4a83aa524c9
parent 46969117
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -3616,6 +3616,19 @@ message AppStartOccurred {

    // The reason why the package was optimized.
    optional int32 package_optimization_compilation_reason = 15;

    enum SourceType {
        UNAVAILABLE = 0;
        LAUNCHER = 1;
        NOTIFICATION = 2;
        LOCKSCREEN = 3;
    }
    // The type of the startup source.
    optional SourceType source_type = 16;

    // The time from the startup source to the beginning of handling the startup event.
    // -1 means not available.
    optional int32 source_event_delay_millis = 17;
}

message AppStartCanceled {
@@ -3660,6 +3673,19 @@ message AppStartFullyDrawn {

    // App startup time (until call to Activity#reportFullyDrawn()).
    optional int64 app_startup_time_millis = 6;

    enum SourceType {
        UNAVAILABLE = 0;
        LAUNCHER = 1;
        NOTIFICATION = 2;
        LOCKSCREEN = 3;
    }
    // The type of the startup source.
    optional SourceType source_type = 7;

    // The time from the startup source to the beginning of handling the startup event.
    // -1 means not available.
    optional int32 source_event_delay_millis = 8;
}

/**
+88 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -37,6 +38,7 @@ import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -54,6 +56,8 @@ import android.view.ViewGroup;
import android.view.Window;
import android.window.WindowContainerToken;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;

/**
@@ -290,6 +294,9 @@ public class ActivityOptions {
    private static final String KEY_EXIT_COORDINATOR_INDEX
            = "android:activity.exitCoordinatorIndex";

    /** See {@link SourceInfo}. */
    private static final String KEY_SOURCE_INFO = "android.activity.sourceInfo";

    private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
    private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint";

@@ -369,6 +376,7 @@ public class ActivityOptions {
    private boolean mAvoidMoveToFront;
    private boolean mFreezeRecentTasksReordering;
    private AppTransitionAnimationSpec mAnimSpecs[];
    private SourceInfo mSourceInfo;
    private int mRotationAnimationHint = -1;
    private Bundle mAppVerificationBundle;
    private IAppTransitionAnimationSpecsFuture mSpecsFuture;
@@ -1055,6 +1063,7 @@ public class ActivityOptions {
            mAnimationFinishedListener = IRemoteCallback.Stub.asInterface(
                    opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER));
        }
        mSourceInfo = opts.getParcelable(KEY_SOURCE_INFO);
        mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT, -1);
        mAppVerificationBundle = opts.getBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE);
        if (opts.containsKey(KEY_SPECS_FUTURE)) {
@@ -1696,6 +1705,9 @@ public class ActivityOptions {
        if (mSpecsFuture != null) {
            b.putBinder(KEY_SPECS_FUTURE, mSpecsFuture.asBinder());
        }
        if (mSourceInfo != null) {
            b.putParcelable(KEY_SOURCE_INFO, mSourceInfo);
        }
        if (mRotationAnimationHint != -1) {
            b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint);
        }
@@ -1736,6 +1748,28 @@ public class ActivityOptions {
        mUsageTimeReport = receiver;
    }

    /**
     * Returns the launch source information set by {@link #setSourceInfo}.
     * @hide
     */
    public @Nullable SourceInfo getSourceInfo() {
        return mSourceInfo;
    }

    /**
     * Sets the source information of the launch event.
     *
     * @param type The type of the startup source.
     * @param uptimeMillis The event time of startup source in milliseconds since boot, not
     *                     including sleep (e.g. from {@link android.view.MotionEvent#getEventTime}
     *                     or {@link android.os.SystemClock#uptimeMillis}).
     * @see SourceInfo
     * @hide
     */
    public void setSourceInfo(@SourceInfo.SourceType int type, long uptimeMillis) {
        mSourceInfo = new SourceInfo(type, uptimeMillis);
    }

    /**
     * Return the filtered options only meant to be seen by the target activity itself
     * @hide
@@ -1863,4 +1897,58 @@ public class ActivityOptions {
            }
        }
    }

    /**
     * The information about the source of activity launch. E.g. describe an activity is launched
     * from launcher by receiving a motion event with a timestamp.
     * @hide
     */
    public static class SourceInfo implements Parcelable {
        /** Launched from launcher. */
        public static final int TYPE_LAUNCHER = 1;
        /** Launched from notification. */
        public static final int TYPE_NOTIFICATION = 2;
        /** Launched from lockscreen, including notification while the device is locked. */
        public static final int TYPE_LOCKSCREEN = 3;

        @IntDef(flag = true, prefix = { "TYPE_" }, value = {
                TYPE_LAUNCHER,
                TYPE_NOTIFICATION,
                TYPE_LOCKSCREEN,
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface SourceType {}

        /** The type of the startup source. */
        public final @SourceType int type;

        /** The timestamp (uptime based) of the source to launch activity. */
        public final long eventTimeMs;

        SourceInfo(@SourceType int srcType, long uptimeMillis) {
            type = srcType;
            eventTimeMs = uptimeMillis;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(type);
            dest.writeLong(eventTimeMs);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        public static final Creator<SourceInfo> CREATOR = new Creator<SourceInfo>() {
            public SourceInfo createFromParcel(Parcel in) {
                return new SourceInfo(in.readInt(), in.readLong());
            }

            public SourceInfo[] newArray(int size) {
                return new SourceInfo[size];
            }
        };
    }
}
+36 −10
Original line number Diff line number Diff line
@@ -67,6 +67,8 @@ import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.ActivityOptions.SourceInfo;
import android.app.WaitResult;
import android.app.WindowConfiguration.WindowingMode;
import android.content.ComponentName;
@@ -161,6 +163,8 @@ class ActivityMetricsLogger {
     * launched successfully.
     */
    static final class LaunchingState {
        /** The device uptime of {@link #notifyActivityLaunching}. */
        private final long mCurrentUpTimeMs = SystemClock.uptimeMillis();
        /** The timestamp of {@link #notifyActivityLaunching}. */
        private long mCurrentTransitionStartTimeNs;
        /** Non-null when a {@link TransitionInfo} is created for this state. */
@@ -199,6 +203,10 @@ class ActivityMetricsLogger {
        /** The latest activity to have been launched. */
        @NonNull ActivityRecord mLastLaunchedActivity;

        /** The type of the source that triggers the launch event. */
        @SourceInfo.SourceType int mSourceType;
        /** The time from the source event (e.g. touch) to {@link #notifyActivityLaunching}. */
        int mSourceEventDelayMs = INVALID_DELAY;
        /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyTransitionStarting}. */
        int mCurrentTransitionDelayMs;
        /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyStartingWindowDrawn}. */
@@ -222,8 +230,8 @@ class ActivityMetricsLogger {
        /** @return Non-null if there will be a window drawn event for the launch. */
        @Nullable
        static TransitionInfo create(@NonNull ActivityRecord r,
                @NonNull LaunchingState launchingState, boolean processRunning,
                boolean processSwitch, int startResult) {
                @NonNull LaunchingState launchingState, @Nullable ActivityOptions options,
                boolean processRunning, boolean processSwitch, int startResult) {
            int transitionType = INVALID_TRANSITION_TYPE;
            if (processRunning) {
                if (startResult == START_SUCCESS) {
@@ -240,22 +248,31 @@ class ActivityMetricsLogger {
                // That means the startResult is neither START_SUCCESS nor START_TASK_TO_FRONT.
                return null;
            }
            return new TransitionInfo(r, launchingState, transitionType, processRunning,
            return new TransitionInfo(r, launchingState, options, transitionType, processRunning,
                    processSwitch);
        }

        /** Use {@link TransitionInfo#create} instead to ensure the transition type is valid. */
        private TransitionInfo(ActivityRecord r, LaunchingState launchingState, int transitionType,
                boolean processRunning, boolean processSwitch) {
        private TransitionInfo(ActivityRecord r, LaunchingState launchingState,
                ActivityOptions options, int transitionType, boolean processRunning,
                boolean processSwitch) {
            mLaunchingState = launchingState;
            mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs;
            mTransitionType = transitionType;
            mProcessRunning = processRunning;
            mProcessSwitch = processSwitch;
            mCurrentTransitionDeviceUptime =
                    (int) TimeUnit.MILLISECONDS.toSeconds(SystemClock.uptimeMillis());
                    (int) TimeUnit.MILLISECONDS.toSeconds(launchingState.mCurrentUpTimeMs);
            setLatestLaunchedActivity(r);
            launchingState.mAssociatedTransitionInfo = this;
            if (options != null) {
                final SourceInfo sourceInfo = options.getSourceInfo();
                if (sourceInfo != null) {
                    mSourceType = sourceInfo.type;
                    mSourceEventDelayMs =
                            (int) (launchingState.mCurrentUpTimeMs - sourceInfo.eventTimeMs);
                }
            }
        }

        /**
@@ -324,6 +341,8 @@ class ActivityMetricsLogger {
        final private String launchedActivityAppRecordRequiredAbi;
        final String launchedActivityShortComponentName;
        final private String processName;
        @VisibleForTesting final @SourceInfo.SourceType int sourceType;
        @VisibleForTesting final int sourceEventDelayMs;
        final private int reason;
        final private int startingWindowDelayMs;
        final private int bindApplicationDelayMs;
@@ -352,12 +371,14 @@ class ActivityMetricsLogger {
                    ? null
                    : launchedActivity.app.getRequiredAbi();
            reason = info.mReason;
            sourceEventDelayMs = info.mSourceEventDelayMs;
            startingWindowDelayMs = info.mStartingWindowDelayMs;
            bindApplicationDelayMs = info.mBindApplicationDelayMs;
            windowsDrawnDelayMs = info.mWindowsDrawnDelayMs;
            type = info.mTransitionType;
            processRecord = launchedActivity.app;
            processName = launchedActivity.processName;
            sourceType = info.mSourceType;
            userId = launchedActivity.mUserId;
            launchedActivityShortComponentName = launchedActivity.shortComponentName;
            activityRecordIdHashCode = System.identityHashCode(launchedActivity);
@@ -513,9 +534,10 @@ class ActivityMetricsLogger {
     * @param resultCode One of the {@link android.app.ActivityManager}.START_* flags, indicating
     *                   the result of the launch.
     * @param launchedActivity The activity that is being launched
     * @param options The given options of the launching activity.
     */
    void notifyActivityLaunched(@NonNull LaunchingState launchingState, int resultCode,
            @Nullable ActivityRecord launchedActivity) {
            @Nullable ActivityRecord launchedActivity, @Nullable ActivityOptions options) {
        if (launchedActivity == null) {
            // The launch is aborted, e.g. intent not resolved, class not found.
            abort(null /* info */, "nothing launched");
@@ -560,7 +582,7 @@ class ActivityMetricsLogger {
        }

        final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState,
                processRunning, processSwitch, resultCode);
                options, processRunning, processSwitch, resultCode);
        if (newInfo == null) {
            abort(info, "unrecognized launch");
            return;
@@ -864,7 +886,9 @@ class ActivityMetricsLogger {
                info.windowsDrawnDelayMs,
                launchToken,
                packageOptimizationInfo.getCompilationReason(),
                packageOptimizationInfo.getCompilationFilter());
                packageOptimizationInfo.getCompilationFilter(),
                info.sourceType,
                info.sourceEventDelayMs);

        if (DEBUG_METRICS) {
            Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
@@ -970,7 +994,9 @@ class ActivityMetricsLogger {
                        : FrameworkStatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
                info.mLastLaunchedActivity.info.name,
                info.mProcessRunning,
                startupTimeMs);
                startupTimeMs,
                info.mSourceType,
                info.mSourceEventDelayMs);

        // Ends the trace started at the beginning of this function. This is located here to allow
        // the trace slice to have a noticable duration.
+1 −1
Original line number Diff line number Diff line
@@ -2512,7 +2512,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
                    targetActivity.applyOptionsLocked();
                } finally {
                    mActivityMetricsLogger.notifyActivityLaunched(launchingState,
                            START_TASK_TO_FRONT, targetActivity);
                            START_TASK_TO_FRONT, targetActivity, activityOptions);
                }

                mService.getActivityStartController().postStartActivityProcessingForLastStarter(
+2 −2
Original line number Diff line number Diff line
@@ -616,7 +616,7 @@ class ActivityStarter {
                    voiceInteractor, startFlags, doResume, options, inTask,
                    false /* restrictedBgActivity */, intentGrants);
            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
                    mLastStartActivityResult, mLastStartActivityRecord);
                    mLastStartActivityResult, mLastStartActivityRecord, options);
        } finally {
            onExecutionComplete();
        }
@@ -704,7 +704,7 @@ class ActivityStarter {
                // ActivityMetricsLogger will then wait for the windows to be drawn and populate
                // WaitResult.
                mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
                        mLastStartActivityRecord);
                        mLastStartActivityRecord, mOptions);
                return getExternalResult(mRequest.waitResult == null ? res
                        : waitForResult(res, mLastStartActivityRecord));
            }
Loading