Merge "Add MainSwitchPreference support for Catalyst" into main
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e7f4dbc..ed6b3d4a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -237,6 +237,7 @@
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SafeZipPathValidatorCallback;
import com.android.internal.os.SomeArgs;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.DecorView;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
@@ -7675,6 +7676,16 @@
}
}
});
+
+ // Register callback to report native memory metrics post GC cleanup
+ if (Flags.reportPostgcMemoryMetrics() &&
+ com.android.libcore.readonly.Flags.postCleanupApis()) {
+ VMRuntime.addPostCleanupCallback(new Runnable() {
+ @Override public void run() {
+ MetricsLoggerWrapper.logPostGcMemorySnapshot();
+ }
+ });
+ }
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index e882bb5..081ce31 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -345,6 +345,15 @@
*/
public AppCompatTaskInfo appCompatTaskInfo = AppCompatTaskInfo.create();
+ /**
+ * The top activity's main window frame if it doesn't match the top activity bounds.
+ * {@code null}, otherwise.
+ *
+ * @hide
+ */
+ @Nullable
+ public Rect topActivityMainWindowFrame;
+
TaskInfo() {
// Do nothing
}
@@ -477,7 +486,8 @@
&& Objects.equals(capturedLink, that.capturedLink)
&& capturedLinkTimestamp == that.capturedLinkTimestamp
&& requestedVisibleTypes == that.requestedVisibleTypes
- && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo);
+ && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo)
+ && Objects.equals(topActivityMainWindowFrame, that.topActivityMainWindowFrame);
}
/**
@@ -553,6 +563,7 @@
capturedLinkTimestamp = source.readLong();
requestedVisibleTypes = source.readInt();
appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR);
+ topActivityMainWindowFrame = source.readTypedObject(Rect.CREATOR);
}
/**
@@ -606,6 +617,7 @@
dest.writeLong(capturedLinkTimestamp);
dest.writeInt(requestedVisibleTypes);
dest.writeTypedObject(appCompatTaskInfo, flags);
+ dest.writeTypedObject(topActivityMainWindowFrame, flags);
}
@Override
@@ -649,6 +661,7 @@
+ " capturedLinkTimestamp=" + capturedLinkTimestamp
+ " requestedVisibleTypes=" + requestedVisibleTypes
+ " appCompatTaskInfo=" + appCompatTaskInfo
+ + " topActivityMainWindowFrame=" + topActivityMainWindowFrame
+ "}";
}
}
diff --git a/core/java/android/app/metrics.aconfig b/core/java/android/app/metrics.aconfig
new file mode 100644
index 0000000..488f1c7
--- /dev/null
+++ b/core/java/android/app/metrics.aconfig
@@ -0,0 +1,10 @@
+package: "android.app"
+container: "system"
+
+flag {
+ namespace: "system_performance"
+ name: "report_postgc_memory_metrics"
+ is_exported: false
+ description: "Controls whether to report memory metrics post GC cleanup"
+ bug: "331243037"
+}
diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/core/java/com/android/internal/os/ProcfsMemoryUtil.java
similarity index 60%
rename from services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
rename to core/java/com/android/internal/os/ProcfsMemoryUtil.java
index 6cb6dc0..382f6c4 100644
--- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
+++ b/core/java/com/android/internal/os/ProcfsMemoryUtil.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.stats.pull;
+package com.android.internal.os;
-import static android.os.Process.PROC_OUT_STRING;
+import static android.os.Process.*;
import android.annotation.Nullable;
import android.os.Process;
@@ -23,6 +23,7 @@
public final class ProcfsMemoryUtil {
private static final int[] CMDLINE_OUT = new int[] { PROC_OUT_STRING };
+ private static final int[] OOM_SCORE_ADJ_OUT = new int[] { PROC_NEWLINE_TERM | PROC_OUT_LONG };
private static final String[] STATUS_KEYS = new String[] {
"Uid:",
"VmHWM:",
@@ -38,17 +39,34 @@
private ProcfsMemoryUtil() {}
/**
- * Reads memory stats of a process from procfs. Returns values of the VmHWM, VmRss, AnonRSS,
- * VmSwap, RssShmem fields in /proc/pid/status in kilobytes or null if not available.
+ * Reads memory stats of a process from procfs.
+ *
+ * Returns values of the VmHWM, VmRss, AnonRSS, VmSwap, RssShmem fields in
+ * /proc/pid/status in kilobytes or null if not available.
*/
@Nullable
public static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
+ return readMemorySnapshotFromProcfs("/proc/" + pid + "/status");
+ }
+
+ /**
+ * Reads memory stats of the current process from procfs.
+ *
+ * Returns values of the VmHWM, VmRss, AnonRSS, VmSwap, RssShmem fields in
+ * /proc/self/status in kilobytes or null if not available.
+ */
+ @Nullable
+ public static MemorySnapshot readMemorySnapshotFromProcfs() {
+ return readMemorySnapshotFromProcfs("/proc/self/status");
+ }
+
+ private static MemorySnapshot readMemorySnapshotFromProcfs(String path) {
long[] output = new long[STATUS_KEYS.length];
output[0] = -1;
output[3] = -1;
output[4] = -1;
output[5] = -1;
- Process.readProcLines("/proc/" + pid + "/status", STATUS_KEYS, output);
+ Process.readProcLines(path, STATUS_KEYS, output);
if (output[0] == -1 || output[3] == -1 || output[4] == -1 || output[5] == -1) {
// Could not open or parse file.
return null;
@@ -70,14 +88,54 @@
* if the file is not available.
*/
public static String readCmdlineFromProcfs(int pid) {
+ return readCmdlineFromProcfs("/proc/" + pid + "/cmdline");
+ }
+
+ /**
+ * Reads cmdline of the current process from procfs.
+ *
+ * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
+ * if the file is not available.
+ */
+ public static String readCmdlineFromProcfs() {
+ return readCmdlineFromProcfs("/proc/self/cmdline");
+ }
+
+ private static String readCmdlineFromProcfs(String path) {
String[] cmdline = new String[1];
- if (!Process.readProcFile("/proc/" + pid + "/cmdline", CMDLINE_OUT, cmdline, null, null)) {
+ if (!Process.readProcFile(path, CMDLINE_OUT, cmdline, null, null)) {
return "";
}
return cmdline[0];
}
/**
+ * Reads oom_score_adj of a process from procfs
+ *
+ * Returns content of /proc/pid/oom_score_adj. Defaults to 0 if reading fails.
+ */
+ public static int readOomScoreAdjFromProcfs(int pid) {
+ return readOomScoreAdjFromProcfs("/proc/" + pid + "/oom_score_adj");
+ }
+
+ /**
+ * Reads oom_score_adj of the current process from procfs
+ *
+ * Returns content of /proc/pid/oom_score_adj. Defaults to 0 if reading fails.
+ */
+ public static int readOomScoreAdjFromProcfs() {
+ return readOomScoreAdjFromProcfs("/proc/self/oom_score_adj");
+ }
+
+ private static int readOomScoreAdjFromProcfs(String path) {
+ long[] oom_score_adj = new long[1];
+ if (Process.readProcFile(path, OOM_SCORE_ADJ_OUT, null, oom_score_adj, null)) {
+ return (int)oom_score_adj[0];
+ }
+ return 0;
+ }
+
+ /**
* Scans all /proc/pid/cmdline entries and returns a mapping between pid and cmdline.
*/
public static SparseArray<String> getProcessCmdlines() {
@@ -109,7 +167,7 @@
/** Reads and parses selected entries of /proc/vmstat. */
@Nullable
- static VmStat readVmStat() {
+ public static VmStat readVmStat() {
long[] vmstat = new long[VMSTAT_KEYS.length];
vmstat[0] = -1;
Process.readProcLines("/proc/vmstat", VMSTAT_KEYS, vmstat);
@@ -121,7 +179,7 @@
return result;
}
- static final class VmStat {
+ public static final class VmStat {
public int oomKillCount;
}
}
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index b42ea7d..e2237f6 100644
--- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -16,9 +16,15 @@
package com.android.internal.os.logging;
+import android.app.Application;
+import android.os.Process;
+import android.util.Log;
import android.view.WindowManager.LayoutParams;
+import com.android.internal.os.ProcfsMemoryUtil;
import com.android.internal.util.FrameworkStatsLog;
+import java.util.Collection;
+import libcore.util.NativeAllocationRegistry;
/**
* Used to wrap different logging calls in one, so that client side code base is clean and more
@@ -49,4 +55,46 @@
}
}
}
+
+ public static void logPostGcMemorySnapshot() {
+ if (!com.android.libcore.Flags.nativeMetrics()) {
+ return;
+ }
+ int pid = Process.myPid();
+ String processName = Application.getProcessName();
+ Collection<NativeAllocationRegistry.Metrics> metrics =
+ NativeAllocationRegistry.getMetrics();
+ int nMetrics = metrics.size();
+
+ String[] classNames = new String[nMetrics];
+ long[] mallocedCount = new long[nMetrics];
+ long[] mallocedBytes = new long[nMetrics];
+ long[] nonmallocedCount = new long[nMetrics];
+ long[] nonmallocedBytes = new long[nMetrics];
+
+ int i = 0;
+ for (NativeAllocationRegistry.Metrics m : metrics) {
+ classNames[i] = m.getClassName();
+ mallocedCount[i] = m.getMallocedCount();
+ mallocedBytes[i] = m.getMallocedBytes();
+ nonmallocedCount[i] = m.getNonmallocedCount();
+ nonmallocedBytes[i] = m.getNonmallocedBytes();
+ i++;
+ }
+
+ ProcfsMemoryUtil.MemorySnapshot m = ProcfsMemoryUtil.readMemorySnapshotFromProcfs();
+ int oom_score_adj = ProcfsMemoryUtil.readOomScoreAdjFromProcfs();
+ FrameworkStatsLog.write(FrameworkStatsLog.POSTGC_MEMORY_SNAPSHOT,
+ m.uid, processName, pid,
+ oom_score_adj,
+ m.rssInKilobytes,
+ m.anonRssInKilobytes,
+ m.swapInKilobytes,
+ m.anonRssInKilobytes + m.swapInKilobytes,
+ classNames,
+ mallocedCount,
+ mallocedBytes,
+ nonmallocedCount,
+ nonmallocedBytes);
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
index 6ad2f08..220fc6f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
@@ -54,6 +54,7 @@
@NonNull
private final BackupIdler mBackupIdler = new BackupIdler();
private boolean mBackupIdlerScheduled;
+ private boolean mSaveEmbeddingState = false;
private final List<ParcelableTaskContainerData> mParcelableTaskContainerDataList =
new ArrayList<>();
@@ -71,11 +72,32 @@
}
}
+ void setAutoSaveEmbeddingState(boolean saveEmbeddingState) {
+ if (mSaveEmbeddingState == saveEmbeddingState) {
+ return;
+ }
+
+ Log.i(TAG, "Set save embedding state: " + saveEmbeddingState);
+ mSaveEmbeddingState = saveEmbeddingState;
+ if (!mSaveEmbeddingState) {
+ removeSavedState();
+ return;
+ }
+
+ if (!hasPendingStateToRestore() && !mController.getTaskContainers().isEmpty()) {
+ scheduleBackup();
+ }
+ }
/**
* Schedules a back-up request. It is no-op if there was a request scheduled and not yet
* completed.
*/
void scheduleBackup() {
+ if (!mSaveEmbeddingState) {
+ // TODO(b/289875940): enabled internally for broader testing.
+ return;
+ }
+
if (!mBackupIdlerScheduled) {
mBackupIdlerScheduled = true;
Looper.getMainLooper().getQueue().addIdleHandler(mBackupIdler);
@@ -128,7 +150,6 @@
final List<TaskFragmentInfo> infos = savedState.getParcelableArrayList(
KEY_RESTORE_TASK_FRAGMENTS_INFO, TaskFragmentInfo.class);
for (TaskFragmentInfo info : infos) {
- if (DEBUG) Log.d(TAG, "Retrieved: " + info);
mTaskFragmentInfos.put(info.getFragmentToken(), info);
mPresenter.updateTaskFragmentInfo(info);
}
@@ -140,6 +161,11 @@
if (DEBUG) Log.d(TAG, "Retrieved: " + info);
mTaskFragmentParentInfos.put(info.getTaskId(), info);
}
+
+ if (DEBUG) {
+ Log.d(TAG, "Retrieved task-fragment info: " + infos.size() + ", task info: "
+ + parentInfos.size());
+ }
}
void abortTaskContainerRebuilding(@NonNull WindowContainerTransaction wct) {
@@ -148,7 +174,6 @@
final TaskFragmentInfo info = mTaskFragmentInfos.valueAt(i);
mPresenter.deleteTaskFragment(wct, info.getFragmentToken());
}
-
removeSavedState();
}
@@ -190,6 +215,9 @@
final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>();
for (EmbeddingRule rule : rules) {
embeddingRuleMap.put(rule.getTag(), rule);
+ if (DEBUG) {
+ Log.d(TAG, "Tag: " + rule.getTag() + " rule: " + rule);
+ }
}
boolean restoredAny = false;
@@ -201,7 +229,7 @@
// has unknown tag, unable to restore.
if (DEBUG) {
Log.d(TAG, "Rebuilding TaskContainer abort! Unknown Tag. Task#"
- + parcelableTaskContainerData.mTaskId);
+ + parcelableTaskContainerData.mTaskId + ", tags = " + tags);
}
continue;
}
@@ -217,7 +245,7 @@
final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData,
mController, mTaskFragmentInfos);
- if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer);
+ if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer.getTaskId());
mController.addTaskContainer(taskContainer.getTaskId(), taskContainer);
for (ParcelableSplitContainerData splitData :
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 3368e2e..60e1a50 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -2886,6 +2886,18 @@
return getActiveSplitForContainer(container) != null;
}
+
+ @Override
+ public void setAutoSaveEmbeddingState(boolean saveEmbeddingState) {
+ if (!Flags.aeBackStackRestore()) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mPresenter.setAutoSaveEmbeddingState(saveEmbeddingState);
+ }
+ }
+
void scheduleBackup() {
synchronized (mLock) {
mPresenter.scheduleBackup();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index b498ee2..9a2f32e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -183,6 +183,10 @@
}
}
+ void setAutoSaveEmbeddingState(boolean saveEmbeddingState) {
+ mBackupHelper.setAutoSaveEmbeddingState(saveEmbeddingState);
+ }
+
void scheduleBackup() {
mBackupHelper.scheduleBackup();
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index b453f1d..6928409 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -48,8 +48,6 @@
import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
-import com.android.window.flags.Flags;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -634,11 +632,7 @@
// pin container.
updateAlwaysOnTopOverlayIfNecessary();
- // TODO(b/289875940): Making backup-restore as an opt-in solution, before the flag goes
- // to next-food.
- if (Flags.aeBackStackRestore()) {
- mSplitController.scheduleBackup();
- }
+ mSplitController.scheduleBackup();
}
private void updateAlwaysOnTopOverlayIfNecessary() {
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 65b2275..00ae05c 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -76,6 +76,7 @@
"truth",
"Nene",
"Harrier",
+ "bedstead-enterprise",
],
libs: [
"android.test.base.stubs.system",
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
index e86e727..9cce431 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
@@ -20,6 +20,9 @@
import static android.provider.Settings.Secure.SYNC_PARENT_SOUNDS;
import static android.provider.Settings.System.RINGTONE;
+import static com.android.bedstead.enterprise.EnterpriseDeviceStateExtensionsKt.workProfile;
+import static com.android.bedstead.multiuser.MultiUserDeviceStateExtensionsKt.secondaryUser;
+
import static com.google.common.truth.Truth.assertThat;
import android.content.pm.PackageManager;
@@ -82,7 +85,7 @@
@RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@EnsureHasWorkProfile
public void testSettings_workProfile() throws Exception {
- UserReference profile = sDeviceState.workProfile();
+ UserReference profile = workProfile(sDeviceState);
// Settings.Global settings are shared between different users
assertSettingsShared(SPACE_GLOBAL, mPrimaryUser.id(), profile.id());
@@ -96,7 +99,7 @@
@RequireRunOnInitialUser
@EnsureHasSecondaryUser
public void testSettings_secondaryUser() throws Exception {
- UserReference secondaryUser = sDeviceState.secondaryUser();
+ UserReference secondaryUser = secondaryUser(sDeviceState);
// Settings.Global settings are shared between different users
assertSettingsShared(SPACE_GLOBAL, mPrimaryUser.id(), secondaryUser.id());
@@ -223,7 +226,7 @@
@RequireRunOnInitialUser
@EnsureHasSecondaryUser
public void testSettings_stopAndRestartSecondaryUser() throws Exception {
- UserReference secondaryUser = sDeviceState.secondaryUser();
+ UserReference secondaryUser = secondaryUser(sDeviceState);
assertSettingsDifferent(SPACE_SECURE, mPrimaryUser.id(), secondaryUser.id());
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index ba4b71c..17fcbf4 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -22,7 +22,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.ProcessRecord.TAG;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static com.android.internal.os.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -58,7 +58,7 @@
import com.android.modules.expresslog.Counter;
import com.android.server.ResourcePressureUtil;
import com.android.server.criticalevents.CriticalEventLog;
-import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
+import com.android.internal.os.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.wm.WindowProcessController;
import java.io.File;
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e7735d8..54e4f8e 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -48,6 +48,9 @@
import static android.util.MathUtils.constrain;
import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
+import static com.android.internal.os.ProcfsMemoryUtil.getProcessCmdlines;
+import static com.android.internal.os.ProcfsMemoryUtil.readCmdlineFromProcfs;
+import static com.android.internal.os.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY;
@@ -68,9 +71,6 @@
import static com.android.server.stats.Flags.applyNetworkStatsPollRateLimit;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import static com.android.server.stats.pull.netstats.NetworkStatsUtils.fromPublicNetworkStats;
import static com.android.server.stats.pull.netstats.NetworkStatsUtils.isAddEntriesSupported;
@@ -209,6 +209,8 @@
import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.ProcfsMemoryUtil;
+import com.android.internal.os.ProcfsMemoryUtil.MemorySnapshot;
import com.android.internal.os.SelectedProcessCpuThreadReader;
import com.android.internal.os.StoragedUidIoStatsReader;
import com.android.internal.util.CollectionUtils;
@@ -229,7 +231,6 @@
import com.android.server.power.stats.KernelWakelockStats;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
-import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.stats.pull.netstats.NetworkStatsAccumulator;
import com.android.server.stats.pull.netstats.NetworkStatsExt;
import com.android.server.stats.pull.netstats.SubInfo;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 4f71719..567eca2 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 @@
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/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 49fcb18..6cc4b1e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -994,7 +994,9 @@
// Ensure the final animation targets which hidden by transition could be visible.
for (int i = 0; i < targets.size(); i++) {
final WindowContainer wc = targets.get(i).mContainer;
- wc.prepareSurfaces();
+ if (wc.mSurfaceControl != null) {
+ wc.prepareSurfaces();
+ }
}
// The pending builder could be cleared due to prepareSurfaces
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 41a5804..6707a27 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.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.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 @@
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 @@
});
}
+ void invalidateTaskLayersAndUpdateOomAdjIfNeeded() {
+ mRankTaskLayersRunnable.mCheckUpdateOomAdj = true;
+ invalidateTaskLayers();
+ }
+
void invalidateTaskLayers() {
if (!mTaskLayersChanged) {
mTaskLayersChanged = true;
@@ -2938,13 +2947,18 @@
// 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,10 +2967,40 @@
});
}
}, true /* traverseTopToBottom */);
-
- if (!mTaskSupervisor.inActivityVisibilityUpdate()) {
- mTaskSupervisor.computeProcessActivityStateBatch();
+ if (mTmpOccludingRegion != null) {
+ mTmpOccludingRegion.setEmpty();
}
+ boolean changed = false;
+ if (!mTaskSupervisor.inActivityVisibilityUpdate()) {
+ 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) {
@@ -3862,6 +3906,8 @@
}
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 4861341..8a624b3 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 @@
// 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. */
@@ -1207,6 +1210,28 @@
}
@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()) {
@@ -3410,6 +3435,36 @@
info.requestedVisibleTypes = (windowState != null && Flags.enableFullyImmersiveInDesktop())
? windowState.getRequestedVisibleTypes() : WindowInsets.Type.defaultVisible();
AppCompatUtils.fillAppCompatTaskInfo(this, info, top);
+ info.topActivityMainWindowFrame = calculateTopActivityMainWindowFrameForTaskInfo(top);
+ }
+
+ /**
+ * Returns the top activity's main window frame if it doesn't match
+ * {@link ActivityRecord#getBounds() the top activity bounds}, or {@code null}, otherwise.
+ *
+ * @param top The top running activity of the task
+ */
+ @Nullable
+ private static Rect calculateTopActivityMainWindowFrameForTaskInfo(
+ @Nullable ActivityRecord top) {
+ if (!Flags.betterSupportNonMatchParentActivity()) {
+ return null;
+ }
+ if (top == null) {
+ return null;
+ }
+ final WindowState mainWindow = top.findMainWindow();
+ if (mainWindow == null) {
+ return null;
+ }
+ if (!mainWindow.mHaveFrame) {
+ return null;
+ }
+ final Rect windowFrame = mainWindow.getFrame();
+ if (top.getBounds().equals(windowFrame)) {
+ return null;
+ }
+ return windowFrame;
}
/**
@@ -5919,6 +5974,10 @@
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/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 3ffeacf..d6ba312 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -3027,7 +3027,11 @@
// The task order may be changed by finishIfPossible() for adjusting focus if there are
// nested tasks, so add all activities into a list to avoid missed removals.
final ArrayList<ActivityRecord> removingActivities = new ArrayList<>();
- forAllActivities((Consumer<ActivityRecord>) removingActivities::add);
+ forAllActivities((r) -> {
+ if (!r.finishing) {
+ removingActivities.add(r);
+ }
+ });
for (int i = removingActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = removingActivities.get(i);
if (withTransition && r.isVisible()) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1a107c2..7f0c336 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 @@
*/
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 @@
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 @@
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 @@
&& 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 ce7776f..ff23145 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 @@
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/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
index f15e533..2f00a1b 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -32,6 +32,7 @@
"androidx.test.runner",
"truth",
"Harrier",
+ "bedstead-multiuser",
],
platform_apis: true,
certificate: "platform",
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index 70a2d48..48cebd7 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.MOVE_PACKAGE;
import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
+import static com.android.bedstead.multiuser.MultiUserDeviceStateExtensionsKt.secondaryUser;
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static com.google.common.truth.Truth.assertThat;
@@ -112,9 +113,9 @@
final UserReference primaryUser = sDeviceState.primaryUser();
if (primaryUser.id() == UserHandle.myUserId()) {
mCurrentUser = primaryUser;
- mOtherUser = sDeviceState.secondaryUser();
+ mOtherUser = secondaryUser(sDeviceState);
} else {
- mCurrentUser = sDeviceState.secondaryUser();
+ mCurrentUser = secondaryUser(sDeviceState);
mOtherUser = primaryUser;
}
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 cf1dcd0..7e8bd38 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 @@
}
@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();