Merge "Keep warming services out of cached adj"

This commit is contained in:
TreeHugger Robot
2020-06-30 09:30:58 +00:00
committed by Android (Google) Code Review
6 changed files with 192 additions and 2 deletions

View File

@@ -4386,4 +4386,6 @@
0.333
</item>
<!-- Component names of the services which will keep critical code path warm -->
<string-array name="config_keep_warming_services" translatable="false" />
</resources>

View File

@@ -4058,4 +4058,5 @@
<java-symbol type="integer" name="config_defaultBinderHeavyHitterAutoSamplerBatchSize" />
<java-symbol type="dimen" name="config_defaultBinderHeavyHitterAutoSamplerThreshold" />
<java-symbol type="array" name="config_keep_warming_services" />
</resources>

View File

@@ -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;
@@ -496,6 +502,10 @@ final class ActivityManagerConstants extends ContentObserver {
BINDER_HEAVY_HITTER_AUTO_SAMPLER_BATCHSIZE = mDefaultBinderHeavyHitterAutoSamplerBatchSize;
BINDER_HEAVY_HITTER_AUTO_SAMPLER_THRESHOLD = mDefaultBinderHeavyHitterAutoSamplerThreshold;
service.scheduleUpdateBinderHeavyHitterWatcherConfig();
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) {

View File

@@ -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.

View File

@@ -43,6 +43,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;
@@ -149,6 +150,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;
@@ -517,6 +520,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
lastActivity = SystemClock.uptimeMillis();
userId = UserHandle.getUserId(appInfo.uid);
createdFromFg = callerIsFg;
updateKeepWarmLocked();
}
public ServiceState getTracker() {
@@ -735,6 +739,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);

View File

@@ -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);
}
}