diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3eb67ab33e7..257d9522796 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9482,6 +9482,13 @@
other {# apps used memory in the last {time}}
}
+
+ Enable memory usage profiling
+
+ Memory usage profiling requires additional system resources.
+
+ Memory profiling disabled
+
Frequency
diff --git a/res/xml/process_stats_summary.xml b/res/xml/process_stats_summary.xml
index 3b3271d769a..9def18fa7b8 100644
--- a/res/xml/process_stats_summary.xml
+++ b/res/xml/process_stats_summary.xml
@@ -19,45 +19,52 @@
android:title="@string/app_memory_use"
android:key="app_list">
+
+
+ android:title="@string/average_memory_use"
+ android:key="memory_info">
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
diff --git a/src/com/android/settings/applications/ProcessStatsSummary.java b/src/com/android/settings/applications/ProcessStatsSummary.java
index 4044794e719..ef76cd5840b 100644
--- a/src/com/android/settings/applications/ProcessStatsSummary.java
+++ b/src/com/android/settings/applications/ProcessStatsSummary.java
@@ -16,20 +16,26 @@
package com.android.settings.applications;
import android.app.settings.SettingsEnums;
+import android.content.ContentResolver;
import android.content.Context;
import android.icu.text.MessageFormat;
import android.os.Bundle;
+import android.os.Flags;
+import android.provider.Settings;
import android.text.format.Formatter;
import android.text.format.Formatter.BytesResult;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SummaryPreference;
import com.android.settings.Utils;
import com.android.settings.applications.ProcStatsData.MemInfo;
import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.development.DisableDevSettingsDialogFragment;
import java.util.HashMap;
import java.util.Locale;
@@ -37,6 +43,8 @@ import java.util.Map;
public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenceClickListener {
+ private static final String KEY_PREF_SCREEN = "app_list";
+ private static final String KEY_MEMORY_INFO_PREF_GROUP = "memory_info";
private static final String KEY_STATUS_HEADER = "status_header";
private static final String KEY_PERFORMANCE = "performance";
@@ -44,7 +52,9 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
private static final String KEY_AVERAGY_USED = "average_used";
private static final String KEY_FREE = "free";
private static final String KEY_APP_LIST = "apps_list";
+ private static final String KEY_FORCE_ENABLE_PSS_PROFILING = "force_enable_pss_profiling";
+ private PreferenceCategory mMemoryInfoPrefCategory;
private SummaryPreference mSummaryPref;
private Preference mPerformance;
@@ -52,12 +62,14 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
private Preference mAverageUsed;
private Preference mFree;
private Preference mAppListPreference;
+ private SwitchPreference mForceEnablePssProfiling;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.process_stats_summary);
+ mMemoryInfoPrefCategory = (PreferenceCategory) findPreference(KEY_MEMORY_INFO_PREF_GROUP);
mSummaryPref = (SummaryPreference) findPreference(KEY_STATUS_HEADER);
mPerformance = findPreference(KEY_PERFORMANCE);
mTotalMemory = findPreference(KEY_TOTAL_MEMORY);
@@ -65,11 +77,37 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
mFree = findPreference(KEY_FREE);
mAppListPreference = findPreference(KEY_APP_LIST);
mAppListPreference.setOnPreferenceClickListener(this);
+
+ // This preference is only applicable if the flag for PSS deprecation in AppProfiler is
+ // enabled. Otherwise, it can immediately be hidden.
+ mForceEnablePssProfiling =
+ (SwitchPreference) findPreference(KEY_FORCE_ENABLE_PSS_PROFILING);
+ if (Flags.removeAppProfilerPssCollection()) {
+ mForceEnablePssProfiling.setOnPreferenceClickListener(this);
+ // Make the toggle reflect the current state of the global setting.
+ mForceEnablePssProfiling.setChecked(isPssProfilingForceEnabled(getContext()));
+ } else {
+ mForceEnablePssProfiling.setVisible(false);
+ }
+ }
+
+ private void refreshPreferences() {
+ // The memory fields should be static if the flag is not enabled.
+ if (!Flags.removeAppProfilerPssCollection()) {
+ return;
+ }
+ mMemoryInfoPrefCategory.setVisible(mForceEnablePssProfiling.isChecked());
}
@Override
public void refreshUi() {
Context context = getContext();
+ refreshPreferences();
+
+ // If PSS collection is not enabled, none of the following work needs to be done.
+ if (Flags.removeAppProfilerPssCollection() && !isPssProfilingForceEnabled(context)) {
+ return;
+ }
MemInfo memInfo = mStatsManager.getMemInfo();
@@ -100,7 +138,8 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
String durationString = getString(sDurationLabels[mDurationIndex]);
int numApps = mStatsManager.getEntries().size();
MessageFormat msgFormat = new MessageFormat(
- getResources().getString(R.string.memory_usage_apps_summary), Locale.getDefault());
+ getResources().getString(R.string.memory_usage_apps_summary),
+ Locale.getDefault());
Map arguments = new HashMap<>();
arguments.put("count", numApps);
arguments.put("time", durationString);
@@ -131,7 +170,34 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
.setSourceMetricsCategory(getMetricsCategory())
.launch();
return true;
+ } else if (preference == mForceEnablePssProfiling) {
+ DisableDevSettingsDialogFragment.show(this);
}
return false;
}
+
+ private boolean isPssProfilingForceEnabled(Context context) {
+ ContentResolver cr = context.getContentResolver();
+ return Settings.Global.getInt(cr, Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1;
+ }
+
+ /**
+ * Called when the reboot confirmation button is clicked.
+ */
+ public void onRebootDialogConfirmed() {
+ Context context = getContext();
+ ContentResolver cr = context.getContentResolver();
+ Settings.Global.putInt(cr, Settings.Global.FORCE_ENABLE_PSS_PROFILING,
+ mForceEnablePssProfiling.isChecked() ? 1 : 0);
+ refreshPreferences();
+ }
+
+ /**
+ * Called when the reboot deny button is clicked.
+ */
+ public void onRebootDialogCanceled() {
+ // Set the toggle to reflect the state of the setting, which should not have changed.
+ mForceEnablePssProfiling.setChecked(isPssProfilingForceEnabled(getContext()));
+ }
+
}
diff --git a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
index 224768b0561..4355f277cb3 100644
--- a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
+++ b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java
@@ -29,6 +29,8 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.applications.ProcessStatsSummary;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment
@@ -42,7 +44,7 @@ public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment
return dialog;
}
- public static void show(DevelopmentSettingsDashboardFragment host) {
+ public static void show(SettingsPreferenceFragment host) {
final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment();
dialog.setTargetFragment(host, 0 /* requestCode */);
// We need to handle data changes and switch state based on which button user clicks,
@@ -75,18 +77,31 @@ public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment
@Override
public void onClick(DialogInterface dialog, int which) {
Fragment fragment = getTargetFragment();
- if (!(fragment instanceof DevelopmentSettingsDashboardFragment)){
+ if (!(fragment instanceof DevelopmentSettingsDashboardFragment)
+ && !(fragment instanceof ProcessStatsSummary)) {
Log.e(TAG, "getTargetFragment return unexpected type");
}
- final DevelopmentSettingsDashboardFragment host =
- (DevelopmentSettingsDashboardFragment) fragment;
- if (which == DialogInterface.BUTTON_POSITIVE) {
- host.onDisableDevelopmentOptionsConfirmed();
- PowerManager pm = getContext().getSystemService(PowerManager.class);
- pm.reboot(null);
- } else {
- host.onDisableDevelopmentOptionsRejected();
+ if (fragment instanceof DevelopmentSettingsDashboardFragment) {
+ final DevelopmentSettingsDashboardFragment host =
+ (DevelopmentSettingsDashboardFragment) fragment;
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ host.onDisableDevelopmentOptionsConfirmed();
+ PowerManager pm = getContext().getSystemService(PowerManager.class);
+ pm.reboot(null);
+ } else {
+ host.onDisableDevelopmentOptionsRejected();
+ }
+ } else if (fragment instanceof ProcessStatsSummary) {
+ final ProcessStatsSummary host =
+ (ProcessStatsSummary) fragment;
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ host.onRebootDialogConfirmed();
+ PowerManager pm = getContext().getSystemService(PowerManager.class);
+ pm.reboot(null);
+ } else {
+ host.onRebootDialogCanceled();
+ }
}
}
}
diff --git a/src/com/android/settings/development/MemoryUsagePreferenceController.java b/src/com/android/settings/development/MemoryUsagePreferenceController.java
index 1b20e70a578..ca8effa4a32 100644
--- a/src/com/android/settings/development/MemoryUsagePreferenceController.java
+++ b/src/com/android/settings/development/MemoryUsagePreferenceController.java
@@ -17,6 +17,8 @@
package com.android.settings.development;
import android.content.Context;
+import android.os.Flags;
+import android.provider.Settings;
import android.text.format.Formatter;
import androidx.annotation.VisibleForTesting;
@@ -65,9 +67,13 @@ public class MemoryUsagePreferenceController extends DeveloperOptionsPreferenceC
(long) memInfo.realUsedRam);
final String totalResult = Formatter.formatShortFileSize(mContext,
(long) memInfo.realTotalRam);
- ThreadUtils.postOnMainThread(
- () -> mPreference.setSummary(mContext.getString(R.string.memory_summary,
- usedResult, totalResult)));
+ boolean displayMemorySummary = !Flags.removeAppProfilerPssCollection();
+ displayMemorySummary |= Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1;
+ String summary = displayMemorySummary
+ ? mContext.getString(R.string.memory_summary, usedResult, totalResult)
+ : mContext.getString(R.string.pss_profiling_disabled);
+ ThreadUtils.postOnMainThread(() -> mPreference.setSummary(summary));
});
}
diff --git a/tests/uitests/src/com/android/settings/ui/MemorySettingsTest.kt b/tests/uitests/src/com/android/settings/ui/MemorySettingsTest.kt
new file mode 100644
index 00000000000..312e2c62bf2
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/ui/MemorySettingsTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.ui
+
+import android.os.Flags
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.clickObject
+import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MemorySettingsTest {
+ private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+ @Before
+ fun setUp() {
+ device.startMainActivityFromHomeScreen(Settings.ACTION_DEVICE_INFO_SETTINGS)
+ device.assertHasTexts(listOf(BUILD_NUMBER))
+ repeat(7) { // Enable development mode
+ device.clickObject(By.text(BUILD_NUMBER))
+ }
+ device.startMainActivityFromHomeScreen(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)
+ device.clickObject(By.text(MEMORY_PAGE))
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+ fun memoryPageIfPssFlagDisabled() {
+ device.assertHasTexts(ON_SCREEN_TEXTS_DEFAULT)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION)
+ fun memoryPageIfPssFlagEnabled() {
+ device.assertHasTexts(ON_SCREEN_TEXTS_PSS_PROFILING_DISABLED)
+ }
+
+ private companion object {
+ private const val BUILD_NUMBER = "Build number"
+ private const val MEMORY_PAGE = "Memory"
+ val ON_SCREEN_TEXTS_DEFAULT = listOf(
+ "Performance",
+ "Total memory",
+ "Average used (%)",
+ "Free",
+ )
+ val ON_SCREEN_TEXTS_PSS_PROFILING_DISABLED = listOf(
+ "Enable memory usage profiling",
+ )
+ }
+}