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..f830f6fe09267 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,26 @@ 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 {} + + /** + * If non-zero, getTopProcessState() will + * return {@link ActivityManager#PROCESS_STATE_IMPORTANT_FOREGROUND} to avoid top app from + * preempting CPU while another process is running an important animation. + */ + @DemoteTopReason + volatile int mDemoteTopAppReasons; + /** 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 +2850,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 ((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) { + 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 +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) { + mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING; final WindowState notificationShade = mRootWindowContainer.getDefaultDisplay() .getDisplayPolicy().getNotificationShade(); proc = notificationShade != null @@ -3399,8 +3432,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. + mDemoteTopAppReasons |= DEMOTE_TOP_REASON_DURING_UNLOCKING; } mRootWindowContainer.forAllDisplays(displayContent -> { @@ -3968,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) { @@ -5595,12 +5634,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public int getTopProcessState() { + final int topState = mTopProcessState; + 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) { // 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/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/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 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();