Loading core/java/android/app/IActivityManager.aidl +9 −0 Original line number Diff line number Diff line Loading @@ -756,6 +756,15 @@ interface IActivityManager { */ void addStartInfoTimestamp(int key, long timestampNs, int userId); /** * Reports view related timestamps to be added to the calling apps most * recent {@link ApplicationStartInfo}. * * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start * @param framePresentedTimeNs Clock monotonic time in nanoseconds of frame presented */ oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs); /** * Return a list of {@link ApplicationExitInfo} records. * Loading core/java/android/view/ViewRootImpl.java +87 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.hardware.SyncFence; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManagerGlobal; Loading Loading @@ -219,6 +220,7 @@ import android.util.proto.ProtoOutputStream; import android.view.InputDevice.InputSourceClass; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl.Transaction; import android.view.SurfaceControl.TransactionStats; import android.view.View.AttachInfo; import android.view.View.FocusDirection; import android.view.View.MeasureSpec; Loading Loading @@ -293,6 +295,7 @@ import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Predicate; /** * The top of a view hierarchy, implementing the needed protocol between View Loading Loading @@ -1189,6 +1192,13 @@ public final class ViewRootImpl implements ViewParent, private String mFpsTraceName; private String mLargestViewTraceName; private final boolean mAppStartInfoTimestampsFlagValue; @GuardedBy("this") private boolean mAppStartTimestampsSent = false; private boolean mAppStartTrackingStarted = false; private long mRenderThreadDrawStartTimeNs = -1; private long mFirstFramePresentedTimeNs = -1; private static boolean sToolkitSetFrameRateReadOnlyFlagValue; private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue; private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; Loading Loading @@ -1306,6 +1316,8 @@ public final class ViewRootImpl implements ViewParent, } else { mSensitiveContentProtectionService = null; } mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps(); } public static void addFirstDrawHandler(Runnable callback) { Loading Loading @@ -2578,6 +2590,12 @@ public final class ViewRootImpl implements ViewParent, notifySurfaceDestroyed(); } destroySurface(); // Reset so they can be sent again for warm starts. mAppStartTimestampsSent = false; mAppStartTrackingStarted = false; mRenderThreadDrawStartTimeNs = -1; mFirstFramePresentedTimeNs = -1; } } } Loading Loading @@ -4376,6 +4394,30 @@ public final class ViewRootImpl implements ViewParent, reportDrawFinished(t, seqId); } }); // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if // {link mTransactionCompletedTimeNs} has already been set. if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) { mAppStartTrackingStarted = true; Transaction transaction = new Transaction(); transaction.addTransactionCompletedListener(mExecutor, new Consumer<TransactionStats>() { @Override public void accept(TransactionStats transactionStats) { SyncFence presentFence = transactionStats.getPresentFence(); if (presentFence.awaitForever()) { if (mFirstFramePresentedTimeNs == -1) { // Only trigger once per {@link ViewRootImpl} instance. mFirstFramePresentedTimeNs = presentFence.getSignalTime(); maybeSendAppStartTimes(); } } presentFence.close(); } }); applyTransactionOnDraw(transaction); } if (DEBUG_BLAST) { Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName()); } Loading @@ -4383,6 +4425,45 @@ public final class ViewRootImpl implements ViewParent, mWmsRequestSyncGroup.add(this, null /* runnable */); } private void maybeSendAppStartTimes() { synchronized (this) { if (mAppStartTimestampsSent) { // Don't send timestamps more than once. return; } // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not // post to main thread and check if we have it there. if (mRenderThreadDrawStartTimeNs != -1) { sendAppStartTimesLocked(); } else { mHandler.post(new Runnable() { @Override public void run() { synchronized (ViewRootImpl.this) { if (mRenderThreadDrawStartTimeNs == -1) { return; } sendAppStartTimesLocked(); } } }); } } } @GuardedBy("this") private void sendAppStartTimesLocked() { try { ActivityManager.getService().reportStartInfoViewTimestamps( mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs); mAppStartTimestampsSent = true; } catch (RemoteException e) { // Ignore, timestamps may be lost. if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e); } } /** * Helper used to notify the service to block projection when a sensitive * view (the view displays sensitive content) is attached to the window. Loading Loading @@ -5569,7 +5650,13 @@ public final class ViewRootImpl implements ViewParent, registerCallbackForPendingTransactions(); } long timeNs = SystemClock.uptimeNanos(); mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); // Only trigger once per {@link ViewRootImpl} instance. if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) { mRenderThreadDrawStartTimeNs = timeNs; } } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface Loading services/core/java/com/android/server/am/ActivityManagerService.java +13 −0 Original line number Diff line number Diff line Loading @@ -10237,6 +10237,19 @@ public class ActivityManagerService extends IActivityManager.Stub addStartInfoTimestampInternal(key, timestampNs, userId, callingUid); } @Override public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs) { int callingUid = Binder.getCallingUid(); int userId = UserHandle.getUserId(callingUid); addStartInfoTimestampInternal( ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME, renderThreadDrawStartTimeNs, userId, callingUid); addStartInfoTimestampInternal( ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE, framePresentedTimeNs, userId, callingUid); } private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) { mProcessList.getAppStartInfoTracker().addTimestampToStart( Settings.getPackageNameForUid(mContext, uid), Loading services/core/java/com/android/server/am/AppStartInfoTracker.java +50 −14 Original line number Diff line number Diff line Loading @@ -1195,31 +1195,67 @@ public final class AppStartInfoTracker { // Records are sorted newest to oldest, grab record at index 0. ApplicationStartInfo startInfo = mInfos.get(0); if (!isAddTimestampAllowed(startInfo, key, timestampNs)) { return; } startInfo.addStartupTimestamp(key, timestampNs); if (key == ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME && android.app.Flags.appStartInfoTimestamps()) { startInfo.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); checkCompletenessAndCallback(startInfo); } } private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key, long timestampNs) { int startupState = startInfo.getStartupState(); // If startup state is error then don't accept any further timestamps. if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) { if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps."); return; return false; } // If startup state is first frame drawn then only accept fully drawn timestamp. if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) { if (DEBUG) { Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully " + "drawn, not accepting new timestamps."); Map<Integer, Long> timestamps = startInfo.getStartupTimestamps(); if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) { switch (key) { case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN: // Allowed, continue to confirm it's not already added. break; case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME: Long firstFrameTimeNs = timestamps .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME); if (firstFrameTimeNs == null) { // This should never happen. State can't be first frame drawn if first // frame timestamp was not provided. return false; } return; if (timestampNs > firstFrameTimeNs) { // Initial renderthread frame has to occur before first frame. return false; } startInfo.addStartupTimestamp(key, timestampNs); // Allowed, continue to confirm it's not already added. break; case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE: // Allowed, continue to confirm it's not already added. break; default: return false; } } if (key == ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME && android.app.Flags.appStartInfoTimestamps()) { startInfo.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); checkCompletenessAndCallback(startInfo); if (timestamps.get(key) != null) { // Timestamp should not occur more than once for a given start. return false; } return true; } @GuardedBy("mLock") Loading Loading
core/java/android/app/IActivityManager.aidl +9 −0 Original line number Diff line number Diff line Loading @@ -756,6 +756,15 @@ interface IActivityManager { */ void addStartInfoTimestamp(int key, long timestampNs, int userId); /** * Reports view related timestamps to be added to the calling apps most * recent {@link ApplicationStartInfo}. * * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start * @param framePresentedTimeNs Clock monotonic time in nanoseconds of frame presented */ oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs); /** * Return a list of {@link ApplicationExitInfo} records. * Loading
core/java/android/view/ViewRootImpl.java +87 −0 Original line number Diff line number Diff line Loading @@ -177,6 +177,7 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.hardware.SyncFence; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManagerGlobal; Loading Loading @@ -219,6 +220,7 @@ import android.util.proto.ProtoOutputStream; import android.view.InputDevice.InputSourceClass; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl.Transaction; import android.view.SurfaceControl.TransactionStats; import android.view.View.AttachInfo; import android.view.View.FocusDirection; import android.view.View.MeasureSpec; Loading Loading @@ -293,6 +295,7 @@ import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Predicate; /** * The top of a view hierarchy, implementing the needed protocol between View Loading Loading @@ -1189,6 +1192,13 @@ public final class ViewRootImpl implements ViewParent, private String mFpsTraceName; private String mLargestViewTraceName; private final boolean mAppStartInfoTimestampsFlagValue; @GuardedBy("this") private boolean mAppStartTimestampsSent = false; private boolean mAppStartTrackingStarted = false; private long mRenderThreadDrawStartTimeNs = -1; private long mFirstFramePresentedTimeNs = -1; private static boolean sToolkitSetFrameRateReadOnlyFlagValue; private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue; private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; Loading Loading @@ -1306,6 +1316,8 @@ public final class ViewRootImpl implements ViewParent, } else { mSensitiveContentProtectionService = null; } mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps(); } public static void addFirstDrawHandler(Runnable callback) { Loading Loading @@ -2578,6 +2590,12 @@ public final class ViewRootImpl implements ViewParent, notifySurfaceDestroyed(); } destroySurface(); // Reset so they can be sent again for warm starts. mAppStartTimestampsSent = false; mAppStartTrackingStarted = false; mRenderThreadDrawStartTimeNs = -1; mFirstFramePresentedTimeNs = -1; } } } Loading Loading @@ -4376,6 +4394,30 @@ public final class ViewRootImpl implements ViewParent, reportDrawFinished(t, seqId); } }); // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if // {link mTransactionCompletedTimeNs} has already been set. if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) { mAppStartTrackingStarted = true; Transaction transaction = new Transaction(); transaction.addTransactionCompletedListener(mExecutor, new Consumer<TransactionStats>() { @Override public void accept(TransactionStats transactionStats) { SyncFence presentFence = transactionStats.getPresentFence(); if (presentFence.awaitForever()) { if (mFirstFramePresentedTimeNs == -1) { // Only trigger once per {@link ViewRootImpl} instance. mFirstFramePresentedTimeNs = presentFence.getSignalTime(); maybeSendAppStartTimes(); } } presentFence.close(); } }); applyTransactionOnDraw(transaction); } if (DEBUG_BLAST) { Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName()); } Loading @@ -4383,6 +4425,45 @@ public final class ViewRootImpl implements ViewParent, mWmsRequestSyncGroup.add(this, null /* runnable */); } private void maybeSendAppStartTimes() { synchronized (this) { if (mAppStartTimestampsSent) { // Don't send timestamps more than once. return; } // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not // post to main thread and check if we have it there. if (mRenderThreadDrawStartTimeNs != -1) { sendAppStartTimesLocked(); } else { mHandler.post(new Runnable() { @Override public void run() { synchronized (ViewRootImpl.this) { if (mRenderThreadDrawStartTimeNs == -1) { return; } sendAppStartTimesLocked(); } } }); } } } @GuardedBy("this") private void sendAppStartTimesLocked() { try { ActivityManager.getService().reportStartInfoViewTimestamps( mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs); mAppStartTimestampsSent = true; } catch (RemoteException e) { // Ignore, timestamps may be lost. if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e); } } /** * Helper used to notify the service to block projection when a sensitive * view (the view displays sensitive content) is attached to the window. Loading Loading @@ -5569,7 +5650,13 @@ public final class ViewRootImpl implements ViewParent, registerCallbackForPendingTransactions(); } long timeNs = SystemClock.uptimeNanos(); mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); // Only trigger once per {@link ViewRootImpl} instance. if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) { mRenderThreadDrawStartTimeNs = timeNs; } } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface Loading
services/core/java/com/android/server/am/ActivityManagerService.java +13 −0 Original line number Diff line number Diff line Loading @@ -10237,6 +10237,19 @@ public class ActivityManagerService extends IActivityManager.Stub addStartInfoTimestampInternal(key, timestampNs, userId, callingUid); } @Override public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs) { int callingUid = Binder.getCallingUid(); int userId = UserHandle.getUserId(callingUid); addStartInfoTimestampInternal( ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME, renderThreadDrawStartTimeNs, userId, callingUid); addStartInfoTimestampInternal( ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE, framePresentedTimeNs, userId, callingUid); } private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) { mProcessList.getAppStartInfoTracker().addTimestampToStart( Settings.getPackageNameForUid(mContext, uid), Loading
services/core/java/com/android/server/am/AppStartInfoTracker.java +50 −14 Original line number Diff line number Diff line Loading @@ -1195,31 +1195,67 @@ public final class AppStartInfoTracker { // Records are sorted newest to oldest, grab record at index 0. ApplicationStartInfo startInfo = mInfos.get(0); if (!isAddTimestampAllowed(startInfo, key, timestampNs)) { return; } startInfo.addStartupTimestamp(key, timestampNs); if (key == ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME && android.app.Flags.appStartInfoTimestamps()) { startInfo.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); checkCompletenessAndCallback(startInfo); } } private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key, long timestampNs) { int startupState = startInfo.getStartupState(); // If startup state is error then don't accept any further timestamps. if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) { if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps."); return; return false; } // If startup state is first frame drawn then only accept fully drawn timestamp. if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) { if (DEBUG) { Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully " + "drawn, not accepting new timestamps."); Map<Integer, Long> timestamps = startInfo.getStartupTimestamps(); if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) { switch (key) { case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN: // Allowed, continue to confirm it's not already added. break; case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME: Long firstFrameTimeNs = timestamps .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME); if (firstFrameTimeNs == null) { // This should never happen. State can't be first frame drawn if first // frame timestamp was not provided. return false; } return; if (timestampNs > firstFrameTimeNs) { // Initial renderthread frame has to occur before first frame. return false; } startInfo.addStartupTimestamp(key, timestampNs); // Allowed, continue to confirm it's not already added. break; case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE: // Allowed, continue to confirm it's not already added. break; default: return false; } } if (key == ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME && android.app.Flags.appStartInfoTimestamps()) { startInfo.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); checkCompletenessAndCallback(startInfo); if (timestamps.get(key) != null) { // Timestamp should not occur more than once for a given start. return false; } return true; } @GuardedBy("mLock") Loading