Loading services/core/java/com/android/server/wm/ActivityMetricsLogger.java +46 −24 Original line number Diff line number Diff line Loading @@ -127,6 +127,12 @@ class ActivityMetricsLogger { private static final int WINDOW_STATE_MULTI_WINDOW = 4; private static final int WINDOW_STATE_INVALID = -1; /** * If a launching activity isn't visible within this duration when the device is sleeping, e.g. * keyguard is locked, its transition info will be dropped. */ private static final long UNKNOWN_VISIBILITY_CHECK_DELAY_MS = 3000; /** * The flag for {@link #notifyActivityLaunching} to skip associating a new launch with an active * transition, in the case the launch is standalone (e.g. from recents). Loading Loading @@ -300,8 +306,7 @@ class ActivityMetricsLogger { } /** @return {@code true} if the activity matches a launched activity in this transition. */ boolean contains(WindowContainer wc) { final ActivityRecord r = AppTransitionController.getAppFromContainer(wc); boolean contains(ActivityRecord r) { return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r)); } Loading Loading @@ -473,10 +478,10 @@ class ActivityMetricsLogger { /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */ @Nullable private TransitionInfo getActiveTransitionInfo(WindowContainer wc) { private TransitionInfo getActiveTransitionInfo(ActivityRecord r) { for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) { final TransitionInfo info = mTransitionInfoList.get(i); if (info.contains(wc)) { if (info.contains(r)) { return info; } } Loading Loading @@ -587,8 +592,8 @@ class ActivityMetricsLogger { return; } if (info != null && info.mLastLaunchedActivity.mDisplayContent == launchedActivity.mDisplayContent) { final DisplayContent targetDisplay = launchedActivity.mDisplayContent; if (info != null && info.mLastLaunchedActivity.mDisplayContent == targetDisplay) { // If we are already in an existing transition on the same display, only update the // activity name, but not the other attributes. Loading Loading @@ -616,6 +621,13 @@ class ActivityMetricsLogger { // As abort for no process switch. launchObserverNotifyIntentFailed(); } if (targetDisplay.isSleeping()) { // It is unknown whether the activity can be drawn or not, e.g. ut depends on the // keyguard states and the attributes or flags set by the activity. If the activity // keeps invisible in the grace period, the tracker will be cancelled so it won't get // a very long launch time that takes unlocking as the end of launch. scheduleCheckActivityToBeDrawn(launchedActivity, UNKNOWN_VISIBILITY_CHECK_DELAY_MS); } } /** Loading Loading @@ -667,12 +679,15 @@ class ActivityMetricsLogger { * ActivityTaskManagerInternal.APP_TRANSITION_* reasons. */ void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) { if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting"); if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting " + activityToReason); final long timestampNs = SystemClock.elapsedRealtimeNanos(); for (int index = activityToReason.size() - 1; index >= 0; index--) { final WindowContainer wc = activityToReason.keyAt(index); final TransitionInfo info = getActiveTransitionInfo(wc); final WindowContainer<?> wc = activityToReason.keyAt(index); final ActivityRecord activity = wc.asActivityRecord(); final ActivityRecord r = activity != null ? activity : wc.getTopActivity(false /* includeFinishing */, true /* includeOverlays */); final TransitionInfo info = getActiveTransitionInfo(r); if (info == null || info.mLoggedTransitionStarting) { // Ignore any subsequent notifyTransitionStarting. continue; Loading Loading @@ -717,26 +732,32 @@ class ActivityMetricsLogger { Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested + " state=" + r.getState() + " finishing=" + r.finishing); } if (r.isState(Task.ActivityState.RESUMED) && r.mDisplayContent.isSleeping()) { // The activity may be launching while keyguard is locked. The keyguard may be dismissed // after the activity finished relayout, so skip the visibility check to avoid aborting // the tracking of launch event. return; } if (!r.mVisibleRequested || r.finishing) { info.removePendingDrawActivity(r); if (info.mLastLaunchedActivity == r) { // Check if the tracker can be cancelled because the last launched activity may be // no longer visible. scheduleCheckActivityToBeDrawn(r, 0 /* delay */); } if (info.mLastLaunchedActivity != r) { return; } // The activity and its task are passed separately because the activity may be removed from // the task later. r.mAtmService.mH.sendMessage(PooledLambda.obtainMessage( ActivityMetricsLogger::checkVisibility, this, r.getTask(), r)); } /** @return {@code true} if the given task has an activity will be drawn. */ private static boolean hasActivityToBeDrawn(Task t) { return t.forAllActivities(r -> r.mVisibleRequested && !r.isReportedDrawn() && !r.finishing); private void scheduleCheckActivityToBeDrawn(@NonNull ActivityRecord r, long delay) { // The activity and its task are passed separately because it is possible that the activity // is removed from the task later. r.mAtmService.mH.sendMessageDelayed(PooledLambda.obtainMessage( ActivityMetricsLogger::checkActivityToBeDrawn, this, r.getTask(), r), delay); } private void checkVisibility(Task t, ActivityRecord r) { /** Cancels the tracking of launch if there won't be an activity to be drawn. */ private void checkActivityToBeDrawn(Task t, ActivityRecord r) { synchronized (mSupervisor.mService.mGlobalLock) { final TransitionInfo info = getActiveTransitionInfo(r); // If we have an active transition that's waiting on a certain activity that will be Loading @@ -757,13 +778,14 @@ class ActivityMetricsLogger { // window drawn event should report later to complete the transition. Otherwise all // activities in this task may be finished, invisible or drawn, so the transition event // should be cancelled. if (hasActivityToBeDrawn(t)) { if (t.forAllActivities( a -> a.mVisibleRequested && !a.isReportedDrawn() && !a.finishing)) { return; } if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible activity=" + r); if (DEBUG_METRICS) Slog.i(TAG, "checkActivityToBeDrawn cancels activity=" + r); logAppTransitionCancel(info); abort(info, "notifyVisibilityChanged to invisible"); abort(info, "checkActivityToBeDrawn (invisible or drawn already)"); } } Loading services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +14 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import android.app.ActivityOptions; Loading Loading @@ -250,6 +251,19 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(noDrawnActivity)); } @Test public void testOnActivityLaunchWhileSleeping() { notifyActivityLaunching(mTopActivity.intent); notifyActivityLaunched(START_SUCCESS, mTopActivity); doReturn(true).when(mTopActivity.mDisplayContent).isSleeping(); mTopActivity.setState(Task.ActivityState.RESUMED, "test"); mTopActivity.setVisibility(false); waitHandlerIdle(mAtm.mH); // Not cancel immediately because in one of real cases, the keyguard may be going away or // occluded later, then the activity can be drawn. verify(mLaunchObserver, never()).onActivityLaunchCancelled(eqProto(mTopActivity)); } @Test public void testOnReportFullyDrawn() { mActivityOptions = ActivityOptions.makeBasic(); Loading Loading
services/core/java/com/android/server/wm/ActivityMetricsLogger.java +46 −24 Original line number Diff line number Diff line Loading @@ -127,6 +127,12 @@ class ActivityMetricsLogger { private static final int WINDOW_STATE_MULTI_WINDOW = 4; private static final int WINDOW_STATE_INVALID = -1; /** * If a launching activity isn't visible within this duration when the device is sleeping, e.g. * keyguard is locked, its transition info will be dropped. */ private static final long UNKNOWN_VISIBILITY_CHECK_DELAY_MS = 3000; /** * The flag for {@link #notifyActivityLaunching} to skip associating a new launch with an active * transition, in the case the launch is standalone (e.g. from recents). Loading Loading @@ -300,8 +306,7 @@ class ActivityMetricsLogger { } /** @return {@code true} if the activity matches a launched activity in this transition. */ boolean contains(WindowContainer wc) { final ActivityRecord r = AppTransitionController.getAppFromContainer(wc); boolean contains(ActivityRecord r) { return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r)); } Loading Loading @@ -473,10 +478,10 @@ class ActivityMetricsLogger { /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */ @Nullable private TransitionInfo getActiveTransitionInfo(WindowContainer wc) { private TransitionInfo getActiveTransitionInfo(ActivityRecord r) { for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) { final TransitionInfo info = mTransitionInfoList.get(i); if (info.contains(wc)) { if (info.contains(r)) { return info; } } Loading Loading @@ -587,8 +592,8 @@ class ActivityMetricsLogger { return; } if (info != null && info.mLastLaunchedActivity.mDisplayContent == launchedActivity.mDisplayContent) { final DisplayContent targetDisplay = launchedActivity.mDisplayContent; if (info != null && info.mLastLaunchedActivity.mDisplayContent == targetDisplay) { // If we are already in an existing transition on the same display, only update the // activity name, but not the other attributes. Loading Loading @@ -616,6 +621,13 @@ class ActivityMetricsLogger { // As abort for no process switch. launchObserverNotifyIntentFailed(); } if (targetDisplay.isSleeping()) { // It is unknown whether the activity can be drawn or not, e.g. ut depends on the // keyguard states and the attributes or flags set by the activity. If the activity // keeps invisible in the grace period, the tracker will be cancelled so it won't get // a very long launch time that takes unlocking as the end of launch. scheduleCheckActivityToBeDrawn(launchedActivity, UNKNOWN_VISIBILITY_CHECK_DELAY_MS); } } /** Loading Loading @@ -667,12 +679,15 @@ class ActivityMetricsLogger { * ActivityTaskManagerInternal.APP_TRANSITION_* reasons. */ void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) { if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting"); if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting " + activityToReason); final long timestampNs = SystemClock.elapsedRealtimeNanos(); for (int index = activityToReason.size() - 1; index >= 0; index--) { final WindowContainer wc = activityToReason.keyAt(index); final TransitionInfo info = getActiveTransitionInfo(wc); final WindowContainer<?> wc = activityToReason.keyAt(index); final ActivityRecord activity = wc.asActivityRecord(); final ActivityRecord r = activity != null ? activity : wc.getTopActivity(false /* includeFinishing */, true /* includeOverlays */); final TransitionInfo info = getActiveTransitionInfo(r); if (info == null || info.mLoggedTransitionStarting) { // Ignore any subsequent notifyTransitionStarting. continue; Loading Loading @@ -717,26 +732,32 @@ class ActivityMetricsLogger { Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested + " state=" + r.getState() + " finishing=" + r.finishing); } if (r.isState(Task.ActivityState.RESUMED) && r.mDisplayContent.isSleeping()) { // The activity may be launching while keyguard is locked. The keyguard may be dismissed // after the activity finished relayout, so skip the visibility check to avoid aborting // the tracking of launch event. return; } if (!r.mVisibleRequested || r.finishing) { info.removePendingDrawActivity(r); if (info.mLastLaunchedActivity == r) { // Check if the tracker can be cancelled because the last launched activity may be // no longer visible. scheduleCheckActivityToBeDrawn(r, 0 /* delay */); } if (info.mLastLaunchedActivity != r) { return; } // The activity and its task are passed separately because the activity may be removed from // the task later. r.mAtmService.mH.sendMessage(PooledLambda.obtainMessage( ActivityMetricsLogger::checkVisibility, this, r.getTask(), r)); } /** @return {@code true} if the given task has an activity will be drawn. */ private static boolean hasActivityToBeDrawn(Task t) { return t.forAllActivities(r -> r.mVisibleRequested && !r.isReportedDrawn() && !r.finishing); private void scheduleCheckActivityToBeDrawn(@NonNull ActivityRecord r, long delay) { // The activity and its task are passed separately because it is possible that the activity // is removed from the task later. r.mAtmService.mH.sendMessageDelayed(PooledLambda.obtainMessage( ActivityMetricsLogger::checkActivityToBeDrawn, this, r.getTask(), r), delay); } private void checkVisibility(Task t, ActivityRecord r) { /** Cancels the tracking of launch if there won't be an activity to be drawn. */ private void checkActivityToBeDrawn(Task t, ActivityRecord r) { synchronized (mSupervisor.mService.mGlobalLock) { final TransitionInfo info = getActiveTransitionInfo(r); // If we have an active transition that's waiting on a certain activity that will be Loading @@ -757,13 +778,14 @@ class ActivityMetricsLogger { // window drawn event should report later to complete the transition. Otherwise all // activities in this task may be finished, invisible or drawn, so the transition event // should be cancelled. if (hasActivityToBeDrawn(t)) { if (t.forAllActivities( a -> a.mVisibleRequested && !a.isReportedDrawn() && !a.finishing)) { return; } if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible activity=" + r); if (DEBUG_METRICS) Slog.i(TAG, "checkActivityToBeDrawn cancels activity=" + r); logAppTransitionCancel(info); abort(info, "notifyVisibilityChanged to invisible"); abort(info, "checkActivityToBeDrawn (invisible or drawn already)"); } } Loading
services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +14 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import android.app.ActivityOptions; Loading Loading @@ -250,6 +251,19 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(noDrawnActivity)); } @Test public void testOnActivityLaunchWhileSleeping() { notifyActivityLaunching(mTopActivity.intent); notifyActivityLaunched(START_SUCCESS, mTopActivity); doReturn(true).when(mTopActivity.mDisplayContent).isSleeping(); mTopActivity.setState(Task.ActivityState.RESUMED, "test"); mTopActivity.setVisibility(false); waitHandlerIdle(mAtm.mH); // Not cancel immediately because in one of real cases, the keyguard may be going away or // occluded later, then the activity can be drawn. verify(mLaunchObserver, never()).onActivityLaunchCancelled(eqProto(mTopActivity)); } @Test public void testOnReportFullyDrawn() { mActivityOptions = ActivityOptions.makeBasic(); Loading