From bea0e67d0da5b57e7317c2638fd40ebecc6eff41 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Tue, 17 May 2022 16:24:19 -0600 Subject: [PATCH 1/2] Demote top app scheduling group when unlocking SystemUI may be running the unlock animation. If both SystemUI and the resuming top app have top-app scheduling group, there may be CPU contention which causes animation jank. An exceptional case is unlocking to home which may also animate, then keep original behavior: both system ui and home will have top-app scheduling group. Bug: 240356132 Bug: 226367352 Test: Launch calculator. Keep executing command "adb shell dumpsys activity o | grep calc" Lock and unlock device. The dump will have the changes: b/A/TPSL ---- t: 0 20687:calc/u0a158 (top-sleeping) F/A/TOP LCMN t: 0 20687:calc/u0a158 (intermediate-top-activity) T/A/TOP LCMN t: 0 20687:calc/u0a158 (top-activity) (b=BG, F=FG, T=TOP) Change-Id: Id0fd2491a11bac7b81bc654e646b3d01643ca8b4 Merged-In: Id0fd2491a11bac7b81bc654e646b3d01643ca8b4 (cherry picked from commit f3139a9e51333b926f5f81a76faf4ccde5afac53) --- .../com/android/server/am/OomAdjuster.java | 16 ++++++--- .../server/wm/ActivityTaskManagerService.java | 34 +++++++++++++++++-- .../server/wm/WindowProcessController.java | 7 ++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index f7fbbe4ebead4..e9658dbc7b1a9 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1556,14 +1556,22 @@ public class OomAdjuster { boolean foregroundActivities = false; boolean hasVisibleActivities = false; - if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) { + if (app == topApp && (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP + || PROCESS_STATE_CUR_TOP == PROCESS_STATE_IMPORTANT_FOREGROUND)) { // The last app on the list is the foreground app. adj = ProcessList.FOREGROUND_APP_ADJ; - schedGroup = ProcessList.SCHED_GROUP_TOP_APP; - state.setAdjType("top-activity"); + if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP) { + schedGroup = ProcessList.SCHED_GROUP_TOP_APP; + state.setAdjType("top-activity"); + } else { + // Demote the scheduling group to avoid CPU contention if there is another more + // important process which also uses top-app, such as if SystemUI is animating. + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; + state.setAdjType("intermediate-top-activity"); + } foregroundActivities = true; hasVisibleActivities = true; - procState = PROCESS_STATE_CUR_TOP; + procState = PROCESS_STATE_TOP; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index aa154292fe7ee..7dbd8e8047a6b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -64,8 +64,8 @@ import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; import static android.provider.Settings.System.FONT_SCALE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; import static android.view.WindowManager.TRANSIT_WAKE; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; @@ -400,6 +400,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** The time at which the previous process was last visible. */ private long mPreviousProcessVisibleTime; + /** + * It can be true from keyguard-going-away to set-keyguard-shown. And getTopProcessState() will + * return {@link ActivityManager#PROCESS_STATE_IMPORTANT_FOREGROUND} to avoid top app from + * preempting CPU while keyguard is animating. + */ + private volatile boolean mDemoteTopAppDuringUnlocking; + /** List of intents that were used to start the most recent tasks. */ private RecentTasks mRecentTasks; /** State of external calls telling us if the device is awake or asleep. */ @@ -2830,12 +2837,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { keyguardShowing); mH.sendMessage(msg); } + // Always reset the state regardless of keyguard-showing change, because that means the + // unlock is either completed or canceled. + if (mDemoteTopAppDuringUnlocking) { + mDemoteTopAppDuringUnlocking = false; + // The scheduling group of top process was demoted by unlocking, so recompute + // to restore its real top priority if possible. + if (mTopApp != null) { + mTopApp.scheduleUpdateOomAdj(); + } + } try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "setLockScreenShown"); mRootWindowContainer.forAllDisplays(displayContent -> { mKeyguardController.setKeyguardShown(displayContent.getDisplayId(), keyguardShowing, aodShowing); }); } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); Binder.restoreCallingIdentity(ident); } } @@ -2862,6 +2881,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // animation of system UI. Even if AOD is not enabled, it should be no harm. final WindowProcessController proc; synchronized (mGlobalLockWithoutBoost) { + mDemoteTopAppDuringUnlocking = false; final WindowState notificationShade = mRootWindowContainer.getDefaultDisplay() .getDisplayPolicy().getNotificationShade(); proc = notificationShade != null @@ -3399,8 +3419,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { // Keyguard asked us to clear the home task snapshot before going away, so do that. - if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) { + if ((flags & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) { mActivityClientController.invalidateHomeTaskSnapshot(null /* token */); + } else if (mKeyguardShown) { + // Only set if it is not unlocking to launcher which may also animate. + mDemoteTopAppDuringUnlocking = true; } mRootWindowContainer.forAllDisplays(displayContent -> { @@ -5595,12 +5618,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public int getTopProcessState() { + final int topState = mTopProcessState; + if (mDemoteTopAppDuringUnlocking && topState == ActivityManager.PROCESS_STATE_TOP) { + // The unlocking UI is more important, so defer the top state of app. + return ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; + } if (mRetainPowerModeAndTopProcessState) { // There is a launching app while device may be sleeping, force the top state so // the launching process can have top-app scheduling group. return ActivityManager.PROCESS_STATE_TOP; } - return mTopProcessState; + return topState; } @HotPath(caller = HotPath.PROCESS_CHANGE) diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 40417a4857d3d..215bcb0cd8e24 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -1126,6 +1126,13 @@ public class WindowProcessController extends ConfigurationContainer Date: Mon, 6 Jun 2022 22:50:20 +0800 Subject: [PATCH 2/2] Demote top app scheduling group when recents is animating With legacy transition, the recents activity doesn't move to top when it is animating. So both the real top app and the recents process will have top-app scheduling group. Then there may be a CPU contention which causes animation jank. Because the recents animation is usually more noticeable (mainly swipe-up to home), demote the current top app to foreground group for reducing the potential contention. Bug: 240356132 Bug: 233992724 Test: atest RecentsAnimationTest Test: adb shell dumpsys activity o | grep -e "A.*calc" Launch calculator: T/A/TOP LCMN t: 0 22249:calc/u0a158 (top-activity) Swipe-up to enter recents: F/A/TOP LCMN t: 0 22249:calc/u0a158 (intermediate-top-activity) Return to calculator: T/A/TOP LCMN t: 0 22249:calc/u0a158 (top-activity) Swipe to home: b/ /LAST ---- t: 0 22249:calc/u0a158 (previous) Change-Id: I0d002ea46de4474fcf863f142f2ee17fe3a5c742 Merged-In: I0d002ea46de4474fcf863f142f2ee17fe3a5c742 (cherry picked from commit a20990ec66bf61f45d36c5b66011f54d85c8095c) --- .../server/wm/ActivityTaskManagerService.java | 34 ++++++++++++++----- .../android/server/wm/RecentsAnimation.java | 28 +++++++++++---- .../server/wm/RecentsAnimationTest.java | 18 ++++++---- .../server/wm/SystemServicesTestRule.java | 30 ++++++++++++++-- .../android/server/wm/WindowTestsBase.java | 19 +++++------ 5 files changed, 96 insertions(+), 33 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 7dbd8e8047a6b..f830f6fe09267 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -400,12 +400,25 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** The time at which the previous process was last visible. */ private long mPreviousProcessVisibleTime; + /** It is set from keyguard-going-away to set-keyguard-shown. */ + static final int DEMOTE_TOP_REASON_DURING_UNLOCKING = 1; + /** It is set if legacy recents animation is running. */ + static final int DEMOTE_TOP_REASON_ANIMATING_RECENTS = 1 << 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + DEMOTE_TOP_REASON_DURING_UNLOCKING, + DEMOTE_TOP_REASON_ANIMATING_RECENTS, + }) + @interface DemoteTopReason {} + /** - * It can be true from keyguard-going-away to set-keyguard-shown. And getTopProcessState() will + * If non-zero, getTopProcessState() will * return {@link ActivityManager#PROCESS_STATE_IMPORTANT_FOREGROUND} to avoid top app from - * preempting CPU while keyguard is animating. + * preempting CPU while another process is running an important animation. */ - private volatile boolean mDemoteTopAppDuringUnlocking; + @DemoteTopReason + volatile int mDemoteTopAppReasons; /** List of intents that were used to start the most recent tasks. */ private RecentTasks mRecentTasks; @@ -2839,8 +2852,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } // Always reset the state regardless of keyguard-showing change, because that means the // unlock is either completed or canceled. - if (mDemoteTopAppDuringUnlocking) { - mDemoteTopAppDuringUnlocking = false; + if ((mDemoteTopAppReasons & DEMOTE_TOP_REASON_DURING_UNLOCKING) != 0) { + mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING; // The scheduling group of top process was demoted by unlocking, so recompute // to restore its real top priority if possible. if (mTopApp != null) { @@ -2881,7 +2894,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // animation of system UI. Even if AOD is not enabled, it should be no harm. final WindowProcessController proc; synchronized (mGlobalLockWithoutBoost) { - mDemoteTopAppDuringUnlocking = false; + mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING; final WindowState notificationShade = mRootWindowContainer.getDefaultDisplay() .getDisplayPolicy().getNotificationShade(); proc = notificationShade != null @@ -3423,7 +3436,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mActivityClientController.invalidateHomeTaskSnapshot(null /* token */); } else if (mKeyguardShown) { // Only set if it is not unlocking to launcher which may also animate. - mDemoteTopAppDuringUnlocking = true; + mDemoteTopAppReasons |= DEMOTE_TOP_REASON_DURING_UNLOCKING; } mRootWindowContainer.forAllDisplays(displayContent -> { @@ -3991,6 +4004,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTaskOrganizerController.dump(pw, " "); mVisibleActivityProcessTracker.dump(pw, " "); mActiveUids.dump(pw, " "); + if (mDemoteTopAppReasons != 0) { + pw.println(" mDemoteTopAppReasons=" + mDemoteTopAppReasons); + } } if (!printedAnything) { @@ -5619,8 +5635,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int getTopProcessState() { final int topState = mTopProcessState; - if (mDemoteTopAppDuringUnlocking && topState == ActivityManager.PROCESS_STATE_TOP) { - // The unlocking UI is more important, so defer the top state of app. + if (mDemoteTopAppReasons != 0 && topState == ActivityManager.PROCESS_STATE_TOP) { + // There may be a more important UI/animation than the top app. return ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; } if (mRetainPowerModeAndTopProcessState) { diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index eca201dc2bdad..f840171b29b06 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -203,9 +203,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan final LaunchingState launchingState = mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent); - if (mCaller != null) { - mCaller.setRunningRecentsAnimation(true); - } + setProcessAnimating(true); mService.deferWindowLayout(); try { @@ -409,15 +407,33 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan if (mWindowManager.mRoot.isLayoutNeeded()) { mWindowManager.mRoot.performSurfacePlacement(); } - if (mCaller != null) { - mCaller.setRunningRecentsAnimation(false); - } + setProcessAnimating(false); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }); } } + /** Gives the owner of recents animation higher priority. */ + private void setProcessAnimating(boolean animating) { + if (mCaller == null) return; + // Apply the top-app scheduling group to who runs the animation. + mCaller.setRunningRecentsAnimation(animating); + int demoteReasons = mService.mDemoteTopAppReasons; + if (animating) { + demoteReasons |= ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS; + } else { + demoteReasons &= ~ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS; + } + mService.mDemoteTopAppReasons = demoteReasons; + // Make the demotion of the real top app take effect. No need to restore top app state for + // finishing recents because addToStopping -> scheduleIdle -> activityIdleInternal -> + // trimApplications will have a full update. + if (animating && mService.mTopApp != null) { + mService.mTopApp.scheduleUpdateOomAdj(); + } + } + @Override public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode, boolean sendUserLeaveHint) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 1b19a28a9790f..28fb0154a3b8a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -37,13 +37,13 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -91,9 +91,15 @@ public class RecentsAnimationTest extends WindowTestsBase { TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); + final WindowProcessController wpc = mSystemServicesTestRule.addProcess( + mRecentsComponent.getPackageName(), mRecentsComponent.getPackageName(), + // Use real pid/uid of the test so the corresponding process can be mapped by + // Binder.getCallingPid/Uid. + android.os.Process.myPid(), android.os.Process.myUid()); ActivityRecord recentActivity = new ActivityBuilder(mAtm) .setComponent(mRecentsComponent) .setTask(recentsStack) + .setUseProcess(wpc) .build(); ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); topActivity.getRootTask().moveToFront("testRecentsActivityVisiblility"); @@ -106,11 +112,14 @@ public class RecentsAnimationTest extends WindowTestsBase { mRecentsComponent, true /* getRecentsAnimation */); // The launch-behind state should make the recents activity visible. assertTrue(recentActivity.mVisibleRequested); + assertEquals(ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS, + mAtm.mDemoteTopAppReasons); // Simulate the animation is cancelled without changing the stack order. recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */); // The non-top recents activity should be invisible by the restored launch-behind state. assertFalse(recentActivity.mVisibleRequested); + assertEquals(0, mAtm.mDemoteTopAppReasons); } @Test @@ -138,11 +147,8 @@ public class RecentsAnimationTest extends WindowTestsBase { anyInt() /* startFlags */, any() /* profilerInfo */); // Assume its process is alive because the caller should be the recents service. - WindowProcessController wpc = new WindowProcessController(mAtm, aInfo.applicationInfo, - aInfo.processName, aInfo.applicationInfo.uid, 0 /* userId */, - mock(Object.class) /* owner */, mock(WindowProcessListener.class)); - wpc.setThread(mock(IApplicationThread.class)); - doReturn(wpc).when(mAtm).getProcessController(eq(wpc.mName), eq(wpc.mUid)); + mSystemServicesTestRule.addProcess(aInfo.packageName, aInfo.processName, 12345 /* pid */, + aInfo.applicationInfo.uid); Intent recentsIntent = new Intent().setComponent(mRecentsComponent); // Null animation indicates to preload. diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index dc9a62554fdb0..258625c751a37 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -43,12 +43,14 @@ import static org.mockito.Mockito.withSettings; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; +import android.app.IApplicationThread; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; @@ -454,8 +456,32 @@ public class SystemServicesTestRule implements TestRule { .spiedInstance(sWakeLock).stubOnly()); } - void setSurfaceFactory(Supplier factory) { - mSurfaceFactory = factory; + WindowProcessController addProcess(String pkgName, String procName, int pid, int uid) { + return addProcess(mAtmService, pkgName, procName, pid, uid); + } + + static WindowProcessController addProcess(ActivityTaskManagerService atmService, String pkgName, + String procName, int pid, int uid) { + final ApplicationInfo info = new ApplicationInfo(); + info.uid = uid; + info.packageName = pkgName; + return addProcess(atmService, info, procName, pid); + } + + static WindowProcessController addProcess(ActivityTaskManagerService atmService, + ApplicationInfo info, String procName, int pid) { + final WindowProcessListener mockListener = mock(WindowProcessListener.class, + withSettings().stubOnly()); + final int uid = info.uid; + final WindowProcessController proc = new WindowProcessController(atmService, + info, procName, uid, UserHandle.getUserId(uid), mockListener, mockListener); + proc.setThread(mock(IApplicationThread.class, withSettings().stubOnly())); + atmService.mProcessNames.put(procName, uid, proc); + if (pid > 0) { + proc.setPid(pid); + atmService.mProcessMap.put(pid, proc); + } + return proc; } void cleanupWindowManagerHandlers() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 20fbda428381c..7347f8825ff70 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -64,7 +64,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; -import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -915,13 +914,15 @@ class WindowTestsBase extends SystemServiceTestsBase { */ protected static class ActivityBuilder { static final int DEFAULT_FAKE_UID = 12345; + static final String DEFAULT_PROCESS_NAME = "procName"; + static int sProcNameSeq; private final ActivityTaskManagerService mService; private ComponentName mComponent; private String mTargetActivity; private Task mTask; - private String mProcessName = "name"; + private String mProcessName = DEFAULT_PROCESS_NAME; private String mAffinity; private int mUid = DEFAULT_FAKE_UID; private boolean mCreateTask = false; @@ -1109,6 +1110,9 @@ class WindowTestsBase extends SystemServiceTestsBase { aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; aInfo.applicationInfo.packageName = mComponent.getPackageName(); aInfo.applicationInfo.uid = mUid; + if (DEFAULT_PROCESS_NAME.equals(mProcessName)) { + mProcessName += ++sProcNameSeq; + } aInfo.processName = mProcessName; aInfo.packageName = mComponent.getPackageName(); aInfo.name = mComponent.getClassName(); @@ -1173,16 +1177,11 @@ class WindowTestsBase extends SystemServiceTestsBase { if (mWpc != null) { wpc = mWpc; } else { - wpc = new WindowProcessController(mService, - aInfo.applicationInfo, mProcessName, mUid, - UserHandle.getUserId(mUid), mock(Object.class), - mock(WindowProcessListener.class)); - wpc.setThread(mock(IApplicationThread.class)); + final WindowProcessController p = mService.getProcessController(mProcessName, mUid); + wpc = p != null ? p : SystemServicesTestRule.addProcess( + mService, aInfo.applicationInfo, mProcessName, 0 /* pid */); } - wpc.setThread(mock(IApplicationThread.class)); activity.setProcess(wpc); - doReturn(wpc).when(mService).getProcessController( - activity.processName, activity.info.applicationInfo.uid); // Resume top activities to make sure all other signals in the system are connected. mService.mRootWindowContainer.resumeFocusedTasksTopActivities();