diff options
| author | 2020-11-10 06:45:09 +0000 | |
|---|---|---|
| committer | 2020-11-10 06:45:09 +0000 | |
| commit | 51a14f20d13f68d62f37312bd823e37f1d541d20 (patch) | |
| tree | 2d512e5fe21bc992303f69fe967f7a7e6e727145 | |
| parent | 7ef0432b7f5c2d37185e256eb43f110ba433d33e (diff) | |
| parent | fb3c099558270c50cf4cbfa52456fcb1e5b295d7 (diff) | |
Merge "Track process with visible activities"
8 files changed, 241 insertions, 92 deletions
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 463ba6d3d5ee..5dbaaaf447ff 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1252,7 +1252,6 @@ class ProcessRecord implements WindowProcessListener { void setHasForegroundActivities(boolean hasForegroundActivities) { mHasForegroundActivities = hasForegroundActivities; - mWindowProcessController.setHasForegroundActivities(hasForegroundActivities); } boolean hasForegroundActivities() { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4b5518c87d23..33df8b88fc2c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -445,7 +445,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private Task task; // the task this is in. private long createTime = System.currentTimeMillis(); long lastVisibleTime; // last time this activity became visible - long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity long pauseTime; // last time we started pausing the activity long launchTickTime; // base time for launch tick messages long topResumedStateLossTime; // last time we reported top resumed state loss to an activity @@ -5010,16 +5009,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A resumeKeyDispatchingLocked(); final Task stack = getRootTask(); mStackSupervisor.mNoAnimActivities.clear(); - - // Mark the point when the activity is resuming - // TODO: To be more accurate, the mark should be before the onCreate, - // not after the onResume. But for subsequent starts, onResume is fine. - if (hasProcess()) { - cpuTimeAtResume = app.getCpuTime(); - } else { - cpuTimeAtResume = 0; // Couldn't get the cpu time of process - } - returningOptions = null; if (canTurnScreenOn()) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 73c47131063e..333455dce29a 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -366,6 +366,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { PendingIntentController mPendingIntentController; IntentFirewall mIntentFirewall; + final VisibleActivityProcessTracker mVisibleActivityProcessTracker; + /* Global service lock used by the package the owns this service. */ final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock(); /** @@ -741,6 +743,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mSystemThread = ActivityThread.currentActivityThread(); mUiContext = mSystemThread.getSystemUiContext(); mLifecycleManager = new ClientLifecycleManager(); + mVisibleActivityProcessTracker = new VisibleActivityProcessTracker(this); mInternal = new LocalService(); GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED); mWindowOrganizerController = new WindowOrganizerController(this); @@ -6103,16 +6106,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean hasResumedActivity(int uid) { - synchronized (mGlobalLock) { - final ArraySet<WindowProcessController> processes = mProcessMap.getProcesses(uid); - for (int i = 0, n = processes.size(); i < n; i++) { - final WindowProcessController process = processes.valueAt(i); - if (process.hasResumedActivity()) { - return true; - } - } - } - return false; + return mVisibleActivityProcessTracker.hasResumedActivity(uid); } public void setBackgroundActivityStartCallback( diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 0b2ba8727abe..1255bb2a02e9 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -162,7 +162,6 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskSnapshot; -import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.AppGlobals; @@ -5689,19 +5688,6 @@ class Task extends WindowContainer<WindowContainer> { if (prev != null) { prev.resumeKeyDispatchingLocked(); - - if (prev.hasProcess() && prev.cpuTimeAtResume > 0) { - final long diff = prev.app.getCpuTime() - prev.cpuTimeAtResume; - if (diff > 0) { - final Runnable r = PooledLambda.obtainRunnable( - ActivityManagerInternal::updateForegroundTimeIfOnBattery, - mAtmService.mAmInternal, prev.info.packageName, - prev.info.applicationInfo.uid, - diff); - mAtmService.mH.post(r); - } - } - prev.cpuTimeAtResume = 0; // reset it } mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); diff --git a/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java b/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java new file mode 100644 index 000000000000..7f626308976b --- /dev/null +++ b/services/core/java/com/android/server/wm/VisibleActivityProcessTracker.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.util.ArrayMap; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.BackgroundThread; + +import java.util.concurrent.Executor; + +/** + * A quick lookup for all processes with visible activities. It also tracks the CPU usage of + * host process with foreground (resumed) activity. + */ +class VisibleActivityProcessTracker { + @GuardedBy("mProcMap") + private final ArrayMap<WindowProcessController, CpuTimeRecord> mProcMap = new ArrayMap<>(); + final Executor mBgExecutor = BackgroundThread.getExecutor(); + final ActivityTaskManagerService mAtms; + + VisibleActivityProcessTracker(ActivityTaskManagerService atms) { + mAtms = atms; + } + + /** Called when any activity is visible in the process that didn't have one. */ + void onAnyActivityVisible(WindowProcessController wpc) { + final CpuTimeRecord r = new CpuTimeRecord(wpc); + synchronized (mProcMap) { + mProcMap.put(wpc, r); + } + if (wpc.hasResumedActivity()) { + r.mShouldGetCpuTime = true; + mBgExecutor.execute(r); + } + } + + /** Called when all visible activities of the process are no longer visible. */ + void onAllActivitiesInvisible(WindowProcessController wpc) { + final CpuTimeRecord r = removeProcess(wpc); + if (r != null && r.mShouldGetCpuTime) { + mBgExecutor.execute(r); + } + } + + /** Called when an activity is resumed on a process which is known to have visible activity. */ + void onActivityResumedWhileVisible(WindowProcessController wpc) { + final CpuTimeRecord r; + synchronized (mProcMap) { + r = mProcMap.get(wpc); + } + if (r != null && !r.mShouldGetCpuTime) { + r.mShouldGetCpuTime = true; + mBgExecutor.execute(r); + } + } + + boolean hasResumedActivity(int uid) { + synchronized (mProcMap) { + for (int i = mProcMap.size() - 1; i >= 0; i--) { + final WindowProcessController wpc = mProcMap.keyAt(i); + if (wpc.mUid == uid && wpc.hasResumedActivity()) { + return true; + } + } + } + return false; + } + + CpuTimeRecord removeProcess(WindowProcessController wpc) { + synchronized (mProcMap) { + return mProcMap.remove(wpc); + } + } + + /** + * Get CPU time in background thread because it will access proc files or the lock of cpu + * tracker is held by a background thread. + */ + private class CpuTimeRecord implements Runnable { + private final WindowProcessController mProc; + private long mCpuTime; + private boolean mHasStartCpuTime; + boolean mShouldGetCpuTime; + + CpuTimeRecord(WindowProcessController wpc) { + mProc = wpc; + } + + @Override + public void run() { + if (mProc.getPid() == 0) { + // The process is dead. + return; + } + if (!mHasStartCpuTime) { + mHasStartCpuTime = true; + mCpuTime = mProc.getCpuTime(); + } else { + final long diff = mProc.getCpuTime() - mCpuTime; + if (diff > 0) { + mAtms.mAmInternal.updateForegroundTimeIfOnBattery( + mProc.mInfo.packageName, mProc.mInfo.uid, diff); + } + } + } + } +} diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 5bfa662ea4cb..0431d1137a58 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -137,8 +137,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private volatile String mRequiredAbi; // Running any services that are foreground? private volatile boolean mHasForegroundServices; - // Running any activities that are foreground? - private volatile boolean mHasForegroundActivities; // Are there any client services with activities? private volatile boolean mHasClientActivities; // Is this process currently showing a non-activity UI that the user is interacting with? @@ -226,10 +224,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private final BackgroundActivityStartCallback mBackgroundActivityStartCallback; // The bits used for mActivityStateFlags. - private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 0x10000000; - private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 0x20000000; - private static final int ACTIVITY_STATE_FLAG_IS_STOPPING = 0x40000000; - private static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 0x80000000; + private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16; + private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17; + private static final int ACTIVITY_STATE_FLAG_IS_STOPPING = 1 << 18; + private static final int ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING = 1 << 19; + private static final int ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE = 1 << 20; + private static final int ACTIVITY_STATE_FLAG_HAS_RESUMED = 1 << 21; private static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff; /** @@ -281,6 +281,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // configuration will update when display is ready. if (thread != null) { setLastReportedConfiguration(getConfiguration()); + } else { + // The process is inactive. + mAtm.mVisibleActivityProcessTracker.removeProcess(this); } } } @@ -349,12 +352,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mHasForegroundServices; } - public void setHasForegroundActivities(boolean hasForegroundActivities) { - mHasForegroundActivities = hasForegroundActivities; - } - boolean hasForegroundActivities() { - return mHasForegroundActivities; + return mAtm.mTopApp == this || (mActivityStateFlags + & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED + | ACTIVITY_STATE_FLAG_IS_STOPPING)) != 0; } public void setHasClientActivities(boolean hasClientActivities) { @@ -892,16 +893,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } boolean hasResumedActivity() { - for (int i = mActivities.size() - 1; i >= 0; --i) { - final ActivityRecord activity = mActivities.get(i); - if (activity.isState(RESUMED)) { - return true; - } - } - return false; + return (mActivityStateFlags & ACTIVITY_STATE_FLAG_HAS_RESUMED) != 0; } - void updateIntentForHeavyWeightActivity(Intent intent) { if (mActivities.isEmpty()) { return; @@ -1041,10 +1035,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Since there could be more than one activities in a process record, we don't need to // compute the OomAdj with each of them, just need to find out the activity with the // "best" state, the order would be visible, pausing, stopping... - Task.ActivityState best = DESTROYED; - boolean finishing = true; + Task.ActivityState bestInvisibleState = DESTROYED; + boolean allStoppingFinishing = true; boolean visible = false; int minTaskLayer = Integer.MAX_VALUE; + int stateFlags = 0; + final boolean wasResumed = hasResumedActivity(); + final boolean wasAnyVisible = (mActivityStateFlags + & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0; for (int i = mActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mActivities.get(i); if (r.app != this) { @@ -1057,7 +1055,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio continue; } } + if (r.isVisible()) { + stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE; + } if (r.mVisibleRequested) { + if (r.isState(RESUMED)) { + stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED; + } final Task task = r.getTask(); if (task != null && minTaskLayer > 0) { final int layer = task.mLayerRank; @@ -1069,29 +1073,39 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // continue the loop, in case there are multiple visible activities in // this process, we'd find out the one with the minimal layer, thus it'll // get a higher adj score. - } else if (best != PAUSING && best != PAUSED) { + } else if (!visible && bestInvisibleState != PAUSING) { if (r.isState(PAUSING, PAUSED)) { - best = PAUSING; + bestInvisibleState = PAUSING; } else if (r.isState(STOPPING)) { - best = STOPPING; + bestInvisibleState = STOPPING; // Not "finishing" if any of activity isn't finishing. - finishing &= r.finishing; + allStoppingFinishing &= r.finishing; } } } - int stateFlags = minTaskLayer & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER; + stateFlags |= minTaskLayer & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER; if (visible) { stateFlags |= ACTIVITY_STATE_FLAG_IS_VISIBLE; - } else if (best == PAUSING) { + } else if (bestInvisibleState == PAUSING) { stateFlags |= ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED; - } else if (best == STOPPING) { + } else if (bestInvisibleState == STOPPING) { stateFlags |= ACTIVITY_STATE_FLAG_IS_STOPPING; - if (finishing) { + if (allStoppingFinishing) { stateFlags |= ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING; } } mActivityStateFlags = stateFlags; + + final boolean anyVisible = (stateFlags + & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0; + if (!wasAnyVisible && anyVisible) { + mAtm.mVisibleActivityProcessTracker.onAnyActivityVisible(this); + } else if (wasAnyVisible && !anyVisible) { + mAtm.mVisibleActivityProcessTracker.onAllActivitiesInvisible(this); + } else if (wasAnyVisible && !wasResumed && hasResumedActivity()) { + mAtm.mVisibleActivityProcessTracker.onActivityResumedWhileVisible(this); + } } /** Called when the process has some oom related changes and it is going to update oom-adj. */ @@ -1643,6 +1657,32 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio pw.println(prefix + " Configuration=" + getConfiguration()); pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration()); pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration); + + final int stateFlags = mActivityStateFlags; + if (stateFlags != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) { + pw.print(prefix + " mActivityStateFlags="); + if ((stateFlags & ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE) != 0) { + pw.print("W|"); + } + if ((stateFlags & ACTIVITY_STATE_FLAG_IS_VISIBLE) != 0) { + pw.print("V|"); + if ((stateFlags & ACTIVITY_STATE_FLAG_HAS_RESUMED) != 0) { + pw.print("R|"); + } + } else if ((stateFlags & ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED) != 0) { + pw.print("P|"); + } else if ((stateFlags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) { + pw.print("S|"); + if ((stateFlags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0) { + pw.print("F|"); + } + } + final int taskLayer = stateFlags & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER; + if (taskLayer != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) { + pw.print("taskLayer=" + taskLayer); + } + pw.println(); + } } void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index a7ced1df6125..c05eb8e56762 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -662,8 +662,8 @@ public class ActivityStarterTests extends WindowTestsBase { ai.uid = callingUid; ai.packageName = "com.android.test.package"; final WindowProcessController callerApp = - new WindowProcessController(mAtm, ai, null, callingUid, -1, null, listener); - callerApp.setHasForegroundActivities(hasForegroundActivities); + spy(new WindowProcessController(mAtm, ai, null, callingUid, -1, null, listener)); + doReturn(hasForegroundActivities).when(callerApp).hasForegroundActivities(); doReturn(callerApp).when(mAtm).getProcessController(caller); // caller is recents RecentTasks recentTasks = mock(RecentTasks.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 21be6ef2e897..5afcedb10dd6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -22,6 +22,9 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.INVALID_DISPLAY; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -168,11 +171,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { mAtm, applicationInfo, null, 0, -1, null, mMockListener); wpc.setThread(mock(IApplicationThread.class)); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setUseProcess(wpc) - .build(); - + final ActivityRecord activity = createActivityRecord(wpc); wpc.addActivityIfNeeded(activity); // System UI owned processes should not be registered for activity config changes. assertFalse(wpc.registeredForActivityConfigChanges()); @@ -185,11 +184,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { // Notify WPC that this process has started an IME service. mWpc.onServiceStarted(serviceInfo); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setUseProcess(mWpc) - .build(); - + final ActivityRecord activity = createActivityRecord(mWpc); mWpc.addActivityIfNeeded(activity); // IME processes should not be registered for activity config changes. assertFalse(mWpc.registeredForActivityConfigChanges()); @@ -202,11 +197,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { // Notify WPC that this process has started an ally service. mWpc.onServiceStarted(serviceInfo); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setUseProcess(mWpc) - .build(); - + final ActivityRecord activity = createActivityRecord(mWpc); mWpc.addActivityIfNeeded(activity); // Ally processes should not be registered for activity config changes. assertFalse(mWpc.registeredForActivityConfigChanges()); @@ -219,11 +210,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { // Notify WPC that this process has started an voice interaction service. mWpc.onServiceStarted(serviceInfo); - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setUseProcess(mWpc) - .build(); - + final ActivityRecord activity = createActivityRecord(mWpc); mWpc.addActivityIfNeeded(activity); // Voice interaction service processes should not be registered for activity config changes. assertFalse(mWpc.registeredForActivityConfigChanges()); @@ -244,7 +231,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { final int globalSeq = 100; mRootWindowContainer.getConfiguration().seq = globalSeq; invertOrientation(mWpc.getConfiguration()); - new ActivityBuilder(mAtm).setCreateTask(true).setUseProcess(mWpc).build(); + createActivityRecord(mWpc); assertTrue(mWpc.registeredForActivityConfigChanges()); assertEquals("Config seq of process should not be affected by activity", @@ -253,10 +240,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { @Test public void testComputeOomAdjFromActivities() { - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setUseProcess(mWpc) - .build(); + final ActivityRecord activity = createActivityRecord(mWpc); activity.mVisibleRequested = true; final int[] callbackResult = { 0 }; final int visible = 1; @@ -308,6 +292,41 @@ public class WindowProcessControllerTests extends WindowTestsBase { assertEquals(other, callbackResult[0]); } + @Test + public void testComputeProcessActivityState() { + final VisibleActivityProcessTracker tracker = mAtm.mVisibleActivityProcessTracker; + spyOn(tracker); + final ActivityRecord activity = createActivityRecord(mWpc); + activity.mVisibleRequested = true; + activity.setState(Task.ActivityState.STARTED, "test"); + + verify(tracker).onAnyActivityVisible(mWpc); + assertTrue(mWpc.hasVisibleActivities()); + + activity.setState(Task.ActivityState.RESUMED, "test"); + + verify(tracker).onActivityResumedWhileVisible(mWpc); + assertTrue(tracker.hasResumedActivity(mWpc.mUid)); + + activity.makeFinishingLocked(); + activity.setState(Task.ActivityState.PAUSING, "test"); + + assertFalse(tracker.hasResumedActivity(mWpc.mUid)); + assertTrue(mWpc.hasForegroundActivities()); + + activity.setVisibility(false); + activity.mVisibleRequested = false; + activity.setState(Task.ActivityState.STOPPED, "test"); + + verify(tracker).onAllActivitiesInvisible(mWpc); + assertFalse(mWpc.hasVisibleActivities()); + assertFalse(mWpc.hasForegroundActivities()); + } + + private ActivityRecord createActivityRecord(WindowProcessController wpc) { + return new ActivityBuilder(mAtm).setCreateTask(true).setUseProcess(wpc).build(); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mAtm, 1000, 1500).build(); } |