summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java13
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java56
-rw-r--r--services/core/java/com/android/server/wm/Task.java29
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java11
-rw-r--r--services/core/java/com/android/server/wm/utils/RegionUtils.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java46
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();