diff options
10 files changed, 259 insertions, 139 deletions
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 9a47553bf446..ede13ef66ac4 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -873,6 +873,52 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> return null; } + ActivityRecord topRunningActivity() { + return topRunningActivity(false /* considerKeyguardState */); + } + + /** + * Returns the top running activity in the focused stack. In the case the focused stack has no + * such activity, the next focusable stack on this display is returned. + * + * @param considerKeyguardState Indicates whether the locked state should be considered. if + * {@code true} and the keyguard is locked, only activities that + * can be shown on top of the keyguard will be considered. + * @return The top running activity. {@code null} if none is available. + */ + ActivityRecord topRunningActivity(boolean considerKeyguardState) { + ActivityRecord topRunning = null; + final ActivityStack focusedStack = getFocusedStack(); + if (focusedStack != null) { + topRunning = focusedStack.topRunningActivityLocked(); + } + + // Look in other focusable stacks. + if (topRunning == null) { + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack stack = mStacks.get(i); + // Only consider focusable stacks other than the current focused one. + if (stack == focusedStack || !stack.isFocusable()) { + continue; + } + topRunning = stack.topRunningActivityLocked(); + if (topRunning != null) { + break; + } + } + } + + // This activity can be considered the top running activity if we are not considering + // the locked state, the keyguard isn't locked, or we can show when locked. + if (topRunning != null && considerKeyguardState + && mSupervisor.getKeyguardController().isKeyguardLocked() + && !topRunning.canShowWhenLocked()) { + return null; + } + + return topRunning; + } + int getIndexOf(ActivityStack stack) { return mStacks.indexOf(stack); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 12ed726eca77..026c5cc3017d 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3877,8 +3877,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // The activity that we are finishing may be over the lock screen. In this case, we do not // want to consider activities that cannot be shown on the lock screen as running and should // proceed with finishing the activity if there is no valid next top running activity. - final ActivityRecord next = mStackSupervisor.topRunningActivityLocked( - true /* considerKeyguardState */); + final ActivityDisplay display = getDisplay(); + final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */); if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible) && next != null && !next.nowVisible) { @@ -3902,23 +3902,25 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r); r.setState(FINISHING, "finishCurrentActivityLocked"); - final boolean finishingActivityInNonFocusedStack - = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack() - && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE; + final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE + && prevState == PAUSED && (r.getStack() != display.getFocusedStack() + || (next == null && display.topRunningActivity() == null)); if (mode == FINISH_IMMEDIATELY || (prevState == PAUSED && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode())) - || finishingActivityInNonFocusedStack + || finishingInNonFocusedStackOrNoRunning || prevState == STOPPING || prevState == STOPPED || prevState == ActivityState.INITIALIZING) { r.makeFinishingLocked(); boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason); - if (finishingActivityInNonFocusedStack) { + if (finishingInNonFocusedStackOrNoRunning) { // Finishing activity that was in paused state and it was in not currently focused - // stack, need to make something visible in its place. + // stack, need to make something visible in its place. Also if the display does not + // have running activity, the configuration may need to be updated for restoring + // original orientation of the display. mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId, false /* markFrozenIfConfigChanged */, true /* deferResume */); } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 90e2f5bcd1b4..257a0042b510 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1214,75 +1214,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } ActivityRecord topRunningActivityLocked() { - return topRunningActivityLocked(false /* considerKeyguardState */); - } - - /** - * Returns the top running activity in the focused stack. In the case the focused stack has no - * such activity, the next focusable stack on top of a display is returned. - * @param considerKeyguardState Indicates whether the locked state should be considered. if - * {@code true} and the keyguard is locked, only activities that - * can be shown on top of the keyguard will be considered. - * @return The top running activity. {@code null} if none is available. - */ - ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) { - final ActivityStack focusedStack = getTopDisplayFocusedStack(); - ActivityRecord r = focusedStack.topRunningActivityLocked(); - if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) { - return r; - } - - // Look in other non-focused and non-home stacks. for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - - // TODO: We probably want to consider the top fullscreen stack as we could have a pinned - // stack on top. - final ActivityStack topStack = display.getTopStack(); - - // Only consider focusable top stacks other than the current focused one. - if (topStack == null || !topStack.isFocusable() || topStack == focusedStack) { - continue; - } - - final ActivityRecord topActivity = topStack.topRunningActivityLocked(); - - // Skip if no top activity. - if (topActivity == null) { - continue; - } - - - // This activity can be considered the top running activity if we are not - // considering the locked state, the keyguard isn't locked, or we can show when - // locked. - if (isValidTopRunningActivity(topActivity, considerKeyguardState)) { + final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity(); + if (topActivity != null) { return topActivity; } } - return null; } - /** - * Verifies an {@link ActivityRecord} can be the top activity based on keyguard state and - * whether we are considering it. - */ - private boolean isValidTopRunningActivity(ActivityRecord record, - boolean considerKeyguardState) { - if (!considerKeyguardState) { - return true; - } - - final boolean keyguardLocked = getKeyguardController().isKeyguardLocked(); - - if (!keyguardLocked) { - return true; - } - - return record.canShowWhenLocked(); - } - @VisibleForTesting void getRunningTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode, diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index 8ae5495cfa05..f79d9aa9ba67 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -5864,6 +5864,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void finishHeavyWeightApp() { synchronized (mGlobalLock) { + if (mHeavyWeightProcess != null) { + mHeavyWeightProcess.finishActivities(); + } ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals( mHeavyWeightProcess); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 506cc441c868..348b8e69ab96 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -719,8 +719,7 @@ public class NotificationManagerService extends SystemService { return; } final long now = System.currentTimeMillis(); - MetricsLogger.action(r.getLogMaker(now) - .setCategory(MetricsEvent.NOTIFICATION_ITEM) + MetricsLogger.action(r.getItemLogMaker() .setType(MetricsEvent.TYPE_ACTION) .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank) .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count)); @@ -865,8 +864,7 @@ public class NotificationManagerService extends SystemService { r.stats.onExpansionChanged(userAction, expanded); final long now = System.currentTimeMillis(); if (userAction) { - MetricsLogger.action(r.getLogMaker(now) - .setCategory(MetricsEvent.NOTIFICATION_ITEM) + MetricsLogger.action(r.getItemLogMaker() .setType(expanded ? MetricsEvent.TYPE_DETAIL : MetricsEvent.TYPE_COLLAPSE)); } @@ -5842,8 +5840,7 @@ public class NotificationManagerService extends SystemService { mArchive.record(r.sbn); final long now = System.currentTimeMillis(); - final LogMaker logMaker = r.getLogMaker(now) - .setCategory(MetricsEvent.NOTIFICATION_ITEM) + final LogMaker logMaker = r.getItemLogMaker() .setType(MetricsEvent.TYPE_DISMISS) .setSubtype(reason); if (rank != -1 && count != -1) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 65ec5808a0dd..19a62d91dbf7 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -611,6 +611,7 @@ public final class NotificationRecord { } public void applyAdjustments() { + long now = System.currentTimeMillis(); synchronized (mAdjustments) { for (Adjustment adjustment: mAdjustments) { Bundle signals = adjustment.getSignals(); @@ -618,17 +619,25 @@ public final class NotificationRecord { final ArrayList<String> people = adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE); setPeopleOverride(people); + MetricsLogger.action(getAdjustmentLogMaker() + .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_PEOPLE, people.size())); } if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) { final ArrayList<SnoozeCriterion> snoozeCriterionList = adjustment.getSignals().getParcelableArrayList( Adjustment.KEY_SNOOZE_CRITERIA); setSnoozeCriteria(snoozeCriterionList); + MetricsLogger.action(getAdjustmentLogMaker() + .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SNOOZE_CRITERIA, + snoozeCriterionList.size())); } if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) { final String groupOverrideKey = adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY); setOverrideGroupKey(groupOverrideKey); + MetricsLogger.action(getAdjustmentLogMaker() + .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_GROUP_KEY, + groupOverrideKey)); } if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) { // Only allow user sentiment update from assistant if user hasn't already @@ -637,19 +646,31 @@ public final class NotificationRecord { && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) { setUserSentiment(adjustment.getSignals().getInt( Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL)); + MetricsLogger.action(getAdjustmentLogMaker() + .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_USER_SENTIMENT, + getUserSentiment())); } } if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) { setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS)); + MetricsLogger.action(getAdjustmentLogMaker() + .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS, + getSmartActions().size())); } if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) { setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES)); + MetricsLogger.action(getAdjustmentLogMaker() + .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES, + getSmartReplies().size())); } if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) { int importance = signals.getInt(Adjustment.KEY_IMPORTANCE); importance = Math.max(IMPORTANCE_UNSPECIFIED, importance); importance = Math.min(IMPORTANCE_HIGH, importance); setAssistantImportance(importance); + MetricsLogger.action(getAdjustmentLogMaker() + .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_IMPORTANCE, + importance)); } } } @@ -1203,6 +1224,16 @@ public final class NotificationRecord { return getLogMaker(System.currentTimeMillis()); } + public LogMaker getItemLogMaker() { + return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM); + } + + public LogMaker getAdjustmentLogMaker() { + return getLogMaker() + .setCategory(MetricsEvent.NOTIFICATION_ITEM) + .setType(MetricsEvent.NOTIFICATION_ASSISTANT_ADJUSTMENT); + } + @VisibleForTesting static final class Light { public final int color; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 760f1559c845..44b80c138346 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1212,27 +1212,89 @@ public class ZenModeHelper { } private final class Metrics extends Callback { - private static final String COUNTER_PREFIX = "dnd_mode_"; + private static final String COUNTER_MODE_PREFIX = "dnd_mode_"; + private static final String COUNTER_TYPE_PREFIX = "dnd_type_"; + private static final int DND_OFF = 0; + private static final int DND_ON_MANUAL = 1; + private static final int DND_ON_AUTOMATIC = 2; + private static final String COUNTER_RULE = "dnd_rule_count"; private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000; + // Total silence, alarms only, priority only private int mPreviousZenMode = -1; - private long mBeginningMs = 0L; + private long mModeLogTimeMs = 0L; + + private int mNumZenRules = -1; + private long mRuleCountLogTime = 0L; + + // automatic (1) vs manual (0) vs dnd off (2) + private int mPreviousZenType = -1; + private long mTypeLogTimeMs = 0L; @Override void onZenModeChanged() { emit(); } + @Override + void onConfigChanged() { + emit(); + } + private void emit() { mHandler.postMetricsTimer(); + emitZenMode(); + emitRules(); + emitDndType(); + } + + private void emitZenMode() { final long now = SystemClock.elapsedRealtime(); - final long since = (now - mBeginningMs); + final long since = (now - mModeLogTimeMs); if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) { if (mPreviousZenMode != -1) { - MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since); + MetricsLogger.count( + mContext, COUNTER_MODE_PREFIX + mPreviousZenMode, (int) since); } mPreviousZenMode = mZenMode; - mBeginningMs = now; + mModeLogTimeMs = now; + } + } + + private void emitRules() { + final long now = SystemClock.elapsedRealtime(); + final long since = (now - mRuleCountLogTime); + synchronized (mConfig) { + int numZenRules = mConfig.automaticRules.size(); + if (mNumZenRules != numZenRules + || since > MINIMUM_LOG_PERIOD_MS) { + if (mNumZenRules != -1) { + MetricsLogger.count(mContext, COUNTER_RULE, + numZenRules - mNumZenRules); + } + mNumZenRules = numZenRules; + + mRuleCountLogTime = since; + } + } + } + + private void emitDndType() { + final long now = SystemClock.elapsedRealtime(); + final long since = (now - mTypeLogTimeMs); + synchronized (mConfig) { + boolean dndOn = mZenMode != Global.ZEN_MODE_OFF; + int zenType = !dndOn ? DND_OFF + : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC; + if (zenType != mPreviousZenType + || since > MINIMUM_LOG_PERIOD_MS) { + if (mPreviousZenType != -1) { + MetricsLogger.count( + mContext, COUNTER_TYPE_PREFIX + mPreviousZenType, (int) since); + } + mTypeLogTimeMs = now; + mPreviousZenType = zenType; + } } } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java index ea90ffd0792f..0da574239666 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java @@ -20,11 +20,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; import android.platform.test.annotations.Presubmit; @@ -131,4 +134,57 @@ public class ActivityDisplayTests extends ActivityTestsBase { new ActivityBuilder(mService).setTask(fullscreenTask).build(); return fullscreenStack; } + + /** + * Verifies the correct activity is returned when querying the top running activity. + */ + @Test + public void testTopRunningActivity() { + // Create stack to hold focus. + final ActivityDisplay display = mSupervisor.getDefaultDisplay(); + final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + + final KeyguardController keyguard = mSupervisor.getKeyguardController(); + final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(stack).build(); + + // Make sure the top running activity is not affected when keyguard is not locked. + assertTopRunningActivity(activity, display); + + // Check to make sure activity not reported when it cannot show on lock and lock is on. + doReturn(true).when(keyguard).isKeyguardLocked(); + assertEquals(activity, display.topRunningActivity()); + assertNull(display.topRunningActivity(true /* considerKeyguardState */)); + + // Change focus to stack with activity. + stack.moveToFront("focusChangeToTestStack"); + assertEquals(stack, display.getFocusedStack()); + assertEquals(activity, display.topRunningActivity()); + assertNull(display.topRunningActivity(true /* considerKeyguardState */)); + + // Add activity that should be shown on the keyguard. + final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService) + .setCreateTask(true) + .setStack(stack) + .setActivityFlags(FLAG_SHOW_WHEN_LOCKED) + .build(); + + // Ensure the show when locked activity is returned. + assertTopRunningActivity(showWhenLockedActivity, display); + + // Change focus back to empty stack. + emptyStack.moveToFront("focusChangeToEmptyStack"); + assertEquals(emptyStack, display.getFocusedStack()); + // If there is no running activity in focused stack, the running activity in next focusable + // stack should be returned. + assertTopRunningActivity(showWhenLockedActivity, display); + } + + private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) { + assertEquals(top, display.topRunningActivity()); + assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */)); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java index cc7a24d5700e..2c993d32c20c 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -25,7 +25,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; -import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; @@ -307,62 +306,6 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { } /** - * Verifies the correct activity is returned when querying the top running activity. - */ - @Test - public void testTopRunningActivity() throws Exception { - // Create stack to hold focus - final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); - final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, true /* onTop */); - - final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(stack).build(); - - // Make sure the top running activity is not affected when keyguard is not locked - assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked()); - assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked( - true /* considerKeyguardState */)); - - // Check to make sure activity not reported when it cannot show on lock and lock is on. - doReturn(true).when(keyguard).isKeyguardLocked(); - assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked()); - assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked( - true /* considerKeyguardState */)); - - // Change focus to stack with activity. - stack.moveToFront("focusChangeToTestStack"); - assertEquals(stack, display.getFocusedStack()); - assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked()); - assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked( - true /* considerKeyguardState */)); - - // Add activity that should be shown on the keyguard. - final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService) - .setCreateTask(true) - .setStack(stack) - .setActivityFlags(FLAG_SHOW_WHEN_LOCKED) - .build(); - - // Ensure the show when locked activity is returned. - assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked()); - assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked( - true /* considerKeyguardState */)); - - // Change focus back to empty stack - emptyStack.moveToFront("focusChangeToEmptyStack"); - assertEquals(emptyStack, display.getFocusedStack()); - // Looking for running activity only in top and focused stack, so nothing should be returned - // from empty stack. - assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked()); - assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked( - true /* considerKeyguardState */)); - } - - /** * Verify that split-screen primary stack will be chosen if activity is launched that targets * split-screen secondary, but a matching existing instance is found on top of split-screen * primary stack. diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 5fcd2aa35e05..53f67afb629e 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -26,6 +26,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static com.android.server.am.ActivityStack.ActivityState.DESTROYING; +import static com.android.server.am.ActivityStack.ActivityState.FINISHING; +import static com.android.server.am.ActivityStack.ActivityState.PAUSED; import static com.android.server.am.ActivityStack.ActivityState.PAUSING; import static com.android.server.am.ActivityStack.ActivityState.RESUMED; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; @@ -35,10 +38,14 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.anyInt; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import android.content.pm.ActivityInfo; import android.os.UserHandle; @@ -655,6 +662,39 @@ public class ActivityStackTests extends ActivityTestsBase { } @Test + public void testFinishCurrentActivity() { + // Create 2 activities on a new display. + final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); + final ActivityStack stack1 = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack stack2 = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + + // There is still an activity1 in stack1 so the activity2 should be added to finishing list + // that will be destroyed until idle. + final ActivityRecord activity2 = finishCurrentActivity(stack2); + assertEquals(FINISHING, activity2.getState()); + assertTrue(mSupervisor.mFinishingActivities.contains(activity2)); + + // The display becomes empty. Since there is no next activity to be idle, the activity + // should be destroyed immediately with updating configuration to restore original state. + final ActivityRecord activity1 = finishCurrentActivity(stack1); + assertEquals(DESTROYING, activity1.getState()); + verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */, + eq(display.mDisplayId), anyBoolean(), anyBoolean()); + } + + private ActivityRecord finishCurrentActivity(ActivityStack stack) { + final ActivityRecord activity = stack.topRunningActivityLocked(); + assertNotNull(activity); + activity.setState(PAUSED, "finishCurrentActivity"); + activity.makeFinishingLocked(); + stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE, + false /* oomAdj */, "finishCurrentActivity"); + return activity; + } + + @Test public void testShouldSleepActivities() throws Exception { // When focused activity and keyguard is going away, we should not sleep regardless // of the display state |