summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java10
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java46
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java123
6 files changed, 194 insertions, 2 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3d145c01de11..a17f356e1fc2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4369,4 +4369,7 @@
<bool name="config_pdp_reject_enable_retry">false</bool>
<!-- pdp data reject retry delay in ms -->
<integer name="config_pdp_reject_retry_delay_ms">-1</integer>
+
+ <!-- Component names of the services which will keep critical code path warm -->
+ <string-array name="config_keep_warming_services" translatable="false" />
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bfb6802a2b80..06f357e79a62 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4048,4 +4048,6 @@
<java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" />
<java-symbol type="array" name="config_notificationMsgPkgsAllowedAsConvos" />
+
+ <java-symbol type="array" name="config_keep_warming_services" />
</resources>
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 135ac9a7846e..7be843f17863 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
import android.app.ActivityThread;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -336,6 +337,11 @@ final class ActivityManagerConstants extends ContentObserver {
*/
public int PENDINGINTENT_WARNING_THRESHOLD = DEFAULT_PENDINGINTENT_WARNING_THRESHOLD;
+ /**
+ * Component names of the services which will keep critical code path of the host warm
+ */
+ public final ArraySet<ComponentName> KEEP_WARMING_SERVICES = new ArraySet<ComponentName>();
+
private List<String> mDefaultImperceptibleKillExemptPackages;
private List<Integer> mDefaultImperceptibleKillExemptProcStates;
@@ -442,6 +448,10 @@ final class ActivityManagerConstants extends ContentObserver {
.boxed().collect(Collectors.toList());
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.addAll(mDefaultImperceptibleKillExemptProcStates);
+ KEEP_WARMING_SERVICES.addAll(Arrays.stream(
+ context.getResources().getStringArray(
+ com.android.internal.R.array.config_keep_warming_services))
+ .map(ComponentName::unflattenFromString).collect(Collectors.toSet()));
}
public void start(ContentResolver resolver) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index da5f48962130..f0343e1d807c 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -76,7 +76,11 @@ import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ServiceInfo;
import android.os.Debug;
import android.os.Handler;
@@ -265,6 +269,43 @@ public final class OomAdjuster {
void initSettings() {
mCachedAppOptimizer.init();
+ if (mService.mConstants.KEEP_WARMING_SERVICES.size() > 0) {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mService) {
+ handleUserSwitchedLocked();
+ }
+ }
+ }, filter, null, mService.mHandler);
+ }
+ }
+
+ /**
+ * Update the keep-warming service flags upon user switches
+ */
+ @VisibleForTesting
+ @GuardedBy("mService")
+ void handleUserSwitchedLocked() {
+ final ArraySet<ComponentName> warmServices = mService.mConstants.KEEP_WARMING_SERVICES;
+ final ArrayList<ProcessRecord> processes = mProcessList.mLruProcesses;
+ for (int i = processes.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = processes.get(i);
+ boolean includeWarmPkg = false;
+ for (int j = warmServices.size() - 1; j >= 0; j--) {
+ if (app.pkgList.containsKey(warmServices.valueAt(j).getPackageName())) {
+ includeWarmPkg = true;
+ break;
+ }
+ }
+ if (!includeWarmPkg) {
+ continue;
+ }
+ for (int j = app.numberOfRunningServices() - 1; j >= 0; j--) {
+ app.getRunningServiceAt(j).updateKeepWarmLocked();
+ }
+ }
}
/**
@@ -1470,7 +1511,7 @@ public final class OomAdjuster {
"Raise procstate to started service: " + app);
}
}
- if (app.hasShownUi && !app.getCachedIsHomeProcess()) {
+ if (!s.mKeepWarming && app.hasShownUi && !app.getCachedIsHomeProcess()) {
// If this process has shown some UI, let it immediately
// go to the LRU list because it may be pretty heavy with
// UI stuff. We'll tag it with a label just to help
@@ -1479,7 +1520,8 @@ public final class OomAdjuster {
app.adjType = "cch-started-ui-services";
}
} else {
- if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
+ if (s.mKeepWarming
+ || now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
// recent memory, so we will keep its process ahead
// of the background processes.
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 9c96e6e02566..fc17ddedb39f 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -40,6 +40,7 @@ import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.LocalServices;
@@ -146,6 +147,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
private int lastStartId; // identifier of most recent start request.
+ boolean mKeepWarming; // Whether or not it'll keep critical code path of the host warm
+
static class StartItem {
final ServiceRecord sr;
final boolean taskRemoved;
@@ -514,6 +517,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
lastActivity = SystemClock.uptimeMillis();
userId = UserHandle.getUserId(appInfo.uid);
createdFromFg = callerIsFg;
+ updateKeepWarmLocked();
}
public ServiceState getTracker() {
@@ -732,6 +736,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
}
+ @GuardedBy("ams")
+ void updateKeepWarmLocked() {
+ mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name)
+ && (ams.mUserController.getCurrentUserId() == userId
+ || ams.isSingleton(processName, appInfo, instanceName.getClassName(),
+ serviceInfo.flags));
+ }
+
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index fde40aa77a0e..2a267c413c31 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -84,6 +84,7 @@ import android.os.Build;
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -130,6 +131,7 @@ public class MockingOomAdjusterTests {
private static final int MOCKAPP5_UID = MOCKAPP_UID + 4;
private static final String MOCKAPP5_PROCESSNAME = "test #5";
private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5";
+ private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE;
private static Context sContext;
private static PackageManagerInternal sPackageManagerInternal;
private static ActivityManagerService sService;
@@ -168,6 +170,8 @@ public class MockingOomAdjusterTests {
mock(SparseArray.class));
setFieldValue(ActivityManagerService.class, sService, "mOomAdjProfiler",
mock(OomAdjProfiler.class));
+ setFieldValue(ActivityManagerService.class, sService, "mUserController",
+ mock(UserController.class));
doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
@@ -1621,6 +1625,117 @@ public class MockingOomAdjusterTests {
assertEquals(SERVICE_ADJ, app.setAdj);
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoAll_Service_KeepWarmingList() {
+ final ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID_OTHER,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ final int userOwner = 0;
+ final int userOther = 1;
+ final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+ doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
+
+ final ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ lru.clear();
+ lru.add(app2);
+ lru.add(app);
+
+ final ComponentName cn = ComponentName.unflattenFromString(
+ MOCKAPP_PACKAGENAME + "/.TestService");
+ final ComponentName cn2 = ComponentName.unflattenFromString(
+ MOCKAPP2_PACKAGENAME + "/.TestService");
+ final long now = SystemClock.uptimeMillis();
+
+ sService.mConstants.KEEP_WARMING_SERVICES.clear();
+ final ServiceInfo si = mock(ServiceInfo.class);
+ si.applicationInfo = mock(ApplicationInfo.class);
+ ServiceRecord s = spy(new ServiceRecord(sService, null, cn, cn, null, 0, null,
+ si, false, null));
+ doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
+ s.startRequested = true;
+ s.lastActivity = now;
+
+ app.setCached(false);
+ app.startService(s);
+ app.hasShownUi = true;
+
+ final ServiceInfo si2 = mock(ServiceInfo.class);
+ si2.applicationInfo = mock(ApplicationInfo.class);
+ si2.applicationInfo.uid = MOCKAPP2_UID_OTHER;
+ ServiceRecord s2 = spy(new ServiceRecord(sService, null, cn2, cn2, null, 0, null,
+ si2, false, null));
+ doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections();
+ s2.startRequested = true;
+ s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+
+ app2.setCached(false);
+ app2.startService(s2);
+ app2.hasShownUi = false;
+
+ sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services");
+
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ app.hasShownUi = false;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+
+ app.setCached(false);
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+
+ app.stopService(s);
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ app.hasShownUi = true;
+ sService.mConstants.KEEP_WARMING_SERVICES.add(cn);
+ sService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
+ s = spy(new ServiceRecord(sService, null, cn, cn, null, 0, null,
+ si, false, null));
+ doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
+ s.startRequested = true;
+ s.lastActivity = now;
+
+ app.startService(s);
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+
+ app.setCached(true);
+ app.setProcState = PROCESS_STATE_NONEXISTENT;
+ app.adjType = null;
+ app.setAdj = UNKNOWN_ADJ;
+ app.hasShownUi = false;
+ s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+
+ doReturn(userOther).when(sService.mUserController).getCurrentUserId();
+ sService.mOomAdjuster.handleUserSwitchedLocked();
+
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+ assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+ assertProcStates(app2, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+ }
+
private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
String packageName, boolean hasShownUi) {
long now = SystemClock.uptimeMillis();
@@ -1747,4 +1862,12 @@ public class MockingOomAdjusterTests {
assertEquals(expectedAdj, app.setAdj);
assertEquals(expectedSchedGroup, app.setSchedGroup);
}
+
+ private void assertProcStates(ProcessRecord app, boolean expectedCached,
+ int expectedProcState, int expectedAdj, String expectedAdjType) {
+ assertEquals(expectedCached, app.isCached());
+ assertEquals(expectedProcState, app.setProcState);
+ assertEquals(expectedAdj, app.setAdj);
+ assertEquals(expectedAdjType, app.adjType);
+ }
}