diff options
6 files changed, 157 insertions, 9 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 4f71719006f5..567eca2f639a 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2572,14 +2572,21 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { wpc.computeProcessActivityState(); } - void computeProcessActivityStateBatch() { + boolean computeProcessActivityStateBatch() { if (mActivityStateChangedProcs.isEmpty()) { - return; + return false; } + boolean changed = false; for (int i = mActivityStateChangedProcs.size() - 1; i >= 0; i--) { - mActivityStateChangedProcs.get(i).computeProcessActivityState(); + final WindowProcessController wpc = mActivityStateChangedProcs.get(i); + final int prevState = wpc.getActivityStateFlags(); + wpc.computeProcessActivityState(); + if (!changed && prevState != wpc.getActivityStateFlags()) { + changed = true; + } } mActivityStateChangedProcs.clear(); + return changed; } /** diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 266fdbba0be7..8e81beacbc8f 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -105,6 +105,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserProperties; import android.content.res.Configuration; import android.graphics.Rect; +import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.power.Mode; @@ -153,6 +154,7 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.WindowManagerPolicy; import com.android.server.utils.Slogf; +import com.android.server.wm.utils.RegionUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -270,6 +272,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private boolean mTaskLayersChanged = true; private int mTmpTaskLayerRank; private final RankTaskLayersRunnable mRankTaskLayersRunnable = new RankTaskLayersRunnable(); + private Region mTmpOccludingRegion; + private Region mTmpTaskRegion; private String mDestroyAllActivitiesReason; private final Runnable mDestroyAllActivitiesRunnable = new Runnable() { @@ -2921,6 +2925,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> }); } + void invalidateTaskLayersAndUpdateOomAdjIfNeeded() { + mRankTaskLayersRunnable.mCheckUpdateOomAdj = true; + invalidateTaskLayers(); + } + void invalidateTaskLayers() { if (!mTaskLayersChanged) { mTaskLayersChanged = true; @@ -2938,13 +2947,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Only rank for leaf tasks because the score of activity is based on immediate parent. forAllLeafTasks(task -> { final int oldRank = task.mLayerRank; - final ActivityRecord r = task.topRunningActivityLocked(); - if (r != null && r.isVisibleRequested()) { + final int oldRatio = task.mNonOccludedFreeformAreaRatio; + task.mNonOccludedFreeformAreaRatio = 0; + if (task.isVisibleRequested()) { task.mLayerRank = ++mTmpTaskLayerRank; + if (task.inFreeformWindowingMode()) { + computeNonOccludedFreeformAreaRatio(task); + } } else { task.mLayerRank = Task.LAYER_RANK_INVISIBLE; } - if (task.mLayerRank != oldRank) { + if (task.mLayerRank != oldRank + || task.mNonOccludedFreeformAreaRatio != oldRatio) { task.forAllActivities(activity -> { if (activity.hasProcess()) { mTaskSupervisor.onProcessActivityStateChanged(activity.app, @@ -2953,12 +2967,42 @@ class RootWindowContainer extends WindowContainer<DisplayContent> }); } }, true /* traverseTopToBottom */); - + if (mTmpOccludingRegion != null) { + mTmpOccludingRegion.setEmpty(); + } + boolean changed = false; if (!mTaskSupervisor.inActivityVisibilityUpdate()) { - mTaskSupervisor.computeProcessActivityStateBatch(); + changed = mTaskSupervisor.computeProcessActivityStateBatch(); + } + if (mRankTaskLayersRunnable.mCheckUpdateOomAdj) { + mRankTaskLayersRunnable.mCheckUpdateOomAdj = false; + if (changed) { + mService.updateOomAdj(); + } } } + /** This method is called for visible freeform task from top to bottom. */ + private void computeNonOccludedFreeformAreaRatio(@NonNull Task task) { + if (!com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()) { + return; + } + if (mTmpOccludingRegion == null) { + mTmpOccludingRegion = new Region(); + mTmpTaskRegion = new Region(); + } + final Rect taskBounds = task.getBounds(); + mTmpTaskRegion.set(taskBounds); + // Exclude the area outside the display. + mTmpTaskRegion.op(task.mDisplayContent.getBounds(), Region.Op.INTERSECT); + // Exclude the area covered by the above tasks. + mTmpTaskRegion.op(mTmpOccludingRegion, Region.Op.DIFFERENCE); + task.mNonOccludedFreeformAreaRatio = 100 * RegionUtils.getAreaSize(mTmpTaskRegion) + / (taskBounds.width() * taskBounds.height()); + // Accumulate the occluding region for other visible tasks behind. + mTmpOccludingRegion.op(taskBounds, Region.Op.UNION); + } + void clearOtherAppTimeTrackers(AppTimeTracker except) { forAllActivities(r -> { if (r.appTimeTracker != except) { @@ -3862,6 +3906,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } private class RankTaskLayersRunnable implements Runnable { + boolean mCheckUpdateOomAdj; + @Override public void run() { synchronized (mService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 98919d9fd617..6d1d3fb4f3e9 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -432,6 +432,9 @@ class Task extends TaskFragment { // This number will be assigned when we evaluate OOM scores for all visible tasks. int mLayerRank = LAYER_RANK_INVISIBLE; + /** A 0~100 ratio to indicate the percentage of visible area on screen of a freeform task. */ + int mNonOccludedFreeformAreaRatio; + /* Unique identifier for this task. */ final int mTaskId; /* User for which this task was created. */ @@ -1202,6 +1205,28 @@ class Task extends TaskFragment { } @Override + void onResize() { + super.onResize(); + updateTaskLayerForFreeform(); + } + + @Override + void onMovedByResize() { + super.onMovedByResize(); + updateTaskLayerForFreeform(); + } + + private void updateTaskLayerForFreeform() { + if (!com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode()) { + return; + } + if (!isVisibleRequested() || !inFreeformWindowingMode()) { + return; + } + mRootWindowContainer.invalidateTaskLayersAndUpdateOomAdjIfNeeded(); + } + + @Override @Nullable ActivityRecord getTopResumedActivity() { if (!isLeafTask()) { @@ -5905,6 +5930,10 @@ class Task extends TaskFragment { pw.print(prefix); pw.print(" mLastNonFullscreenBounds="); pw.println(mLastNonFullscreenBounds); } + if (mNonOccludedFreeformAreaRatio != 0) { + pw.print(prefix); pw.print(" mNonOccludedFreeformAreaRatio="); + pw.println(mNonOccludedFreeformAreaRatio); + } if (isLeafTask()) { pw.println(prefix + " isSleeping=" + shouldSleepActivities()); printThisActivity(pw, getTopPausingActivity(), dumpPackage, false, diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 86adc1944371..ffb9a7143ca7 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -121,6 +121,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio */ private static final int MAX_NUM_PERCEPTIBLE_FREEFORM = SystemProperties.getInt("persist.wm.max_num_perceptible_freeform", 1); + /** + * If the visible area percentage of a resumed freeform task is greater than or equal to this + * ratio, its process will have a higher priority. + */ + private static final int PERCEPTIBLE_FREEFORM_VISIBLE_RATIO = 90; private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 200; private static final long RAPID_ACTIVITY_LAUNCH_MS = 500; @@ -1234,6 +1239,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio boolean hasResumedFreeform = false; int minTaskLayer = Integer.MAX_VALUE; int stateFlags = 0; + int nonOccludedRatio = 0; final boolean wasResumed = hasResumedActivity(); final boolean wasAnyVisible = (mActivityStateFlags & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0; @@ -1261,6 +1267,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio stateFlags |= ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN; } else if (windowingMode == WINDOWING_MODE_FREEFORM) { hasResumedFreeform = true; + nonOccludedRatio = + Math.max(task.mNonOccludedFreeformAreaRatio, nonOccludedRatio); } } if (minTaskLayer > 0) { @@ -1298,7 +1306,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio && com.android.window.flags.Flags.processPriorityPolicyForMultiWindowMode() // Exclude task layer 1 because it is already the top most. && minTaskLayer > 1) { - if (minTaskLayer <= 1 + MAX_NUM_PERCEPTIBLE_FREEFORM) { + if (minTaskLayer <= 1 + MAX_NUM_PERCEPTIBLE_FREEFORM + || nonOccludedRatio >= PERCEPTIBLE_FREEFORM_VISIBLE_RATIO) { stateFlags |= ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM; } else { stateFlags |= ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE; diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java index ce7776f270fd..ff23145fc6b9 100644 --- a/services/core/java/com/android/server/wm/utils/RegionUtils.java +++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java @@ -81,4 +81,15 @@ public class RegionUtils { Collections.reverse(rects); rects.forEach(rectConsumer); } + + /** Returns the area size of the region. */ + public static int getAreaSize(Region region) { + final RegionIterator regionIterator = new RegionIterator(region); + int area = 0; + final Rect rect = new Rect(); + while (regionIterator.next(rect)) { + area += rect.width() * rect.height(); + } + return area; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index cf1dcd0515d1..7e8bd38fb6a9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -285,6 +285,52 @@ public class RootWindowContainerTests extends WindowTestsBase { } @Test + public void testTaskLayerRankFreeform() { + mSetFlagsRule.enableFlags(com.android.window.flags.Flags + .FLAG_PROCESS_PRIORITY_POLICY_FOR_MULTI_WINDOW_MODE); + final Task[] freeformTasks = new Task[3]; + final WindowProcessController[] processes = new WindowProcessController[3]; + for (int i = 0; i < freeformTasks.length; i++) { + freeformTasks[i] = new TaskBuilder(mSupervisor) + .setWindowingMode(WINDOWING_MODE_FREEFORM).setCreateActivity(true).build(); + final ActivityRecord r = freeformTasks[i].getTopMostActivity(); + r.setState(RESUMED, "test"); + processes[i] = r.app; + } + resizeDisplay(mDisplayContent, 2400, 2000); + // --------- + // | 2 | 1 | + // --------- + // | 0 | | + // --------- + freeformTasks[2].setBounds(0, 0, 1000, 1000); + freeformTasks[1].setBounds(1000, 0, 2000, 1000); + freeformTasks[0].setBounds(0, 1000, 1000, 2000); + mRootWindowContainer.rankTaskLayers(); + assertEquals(1, freeformTasks[2].mLayerRank); + assertEquals(2, freeformTasks[1].mLayerRank); + assertEquals(3, freeformTasks[0].mLayerRank); + assertFalse("Top doesn't need perceptible hint", (processes[2].getActivityStateFlags() + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + assertTrue((processes[1].getActivityStateFlags() + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + assertTrue((processes[0].getActivityStateFlags() + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + + // Make task2 occlude half of task0. + clearInvocations(mAtm); + freeformTasks[2].setBounds(0, 0, 1000, 1500); + waitHandlerIdle(mWm.mH); + // The process of task0 will demote from perceptible to visible. + final int stateFlags0 = processes[0].getActivityStateFlags(); + assertTrue((stateFlags0 + & WindowProcessController.ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE) != 0); + assertFalse((stateFlags0 + & WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM) != 0); + verify(mAtm).updateOomAdj(); + } + + @Test public void testForceStopPackage() { final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = task.getTopMostActivity(); |