Loading core/java/android/app/ActivityManagerInternal.java +12 −11 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.Bundle; import android.os.IBinder; import android.os.IBinder; import android.service.voice.IVoiceInteractionSession; import android.service.voice.IVoiceInteractionSession; import android.util.SparseIntArray; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.IVoiceInteractor; Loading @@ -47,9 +48,9 @@ public abstract class ActivityManagerInternal { /** /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew * the starting window. * the splash screen. */ */ public static final int APP_TRANSITION_STARTING_WINDOW = 1; public static final int APP_TRANSITION_SPLASH_SCREEN = 1; /** /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all Loading @@ -63,6 +64,12 @@ public abstract class ActivityManagerInternal { */ */ public static final int APP_TRANSITION_TIMEOUT = 3; public static final int APP_TRANSITION_TIMEOUT = 3; /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a * we drew a task snapshot. */ public static final int APP_TRANSITION_SNAPSHOT = 4; /** /** * Grant Uri permissions from one app to another. This method only extends * Grant Uri permissions from one app to another. This method only extends * permission grants if {@code callingUid} has permission to them. * permission grants if {@code callingUid} has permission to them. Loading Loading @@ -121,20 +128,14 @@ public abstract class ActivityManagerInternal { IVoiceInteractionSession mSession, IVoiceInteractionSession mSession, IVoiceInteractor mInteractor); IVoiceInteractor mInteractor); /** * Callback for window manager to let activity manager know that the starting window has been * drawn */ public abstract void notifyStartingWindowDrawn(); /** /** * Callback for window manager to let activity manager know that we are finally starting the * Callback for window manager to let activity manager know that we are finally starting the * app transition; * app transition; * * * @param reason The reason why the app transition started. Must be one of the APP_TRANSITION_* * @param reasons A map from stack id to a reason integer why the transition was started,, which * values. * must be one of the APP_TRANSITION_* values. */ */ public abstract void notifyAppTransitionStarting(int reason); public abstract void notifyAppTransitionStarting(SparseIntArray reasons); /** /** * Callback for window manager to let activity manager know that the app transition was * Callback for window manager to let activity manager know that the app transition was Loading proto/src/metrics_constants.proto +15 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,18 @@ message MetricsEvent { // The view or control was updated. // The view or control was updated. TYPE_UPDATE = 6; TYPE_UPDATE = 6; // Type for APP_TRANSITION event: The transition started a new activity for which it's process // wasn't running. TYPE_TRANSITION_COLD_LAUNCH = 7; // Type for APP_TRANSITION event: The transition started a new activity for which it's process // was already running. TYPE_TRANSITION_WARM_LAUNCH = 8; // Type for APP_TRANSITION event: The transition brought an already existing activity to the // front. TYPE_TRANSITION_HOT_LAUNCH = 9; } } // Known visual elements: views or controls. // Known visual elements: views or controls. Loading Loading @@ -3559,6 +3571,9 @@ message MetricsEvent { // ACTION: Settings > Connected devices > Bluetooth master switch Toggle // ACTION: Settings > Connected devices > Bluetooth master switch Toggle ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870; ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870; // The name of the activity being launched in an app transition event. APP_TRANSITION_ACTIVITY_NAME = 871; // ---- End O Constants, all O constants go above this line ---- // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. // Add new aosp constants above this line. Loading services/core/java/com/android/server/am/ActivityManagerService.java +2 −9 Original line number Original line Diff line number Diff line Loading @@ -22957,16 +22957,9 @@ public class ActivityManagerService extends IActivityManager.Stub } } @Override @Override public void notifyStartingWindowDrawn() { public void notifyAppTransitionStarting(SparseIntArray reasons) { synchronized (ActivityManagerService.this) { synchronized (ActivityManagerService.this) { mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(); mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reasons); } } @Override public void notifyAppTransitionStarting(int reason) { synchronized (ActivityManagerService.this) { mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason); } } } } services/core/java/com/android/server/am/ActivityMetricsLogger.java +145 −54 Original line number Original line Diff line number Diff line package com.android.server.am; package com.android.server.am; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_ACTIVITY_NAME; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import android.annotation.Nullable; import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackId; import android.content.Context; import android.content.Context; import android.metrics.LogMaker; import android.os.SystemClock; import android.os.SystemClock; import android.util.Slog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.metrics.LogMaker; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -48,12 +60,27 @@ class ActivityMetricsLogger { private long mLastLogTimeSecs; private long mLastLogTimeSecs; private final ActivityStackSupervisor mSupervisor; private final ActivityStackSupervisor mSupervisor; private final Context mContext; private final Context mContext; private final MetricsLogger mMetricsLogger = new MetricsLogger(); private long mCurrentTransitionStartTime = INVALID_START_TIME; private long mCurrentTransitionStartTime = INVALID_START_TIME; private boolean mLoggedWindowsDrawn; private boolean mLoggedStartingWindowDrawn; private int mCurrentTransitionDeviceUptime; private int mCurrentTransitionDelayMs; private boolean mLoggedTransitionStarting; private boolean mLoggedTransitionStarting; private final SparseArray<StackTransitionInfo> mStackTransitionInfo = new SparseArray<>(); private final class StackTransitionInfo { private ActivityRecord launchedActivity; private int startResult; private boolean currentTransitionProcessRunning; private int windowsDrawnDelayMs; private int startingWindowDelayMs; private int reason = APP_TRANSITION_TIMEOUT; private boolean loggedWindowsDrawn; private boolean loggedStartingWindowDrawn; } ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) { ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) { mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000; mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000; mSupervisor = supervisor; mSupervisor = supervisor; Loading Loading @@ -102,8 +129,10 @@ class ActivityMetricsLogger { * activity. * activity. */ */ void notifyActivityLaunching() { void notifyActivityLaunching() { if (!isAnyTransitionActive()) { mCurrentTransitionStartTime = System.currentTimeMillis(); mCurrentTransitionStartTime = System.currentTimeMillis(); } } } /** /** * Notifies the tracker that the activity is actually launching. * Notifies the tracker that the activity is actually launching. Loading @@ -118,9 +147,6 @@ class ActivityMetricsLogger { launchedActivity.appInfo.uid) launchedActivity.appInfo.uid) : null; : null; final boolean processRunning = processRecord != null; final boolean processRunning = processRecord != null; final String componentName = launchedActivity != null ? launchedActivity.shortComponentName : null; // We consider this a "process switch" if the process of the activity that gets launched // We consider this a "process switch" if the process of the activity that gets launched // didn't have an activity that was in started state. In this case, we assume that lot // didn't have an activity that was in started state. In this case, we assume that lot Loading @@ -129,7 +155,7 @@ class ActivityMetricsLogger { final boolean processSwitch = processRecord == null final boolean processSwitch = processRecord == null || !hasStartedActivity(processRecord, launchedActivity); || !hasStartedActivity(processRecord, launchedActivity); notifyActivityLaunched(resultCode, componentName, processRunning, processSwitch); notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch); } } private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) { private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) { Loading @@ -151,92 +177,120 @@ class ActivityMetricsLogger { * * * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the * launch * launch * @param componentName the component name of the activity being launched * @param launchedActivity the activity being launched * @param processRunning whether the process that will contains the activity is already running * @param processRunning whether the process that will contains the activity is already running * @param processSwitch whether the process that will contain the activity didn't have any * @param processSwitch whether the process that will contain the activity didn't have any * activity that was stopped, i.e. the started activity is "switching" * activity that was stopped, i.e. the started activity is "switching" * processes * processes */ */ private void notifyActivityLaunched(int resultCode, @Nullable String componentName, private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity, boolean processRunning, boolean processSwitch) { boolean processRunning, boolean processSwitch) { if (resultCode < 0 || componentName == null || !processSwitch) { // If we are already in an existing transition, only update the activity name, but not the // other attributes. final int stackId = launchedActivity != null && launchedActivity.getStack() != null ? launchedActivity.getStack().mStackId : INVALID_STACK_ID; final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (launchedActivity != null && info != null) { info.launchedActivity = launchedActivity; return; } final boolean otherStacksLaunching = mStackTransitionInfo.size() > 0 && info == null; if ((resultCode < 0 || launchedActivity == null || !processSwitch || stackId == INVALID_STACK_ID) && !otherStacksLaunching) { // Failed to launch or it was not a process switch, so we don't care about the timing. // Failed to launch or it was not a process switch, so we don't care about the timing. reset(); reset(true /* abort */); return; } else if (otherStacksLaunching) { // Don't log this stack but continue with the other stacks. return; return; } } MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_COMPONENT_NAME, final StackTransitionInfo newInfo = new StackTransitionInfo(); componentName); newInfo.launchedActivity = launchedActivity; MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, newInfo.currentTransitionProcessRunning = processRunning; processRunning); newInfo.startResult = resultCode; MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS, mStackTransitionInfo.append(stackId, newInfo); (int) (SystemClock.uptimeMillis() / 1000)); mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000); LogMaker builder = new LogMaker(MetricsEvent.APP_TRANSITION); builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName); builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0); builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS, SystemClock.uptimeMillis() / 1000); MetricsLogger.action(builder); } } /** /** * Notifies the tracker that all windows of the app have been drawn. * Notifies the tracker that all windows of the app have been drawn. */ */ void notifyWindowsDrawn() { void notifyWindowsDrawn(int stackId) { if (!isTransitionActive() || mLoggedWindowsDrawn) { final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null || info.loggedWindowsDrawn) { return; return; } } MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs = calculateCurrentDelay(); calculateCurrentDelay()); info.loggedWindowsDrawn = true; mLoggedWindowsDrawn = true; if (allStacksWindowsDrawn() && mLoggedTransitionStarting) { if (mLoggedTransitionStarting) { reset(false /* abort */); reset(); } } } } /** /** * Notifies the tracker that the starting window was drawn. * Notifies the tracker that the starting window was drawn. */ */ void notifyStartingWindowDrawn() { void notifyStartingWindowDrawn(int stackId) { if (!isTransitionActive() || mLoggedStartingWindowDrawn) { final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null || info.loggedStartingWindowDrawn) { return; return; } } mLoggedStartingWindowDrawn = true; info.loggedStartingWindowDrawn = true; MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS, info.startingWindowDelayMs = calculateCurrentDelay(); calculateCurrentDelay()); } } /** /** * Notifies the tracker that the app transition is starting. * Notifies the tracker that the app transition is starting. * * * @param reason The reason why we started it. Must be on of * @param stackIdReasons A map from stack id to a reason integer, which must be on of * ActivityManagerInternal.APP_TRANSITION_* reasons. * ActivityManagerInternal.APP_TRANSITION_* reasons. */ */ void notifyTransitionStarting(int reason) { void notifyTransitionStarting(SparseIntArray stackIdReasons) { if (!isTransitionActive() || mLoggedTransitionStarting) { if (!isAnyTransitionActive() || mLoggedTransitionStarting) { return; return; } } MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_REASON, reason); mCurrentTransitionDelayMs = calculateCurrentDelay(); MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DELAY_MS, calculateCurrentDelay()); mLoggedTransitionStarting = true; mLoggedTransitionStarting = true; if (mLoggedWindowsDrawn) { for (int index = stackIdReasons.size() - 1; index >= 0; index--) { reset(); final int stackId = stackIdReasons.keyAt(index); final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null) { continue; } info.reason = stackIdReasons.valueAt(index); } if (allStacksWindowsDrawn()) { reset(false /* abort */); } } } } private boolean isTransitionActive() { private boolean allStacksWindowsDrawn() { return mCurrentTransitionStartTime != INVALID_START_TIME; for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) { if (!mStackTransitionInfo.valueAt(index).loggedWindowsDrawn) { return false; } } return true; } private boolean isAnyTransitionActive() { return mCurrentTransitionStartTime != INVALID_START_TIME && mStackTransitionInfo.size() > 0; } } private void reset() { private void reset(boolean abort) { if (!abort && isAnyTransitionActive()) { logAppTransitionMultiEvents(); } mCurrentTransitionStartTime = INVALID_START_TIME; mCurrentTransitionStartTime = INVALID_START_TIME; mLoggedWindowsDrawn = false; mCurrentTransitionDelayMs = -1; mLoggedTransitionStarting = false; mLoggedTransitionStarting = false; mLoggedStartingWindowDrawn = false; mStackTransitionInfo.clear(); } } private int calculateCurrentDelay() { private int calculateCurrentDelay() { Loading @@ -244,4 +298,41 @@ class ActivityMetricsLogger { // Shouldn't take more than 25 days to launch an app, so int is fine here. // Shouldn't take more than 25 days to launch an app, so int is fine here. return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime); return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime); } } private void logAppTransitionMultiEvents() { for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) { final StackTransitionInfo info = mStackTransitionInfo.valueAt(index); final int type = getTransitionType(info); if (type == -1) { return; } final LogMaker builder = new LogMaker(APP_TRANSITION); builder.setPackageName(info.launchedActivity.packageName); builder.addTaggedData(APP_TRANSITION_ACTIVITY_NAME, info.launchedActivity.info.name); builder.setType(type); builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS, mCurrentTransitionDeviceUptime); builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs); builder.setSubtype(info.reason); if (info.startingWindowDelayMs != -1) { builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS, info.startingWindowDelayMs); } builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs); mMetricsLogger.write(builder); } } private int getTransitionType(StackTransitionInfo info) { if (info.currentTransitionProcessRunning) { if (info.startResult == START_SUCCESS) { return TYPE_TRANSITION_WARM_LAUNCH; } else if (info.startResult == START_TASK_TO_FRONT) { return TYPE_TRANSITION_HOT_LAUNCH; } } else if (info.startResult == START_SUCCESS) { return TYPE_TRANSITION_COLD_LAUNCH; } return -1; } } } services/core/java/com/android/server/am/ActivityRecord.java +13 −1 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE; import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE; Loading Loading @@ -890,6 +891,10 @@ final class ActivityRecord implements AppWindowContainerListener { return task != null ? (T) task.getStack() : null; return task != null ? (T) task.getStack() : null; } } private int getStackId() { return getStack() != null ? getStack().mStackId : INVALID_STACK_ID; } boolean changeWindowTranslucency(boolean toOpaque) { boolean changeWindowTranslucency(boolean toOpaque) { if (fullscreen == toOpaque) { if (fullscreen == toOpaque) { return false; return false; Loading Loading @@ -1705,10 +1710,17 @@ final class ActivityRecord implements AppWindowContainerListener { stack.mLaunchStartTime = 0; stack.mLaunchStartTime = 0; } } @Override public void onStartingWindowDrawn() { synchronized (service) { mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(getStackId()); } } @Override @Override public void onWindowsDrawn() { public void onWindowsDrawn() { synchronized (service) { synchronized (service) { mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(); mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId()); if (displayStartTime != 0) { if (displayStartTime != 0) { reportLaunchTimeLocked(SystemClock.uptimeMillis()); reportLaunchTimeLocked(SystemClock.uptimeMillis()); } } Loading Loading
core/java/android/app/ActivityManagerInternal.java +12 −11 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.os.Bundle; import android.os.IBinder; import android.os.IBinder; import android.service.voice.IVoiceInteractionSession; import android.service.voice.IVoiceInteractionSession; import android.util.SparseIntArray; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.IVoiceInteractor; Loading @@ -47,9 +48,9 @@ public abstract class ActivityManagerInternal { /** /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew * the starting window. * the splash screen. */ */ public static final int APP_TRANSITION_STARTING_WINDOW = 1; public static final int APP_TRANSITION_SPLASH_SCREEN = 1; /** /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all Loading @@ -63,6 +64,12 @@ public abstract class ActivityManagerInternal { */ */ public static final int APP_TRANSITION_TIMEOUT = 3; public static final int APP_TRANSITION_TIMEOUT = 3; /** * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a * we drew a task snapshot. */ public static final int APP_TRANSITION_SNAPSHOT = 4; /** /** * Grant Uri permissions from one app to another. This method only extends * Grant Uri permissions from one app to another. This method only extends * permission grants if {@code callingUid} has permission to them. * permission grants if {@code callingUid} has permission to them. Loading Loading @@ -121,20 +128,14 @@ public abstract class ActivityManagerInternal { IVoiceInteractionSession mSession, IVoiceInteractionSession mSession, IVoiceInteractor mInteractor); IVoiceInteractor mInteractor); /** * Callback for window manager to let activity manager know that the starting window has been * drawn */ public abstract void notifyStartingWindowDrawn(); /** /** * Callback for window manager to let activity manager know that we are finally starting the * Callback for window manager to let activity manager know that we are finally starting the * app transition; * app transition; * * * @param reason The reason why the app transition started. Must be one of the APP_TRANSITION_* * @param reasons A map from stack id to a reason integer why the transition was started,, which * values. * must be one of the APP_TRANSITION_* values. */ */ public abstract void notifyAppTransitionStarting(int reason); public abstract void notifyAppTransitionStarting(SparseIntArray reasons); /** /** * Callback for window manager to let activity manager know that the app transition was * Callback for window manager to let activity manager know that the app transition was Loading
proto/src/metrics_constants.proto +15 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,18 @@ message MetricsEvent { // The view or control was updated. // The view or control was updated. TYPE_UPDATE = 6; TYPE_UPDATE = 6; // Type for APP_TRANSITION event: The transition started a new activity for which it's process // wasn't running. TYPE_TRANSITION_COLD_LAUNCH = 7; // Type for APP_TRANSITION event: The transition started a new activity for which it's process // was already running. TYPE_TRANSITION_WARM_LAUNCH = 8; // Type for APP_TRANSITION event: The transition brought an already existing activity to the // front. TYPE_TRANSITION_HOT_LAUNCH = 9; } } // Known visual elements: views or controls. // Known visual elements: views or controls. Loading Loading @@ -3559,6 +3571,9 @@ message MetricsEvent { // ACTION: Settings > Connected devices > Bluetooth master switch Toggle // ACTION: Settings > Connected devices > Bluetooth master switch Toggle ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870; ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870; // The name of the activity being launched in an app transition event. APP_TRANSITION_ACTIVITY_NAME = 871; // ---- End O Constants, all O constants go above this line ---- // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. // Add new aosp constants above this line. Loading
services/core/java/com/android/server/am/ActivityManagerService.java +2 −9 Original line number Original line Diff line number Diff line Loading @@ -22957,16 +22957,9 @@ public class ActivityManagerService extends IActivityManager.Stub } } @Override @Override public void notifyStartingWindowDrawn() { public void notifyAppTransitionStarting(SparseIntArray reasons) { synchronized (ActivityManagerService.this) { synchronized (ActivityManagerService.this) { mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(); mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reasons); } } @Override public void notifyAppTransitionStarting(int reason) { synchronized (ActivityManagerService.this) { mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason); } } } }
services/core/java/com/android/server/am/ActivityMetricsLogger.java +145 −54 Original line number Original line Diff line number Diff line package com.android.server.am; package com.android.server.am; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_ACTIVITY_NAME; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import android.annotation.Nullable; import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackId; import android.content.Context; import android.content.Context; import android.metrics.LogMaker; import android.os.SystemClock; import android.os.SystemClock; import android.util.Slog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.metrics.LogMaker; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -48,12 +60,27 @@ class ActivityMetricsLogger { private long mLastLogTimeSecs; private long mLastLogTimeSecs; private final ActivityStackSupervisor mSupervisor; private final ActivityStackSupervisor mSupervisor; private final Context mContext; private final Context mContext; private final MetricsLogger mMetricsLogger = new MetricsLogger(); private long mCurrentTransitionStartTime = INVALID_START_TIME; private long mCurrentTransitionStartTime = INVALID_START_TIME; private boolean mLoggedWindowsDrawn; private boolean mLoggedStartingWindowDrawn; private int mCurrentTransitionDeviceUptime; private int mCurrentTransitionDelayMs; private boolean mLoggedTransitionStarting; private boolean mLoggedTransitionStarting; private final SparseArray<StackTransitionInfo> mStackTransitionInfo = new SparseArray<>(); private final class StackTransitionInfo { private ActivityRecord launchedActivity; private int startResult; private boolean currentTransitionProcessRunning; private int windowsDrawnDelayMs; private int startingWindowDelayMs; private int reason = APP_TRANSITION_TIMEOUT; private boolean loggedWindowsDrawn; private boolean loggedStartingWindowDrawn; } ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) { ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) { mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000; mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000; mSupervisor = supervisor; mSupervisor = supervisor; Loading Loading @@ -102,8 +129,10 @@ class ActivityMetricsLogger { * activity. * activity. */ */ void notifyActivityLaunching() { void notifyActivityLaunching() { if (!isAnyTransitionActive()) { mCurrentTransitionStartTime = System.currentTimeMillis(); mCurrentTransitionStartTime = System.currentTimeMillis(); } } } /** /** * Notifies the tracker that the activity is actually launching. * Notifies the tracker that the activity is actually launching. Loading @@ -118,9 +147,6 @@ class ActivityMetricsLogger { launchedActivity.appInfo.uid) launchedActivity.appInfo.uid) : null; : null; final boolean processRunning = processRecord != null; final boolean processRunning = processRecord != null; final String componentName = launchedActivity != null ? launchedActivity.shortComponentName : null; // We consider this a "process switch" if the process of the activity that gets launched // We consider this a "process switch" if the process of the activity that gets launched // didn't have an activity that was in started state. In this case, we assume that lot // didn't have an activity that was in started state. In this case, we assume that lot Loading @@ -129,7 +155,7 @@ class ActivityMetricsLogger { final boolean processSwitch = processRecord == null final boolean processSwitch = processRecord == null || !hasStartedActivity(processRecord, launchedActivity); || !hasStartedActivity(processRecord, launchedActivity); notifyActivityLaunched(resultCode, componentName, processRunning, processSwitch); notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch); } } private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) { private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) { Loading @@ -151,92 +177,120 @@ class ActivityMetricsLogger { * * * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the * launch * launch * @param componentName the component name of the activity being launched * @param launchedActivity the activity being launched * @param processRunning whether the process that will contains the activity is already running * @param processRunning whether the process that will contains the activity is already running * @param processSwitch whether the process that will contain the activity didn't have any * @param processSwitch whether the process that will contain the activity didn't have any * activity that was stopped, i.e. the started activity is "switching" * activity that was stopped, i.e. the started activity is "switching" * processes * processes */ */ private void notifyActivityLaunched(int resultCode, @Nullable String componentName, private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity, boolean processRunning, boolean processSwitch) { boolean processRunning, boolean processSwitch) { if (resultCode < 0 || componentName == null || !processSwitch) { // If we are already in an existing transition, only update the activity name, but not the // other attributes. final int stackId = launchedActivity != null && launchedActivity.getStack() != null ? launchedActivity.getStack().mStackId : INVALID_STACK_ID; final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (launchedActivity != null && info != null) { info.launchedActivity = launchedActivity; return; } final boolean otherStacksLaunching = mStackTransitionInfo.size() > 0 && info == null; if ((resultCode < 0 || launchedActivity == null || !processSwitch || stackId == INVALID_STACK_ID) && !otherStacksLaunching) { // Failed to launch or it was not a process switch, so we don't care about the timing. // Failed to launch or it was not a process switch, so we don't care about the timing. reset(); reset(true /* abort */); return; } else if (otherStacksLaunching) { // Don't log this stack but continue with the other stacks. return; return; } } MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_COMPONENT_NAME, final StackTransitionInfo newInfo = new StackTransitionInfo(); componentName); newInfo.launchedActivity = launchedActivity; MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, newInfo.currentTransitionProcessRunning = processRunning; processRunning); newInfo.startResult = resultCode; MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS, mStackTransitionInfo.append(stackId, newInfo); (int) (SystemClock.uptimeMillis() / 1000)); mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000); LogMaker builder = new LogMaker(MetricsEvent.APP_TRANSITION); builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName); builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0); builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS, SystemClock.uptimeMillis() / 1000); MetricsLogger.action(builder); } } /** /** * Notifies the tracker that all windows of the app have been drawn. * Notifies the tracker that all windows of the app have been drawn. */ */ void notifyWindowsDrawn() { void notifyWindowsDrawn(int stackId) { if (!isTransitionActive() || mLoggedWindowsDrawn) { final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null || info.loggedWindowsDrawn) { return; return; } } MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs = calculateCurrentDelay(); calculateCurrentDelay()); info.loggedWindowsDrawn = true; mLoggedWindowsDrawn = true; if (allStacksWindowsDrawn() && mLoggedTransitionStarting) { if (mLoggedTransitionStarting) { reset(false /* abort */); reset(); } } } } /** /** * Notifies the tracker that the starting window was drawn. * Notifies the tracker that the starting window was drawn. */ */ void notifyStartingWindowDrawn() { void notifyStartingWindowDrawn(int stackId) { if (!isTransitionActive() || mLoggedStartingWindowDrawn) { final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null || info.loggedStartingWindowDrawn) { return; return; } } mLoggedStartingWindowDrawn = true; info.loggedStartingWindowDrawn = true; MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS, info.startingWindowDelayMs = calculateCurrentDelay(); calculateCurrentDelay()); } } /** /** * Notifies the tracker that the app transition is starting. * Notifies the tracker that the app transition is starting. * * * @param reason The reason why we started it. Must be on of * @param stackIdReasons A map from stack id to a reason integer, which must be on of * ActivityManagerInternal.APP_TRANSITION_* reasons. * ActivityManagerInternal.APP_TRANSITION_* reasons. */ */ void notifyTransitionStarting(int reason) { void notifyTransitionStarting(SparseIntArray stackIdReasons) { if (!isTransitionActive() || mLoggedTransitionStarting) { if (!isAnyTransitionActive() || mLoggedTransitionStarting) { return; return; } } MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_REASON, reason); mCurrentTransitionDelayMs = calculateCurrentDelay(); MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DELAY_MS, calculateCurrentDelay()); mLoggedTransitionStarting = true; mLoggedTransitionStarting = true; if (mLoggedWindowsDrawn) { for (int index = stackIdReasons.size() - 1; index >= 0; index--) { reset(); final int stackId = stackIdReasons.keyAt(index); final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null) { continue; } info.reason = stackIdReasons.valueAt(index); } if (allStacksWindowsDrawn()) { reset(false /* abort */); } } } } private boolean isTransitionActive() { private boolean allStacksWindowsDrawn() { return mCurrentTransitionStartTime != INVALID_START_TIME; for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) { if (!mStackTransitionInfo.valueAt(index).loggedWindowsDrawn) { return false; } } return true; } private boolean isAnyTransitionActive() { return mCurrentTransitionStartTime != INVALID_START_TIME && mStackTransitionInfo.size() > 0; } } private void reset() { private void reset(boolean abort) { if (!abort && isAnyTransitionActive()) { logAppTransitionMultiEvents(); } mCurrentTransitionStartTime = INVALID_START_TIME; mCurrentTransitionStartTime = INVALID_START_TIME; mLoggedWindowsDrawn = false; mCurrentTransitionDelayMs = -1; mLoggedTransitionStarting = false; mLoggedTransitionStarting = false; mLoggedStartingWindowDrawn = false; mStackTransitionInfo.clear(); } } private int calculateCurrentDelay() { private int calculateCurrentDelay() { Loading @@ -244,4 +298,41 @@ class ActivityMetricsLogger { // Shouldn't take more than 25 days to launch an app, so int is fine here. // Shouldn't take more than 25 days to launch an app, so int is fine here. return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime); return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime); } } private void logAppTransitionMultiEvents() { for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) { final StackTransitionInfo info = mStackTransitionInfo.valueAt(index); final int type = getTransitionType(info); if (type == -1) { return; } final LogMaker builder = new LogMaker(APP_TRANSITION); builder.setPackageName(info.launchedActivity.packageName); builder.addTaggedData(APP_TRANSITION_ACTIVITY_NAME, info.launchedActivity.info.name); builder.setType(type); builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS, mCurrentTransitionDeviceUptime); builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs); builder.setSubtype(info.reason); if (info.startingWindowDelayMs != -1) { builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS, info.startingWindowDelayMs); } builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs); mMetricsLogger.write(builder); } } private int getTransitionType(StackTransitionInfo info) { if (info.currentTransitionProcessRunning) { if (info.startResult == START_SUCCESS) { return TYPE_TRANSITION_WARM_LAUNCH; } else if (info.startResult == START_TASK_TO_FRONT) { return TYPE_TRANSITION_HOT_LAUNCH; } } else if (info.startResult == START_SUCCESS) { return TYPE_TRANSITION_COLD_LAUNCH; } return -1; } } }
services/core/java/com/android/server/am/ActivityRecord.java +13 −1 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE; import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE; Loading Loading @@ -890,6 +891,10 @@ final class ActivityRecord implements AppWindowContainerListener { return task != null ? (T) task.getStack() : null; return task != null ? (T) task.getStack() : null; } } private int getStackId() { return getStack() != null ? getStack().mStackId : INVALID_STACK_ID; } boolean changeWindowTranslucency(boolean toOpaque) { boolean changeWindowTranslucency(boolean toOpaque) { if (fullscreen == toOpaque) { if (fullscreen == toOpaque) { return false; return false; Loading Loading @@ -1705,10 +1710,17 @@ final class ActivityRecord implements AppWindowContainerListener { stack.mLaunchStartTime = 0; stack.mLaunchStartTime = 0; } } @Override public void onStartingWindowDrawn() { synchronized (service) { mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(getStackId()); } } @Override @Override public void onWindowsDrawn() { public void onWindowsDrawn() { synchronized (service) { synchronized (service) { mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(); mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId()); if (displayStartTime != 0) { if (displayStartTime != 0) { reportLaunchTimeLocked(SystemClock.uptimeMillis()); reportLaunchTimeLocked(SystemClock.uptimeMillis()); } } Loading