blob: b00dcd6ccf1f0fc2d3548be536196c83755e4476 [file] [log] [blame]
/*
* Copyright (C) 2019 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.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ACTIVITY;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_ALLOWLIST;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BACKUP;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BIND_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_REMOVE_TASK;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHELL;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_STOP_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SYSTEM_INIT;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UID_IDLE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UI_VISIBILITY;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UNBIND_SERVICE;
import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
import static android.os.Process.SCHED_OTHER;
import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
import static android.os.Process.THREAD_GROUP_RESTRICTED;
import static android.os.Process.THREAD_GROUP_TOP_APP;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
import static android.os.Process.setProcessGroup;
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ_REASON;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG;
import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
import static com.android.server.am.ActivityManagerService.TAG_OOM_ADJ;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
import static com.android.server.am.AppProfiler.TAG_PSS;
import static com.android.server.am.PlatformCompatCache.CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY;
import static com.android.server.am.PlatformCompatCache.CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY;
import static com.android.server.am.PlatformCompatCache.CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME;
import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
import static com.android.server.am.ProcessList.HOME_APP_ADJ;
import static com.android.server.am.ProcessList.INVALID_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ;
import static com.android.server.am.ProcessList.PREVIOUS_APP_ADJ;
import static com.android.server.am.ProcessList.SCHED_GROUP_BACKGROUND;
import static com.android.server.am.ProcessList.SCHED_GROUP_DEFAULT;
import static com.android.server.am.ProcessList.SCHED_GROUP_RESTRICTED;
import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP;
import static com.android.server.am.ProcessList.SCHED_GROUP_TOP_APP_BOUND;
import static com.android.server.am.ProcessList.SERVICE_ADJ;
import static com.android.server.am.ProcessList.SERVICE_B_ADJ;
import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS;
import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal.OomAdjReason;
import android.app.ActivityThread;
import android.app.AppProtoEnums;
import android.app.ApplicationExitInfo;
import android.app.usage.UsageEvents;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.net.NetworkPolicyManager;
import android.os.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.ServiceThread;
import com.android.server.am.PlatformCompatCache.CachedCompatChangeId;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowProcessController;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* All of the code required to compute proc states and oom_adj values.
*/
public class OomAdjuster {
static final String TAG = "OomAdjuster";
public static final int oomAdjReasonToProto(@OomAdjReason int oomReason) {
switch (oomReason) {
case OOM_ADJ_REASON_NONE:
return AppProtoEnums.OOM_ADJ_REASON_NONE;
case OOM_ADJ_REASON_ACTIVITY:
return AppProtoEnums.OOM_ADJ_REASON_ACTIVITY;
case OOM_ADJ_REASON_FINISH_RECEIVER:
return AppProtoEnums.OOM_ADJ_REASON_FINISH_RECEIVER;
case OOM_ADJ_REASON_START_RECEIVER:
return AppProtoEnums.OOM_ADJ_REASON_START_RECEIVER;
case OOM_ADJ_REASON_BIND_SERVICE:
return AppProtoEnums.OOM_ADJ_REASON_BIND_SERVICE;
case OOM_ADJ_REASON_UNBIND_SERVICE:
return AppProtoEnums.OOM_ADJ_REASON_UNBIND_SERVICE;
case OOM_ADJ_REASON_START_SERVICE:
return AppProtoEnums.OOM_ADJ_REASON_START_SERVICE;
case OOM_ADJ_REASON_GET_PROVIDER:
return AppProtoEnums.OOM_ADJ_REASON_GET_PROVIDER;
case OOM_ADJ_REASON_REMOVE_PROVIDER:
return AppProtoEnums.OOM_ADJ_REASON_REMOVE_PROVIDER;
case OOM_ADJ_REASON_UI_VISIBILITY:
return AppProtoEnums.OOM_ADJ_REASON_UI_VISIBILITY;
case OOM_ADJ_REASON_ALLOWLIST:
return AppProtoEnums.OOM_ADJ_REASON_ALLOWLIST;
case OOM_ADJ_REASON_PROCESS_BEGIN:
return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN;
case OOM_ADJ_REASON_PROCESS_END:
return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END;
case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT:
return AppProtoEnums.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT;
case OOM_ADJ_REASON_SYSTEM_INIT:
return AppProtoEnums.OOM_ADJ_REASON_SYSTEM_INIT;
case OOM_ADJ_REASON_BACKUP:
return AppProtoEnums.OOM_ADJ_REASON_BACKUP;
case OOM_ADJ_REASON_SHELL:
return AppProtoEnums.OOM_ADJ_REASON_SHELL;
case OOM_ADJ_REASON_REMOVE_TASK:
return AppProtoEnums.OOM_ADJ_REASON_REMOVE_TASK;
case OOM_ADJ_REASON_UID_IDLE:
return AppProtoEnums.OOM_ADJ_REASON_UID_IDLE;
case OOM_ADJ_REASON_STOP_SERVICE:
return AppProtoEnums.OOM_ADJ_REASON_STOP_SERVICE;
case OOM_ADJ_REASON_EXECUTING_SERVICE:
return AppProtoEnums.OOM_ADJ_REASON_EXECUTING_SERVICE;
case OOM_ADJ_REASON_RESTRICTION_CHANGE:
return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE;
case OOM_ADJ_REASON_COMPONENT_DISABLED:
return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED;
default:
return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
}
}
public static final String oomAdjReasonToString(@OomAdjReason int oomReason) {
final String OOM_ADJ_REASON_METHOD = "updateOomAdj";
switch (oomReason) {
case OOM_ADJ_REASON_NONE:
return OOM_ADJ_REASON_METHOD + "_meh";
case OOM_ADJ_REASON_ACTIVITY:
return OOM_ADJ_REASON_METHOD + "_activityChange";
case OOM_ADJ_REASON_FINISH_RECEIVER:
return OOM_ADJ_REASON_METHOD + "_finishReceiver";
case OOM_ADJ_REASON_START_RECEIVER:
return OOM_ADJ_REASON_METHOD + "_startReceiver";
case OOM_ADJ_REASON_BIND_SERVICE:
return OOM_ADJ_REASON_METHOD + "_bindService";
case OOM_ADJ_REASON_UNBIND_SERVICE:
return OOM_ADJ_REASON_METHOD + "_unbindService";
case OOM_ADJ_REASON_START_SERVICE:
return OOM_ADJ_REASON_METHOD + "_startService";
case OOM_ADJ_REASON_GET_PROVIDER:
return OOM_ADJ_REASON_METHOD + "_getProvider";
case OOM_ADJ_REASON_REMOVE_PROVIDER:
return OOM_ADJ_REASON_METHOD + "_removeProvider";
case OOM_ADJ_REASON_UI_VISIBILITY:
return OOM_ADJ_REASON_METHOD + "_uiVisibility";
case OOM_ADJ_REASON_ALLOWLIST:
return OOM_ADJ_REASON_METHOD + "_allowlistChange";
case OOM_ADJ_REASON_PROCESS_BEGIN:
return OOM_ADJ_REASON_METHOD + "_processBegin";
case OOM_ADJ_REASON_PROCESS_END:
return OOM_ADJ_REASON_METHOD + "_processEnd";
case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT:
return OOM_ADJ_REASON_METHOD + "_shortFgs";
case OOM_ADJ_REASON_SYSTEM_INIT:
return OOM_ADJ_REASON_METHOD + "_systemInit";
case OOM_ADJ_REASON_BACKUP:
return OOM_ADJ_REASON_METHOD + "_backup";
case OOM_ADJ_REASON_SHELL:
return OOM_ADJ_REASON_METHOD + "_shell";
case OOM_ADJ_REASON_REMOVE_TASK:
return OOM_ADJ_REASON_METHOD + "_removeTask";
case OOM_ADJ_REASON_UID_IDLE:
return OOM_ADJ_REASON_METHOD + "_uidIdle";
case OOM_ADJ_REASON_STOP_SERVICE:
return OOM_ADJ_REASON_METHOD + "_stopService";
case OOM_ADJ_REASON_EXECUTING_SERVICE:
return OOM_ADJ_REASON_METHOD + "_executingService";
case OOM_ADJ_REASON_RESTRICTION_CHANGE:
return OOM_ADJ_REASON_METHOD + "_restrictionChange";
case OOM_ADJ_REASON_COMPONENT_DISABLED:
return OOM_ADJ_REASON_METHOD + "_componentDisabled";
default:
return "_unknown";
}
}
/**
* Flag {@link android.content.Context#BIND_INCLUDE_CAPABILITIES} is used
* to pass while-in-use capabilities from client process to bound service. In targetSdkVersion
* R and above, if client is a TOP activity, when this flag is present, bound service gets all
* while-in-use capabilities; when this flag is not present, bound service gets no while-in-use
* capability from client.
*/
@ChangeId
@EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
static final long PROCESS_CAPABILITY_CHANGE_ID = 136274596L;
/**
* In targetSdkVersion R and above, foreground service has camera and microphone while-in-use
* capability only when the {@link android.R.attr#foregroundServiceType} is configured as
* {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA} and
* {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE} respectively in the
* manifest file.
* In targetSdkVersion below R, foreground service automatically have camera and microphone
* capabilities.
*/
@ChangeId
@EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L;
/**
* For apps targeting S+, this determines whether to use a shorter timeout before elevating the
* standby bucket to ACTIVE when apps start a foreground service.
*/
@ChangeId
@EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
static final long USE_SHORT_FGS_USAGE_INTERACTION_TIME = 183972877L;
/**
* Service for optimizing resource usage from background apps.
*/
CachedAppOptimizer mCachedAppOptimizer;
/**
* Re-rank apps getting a cache oom adjustment from lru to weighted order
* based on weighted scores for LRU, PSS and cache use count.
*/
CacheOomRanker mCacheOomRanker;
ActivityManagerConstants mConstants;
final long[] mTmpLong = new long[3];
/**
* Current sequence id for oom_adj computation traversal.
*/
int mAdjSeq = 0;
/**
* Keep track of the number of service processes we last found, to
* determine on the next iteration which should be B services.
*/
int mNumServiceProcs = 0;
int mNewNumAServiceProcs = 0;
int mNewNumServiceProcs = 0;
/**
* Keep track of the non-cached/empty process we last found, to help
* determine how to distribute cached/empty processes next time.
*/
int mNumNonCachedProcs = 0;
/**
* Keep track of the number of cached hidden procs, to balance oom adj
* distribution between those and empty procs.
*/
int mNumCachedHiddenProcs = 0;
/** Track all uids that have actively running processes. */
@CompositeRWLock({"mService", "mProcLock"})
ActiveUids mActiveUids;
/**
* The handler to execute {@link #setProcessGroup} (it may be heavy if the process has many
* threads) for reducing the time spent in {@link #applyOomAdjLSP}.
*/
private final Handler mProcessGroupHandler;
protected final int[] mTmpSchedGroup = new int[1];
final ActivityManagerService mService;
final ProcessList mProcessList;
final ActivityManagerGlobalLock mProcLock;
private final int mNumSlots;
protected final ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>();
protected final ArrayList<ProcessRecord> mTmpProcessList2 = new ArrayList<ProcessRecord>();
protected final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
protected final ActiveUids mTmpUidRecords;
protected final ArrayDeque<ProcessRecord> mTmpQueue;
protected final ArraySet<ProcessRecord> mTmpProcessSet = new ArraySet<>();
protected final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
/**
* Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate
* could be called recursively because of the indirect calls during the update;
* however the oomAdjUpdate itself doesn't support recursion - in this case we'd
* have to queue up the new targets found during the update, and perform another
* round of oomAdjUpdate at the end of last update.
*/
@GuardedBy("mService")
private boolean mOomAdjUpdateOngoing = false;
/**
* Flag to mark if there is a pending full oomAdjUpdate.
*/
@GuardedBy("mService")
private boolean mPendingFullOomAdjUpdate = false;
/** Overrideable by a test */
@VisibleForTesting
protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
ApplicationInfo app, boolean defaultValue) {
return PlatformCompatCache.getInstance()
.isChangeEnabled(cachedCompatChangeId, app, defaultValue);
}
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
this(service, processList, activeUids, createAdjusterThread());
}
static ServiceThread createAdjusterThread() {
// The process group is usually critical to the response time of foreground app, so the
// setter should apply it as soon as possible.
final ServiceThread adjusterThread =
new ServiceThread(TAG, THREAD_PRIORITY_TOP_APP_BOOST, false /* allowIo */);
adjusterThread.start();
return adjusterThread;
}
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
ServiceThread adjusterThread) {
mService = service;
mProcessList = processList;
mProcLock = service.mProcLock;
mActiveUids = activeUids;
mConstants = mService.mConstants;
mCachedAppOptimizer = new CachedAppOptimizer(mService);
mCacheOomRanker = new CacheOomRanker(service);
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
final int pid = msg.arg1;
final int group = msg.arg2;
if (pid == ActivityManagerService.MY_PID) {
// Skip setting the process group for system_server, keep it as default.
return true;
}
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
+ msg.obj + " to " + group);
}
try {
setProcessGroup(pid, group);
} catch (Exception e) {
if (DEBUG_ALL) {
Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
return true;
});
mTmpUidRecords = new ActiveUids(service, false);
mTmpQueue = new ArrayDeque<ProcessRecord>(mConstants.CUR_MAX_CACHED_PROCESSES << 1);
mNumSlots = ((CACHED_APP_MAX_ADJ - CACHED_APP_MIN_ADJ + 1) >> 1)
/ CACHED_APP_IMPORTANCE_LEVELS;
}
void initSettings() {
mCachedAppOptimizer.init();
mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor());
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() {
mProcessList.forEachLruProcessesLOSP(false,
this::updateKeepWarmIfNecessaryForProcessLocked);
}
@GuardedBy("mService")
private void updateKeepWarmIfNecessaryForProcessLocked(final ProcessRecord app) {
final ArraySet<ComponentName> warmServices = mService.mConstants.KEEP_WARMING_SERVICES;
boolean includeWarmPkg = false;
final PackageList pkgList = app.getPkgList();
for (int j = warmServices.size() - 1; j >= 0; j--) {
if (pkgList.containsKey(warmServices.valueAt(j).getPackageName())) {
includeWarmPkg = true;
break;
}
}
if (!includeWarmPkg) {
return;
}
final ProcessServiceRecord psr = app.mServices;
for (int j = psr.numberOfRunningServices() - 1; j >= 0; j--) {
psr.getRunningServiceAt(j).updateKeepWarmLocked();
}
}
/**
* Perform oom adj update on the given process. It does NOT do the re-computation
* if there is a cycle, caller should check {@link #mProcessesInCycle} and do it on its own.
*/
@GuardedBy({"mService", "mProcLock"})
private boolean performUpdateOomAdjLSP(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, long now, @OomAdjReason int oomAdjReason) {
if (app.getThread() == null) {
return false;
}
app.mState.resetCachedInfo();
app.mState.setCurBoundByNonBgRestrictedApp(false);
UidRecord uidRec = app.getUidRecord();
if (uidRec != null) {
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
}
uidRec.reset();
}
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
mProcessesInCycle.clear();
computeOomAdjLSP(app, cachedAdj, topApp, false, now, false, true, oomAdjReason, true);
if (!mProcessesInCycle.isEmpty()) {
// We can't use the score here if there is a cycle, abort.
for (int i = mProcessesInCycle.size() - 1; i >= 0; i--) {
// Reset the adj seq
mProcessesInCycle.valueAt(i).mState.setCompletedAdjSeq(mAdjSeq - 1);
}
return true;
}
if (uidRec != null) {
// After uidRec.reset() above, for UidRecord with multiple processes (ProcessRecord),
// we need to apply all ProcessRecord into UidRecord.
uidRec.forEachProcess(this::updateAppUidRecIfNecessaryLSP);
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
&& (uidRec.getSetProcState() != uidRec.getCurProcState()
|| uidRec.getSetCapability() != uidRec.getCurCapability()
|| uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) {
final ActiveUids uids = mTmpUidRecords;
uids.clear();
uids.put(uidRec.getUid(), uidRec);
updateUidsLSP(uids, SystemClock.elapsedRealtime());
}
}
return applyOomAdjLSP(app, false, now, SystemClock.elapsedRealtime(), oomAdjReason);
}
/**
* Update OomAdj for all processes in LRU list
*/
@GuardedBy("mService")
void updateOomAdjLocked(@OomAdjReason int oomAdjReason) {
synchronized (mProcLock) {
updateOomAdjLSP(oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
private void updateOomAdjLSP(@OomAdjReason int oomAdjReason) {
if (checkAndEnqueueOomAdjTargetLocked(null)) {
// Simply return as there is an oomAdjUpdate ongoing
return;
}
try {
mOomAdjUpdateOngoing = true;
performUpdateOomAdjLSP(oomAdjReason);
} finally {
// Kick off the handling of any pending targets enqueued during the above update
mOomAdjUpdateOngoing = false;
updateOomAdjPendingTargetsLocked(oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
private void performUpdateOomAdjLSP(@OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
// Clear any pending ones because we are doing a full update now.
mPendingProcessSet.clear();
mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
updateOomAdjInnerLSP(oomAdjReason, topApp , null, null, true, true);
}
/**
* Update OomAdj for specific process and its reachable processes (with direction/indirect
* bindings from this process); Note its clients' proc state won't be re-evaluated if this proc
* is hosting any service/content provider.
*
* @param app The process to update, or null to update all processes
* @param oomAdjReason
*/
@GuardedBy("mService")
boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {
synchronized (mProcLock) {
return updateOomAdjLSP(app, oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
private boolean updateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
if (app == null || !mConstants.OOMADJ_UPDATE_QUICK) {
updateOomAdjLSP(oomAdjReason);
return true;
}
if (checkAndEnqueueOomAdjTargetLocked(app)) {
// Simply return true as there is an oomAdjUpdate ongoing
return true;
}
try {
mOomAdjUpdateOngoing = true;
return performUpdateOomAdjLSP(app, oomAdjReason);
} finally {
// Kick off the handling of any pending targets enqueued during the above update
mOomAdjUpdateOngoing = false;
updateOomAdjPendingTargetsLocked(oomAdjReason);
}
}
@GuardedBy({"mService", "mProcLock"})
protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
mAdjSeq++;
final ProcessStateRecord state = app.mState;
final boolean wasCached = state.isCached();
final int oldAdj = state.getCurRawAdj();
final int cachedAdj = oldAdj >= CACHED_APP_MIN_ADJ
? oldAdj : UNKNOWN_ADJ;
// Firstly, try to see if the importance of itself gets changed
final boolean wasBackground = ActivityManager.isProcStateBackground(
state.getSetProcState());
final int oldCap = state.getSetCapability();
state.setContainsCycle(false);
state.setProcStateChanged(false);
state.resetCachedInfo();
state.setCurBoundByNonBgRestrictedApp(false);
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp,
SystemClock.uptimeMillis(), oomAdjReason);
// The 'app' here itself might or might not be in the cycle, for example,
// the case A <=> B vs. A -> B <=> C; anyway, if we spot a cycle here, re-compute them.
if (!success || (wasCached == state.isCached() && oldAdj != INVALID_ADJ
&& mProcessesInCycle.isEmpty() /* Force re-compute if there is a cycle */
&& oldCap == state.getCurCapability()
&& wasBackground == ActivityManager.isProcStateBackground(
state.getSetProcState()))) {
mProcessesInCycle.clear();
// Okay, it's unchanged, it won't impact any service it binds to, we're done here.
if (DEBUG_OOM_ADJ) {
Slog.i(TAG_OOM_ADJ, "No oomadj changes for " + app);
}
mService.mOomAdjProfiler.oomAdjEnded();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return success;
}
// Next to find out all its reachable processes
ArrayList<ProcessRecord> processes = mTmpProcessList;
ActiveUids uids = mTmpUidRecords;
mPendingProcessSet.add(app);
// Add all processes with cycles into the list to scan
for (int i = mProcessesInCycle.size() - 1; i >= 0; i--) {
mPendingProcessSet.add(mProcessesInCycle.valueAt(i));
}
mProcessesInCycle.clear();
boolean containsCycle = collectReachableProcessesLocked(mPendingProcessSet,
processes, uids);
// Clear the pending set as they should've been included in 'processes'.
mPendingProcessSet.clear();
if (!containsCycle) {
// Remove this app from the return list because we've done the computation on it.
processes.remove(app);
}
int size = processes.size();
if (size > 0) {
mAdjSeq--;
// Update these reachable processes
updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false);
} else if (state.getCurRawAdj() == UNKNOWN_ADJ) {
// In case the app goes from non-cached to cached but it doesn't have other reachable
// processes, its adj could be still unknown as of now, assign one.
processes.add(app);
assignCachedAdjIfNecessary(processes);
applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),
SystemClock.elapsedRealtime(), oomAdjReason);
}
mTmpProcessList.clear();
mService.mOomAdjProfiler.oomAdjEnded();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return true;
}
/**
* Collect the reachable processes from the given {@code apps}, the result will be
* returned in the given {@code processes}, which will include the processes from
* the given {@code apps}.
*/
@GuardedBy("mService")
protected boolean collectReachableProcessesLocked(ArraySet<ProcessRecord> apps,
ArrayList<ProcessRecord> processes, ActiveUids uids) {
final ArrayDeque<ProcessRecord> queue = mTmpQueue;
queue.clear();
processes.clear();
for (int i = 0, size = apps.size(); i < size; i++) {
final ProcessRecord app = apps.valueAt(i);
app.mState.setReachable(true);
queue.offer(app);
}
uids.clear();
// Track if any of them reachables could include a cycle
boolean containsCycle = false;
// Scan downstreams of the process record
for (ProcessRecord pr = queue.poll(); pr != null; pr = queue.poll()) {
processes.add(pr);
final UidRecord uidRec = pr.getUidRecord();
if (uidRec != null) {
uids.put(uidRec.getUid(), uidRec);
}
final ProcessServiceRecord psr = pr.mServices;
for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
ConnectionRecord cr = psr.getConnectionAt(i);
ProcessRecord service = cr.hasFlag(ServiceInfo.FLAG_ISOLATED_PROCESS)
? cr.binding.service.isolationHostProc : cr.binding.service.app;
if (service == null || service == pr
|| ((service.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ)
&& (service.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) {
continue;
}
containsCycle |= service.mState.isReachable();
if (service.mState.isReachable()) {
continue;
}
if (cr.hasFlag(Context.BIND_WAIVE_PRIORITY)
&& cr.notHasFlag(Context.BIND_TREAT_LIKE_ACTIVITY
| Context.BIND_ADJUST_WITH_ACTIVITY)) {
continue;
}
queue.offer(service);
service.mState.setReachable(true);
}
final ProcessProviderRecord ppr = pr.mProviders;
for (int i = ppr.numberOfProviderConnections() - 1; i >= 0; i--) {
ContentProviderConnection cpc = ppr.getProviderConnectionAt(i);
ProcessRecord provider = cpc.provider.proc;
if (provider == null || provider == pr
|| ((provider.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ)
&& (provider.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) {
continue;
}
containsCycle |= provider.mState.isReachable();
if (provider.mState.isReachable()) {
continue;
}
queue.offer(provider);
provider.mState.setReachable(true);
}
// See if this process has any corresponding SDK sandbox processes running, and if so
// scan them as well.
final List<ProcessRecord> sdkSandboxes =
mProcessList.getSdkSandboxProcessesForAppLocked(pr.uid);
final int numSdkSandboxes = sdkSandboxes != null ? sdkSandboxes.size() : 0;
for (int i = numSdkSandboxes - 1; i >= 0; i--) {
ProcessRecord sdkSandbox = sdkSandboxes.get(i);
containsCycle |= sdkSandbox.mState.isReachable();
if (sdkSandbox.mState.isReachable()) {
continue;
}
queue.offer(sdkSandbox);
sdkSandbox.mState.setReachable(true);
}
// If this process is a sandbox itself, also scan the app on whose behalf its running
if (pr.isSdkSandbox) {
for (int is = psr.numberOfRunningServices() - 1; is >= 0; is--) {
ServiceRecord s = psr.getRunningServiceAt(is);
ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
s.getConnections();
for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
for (int i = clist.size() - 1; i >= 0; i--) {
ConnectionRecord cr = clist.get(i);
ProcessRecord attributedApp = cr.binding.attributedClient;
if (attributedApp == null || attributedApp == pr
|| ((attributedApp.mState.getMaxAdj() >= ProcessList.SYSTEM_ADJ)
&& (attributedApp.mState.getMaxAdj() < FOREGROUND_APP_ADJ))) {
continue;
}
if (attributedApp.mState.isReachable()) {
continue;
}
queue.offer(attributedApp);
attributedApp.mState.setReachable(true);
}
}
}
}
}
int size = processes.size();
if (size > 0) {
// Reverse the process list, since the updateOomAdjInnerLSP scans from the end of it.
for (int l = 0, r = size - 1; l < r; l++, r--) {
final ProcessRecord t = processes.get(l);
final ProcessRecord u = processes.get(r);
t.mState.setReachable(false);
u.mState.setReachable(false);
processes.set(l, u);
processes.set(r, t);
}
}
return containsCycle;
}
/**
* Enqueue the given process for a later oom adj update
*/
@GuardedBy("mService")
void enqueueOomAdjTargetLocked(ProcessRecord app) {
if (app != null && app.mState.getMaxAdj() > FOREGROUND_APP_ADJ) {
mPendingProcessSet.add(app);
}
}
@GuardedBy("mService")
void removeOomAdjTargetLocked(ProcessRecord app, boolean procDied) {
if (app != null) {
mPendingProcessSet.remove(app);
if (procDied) {
PlatformCompatCache.getInstance().invalidate(app.info);
}
}
}
/**
* Check if there is an ongoing oomAdjUpdate, enqueue the given process record
* to {@link #mPendingProcessSet} if there is one.
*
* @param app The target app to get an oomAdjUpdate, or a full oomAdjUpdate if it's null.
* @return {@code true} if there is an ongoing oomAdjUpdate.
*/
@GuardedBy("mService")
private boolean checkAndEnqueueOomAdjTargetLocked(@Nullable ProcessRecord app) {
if (!mOomAdjUpdateOngoing) {
return false;
}
if (app != null) {
mPendingProcessSet.add(app);
} else {
mPendingFullOomAdjUpdate = true;
}
return true;
}
/**
* Kick off an oom adj update pass for the pending targets which are enqueued via
* {@link #enqueueOomAdjTargetLocked}.
*/
@GuardedBy("mService")
void updateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
// First check if there is pending full update
if (mPendingFullOomAdjUpdate) {
mPendingFullOomAdjUpdate = false;
mPendingProcessSet.clear();
updateOomAdjLocked(oomAdjReason);
return;
}
if (mPendingProcessSet.isEmpty()) {
return;
}
if (mOomAdjUpdateOngoing) {
// There's another oomAdjUpdate ongoing, return from here now;
// that ongoing update would call us again at the end of it.
return;
}
try {
mOomAdjUpdateOngoing = true;
performUpdateOomAdjPendingTargetsLocked(oomAdjReason);
} finally {
// Kick off the handling of any pending targets enqueued during the above update
mOomAdjUpdateOngoing = false;
updateOomAdjPendingTargetsLocked(oomAdjReason);
}
}
@GuardedBy("mService")
private void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
final ArrayList<ProcessRecord> processes = mTmpProcessList;
final ActiveUids uids = mTmpUidRecords;
collectReachableProcessesLocked(mPendingProcessSet, processes, uids);
mPendingProcessSet.clear();
synchronized (mProcLock) {
updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, true, false);
}
processes.clear();
mService.mOomAdjProfiler.oomAdjEnded();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
/**
* Update OomAdj for all processes within the given list (could be partial), or the whole LRU
* list if the given list is null; when it's partial update, each process's client proc won't
* get evaluated recursively here.
*
* <p>Note: If the given {@code processes} is not null, the expectation to it is, the caller
* must have called {@link collectReachableProcessesLocked} on it.
*/
@GuardedBy({"mService", "mProcLock"})
protected void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
boolean startProfiling) {
final boolean fullUpdate = processes == null;
final ArrayList<ProcessRecord> activeProcesses = fullUpdate
? mProcessList.getLruProcessesLOSP() : processes;
ActiveUids activeUids = uids;
if (activeUids == null) {
final int numUids = mActiveUids.size();
activeUids = mTmpUidRecords;
activeUids.clear();
for (int i = 0; i < numUids; i++) {
UidRecord uidRec = mActiveUids.valueAt(i);
activeUids.put(uidRec.getUid(), uidRec);
}
}
if (startProfiling) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
}
final long now = SystemClock.uptimeMillis();
final long nowElapsed = SystemClock.elapsedRealtime();
final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
final int numProc = activeProcesses.size();
mAdjSeq++;
if (fullUpdate) {
mNewNumServiceProcs = 0;
mNewNumAServiceProcs = 0;
}
// Reset state in all uid records.
resetUidRecordsLsp(activeUids);
boolean retryCycles = false;
boolean computeClients = fullUpdate || potentialCycles;
// need to reset cycle state before calling computeOomAdjLSP because of service conns
for (int i = numProc - 1; i >= 0; i--) {
ProcessRecord app = activeProcesses.get(i);
final ProcessStateRecord state = app.mState;
state.setReachable(false);
// No need to compute again it has been evaluated in previous iteration
if (state.getAdjSeq() != mAdjSeq) {
state.setContainsCycle(false);
state.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
state.setCurRawAdj(UNKNOWN_ADJ);
state.setSetCapability(PROCESS_CAPABILITY_NONE);
state.resetCachedInfo();
state.setCurBoundByNonBgRestrictedApp(false);
}
}
mProcessesInCycle.clear();
for (int i = numProc - 1; i >= 0; i--) {
ProcessRecord app = activeProcesses.get(i);
final ProcessStateRecord state = app.mState;
if (!app.isKilledByAm() && app.getThread() != null) {
state.setProcStateChanged(false);
app.mOptRecord.setLastOomAdjChangeReason(oomAdjReason);
// It won't enter cycle if not computing clients.
computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, fullUpdate, now, false,
computeClients, oomAdjReason, true);
// if any app encountered a cycle, we need to perform an additional loop later
retryCycles |= state.containsCycle();
// Keep the completedAdjSeq to up to date.
state.setCompletedAdjSeq(mAdjSeq);
}
}
if (mCacheOomRanker.useOomReranking()) {
mCacheOomRanker.reRankLruCachedAppsLSP(mProcessList.getLruProcessesLSP(),
mProcessList.getLruProcessServiceStartLOSP());
}
if (computeClients) { // There won't be cycles if we didn't compute clients above.
// Cycle strategy:
// - Retry computing any process that has encountered a cycle.
// - Continue retrying until no process was promoted.
// - Iterate from least important to most important.
int cycleCount = 0;
while (retryCycles && cycleCount < 10) {
cycleCount++;
retryCycles = false;
for (int i = 0; i < numProc; i++) {
ProcessRecord app = activeProcesses.get(i);
final ProcessStateRecord state = app.mState;
if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) {
state.decAdjSeq();
state.decCompletedAdjSeq();
}
}
for (int i = 0; i < numProc; i++) {
ProcessRecord app = activeProcesses.get(i);
final ProcessStateRecord state = app.mState;
if (!app.isKilledByAm() && app.getThread() != null && state.containsCycle()) {
if (computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, true, now,
true, true, oomAdjReason, true)) {
retryCycles = true;
}
}
}
}
}
mProcessesInCycle.clear();
assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
if (startProfiling) {
mService.mOomAdjProfiler.oomAdjEnded();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@GuardedBy({"mService", "mProcLock"})
private void resetUidRecordsLsp(@NonNull ActiveUids activeUids) {
// Reset state in all uid records.
for (int i = activeUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = activeUids.valueAt(i);
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec);
}
uidRec.reset();
}
}
@GuardedBy({"mService", "mProcLock"})
protected void postUpdateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, ActiveUids activeUids,
long now, long nowElapsed, long oldTime) {
mNumNonCachedProcs = 0;
mNumCachedHiddenProcs = 0;
final boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids,
oomAdjReason);
mNumServiceProcs = mNewNumServiceProcs;
if (mService.mAlwaysFinishActivities) {
// Need to do this on its own message because the stack may not
// be in a consistent state at this point.
mService.mAtmInternal.scheduleDestroyAllActivities("always-finish");
}
if (allChanged) {
mService.mAppProfiler.requestPssAllProcsLPr(now, false,
mService.mProcessStats.isMemFactorLowered());
}
updateUidsLSP(activeUids, nowElapsed);
synchronized (mService.mProcessStats.mLock) {
final long nowUptime = SystemClock.uptimeMillis();
if (mService.mProcessStats.shouldWriteNowLocked(nowUptime)) {
mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
mService.mProcessStats));
}
// Run this after making sure all procstates are updated.
mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, nowUptime);
}
if (DEBUG_OOM_ADJ) {
final long duration = SystemClock.uptimeMillis() - now;
if (false) {
Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
new RuntimeException("here").fillInStackTrace());
} else {
Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");
}
}
}
@GuardedBy({"mService", "mProcLock"})
protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
final int numLru = lruList.size();
if (mConstants.USE_TIERED_CACHED_ADJ) {
final long now = SystemClock.uptimeMillis();
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
>= UNKNOWN_ADJ) {
final ProcessServiceRecord psr = app.mServices;
int targetAdj = CACHED_APP_MIN_ADJ;
if (opt != null && opt.isFreezeExempt()) {
// BIND_WAIVE_PRIORITY and the like get oom_adj 900
targetAdj += 0;
} else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ)
&& (state.getLastStateTime()
+ mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) {
// Older cached apps get 950
targetAdj += 50;
} else {
// Newer cached apps get 910
targetAdj += 10;
}
state.setCurRawAdj(targetAdj);
state.setCurAdj(psr.modifyRawOomAdj(targetAdj));
}
}
} else {
// First update the OOM adjustment for each of the
// application processes based on their current state.
int curCachedAdj = CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
int curCachedImpAdj = 0;
int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS;
int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
- emptyProcessLimit;
// Let's determine how many processes we have running vs.
// how many slots we have for background processes; we may want
// to put multiple processes in a slot of there are enough of
// them.
int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs;
if (numEmptyProcs > cachedProcessLimit) {
// If there are more empty processes than our limit on cached
// processes, then use the cached process limit for the factor.
// This ensures that the really old empty processes get pushed
// down to the bottom, so if we are running low on memory we will
// have a better chance at keeping around more cached processes
// instead of a gazillion empty processes.
numEmptyProcs = cachedProcessLimit;
}
int cachedFactor = (mNumCachedHiddenProcs > 0
? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
/ mNumSlots;
if (cachedFactor < 1) cachedFactor = 1;
int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots;
if (emptyFactor < 1) emptyFactor = 1;
int stepCached = -1;
int stepEmpty = -1;
int lastCachedGroup = 0;
int lastCachedGroupImportance = 0;
int lastCachedGroupUid = 0;
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
// If we haven't yet assigned the final cached adj
// to the process, do that now.
if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
>= UNKNOWN_ADJ) {
final ProcessServiceRecord psr = app.mServices;
switch (state.getCurProcState()) {
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
// Figure out the next cached level, taking into account groups.
boolean inGroup = false;
final int connectionGroup = psr.getConnectionGroup();
if (connectionGroup != 0) {
final int connectionImportance = psr.getConnectionImportance();
if (lastCachedGroupUid == app.uid
&& lastCachedGroup == connectionGroup) {
// This is in the same group as the last process, just tweak
// adjustment by importance.
if (connectionImportance > lastCachedGroupImportance) {
lastCachedGroupImportance = connectionImportance;
if (curCachedAdj < nextCachedAdj
&& curCachedAdj < CACHED_APP_MAX_ADJ) {
curCachedImpAdj++;
}
}
inGroup = true;
} else {
lastCachedGroupUid = app.uid;
lastCachedGroup = connectionGroup;
lastCachedGroupImportance = connectionImportance;
}
}
if (!inGroup && curCachedAdj != nextCachedAdj) {
stepCached++;
curCachedImpAdj = 0;
if (stepCached >= cachedFactor) {
stepCached = 0;
curCachedAdj = nextCachedAdj;
nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextCachedAdj > CACHED_APP_MAX_ADJ) {
nextCachedAdj = CACHED_APP_MAX_ADJ;
}
}
}
// This process is a cached process holding activities...
// assign it the next cached value for that type, and then
// step that cached level.
state.setCurRawAdj(curCachedAdj + curCachedImpAdj);
state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+ " adj: " + state.getCurAdj()
+ " (curCachedAdj=" + curCachedAdj
+ " curCachedImpAdj=" + curCachedImpAdj + ")");
}
break;
default:
// Figure out the next cached level.
if (curEmptyAdj != nextEmptyAdj) {
stepEmpty++;
if (stepEmpty >= emptyFactor) {
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextEmptyAdj > CACHED_APP_MAX_ADJ) {
nextEmptyAdj = CACHED_APP_MAX_ADJ;
}
}
}
// For everything else, assign next empty cached process
// level and bump that up. Note that this means that
// long-running services that have dropped down to the
// cached level will be treated as empty (since their process
// state is still as a service), which is what we want.
state.setCurRawAdj(curEmptyAdj);
state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+ " adj: " + state.getCurAdj()
+ " (curEmptyAdj=" + curEmptyAdj
+ ")");
}
break;
}
}
}
}
}
private long mNextNoKillDebugMessageTime;
private double mLastFreeSwapPercent = 1.00;
private static double getFreeSwapPercent() {
return CachedAppOptimizer.getFreeSwapPercent();
}
@GuardedBy({"mService", "mProcLock"})
private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,
final long oldTime, final ActiveUids activeUids, @OomAdjReason int oomAdjReason) {
ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();
final int numLru = lruList.size();
final boolean doKillExcessiveProcesses = shouldKillExcessiveProcesses(now);
if (!doKillExcessiveProcesses) {
if (mNextNoKillDebugMessageTime < now) {
Slog.d(TAG, "Not killing cached processes"); // STOPSHIP Remove it b/222365734
mNextNoKillDebugMessageTime = now + 5000; // Every 5 seconds
}
}
final int emptyProcessLimit = doKillExcessiveProcesses
? mConstants.CUR_MAX_EMPTY_PROCESSES : Integer.MAX_VALUE;
final int cachedProcessLimit = doKillExcessiveProcesses
? (mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit) : Integer.MAX_VALUE;
int lastCachedGroup = 0;
int lastCachedGroupUid = 0;
int numCached = 0;
int numCachedExtraGroup = 0;
int numEmpty = 0;
int numTrimming = 0;
boolean proactiveKillsEnabled = mConstants.PROACTIVE_KILLS_ENABLED;
double lowSwapThresholdPercent = mConstants.LOW_SWAP_THRESHOLD_PERCENT;
double freeSwapPercent = proactiveKillsEnabled ? getFreeSwapPercent() : 1.00;
ProcessRecord lruCachedApp = null;
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
if (!app.isKilledByAm() && app.getThread() != null) {
// We don't need to apply the update for the process which didn't get computed
if (state.getCompletedAdjSeq() == mAdjSeq) {
applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason);
}
if (app.isPendingFinishAttach()) {
// Avoid trimming processes that are still initializing. If they aren't
// hosting any components yet because they may be unfairly killed.
// We however apply the oom scores set at #setAttachingProcessStatesLSP.
continue;
}
final ProcessServiceRecord psr = app.mServices;
// Count the number of process types.
switch (state.getCurProcState()) {
case PROCESS_STATE_CACHED_ACTIVITY:
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
final int connectionGroup = psr.getConnectionGroup();
if (connectionGroup != 0) {
if (lastCachedGroupUid == app.info.uid
&& lastCachedGroup == connectionGroup) {
// If this process is the next in the same group, we don't
// want it to count against our limit of the number of cached
// processes, so bump up the group count to account for it.
numCachedExtraGroup++;
} else {
lastCachedGroupUid = app.info.uid;
lastCachedGroup = connectionGroup;
}
} else {
lastCachedGroupUid = lastCachedGroup = 0;
}
if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
app.killLocked("cached #" + numCached,
"too many cached",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
true);
} else if (proactiveKillsEnabled) {
lruCachedApp = app;
}
break;
case PROCESS_STATE_CACHED_EMPTY:
if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
&& app.getLastActivityTime() < oldTime) {
app.killLocked("empty for " + ((now
- app.getLastActivityTime()) / 1000) + "s",
"empty for too long",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
true);
} else {
numEmpty++;
if (numEmpty > emptyProcessLimit) {
app.killLocked("empty #" + numEmpty,
"too many empty",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY,
true);
} else if (proactiveKillsEnabled) {
lruCachedApp = app;
}
}
break;
default:
mNumNonCachedProcs++;
break;
}
if (app.isolated && psr.numberOfRunningServices() <= 0
&& app.getIsolatedEntryPoint() == null) {
// If this is an isolated process, there are no services
// running in it, and it's not a special process with a
// custom entry point, then the process is no longer
// needed. We agressively kill these because we can by
// definition not re-use the same process again, and it is
// good to avoid having whatever code was running in them
// left sitting around after no longer needed.
app.killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
} else if (app.isSdkSandbox && psr.numberOfRunningServices() <= 0
&& app.getActiveInstrumentation() == null) {
// If this is an SDK sandbox process and there are no services running it, we
// aggressively kill the sandbox as we usually don't want to re-use the same
// sandbox again.
app.killLocked("sandbox not needed", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_SDK_SANDBOX_NOT_NEEDED, true);
} else {
// Keeping this process, update its uid.
updateAppUidRecLSP(app);
}
if (state.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
&& !app.isKilledByAm()) {
numTrimming++;
}
}
}
if (proactiveKillsEnabled // Proactive kills enabled?
&& doKillExcessiveProcesses // Should kill excessive processes?
&& freeSwapPercent < lowSwapThresholdPercent // Swap below threshold?
&& lruCachedApp != null // If no cached app, let LMKD decide
// If swap is non-decreasing, give reclaim a chance to catch up
&& freeSwapPercent < mLastFreeSwapPercent) {
lruCachedApp.killLocked("swap low and too many cached",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
true);
}
mLastFreeSwapPercent = freeSwapPercent;
return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming, now);
}
@GuardedBy({"mService", "mProcLock"})
protected void updateAppUidRecIfNecessaryLSP(final ProcessRecord app) {
if (!app.isKilledByAm() && app.getThread() != null) {
if (app.isolated && app.mServices.numberOfRunningServices() <= 0
&& app.getIsolatedEntryPoint() == null) {
// No op.
} else {
// Keeping this process, update its uid.
updateAppUidRecLSP(app);
}
}
}
@GuardedBy({"mService", "mProcLock"})
private void updateAppUidRecLSP(ProcessRecord app) {
final UidRecord uidRec = app.getUidRecord();
if (uidRec != null) {
final ProcessStateRecord state = app.mState;
uidRec.setEphemeral(app.info.isInstantApp());
if (uidRec.getCurProcState() > state.getCurProcState()) {
uidRec.setCurProcState(state.getCurProcState());
}
if (app.mServices.hasForegroundServices()) {
uidRec.setForegroundServices(true);
}
uidRec.setCurCapability(uidRec.getCurCapability() | state.getCurCapability());
}
}
@GuardedBy({"mService", "mProcLock"})
protected void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) {
// This compares previously set procstate to the current procstate in regards to whether
// or not the app's network access will be blocked. So, this needs to be called before
// we update the UidRecord's procstate by calling {@link UidRecord#setSetProcState}.
mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(activeUids);
ArrayList<UidRecord> becameIdle = mTmpBecameIdle;
becameIdle.clear();
// Update from any uid changes.
if (mService.mLocalPowerManager != null) {
mService.mLocalPowerManager.startUidChanges();
}
for (int i = activeUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = activeUids.valueAt(i);
if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT) {
if (uidRec.getSetProcState() != uidRec.getCurProcState()
|| uidRec.getSetCapability() != uidRec.getCurCapability()
|| uidRec.isSetAllowListed() != uidRec.isCurAllowListed()
|| uidRec.getProcAdjChanged()) {
int uidChange = 0;
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
+ ": proc state from " + uidRec.getSetProcState() + " to "
+ uidRec.getCurProcState() + ", capability from "
+ uidRec.getSetCapability() + " to " + uidRec.getCurCapability()
+ ", allowlist from " + uidRec.isSetAllowListed()
+ " to " + uidRec.isCurAllowListed()
+ ", procAdjChanged: " + uidRec.getProcAdjChanged());
}
if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
&& !uidRec.isCurAllowListed()) {
// UID is now in the background (and not on the temp allowlist). Was it
// previously in the foreground (or on the temp allowlist)?
if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState())
|| uidRec.isSetAllowListed()) {
uidRec.setLastBackgroundTime(nowElapsed);
if (mService.mDeterministicUidIdle
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
// Note: the background settle time is in elapsed realtime, while
// the handler time base is uptime. All this means is that we may
// stop background uids later than we had intended, but that only
// happens because the device was sleeping so we are okay anyway.
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
mConstants.BACKGROUND_SETTLE_TIME);
}
}
if (uidRec.isIdle() && !uidRec.isSetIdle()) {
uidChange |= UidRecord.CHANGE_IDLE;
if (uidRec.getSetProcState() != PROCESS_STATE_NONEXISTENT) {
// don't stop the bg services if it's just started.
becameIdle.add(uidRec);
}
}
} else {
if (uidRec.isIdle()) {
uidChange |= UidRecord.CHANGE_ACTIVE;
EventLogTags.writeAmUidActive(uidRec.getUid());
uidRec.setIdle(false);
}
uidRec.setLastBackgroundTime(0);
}
final boolean wasCached = uidRec.getSetProcState()
> ActivityManager.PROCESS_STATE_RECEIVER;
final boolean isCached = uidRec.getCurProcState()
> ActivityManager.PROCESS_STATE_RECEIVER;
if (wasCached != isCached
|| uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
uidChange |= isCached ? UidRecord.CHANGE_CACHED :
UidRecord.CHANGE_UNCACHED;
}
if (uidRec.getSetCapability() != uidRec.getCurCapability()) {
uidChange |= UidRecord.CHANGE_CAPABILITY;
}
if (uidRec.getSetProcState() != uidRec.getCurProcState()) {
uidChange |= UidRecord.CHANGE_PROCSTATE;
}
if (uidRec.getProcAdjChanged()) {
uidChange |= UidRecord.CHANGE_PROCADJ;
}
uidRec.setSetProcState(uidRec.getCurProcState());
uidRec.setSetCapability(uidRec.getCurCapability());
uidRec.setSetAllowListed(uidRec.isCurAllowListed());
uidRec.setSetIdle(uidRec.isIdle());
uidRec.clearProcAdjChanged();
if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0
|| (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) {
mService.mAtmInternal.onUidProcStateChanged(
uidRec.getUid(), uidRec.getSetProcState());
}
if (uidChange != 0) {
mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
}
if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0
|| (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) {
mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
uidRec.getCurCapability());
}
if (uidRec.hasForegroundServices()) {
mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
}
}
}
mService.mInternal.deletePendingTopUid(uidRec.getUid(), nowElapsed);
}
if (mService.mLocalPowerManager != null) {
mService.mLocalPowerManager.finishUidChanges();
}
int size = becameIdle.size();
if (size > 0) {
// If we have any new uids that became idle this time, we need to make sure
// they aren't left with running services.
for (int i = size - 1; i >= 0; i--) {
mService.mServices.stopInBackgroundLocked(becameIdle.get(i).getUid());
}
}
}
/**
* Return true if we should kill excessive cached/empty processes.
*/
private boolean shouldKillExcessiveProcesses(long nowUptime) {
final long lastUserUnlockingUptime = mService.mUserController.getLastUserUnlockingUptime();
if (lastUserUnlockingUptime == 0) {
// No users have been unlocked.
return !mConstants.mNoKillCachedProcessesUntilBootCompleted;
}
final long noKillCachedProcessesPostBootCompletedDurationMillis =
mConstants.mNoKillCachedProcessesPostBootCompletedDurationMillis;
if ((lastUserUnlockingUptime + noKillCachedProcessesPostBootCompletedDurationMillis)
> nowUptime) {
return false;
}
return true;
}
protected final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
new ComputeOomAdjWindowCallback();
/** These methods are called inline during computeOomAdjLSP(), on the same thread */
final class ComputeOomAdjWindowCallback
implements WindowProcessController.ComputeOomAdjCallback {
ProcessRecord app;
int adj;
boolean foregroundActivities;
boolean mHasVisibleActivities;
int procState;
int schedGroup;
int appUid;
int logUid;
int processStateCurTop;
ProcessStateRecord mState;
void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
boolean hasVisibleActivities, int procState, int schedGroup, int appUid,
int logUid, int processStateCurTop) {
this.app = app;
this.adj = adj;
this.foregroundActivities = foregroundActivities;
this.mHasVisibleActivities = hasVisibleActivities;
this.procState = procState;
this.schedGroup = schedGroup;
this.appUid = appUid;
this.logUid = logUid;
this.processStateCurTop = processStateCurTop;
this.mState = app.mState;
}
@Override
public void onVisibleActivity() {
// App has a visible activity; only upgrade adjustment.
if (adj > VISIBLE_APP_ADJ) {
adj = VISIBLE_APP_ADJ;
mState.setAdjType("vis-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
}
}
if (procState > processStateCurTop) {
procState = processStateCurTop;
mState.setAdjType("vis-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to vis-activity (top): " + app);
}
}
if (schedGroup < SCHED_GROUP_DEFAULT) {
schedGroup = SCHED_GROUP_DEFAULT;
}
mState.setCached(false);
mState.setEmpty(false);
foregroundActivities = true;
mHasVisibleActivities = true;
}
@Override
public void onPausedActivity() {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
mState.setAdjType("pause-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: " + app);
}
}
if (procState > processStateCurTop) {
procState = processStateCurTop;
mState.setAdjType("pause-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to pause-activity (top): " + app);
}
}
if (schedGroup < SCHED_GROUP_DEFAULT) {
schedGroup = SCHED_GROUP_DEFAULT;
}
mState.setCached(false);
mState.setEmpty(false);
foregroundActivities = true;
mHasVisibleActivities = false;
}
@Override
public void onStoppingActivity(boolean finishing) {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
mState.setAdjType("stop-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to stop-activity: " + app);
}
}
// For the process state, we will at this point consider the process to be cached. It
// will be cached either as an activity or empty depending on whether the activity is
// finishing. We do this so that we can treat the process as cached for purposes of
// memory trimming (determining current memory level, trim command to send to process)
// since there can be an arbitrary number of stopping processes and they should soon all
// go into the cached state.
if (!finishing) {
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
mState.setAdjType("stop-activity");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to stop-activity: " + app);
}
}
}
mState.setCached(false);
mState.setEmpty(false);
foregroundActivities = true;
mHasVisibleActivities = false;
}
@Override
public void onOtherActivity() {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
mState.setAdjType("cch-act");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to cached activity: " + app);
}
}
mHasVisibleActivities = false;
}
}
private boolean isScreenOnOrAnimatingLocked(ProcessStateRecord state) {
return mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE
|| state.isRunningRemoteAnimation();
}
@GuardedBy({"mService", "mProcLock"})
protected boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients, int oomAdjReason, boolean couldRecurse) {
final ProcessStateRecord state = app.mState;
if (couldRecurse) {
if (mAdjSeq == state.getAdjSeq()) {
if (state.getAdjSeq() == state.getCompletedAdjSeq()) {
// This adjustment has already been computed successfully.
return false;
} else {
// The process is being computed, so there is a cycle. We cannot
// rely on this process's state.
state.setContainsCycle(true);
mProcessesInCycle.add(app);
return false;
}
}
}
int prevAppAdj = getInitialAdj(app);
int prevProcState = getInitialProcState(app);
int prevCapability = getInitialCapability(app);
if (app.getThread() == null) {
state.setAdjSeq(mAdjSeq);
state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND);
state.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
state.setCurAdj(CACHED_APP_MAX_ADJ);
state.setCurRawAdj(CACHED_APP_MAX_ADJ);
state.setCompletedAdjSeq(state.getAdjSeq());
state.setCurCapability(PROCESS_CAPABILITY_NONE);
onProcessStateChanged(app, prevProcState);
onProcessOomAdjChanged(app, prevAppAdj);
return false;
}
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN);
state.setAdjSource(null);
state.setAdjTarget(null);
state.setEmpty(false);
state.setCached(false);
if (!couldRecurse || !cycleReEval) {
// Don't reset this flag when doing cycles re-evaluation.
state.setNoKillOnBgRestrictedAndIdle(false);
// If this UID is currently allowlisted, it should not be frozen.
final UidRecord uidRec = app.getUidRecord();
app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed());
}
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
final ProcessServiceRecord psr = app.mServices;
if (state.getMaxAdj() <= FOREGROUND_APP_ADJ) {
// The max adjustment doesn't allow this app to be anything
// below foreground, so it is not worth doing work for it.
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app);
}
state.setAdjType("fixed");
state.setAdjSeq(mAdjSeq);
state.setCurRawAdj(state.getMaxAdj());
state.setHasForegroundActivities(false);
state.setCurrentSchedulingGroup(SCHED_GROUP_DEFAULT);
state.setCurCapability(PROCESS_CAPABILITY_ALL); // BFSL allowed
state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
// facilitate this, here we need to determine whether or not it
// is currently showing UI.
state.setSystemNoUi(true);
if (app == topApp) {
state.setSystemNoUi(false);
state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP);
state.setAdjType("pers-top-activity");
} else if (state.hasTopUi()) {
// sched group/proc state adjustment is below
state.setSystemNoUi(false);
state.setAdjType("pers-top-ui");
} else if (state.getCachedHasVisibleActivities()) {
state.setSystemNoUi(false);
}
if (!state.isSystemNoUi()) {
if (isScreenOnOrAnimatingLocked(state)) {
// screen on or animating, promote UI
state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP);
} else if (!app.getWindowProcessController().isShowingUiWhileDozing()) {
// screen off, restrict UI scheduling
state.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
state.setCurrentSchedulingGroup(SCHED_GROUP_RESTRICTED);
}
}
state.setCurRawProcState(state.getCurProcState());
state.setCurAdj(state.getMaxAdj());
state.setCompletedAdjSeq(state.getAdjSeq());
onProcessStateChanged(app, prevProcState);
onProcessOomAdjChanged(app, prevAppAdj);
// if curAdj is less than prevAppAdj, then this process was promoted
return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState;
}
state.setSystemNoUi(false);
final int PROCESS_STATE_CUR_TOP = mService.mAtmInternal.getTopProcessState();
// Determine the importance of the process, starting with most
// important to least, and assign an appropriate OOM adjustment.
int adj;
int schedGroup;
int procState;
int capability = cycleReEval ? getInitialCapability(app) : 0;
boolean foregroundActivities = false;
boolean hasVisibleActivities = false;
if (app == topApp && PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP) {
// The last app on the list is the foreground app.
adj = FOREGROUND_APP_ADJ;
if (mService.mAtmInternal.useTopSchedGroupForTopProcess()) {
schedGroup = SCHED_GROUP_TOP_APP;
state.setAdjType("top-activity");
} else {
// Demote the scheduling group to avoid CPU contention if there is another more
// important process which also uses top-app, such as if SystemUI is animating.
schedGroup = SCHED_GROUP_DEFAULT;
state.setAdjType("intermediate-top-activity");
}
foregroundActivities = true;
hasVisibleActivities = true;
procState = PROCESS_STATE_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
}
} else if (state.isRunningRemoteAnimation()) {
adj = VISIBLE_APP_ADJ;
schedGroup = SCHED_GROUP_TOP_APP;
state.setAdjType("running-remote-anim");
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
}
} else if (app.getActiveInstrumentation() != null) {
// Don't want to kill running instrumentation.
adj = FOREGROUND_APP_ADJ;
schedGroup = SCHED_GROUP_DEFAULT;
state.setAdjType("instrumentation");
procState = PROCESS_STATE_FOREGROUND_SERVICE;
capability |= PROCESS_CAPABILITY_BFSL;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app);
}
} else if (state.getCachedIsReceivingBroadcast(mTmpSchedGroup)) {
// An app that is currently receiving a broadcast also
// counts as being in the foreground for OOM killer purposes.
// It's placed in a sched group based on the nature of the
// broadcast as reflected by which queue it's active in.
adj = FOREGROUND_APP_ADJ;
schedGroup = mTmpSchedGroup[0];
state.setAdjType("broadcast");
procState = ActivityManager.PROCESS_STATE_RECEIVER;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
}
} else if (psr.numberOfExecutingServices() > 0) {
// An app that is currently executing a service callback also
// counts as being in the foreground.
adj = FOREGROUND_APP_ADJ;
schedGroup = psr.shouldExecServicesFg()
? SCHED_GROUP_DEFAULT : SCHED_GROUP_BACKGROUND;
state.setAdjType("exec-service");
procState = PROCESS_STATE_SERVICE;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app);
}
} else if (app == topApp) {
adj = FOREGROUND_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setAdjType("top-sleeping");
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app);
}
} else {
// As far as we know the process is empty. We may change our mind later.
schedGroup = SCHED_GROUP_BACKGROUND;
// At this point we don't actually know the adjustment. Use the cached adj
// value that the caller wants us to.
adj = cachedAdj;
procState = PROCESS_STATE_CACHED_EMPTY;
if (!couldRecurse || !state.containsCycle()) {
state.setCached(true);
state.setEmpty(true);
state.setAdjType("cch-empty");
}
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app);
}
}
// Examine all activities if not already foreground.
if (!foregroundActivities && state.getCachedHasActivities()) {
state.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
adj, foregroundActivities, hasVisibleActivities, procState, schedGroup,
appUid, logUid, PROCESS_STATE_CUR_TOP);
adj = state.getCachedAdj();
foregroundActivities = state.getCachedForegroundActivities();
hasVisibleActivities = state.getCachedHasVisibleActivities();
procState = state.getCachedProcState();
schedGroup = state.getCachedSchedGroup();
}
if (procState > PROCESS_STATE_CACHED_RECENT && state.getCachedHasRecentTasks()) {
procState = PROCESS_STATE_CACHED_RECENT;
state.setAdjType("cch-rec");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app);
}
}
int capabilityFromFGS = 0; // capability from foreground service.
final boolean hasForegroundServices = psr.hasForegroundServices();
final boolean hasNonShortForegroundServices = psr.hasNonShortForegroundServices();
final boolean hasShortForegroundServices = hasForegroundServices
&& !psr.areAllShortForegroundServicesProcstateTimedOut(now);
// Adjust for FGS or "has-overlay-ui".
if (adj > PERCEPTIBLE_APP_ADJ
|| procState > PROCESS_STATE_FOREGROUND_SERVICE) {
String adjType = null;
int newAdj = 0;
int newProcState = 0;
if (hasForegroundServices && hasNonShortForegroundServices) {
// For regular (non-short) FGS.
adjType = "fg-service";
newAdj = PERCEPTIBLE_APP_ADJ;
newProcState = PROCESS_STATE_FOREGROUND_SERVICE;
capabilityFromFGS |= PROCESS_CAPABILITY_BFSL;
} else if (hasShortForegroundServices) {
// For short FGS.
adjType = "fg-service-short";
// We use MEDIUM_APP_ADJ + 1 so we can tell apart EJ
// (which uses MEDIUM_APP_ADJ + 1)
// from short-FGS.
// (We use +1 and +2, not +0 and +1, to be consistent with the following
// RECENT_FOREGROUND_APP_ADJ tweak)
newAdj = PERCEPTIBLE_MEDIUM_APP_ADJ + 1;
// We give the FGS procstate, but not PROCESS_CAPABILITY_BFSL, so
// short-fgs can't start FGS from the background.
newProcState = PROCESS_STATE_FOREGROUND_SERVICE;
} else if (state.hasOverlayUi()) {
adjType = "has-overlay-ui";
newAdj = PERCEPTIBLE_APP_ADJ;
newProcState = PROCESS_STATE_IMPORTANT_FOREGROUND;
}
if (adjType != null) {
adj = newAdj;
procState = newProcState;
state.setAdjType(adjType);
state.setCached(false);
schedGroup = SCHED_GROUP_DEFAULT;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType + ": "
+ app + " ");
}
}
}
// If the app was recently in the foreground and moved to a foreground service status,
// allow it to get a higher rank in memory for some time, compared to other foreground
// services so that it can finish performing any persistence/processing of in-memory state.
if (psr.hasForegroundServices() && adj > PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ
&& (state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION > now
|| state.getSetProcState() <= PROCESS_STATE_TOP)) {
if (psr.hasNonShortForegroundServices()) {
adj = PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ;
state.setAdjType("fg-service-act");
} else {
// For short-service FGS, we +1 the value, so we'll be able to detect it in
// various dashboards.
adj = PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 1;
state.setAdjType("fg-service-short-act");
}
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
}
}
// If the app was recently in the foreground and has expedited jobs running,
// allow it to get a higher rank in memory for some time, compared to other EJS and even
// foreground services so that it can finish performing any persistence/processing of
// in-memory state.
if (psr.hasTopStartedAlmostPerceptibleServices()
&& (adj > PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2)
&& (state.getLastTopTime()
+ mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION > now
|| state.getSetProcState() <= PROCESS_STATE_TOP)) {
// For EJ, we +2 the value, so we'll be able to detect it in
// various dashboards.
adj = PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2;
// This shall henceforth be called the "EJ" exemption, despite utilizing the
// ALMOST_PERCEPTIBLE flag to work.
state.setAdjType("top-ej-act");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app);
}
}
if (adj > PERCEPTIBLE_APP_ADJ
|| procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
if (state.getForcingToImportant() != null) {
// This is currently used for toasts... they are not interactive, and
// we don't want them to cause the app to become fully foreground (and
// thus out of background check), so we yes the best background level we can.
adj = PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
state.setCached(false);
state.setAdjType("force-imp");
state.setAdjSource(state.getForcingToImportant());
schedGroup = SCHED_GROUP_DEFAULT;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app);
}
}
}
if (state.getCachedIsHeavyWeight()) {
if (adj > HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = HEAVY_WEIGHT_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setCached(false);
state.setAdjType("heavy");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
}
}
if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
state.setAdjType("heavy");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app);
}
}
}
if (state.getCachedIsHomeProcess()) {
if (adj > HOME_APP_ADJ) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
adj = HOME_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setCached(false);
state.setAdjType("home");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
}
}
if (procState > ActivityManager.PROCESS_STATE_HOME) {
procState = ActivityManager.PROCESS_STATE_HOME;
state.setAdjType("home");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
}
}
}
if (state.getCachedIsPreviousProcess() && state.getCachedHasActivities()) {
// This was the previous process that showed UI to the user. We want to
// try to keep it around more aggressively, to give a good experience
// around switching between two apps. However, we don't want to keep the
// process in this privileged state indefinitely. Eventually, allow the
// app to be demoted to cached.
if (procState >= PROCESS_STATE_LAST_ACTIVITY
&& state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY
&& (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now) {
procState = PROCESS_STATE_LAST_ACTIVITY;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setAdjType("previous-expired");
adj = CACHED_APP_MIN_ADJ;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Expire prev adj: " + app);
}
} else {
if (adj > PREVIOUS_APP_ADJ) {
adj = PREVIOUS_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setCached(false);
state.setAdjType("previous");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
}
}
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
state.setAdjType("previous");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
}
}
}
}
if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
+ " reason=" + state.getAdjType());
// By default, we use the computed adjustment. It may be changed if
// there are applications dependent on our services or providers, but
// this gives us a baseline and makes sure we don't get into an
// infinite recursion. If we're re-evaluating due to cycles, use the previously computed
// values.
if (cycleReEval) {
procState = Math.min(procState, state.getCurRawProcState());
adj = Math.min(adj, state.getCurRawAdj());
schedGroup = Math.max(schedGroup, state.getCurrentSchedulingGroup());
}
state.setCurRawAdj(adj);
state.setCurRawProcState(procState);
state.setHasStartedServices(false);
state.setAdjSeq(mAdjSeq);
final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
if (backupTarget != null && app == backupTarget.app) {
// If possible we want to avoid killing apps while they're being backed up
if (adj > BACKUP_APP_ADJ) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
adj = BACKUP_APP_ADJ;
if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
}
state.setAdjType("backup");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
}
state.setCached(false);
}
if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
procState = ActivityManager.PROCESS_STATE_BACKUP;
state.setAdjType("backup");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app);
}
}
}
state.setCurBoundByNonBgRestrictedApp(getInitialIsCurBoundByNonBgRestrictedApp(app));
state.setScheduleLikeTopApp(false);
for (int is = psr.numberOfRunningServices() - 1;
is >= 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
is--) {
ServiceRecord s = psr.getRunningServiceAt(is);
if (s.startRequested) {
state.setHasStartedServices(true);
if (procState > PROCESS_STATE_SERVICE) {
procState = PROCESS_STATE_SERVICE;
state.setAdjType("started-services");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to started service: " + app);
}
}
if (!s.mKeepWarming && state.hasShownUi() && !state.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
// debug and understand what is going on.
if (adj > SERVICE_ADJ) {
state.setAdjType("cch-started-ui-services");
}
} else {
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. This does not apply
// to the SDK sandbox process since it should never
// be more important than its corresponding app.
if (!app.isSdkSandbox && adj > SERVICE_ADJ) {
adj = SERVICE_ADJ;
state.setAdjType("started-services");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to started service: " + app);
}
state.setCached(false);
}
}
// If we have let the service slide into the background
// state, still have some text describing what it is doing
// even though the service no longer has an impact.
if (adj > SERVICE_ADJ) {
state.setAdjType("cch-started-services");
}
}
}
if (s.isForeground) {
final int fgsType = s.foregroundServiceType;
if (s.isFgsAllowedWIU()) {
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
final boolean enabled = state.getCachedCompatChange(
CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY);
if (enabled) {
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_CAMERA)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0;
capabilityFromFGS |=
(fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0;
} else {
capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
| PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
}
}
}
if (!couldRecurse) {
// We're entering recursive functions below, if we're told it's not a recursive
// loop, abort here.
continue;
}
state.setCurRawAdj(adj);
state.setCurRawProcState(procState);
state.setCurrentSchedulingGroup(schedGroup);
state.setCurCapability(capability);
ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = s.getConnections();
for (int conni = serviceConnections.size() - 1;
conni >= 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
conni--) {
ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
for (int i = 0;
i < clist.size() && (adj > FOREGROUND_APP_ADJ
|| schedGroup == SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
i++) {
// XXX should compute this based on the max of
// all connected clients.
ConnectionRecord cr = clist.get(i);
if (cr.binding.client == app) {
// Binding to oneself is not interesting.
continue;
}
computeServiceHostOomAdjLSP(cr, app, cr.binding.client, now, topApp, doingAll,
cycleReEval, computeClients, oomAdjReason, cachedAdj, true);
adj = state.getCurRawAdj();
procState = state.getCurRawProcState();
schedGroup = state.getCurrentSchedulingGroup();
capability = state.getCurCapability();
}
}
}
final ProcessProviderRecord ppr = app.mProviders;
for (int provi = ppr.numberOfProviders() - 1;
provi >= 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
provi--) {
ContentProviderRecord cpr = ppr.getProviderAt(provi);
if (couldRecurse) {
// We're entering recursive functions below.
state.setCurRawAdj(adj);
state.setCurRawProcState(procState);
state.setCurrentSchedulingGroup(schedGroup);
state.setCurCapability(capability);
for (int i = cpr.connections.size() - 1;
i >= 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
i--) {
ContentProviderConnection conn = cpr.connections.get(i);
ProcessRecord client = conn.client;
computeProviderHostOomAdjLSP(conn, app, client, now, topApp, doingAll,
cycleReEval, computeClients, oomAdjReason, cachedAdj, true);
adj = state.getCurRawAdj();
procState = state.getCurRawProcState();
schedGroup = state.getCurrentSchedulingGroup();
capability = state.getCurCapability();
}
}
// If the provider has external (non-framework) process
// dependencies, ensure that its adjustment is at least
// FOREGROUND_APP_ADJ.
if (cpr.hasExternalProcessHandles()) {
if (adj > FOREGROUND_APP_ADJ) {
adj = FOREGROUND_APP_ADJ;
state.setCurRawAdj(adj);
schedGroup = SCHED_GROUP_DEFAULT;
state.setCached(false);
state.setAdjType("ext-provider");
state.setAdjTarget(cpr.name);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to external provider: " + app);
}
}
if (procState > PROCESS_STATE_IMPORTANT_FOREGROUND) {
procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
state.setCurRawProcState(procState);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to external provider: " + app);
}
}
}
}
if ((ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > PREVIOUS_APP_ADJ) {
adj = PREVIOUS_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setCached(false);
state.setAdjType("recent-provider");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to recent provider: " + app);
}
}
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
state.setAdjType("recent-provider");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to recent provider: " + app);
}
}
}
if (procState >= PROCESS_STATE_CACHED_EMPTY) {
if (psr.hasClientActivities()) {
// This is a cached process, but with client activities. Mark it so.
procState = PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
state.setAdjType("cch-client-act");
} else if (psr.isTreatedLikeActivity()) {
// This is a cached process, but somebody wants us to treat it like it has
// an activity, okay!
procState = PROCESS_STATE_CACHED_ACTIVITY;
state.setAdjType("cch-as-act");
}
}
if (adj == SERVICE_ADJ) {
if (doingAll && !cycleReEval) {
state.setServiceB(mNewNumAServiceProcs > (mNumServiceProcs / 3));
mNewNumServiceProcs++;
if (!state.isServiceB()) {
// This service isn't far enough down on the LRU list to
// normally be a B service, but if we are low on RAM and it
// is large we want to force it down since we would prefer to
// keep launcher over it.
long lastPssOrRss = !Flags.removeAppProfilerPssCollection()
? app.mProfile.getLastPss() : app.mProfile.getLastRss();
// RSS is larger than PSS, but the RSS/PSS ratio varies per-process based on how
// many shared pages a process uses. The threshold is increased if the flag for
// reading RSS instead of PSS is enabled.
//
// TODO(b/296454553): Tune the second value so that the relative number of
// service B is similar before/after this flag is enabled.
double thresholdModifier = !Flags.removeAppProfilerPssCollection()
? 1
: mConstants.PSS_TO_RSS_THRESHOLD_MODIFIER;
double cachedRestoreThreshold =
mProcessList.getCachedRestoreThresholdKb() * thresholdModifier;
if (!mService.mAppProfiler.isLastMemoryLevelNormal()
&& lastPssOrRss >= cachedRestoreThreshold) {
state.setServiceHighRam(true);
state.setServiceB(true);
//Slog.i(TAG, "ADJ " + app + " high ram!");
} else {
mNewNumAServiceProcs++;
//Slog.i(TAG, "ADJ " + app + " not high ram!");
}
} else {
state.setServiceHighRam(false);
}
}
if (state.isServiceB()) {
adj = SERVICE_B_ADJ;
}
}
state.setCurRawAdj(adj);
adj = psr.modifyRawOomAdj(adj);
if (adj > state.getMaxAdj()) {
adj = state.getMaxAdj();
if (adj <= PERCEPTIBLE_LOW_APP_ADJ) {
schedGroup = SCHED_GROUP_DEFAULT;
}
}
// Put bound foreground services in a special sched group for additional
// restrictions on screen off
if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
&& !state.shouldScheduleLikeTopApp()) {
if (schedGroup > SCHED_GROUP_RESTRICTED) {
schedGroup = SCHED_GROUP_RESTRICTED;
}
}
// apply capability from FGS.
if (psr.hasForegroundServices()) {
capability |= capabilityFromFGS;
}
capability |= getDefaultCapability(app, procState);
// Procstates below BFGS should never have this capability.
if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
capability &= ~PROCESS_CAPABILITY_BFSL;
}
if (app.isPendingFinishAttach()) {
// If the app is still starting up. We reset the computations to the
// hardcoded values in setAttachingProcessStatesLSP. This ensures that the app keeps
// hard-coded default 'startup' oom scores while starting up. When it finishes startup,
// we'll recompute oom scores based on it's actual hosted compoenents.
setAttachingProcessStatesLSP(app);
state.setAdjSeq(mAdjSeq);
state.setCompletedAdjSeq(state.getAdjSeq());
return false;
}
// Do final modification to adj. Everything we do between here and applying
// the final setAdj must be done in this function, because we will also use
// it when computing the final cached adj later. Note that we don't need to
// worry about this for max adj above, since max adj will always be used to
// keep it out of the cached vaues.
state.setCurCapability(capability);
state.updateLastInvisibleTime(hasVisibleActivities);
state.setHasForegroundActivities(foregroundActivities);
state.setCompletedAdjSeq(mAdjSeq);
schedGroup = setIntermediateAdjLSP(app, adj, prevAppAdj, schedGroup);
setIntermediateProcStateLSP(app, procState, prevProcState);
setIntermediateSchedGroupLSP(state, schedGroup);
// if curAdj or curProcState improved, then this process was promoted
return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
|| state.getCurCapability() != prevCapability;
}
/**
* @return The proposed change to the schedGroup.
*/
@GuardedBy({"mService", "mProcLock"})
protected int setIntermediateAdjLSP(ProcessRecord app, int adj, int prevRawAppAdj,
int schedGroup) {
final ProcessStateRecord state = app.mState;
state.setCurRawAdj(adj);
adj = app.mServices.modifyRawOomAdj(adj);
if (adj > state.getMaxAdj()) {
adj = state.getMaxAdj();
if (adj <= PERCEPTIBLE_LOW_APP_ADJ) {
schedGroup = SCHED_GROUP_DEFAULT;
}
}
state.setCurAdj(adj);
return schedGroup;
}
@GuardedBy({"mService", "mProcLock"})
protected void setIntermediateProcStateLSP(ProcessRecord app, int procState,
int prevProcState) {
final ProcessStateRecord state = app.mState;
state.setCurProcState(procState);
state.setCurRawProcState(procState);
}
@GuardedBy({"mService", "mProcLock"})
protected void setIntermediateSchedGroupLSP(ProcessStateRecord state, int schedGroup) {
// Put bound foreground services in a special sched group for additional
// restrictions on screen off
if (state.getCurProcState() >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
&& !state.shouldScheduleLikeTopApp()) {
if (schedGroup > SCHED_GROUP_RESTRICTED) {
schedGroup = SCHED_GROUP_RESTRICTED;
}
}
state.setCurrentSchedulingGroup(schedGroup);
}
@GuardedBy({"mService", "mProcLock"})
protected void computeServiceHostOomAdjLSP(ConnectionRecord cr, ProcessRecord app,
ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj,
boolean couldRecurse) {
if (app.isPendingFinishAttach()) {
// We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here.
return;
}
final ProcessStateRecord state = app.mState;
ProcessStateRecord cstate = client.mState;
if (couldRecurse) {
if (app.isSdkSandbox && cr.binding.attributedClient != null) {
// For SDK sandboxes, use the attributed client (eg the app that
// requested the sandbox)
client = cr.binding.attributedClient;
cstate = client.mState;
}
if (computeClients) {
computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true,
oomAdjReason, true);
} else {
cstate.setCurRawAdj(cstate.getCurAdj());
cstate.setCurRawProcState(cstate.getCurProcState());
}
}
int clientAdj = cstate.getCurRawAdj();
int clientProcState = cstate.getCurRawProcState();
final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP;
int adj = state.getCurRawAdj();
int procState = state.getCurRawProcState();
int schedGroup = state.getCurrentSchedulingGroup();
int capability = state.getCurCapability();
final int prevRawAdj = adj;
final int prevProcState = procState;
final int prevSchedGroup = schedGroup;
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
|| cstate.isCurBoundByNonBgRestrictedApp()
|| clientProcState <= PROCESS_STATE_BOUND_TOP
|| (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
&& !cstate.isBackgroundRestricted()));
if (client.mOptRecord.shouldNotFreeze()) {
// Propagate the shouldNotFreeze flag down the bindings.
app.mOptRecord.setShouldNotFreeze(true);
}
boolean trackedProcState = false;
// We always propagate PROCESS_CAPABILITY_BFSL over bindings here,
// but, right before actually setting it to the process,
// we check the final procstate, and remove it if the procsate is below BFGS.
capability |= getBfslCapabilityFromClient(client);
if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) {
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
capability |= cstate.getCurCapability();
}
// If an app has network capability by default
// (by having procstate <= BFGS), then the apps it binds to will get
// elevated to a high enough procstate anyway to get network unless they
// request otherwise, so don't propagate the network capability by default
// in this case unless they explicitly request it.
if ((cstate.getCurCapability()
& PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0) {
if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
// This is used to grant network access to Expedited Jobs.
if (cr.hasFlag(Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS)) {
capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
}
} else {
capability |= PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
}
}
if ((cstate.getCurCapability()
& PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0) {
if (clientProcState <= PROCESS_STATE_IMPORTANT_FOREGROUND) {
// This is used to grant network access to User Initiated Jobs.
if (cr.hasFlag(Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS)) {
capability |= PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
}
}
}
if (couldRecurse && shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
return;
}
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
// doesn't propagate except under certain conditions.
clientProcState = PROCESS_STATE_CACHED_EMPTY;
}
String adjType = null;
if (cr.hasFlag(Context.BIND_ALLOW_OOM_MANAGEMENT)) {
// Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
if (clientAdj < CACHED_APP_MIN_ADJ) {
app.mOptRecord.setShouldNotFreeze(true);
}
// Not doing bind OOM management, so treat
// this guy more like a started service.
if (state.hasShownUi() && !state.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
// debug and understand what is going on.
if (adj > clientAdj) {
adjType = "cch-bound-ui-services";
}
state.setCached(false);
clientAdj = adj;
clientProcState = procState;
} else {
if (now >= (cr.binding.service.lastActivity
+ mConstants.MAX_SERVICE_INACTIVITY)) {
// This service has not seen activity within
// recent memory, so allow it to drop to the
// LRU list if there is no other reason to keep
// it around. We'll also tag it with a label just
// to help debug and undertand what is going on.
if (adj > clientAdj) {
adjType = "cch-bound-services";
}
clientAdj = adj;
}
}
}
if (adj > clientAdj) {
// If this process has recently shown UI, and
// the process that is binding to it is less
// important than being visible, then we don't
// care about the binding as much as we care
// about letting this process get into the LRU
// list to be killed and restarted if needed for
// memory.
if (state.hasShownUi() && !state.getCachedIsHomeProcess()
&& clientAdj > PERCEPTIBLE_APP_ADJ) {
if (adj >= CACHED_APP_MIN_ADJ) {
adjType = "cch-bound-ui-services";
}
} else {
int newAdj;
int lbAdj = VISIBLE_APP_ADJ; // lower bound of adj.
if (cr.hasFlag(Context.BIND_ABOVE_CLIENT
| Context.BIND_IMPORTANT)) {
if (clientAdj >= PERSISTENT_SERVICE_ADJ) {
newAdj = clientAdj;
} else {
// make this service persistent
newAdj = PERSISTENT_SERVICE_ADJ;
schedGroup = SCHED_GROUP_DEFAULT;
procState = ActivityManager.PROCESS_STATE_PERSISTENT;
cr.trackProcState(procState, mAdjSeq);
trackedProcState = true;
}
} else if (cr.hasFlag(Context.BIND_NOT_PERCEPTIBLE)
&& clientAdj <= PERCEPTIBLE_APP_ADJ
&& adj >= (lbAdj = PERCEPTIBLE_LOW_APP_ADJ)) {
newAdj = PERCEPTIBLE_LOW_APP_ADJ;
} else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)
&& cr.notHasFlag(Context.BIND_NOT_FOREGROUND)
&& clientAdj < PERCEPTIBLE_APP_ADJ
&& adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) {
// This is for user-initiated jobs.
// We use APP_ADJ + 1 here, so we can tell them apart from FGS.
newAdj = PERCEPTIBLE_APP_ADJ + 1;
} else if (cr.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)
&& cr.hasFlag(Context.BIND_NOT_FOREGROUND)
&& clientAdj < PERCEPTIBLE_APP_ADJ
&& adj >= (lbAdj = (PERCEPTIBLE_MEDIUM_APP_ADJ + 2))) {
// This is for expedited jobs.
// We use MEDIUM_APP_ADJ + 2 here, so we can tell apart
// EJ and short-FGS.
newAdj = PERCEPTIBLE_MEDIUM_APP_ADJ + 2;
} else if (cr.hasFlag(Context.BIND_NOT_VISIBLE)
&& clientAdj < PERCEPTIBLE_APP_ADJ
&& adj >= (lbAdj = PERCEPTIBLE_APP_ADJ)) {
newAdj = PERCEPTIBLE_APP_ADJ;
} else if (clientAdj >= PERCEPTIBLE_APP_ADJ) {
newAdj = clientAdj;
} else if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE)
&& clientAdj <= VISIBLE_APP_ADJ
&& adj > VISIBLE_APP_ADJ) {
newAdj = VISIBLE_APP_ADJ;
} else {
if (adj > VISIBLE_APP_ADJ) {
// TODO: Is this too limiting for apps bound from TOP?
newAdj = Math.max(clientAdj, lbAdj);
} else {
newAdj = adj;
}
}
if (!cstate.isCached()) {
state.setCached(false);
}
if (adj > newAdj) {
adj = newAdj;
state.setCurRawAdj(adj);
adjType = "service";
}
}
}
if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND
| Context.BIND_IMPORTANT_BACKGROUND)) {
// This will treat important bound services identically to
// the top app, which may behave differently than generic
// foreground work.
final int curSchedGroup = cstate.getCurrentSchedulingGroup();
if (curSchedGroup > schedGroup) {
if (cr.hasFlag(Context.BIND_IMPORTANT)) {
schedGroup = curSchedGroup;
} else {
schedGroup = SCHED_GROUP_DEFAULT;
}
}
if (clientProcState < PROCESS_STATE_TOP) {
// Special handling for above-top states (persistent
// processes). These should not bring the current process
// into the top state, since they are not on top. Instead
// give them the best bound state after that.
if (cr.hasFlag(BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE)) {
clientProcState = PROCESS_STATE_FOREGROUND_SERVICE;
} else if (cr.hasFlag(Context.BIND_FOREGROUND_SERVICE)) {
clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
} else if (mService.mWakefulness.get()
== PowerManagerInternal.WAKEFULNESS_AWAKE
&& cr.hasFlag(Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) {
clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
} else {
clientProcState =
PROCESS_STATE_IMPORTANT_FOREGROUND;
}
} else if (clientProcState == PROCESS_STATE_TOP) {
// Go at most to BOUND_TOP, unless requested to elevate
// to client's state.
clientProcState = PROCESS_STATE_BOUND_TOP;
final boolean enabled = cstate.getCachedCompatChange(
CACHED_COMPAT_CHANGE_PROCESS_CAPABILITY);
if (enabled) {
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
// TOP process passes all capabilities to the service.
capability |= cstate.getCurCapability();
} else {
// TOP process passes no capability to the service.
}
} else {
// TOP process passes all capabilities to the service.
capability |= cstate.getCurCapability();
}
}
} else if (cr.notHasFlag(Context.BIND_IMPORTANT_BACKGROUND)) {
if (clientProcState < PROCESS_STATE_TRANSIENT_BACKGROUND) {
clientProcState =
PROCESS_STATE_TRANSIENT_BACKGROUND;
}
} else {
if (clientProcState < PROCESS_STATE_IMPORTANT_BACKGROUND) {
clientProcState =
PROCESS_STATE_IMPORTANT_BACKGROUND;
}
}
if (cr.hasFlag(Context.BIND_SCHEDULE_LIKE_TOP_APP) && clientIsSystem) {
schedGroup = SCHED_GROUP_TOP_APP;
state.setScheduleLikeTopApp(true);
}
if (!trackedProcState) {
cr.trackProcState(clientProcState, mAdjSeq);
}
if (procState > clientProcState) {
procState = clientProcState;
state.setCurRawProcState(procState);
if (adjType == null) {
adjType = "service";
}
}
if (procState < PROCESS_STATE_IMPORTANT_BACKGROUND
&& cr.hasFlag(Context.BIND_SHOWING_UI)) {
app.setPendingUiClean(true);
}
if (adjType != null) {
state.setAdjType(adjType);
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE);
state.setAdjSource(client);
state.setAdjSourceProcState(clientProcState);
state.setAdjTarget(cr.binding.service.instanceName);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+ ": " + app + ", due to " + client
+ " adj=" + adj + " procState="
+ ProcessList.makeProcStateString(procState));
}
}
} else { // BIND_WAIVE_PRIORITY == true
// BIND_WAIVE_PRIORITY bindings are special when it comes to the
// freezer. Processes bound via WPRI are expected to be running,
// but they are not promoted in the LRU list to keep them out of
// cached. As a result, they can freeze based on oom_adj alone.
// Normally, bindToDeath would fire when a cached app would die
// in the background, but nothing will fire when a running process
// pings a frozen process. Accordingly, any cached app that is
// bound by an unfrozen app via a WPRI binding has to remain
// unfrozen.
if (clientAdj < CACHED_APP_MIN_ADJ) {
app.mOptRecord.setShouldNotFreeze(true);
}
}
if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
app.mServices.setTreatLikeActivity(true);
if (clientProcState <= PROCESS_STATE_CACHED_ACTIVITY
&& procState > PROCESS_STATE_CACHED_ACTIVITY) {
// This is a cached process, but somebody wants us to treat it like it has
// an activity, okay!
procState = PROCESS_STATE_CACHED_ACTIVITY;
state.setAdjType("cch-as-act");
}
}
final ActivityServiceConnectionsHolder a = cr.activity;
if (cr.hasFlag(Context.BIND_ADJUST_WITH_ACTIVITY)) {
if (a != null && adj > FOREGROUND_APP_ADJ
&& a.isActivityVisible()) {
adj = FOREGROUND_APP_ADJ;
state.setCurRawAdj(adj);
if (cr.notHasFlag(Context.BIND_NOT_FOREGROUND)) {
if (cr.hasFlag(Context.BIND_IMPORTANT)) {
schedGroup = SCHED_GROUP_TOP_APP_BOUND;
} else {
schedGroup = SCHED_GROUP_DEFAULT;
}
}
state.setCached(false);
state.setAdjType("service");
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE);
state.setAdjSource(a);
state.setAdjSourceProcState(procState);
state.setAdjTarget(cr.binding.service.instanceName);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise to service w/activity: " + app);
}
}
}
capability |= getDefaultCapability(app, procState);
// Procstates below BFGS should never have this capability.
if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
capability &= ~PROCESS_CAPABILITY_BFSL;
}
if (adj < prevRawAdj) {
schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup);
}
if (procState < prevProcState) {
setIntermediateProcStateLSP(app, procState, prevProcState);
}
if (schedGroup > prevSchedGroup) {
setIntermediateSchedGroupLSP(state, schedGroup);
}
state.setCurCapability(capability);
state.setEmpty(false);
}
protected void computeProviderHostOomAdjLSP(ContentProviderConnection conn, ProcessRecord app,
ProcessRecord client, long now, ProcessRecord topApp, boolean doingAll,
boolean cycleReEval, boolean computeClients, int oomAdjReason, int cachedAdj,
boolean couldRecurse) {
if (app.isPendingFinishAttach()) {
// We've set the attaching process state in the computeInitialOomAdjLSP. Skip it here.
return;
}
final ProcessStateRecord state = app.mState;
final ProcessStateRecord cstate = client.mState;
if (client == app) {
// Being our own client is not interesting.
return;
}
if (couldRecurse) {
if (computeClients) {
computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true,
oomAdjReason, true);
} else if (couldRecurse) {
cstate.setCurRawAdj(cstate.getCurAdj());
cstate.setCurRawProcState(cstate.getCurProcState());
}
if (shouldSkipDueToCycle(app, cstate, state.getCurRawProcState(), state.getCurRawAdj(),
cycleReEval)) {
return;
}
}
int clientAdj = cstate.getCurRawAdj();
int clientProcState = cstate.getCurRawProcState();
int adj = state.getCurRawAdj();
int procState = state.getCurRawProcState();
int schedGroup = state.getCurrentSchedulingGroup();
int capability = state.getCurCapability();
final int prevRawAdj = adj;
final int prevProcState = procState;
final int prevSchedGroup = schedGroup;
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
// We always propagate PROCESS_CAPABILITY_BFSL to providers here,
// but, right before actually setting it to the process,
// we check the final procstate, and remove it if the procsate is below BFGS.
capability |= getBfslCapabilityFromClient(client);
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty.
clientProcState = PROCESS_STATE_CACHED_EMPTY;
}
if (client.mOptRecord.shouldNotFreeze()) {
// Propagate the shouldNotFreeze flag down the bindings.
app.mOptRecord.setShouldNotFreeze(true);
}
state.setCurBoundByNonBgRestrictedApp(state.isCurBoundByNonBgRestrictedApp()
|| cstate.isCurBoundByNonBgRestrictedApp()
|| clientProcState <= PROCESS_STATE_BOUND_TOP
|| (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
&& !cstate.isBackgroundRestricted()));
String adjType = null;
if (adj > clientAdj) {
if (state.hasShownUi() && !state.getCachedIsHomeProcess()
&& clientAdj > PERCEPTIBLE_APP_ADJ) {
adjType = "cch-ui-provider";
} else {
adj = Math.max(clientAdj, FOREGROUND_APP_ADJ);
state.setCurRawAdj(adj);
adjType = "provider";
}
state.setCached(state.isCached() & cstate.isCached());
}
if (clientProcState <= PROCESS_STATE_FOREGROUND_SERVICE) {
if (adjType == null) {
adjType = "provider";
}
if (clientProcState == PROCESS_STATE_TOP) {
clientProcState = PROCESS_STATE_BOUND_TOP;
} else {
clientProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
}
}
conn.trackProcState(clientProcState, mAdjSeq);
if (procState > clientProcState) {
procState = clientProcState;
state.setCurRawProcState(procState);
}
if (cstate.getCurrentSchedulingGroup() > schedGroup) {
schedGroup = SCHED_GROUP_DEFAULT;
}
if (adjType != null) {
state.setAdjType(adjType);
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
.REASON_PROVIDER_IN_USE);
state.setAdjSource(client);
state.setAdjSourceProcState(clientProcState);
state.setAdjTarget(conn.provider.name);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType
+ ": " + app + ", due to " + client
+ " adj=" + adj + " procState="
+ ProcessList.makeProcStateString(procState));
}
}
// Procstates below BFGS should never have this capability.
if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
capability &= ~PROCESS_CAPABILITY_BFSL;
}
if (adj < prevRawAdj) {
schedGroup = setIntermediateAdjLSP(app, adj, prevRawAdj, schedGroup);
}
if (procState < prevProcState) {
setIntermediateProcStateLSP(app, procState, prevProcState);
}
if (schedGroup > prevSchedGroup) {
setIntermediateSchedGroupLSP(state, schedGroup);
}
state.setCurCapability(capability);
state.setEmpty(false);
}
protected int getDefaultCapability(ProcessRecord app, int procState) {
final int networkCapabilities =
NetworkPolicyManager.getDefaultProcessNetworkCapabilities(procState);
final int baseCapabilities;
switch (procState) {
case PROCESS_STATE_PERSISTENT:
case PROCESS_STATE_PERSISTENT_UI:
case PROCESS_STATE_TOP:
baseCapabilities = PROCESS_CAPABILITY_ALL; // BFSL allowed
break;
case PROCESS_STATE_BOUND_TOP:
baseCapabilities = PROCESS_CAPABILITY_BFSL;
break;
case PROCESS_STATE_FOREGROUND_SERVICE:
if (app.getActiveInstrumentation() != null) {
baseCapabilities = PROCESS_CAPABILITY_ALL_IMPLICIT;
} else {
// Capability from foreground service is conditional depending on
// foregroundServiceType in the manifest file and the
// mAllowWhileInUsePermissionInFgs flag.
baseCapabilities = PROCESS_CAPABILITY_NONE;
}
break;
default:
baseCapabilities = PROCESS_CAPABILITY_NONE;
break;
}
return baseCapabilities | networkCapabilities;
}
/**
* @return the BFSL capability from a client (of a service binding or provider).
*/
protected int getBfslCapabilityFromClient(ProcessRecord client) {
// Procstates above FGS should always have this flag. We shouldn't need this logic,
// but let's do it just in case.
if (client.mState.getCurProcState() < PROCESS_STATE_FOREGROUND_SERVICE) {
return PROCESS_CAPABILITY_BFSL;
}
// Otherwise, use the process's cur capability.
// Note: BFSL is a per-UID check, not per-process, but here, the BFSL capability is still
// propagated on a per-process basis.
//
// For example, consider this case:
// - There are App 1 and App 2.
// - App 1 has two processes
// Proc #1A, procstate BFGS with CAPABILITY_BFSL
// Proc #1B, procstate FGS with no CAPABILITY_BFSL (i.e. process has a short FGS)
// And this process binds to Proc #2 of App 2.
//
// (Note because #1A has CAPABILITY_BFSL, App 1's UidRecord has CAPABILITY_BFSL.)
//
// - App 2 has one process:
// Proc #2, procstate FGS due to the above binding, _with no CAPABILITY_BFSL_.
//
// In this case, #2 will not get CAPABILITY_BFSL because the binding client (#1B)
// doesn't have this capability. (Even though App 1's UidRecord has it.)
//
// This may look weird, because App 2 _is_ still BFSL allowed, because "it's bound by
// an app that is BFSL-allowed". (See [bookmark: 61867f60-007c-408c-a2c4-e19e96056135]
// in ActiveServices.)
//
// So why don't we propagate PROCESS_CAPABILITY_BFSL from App 1's UID record?
// This is because short-FGS acts like "below BFGS" as far as BFSL is concerned,
// similar to how JobScheduler jobs are below BFGS and apps can't start FGS from there.
//
// If #1B was running a job instead of a short-FGS, then its procstate would be below BFGS.
// Then #2's procstate would also be below BFGS. So #2 wouldn't get CAPABILITY_BFSL.
// Similarly, if #1B has a short FGS, even though the procstate of #1B and #2 would be FGS,
// they both still wouldn't get CAPABILITY_BFSL.
//
// However, again, because #2 is bound by App 1, which is BFSL-allowed (because of #1A)
// App 2 would still BFSL-allowed, due to the aforementioned check in ActiveServices.
return client.mState.getCurCapability() & PROCESS_CAPABILITY_BFSL;
}
/**
* Checks if for the given app and client, there's a cycle that should skip over the client
* for now or use partial values to evaluate the effect of the client binding.
* @param app
* @param client
* @param procState procstate evaluated so far for this app
* @param adj oom_adj evaluated so far for this app
* @param cycleReEval whether we're currently re-evaluating due to a cycle, and not the first
* evaluation.
* @return whether to skip using the client connection at this time
*/
private boolean shouldSkipDueToCycle(ProcessRecord app, ProcessStateRecord client,
int procState, int adj, boolean cycleReEval) {
if (client.containsCycle()) {
// We've detected a cycle. We should retry computeOomAdjLSP later in
// case a later-checked connection from a client would raise its
// priority legitimately.
app.mState.setContainsCycle(true);
mProcessesInCycle.add(app);
// If the client has not been completely evaluated, check if it's worth
// using the partial values.
if (client.getCompletedAdjSeq() < mAdjSeq) {
if (cycleReEval) {
// If the partial values are no better, skip until the next
// attempt
if (client.getCurRawProcState() >= procState
&& client.getCurRawAdj() >= adj) {
return true;
}
// Else use the client's partial procstate and adj to adjust the
// effect of the binding
} else {
return true;
}
}
}
return false;
}
/** Inform the oomadj observer of changes to oomadj. Used by tests. */
@GuardedBy("mService")
protected void reportOomAdjMessageLocked(String tag, String msg) {
Slog.d(tag, msg);
synchronized (mService.mOomAdjObserverLock) {
if (mService.mCurOomAdjObserver != null) {
mService.mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg)
.sendToTarget();
}
}
}
void onWakefulnessChanged(int wakefulness) {
mCachedAppOptimizer.onWakefulnessChanged(wakefulness);
}
/** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
@GuardedBy({"mService", "mProcLock"})
protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
long nowElapsed, @OomAdjReason int oomAdjReson) {
boolean success = true;
final ProcessStateRecord state = app.mState;
final UidRecord uidRec = app.getUidRecord();
if (state.getCurRawAdj() != state.getSetRawAdj()) {
state.setSetRawAdj(state.getCurRawAdj());
}
int changes = 0;
if (state.getCurAdj() != state.getSetAdj()) {
mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
}
if (state.getCurAdj() != state.getSetAdj()) {
ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
String msg = "Set " + app.getPid() + " " + app.processName + " adj "
+ state.getCurAdj() + ": " + state.getAdjType();
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
state.setSetAdj(state.getCurAdj());
if (uidRec != null) {
uidRec.noteProcAdjChanged();
}
state.setVerifiedAdj(INVALID_ADJ);
}
final int curSchedGroup = state.getCurrentSchedulingGroup();
if (state.getSetSchedGroup() != curSchedGroup) {
int oldSchedGroup = state.getSetSchedGroup();
state.setSetSchedGroup(curSchedGroup);
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
String msg = "Setting sched group of " + app.processName
+ " to " + curSchedGroup + ": " + state.getAdjType();
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
if (app.getWaitingToKill() != null && app.mReceivers.numberOfCurReceivers() == 0
&& ActivityManager.isProcStateBackground(state.getSetProcState())) {
app.killLocked(app.getWaitingToKill(), ApplicationExitInfo.REASON_USER_REQUESTED,
ApplicationExitInfo.SUBREASON_REMOVE_TASK, true);
success = false;
} else {
int processGroup;
switch (curSchedGroup) {
case SCHED_GROUP_BACKGROUND:
processGroup = THREAD_GROUP_BACKGROUND;
break;
case SCHED_GROUP_TOP_APP:
case SCHED_GROUP_TOP_APP_BOUND:
processGroup = THREAD_GROUP_TOP_APP;
break;
case SCHED_GROUP_RESTRICTED:
processGroup = THREAD_GROUP_RESTRICTED;
break;
default:
processGroup = THREAD_GROUP_DEFAULT;
break;
}
mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
0 /* unused */, app.getPid(), processGroup, app.processName));
try {
final int renderThreadTid = app.getRenderThreadTid();
if (curSchedGroup == SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != SCHED_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
if (mService.mUseFifoUiScheduling) {
// Switch UI pipeline for app to SCHED_FIFO
state.setSavedPriority(Process.getThreadPriority(app.getPid()));
mService.scheduleAsFifoPriority(app.getPid(), true);
if (renderThreadTid != 0) {
mService.scheduleAsFifoPriority(renderThreadTid,
/* suppressLogs */true);
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Set RenderThread (TID " +
renderThreadTid + ") to FIFO");
}
} else {
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Not setting RenderThread TID");
}
}
} else {
// Boost priority for top app UI and render threads
setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
if (renderThreadTid != 0) {
try {
setThreadPriority(renderThreadTid,
THREAD_PRIORITY_TOP_APP_BOOST);
} catch (IllegalArgumentException e) {
// thread died, ignore
}
}
}
}
} else if (oldSchedGroup == SCHED_GROUP_TOP_APP
&& curSchedGroup != SCHED_GROUP_TOP_APP) {
app.getWindowProcessController().onTopProcChanged();
if (mService.mUseFifoUiScheduling) {
try {
// Reset UI pipeline to SCHED_OTHER
setThreadScheduler(app.getPid(), SCHED_OTHER, 0);
setThreadPriority(app.getPid(), state.getSavedPriority());
if (renderThreadTid != 0) {
setThreadScheduler(renderThreadTid,
SCHED_OTHER, 0);
}
} catch (IllegalArgumentException e) {
Slog.w(TAG,
"Failed to set scheduling policy, thread does not exist:\n"
+ e);
} catch (SecurityException e) {
Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
}
} else {
// Reset priority for top app UI and render threads
setThreadPriority(app.getPid(), 0);
}
if (renderThreadTid != 0) {
setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY);
}
}
} catch (Exception e) {
if (DEBUG_ALL) {
Slog.w(TAG, "Failed setting thread priority of " + app.getPid(), e);
}
}
}
}
if (state.hasRepForegroundActivities() != state.hasForegroundActivities()) {
state.setRepForegroundActivities(state.hasForegroundActivities());
changes |= ActivityManagerService.ProcessChangeItem.CHANGE_ACTIVITIES;
}
updateAppFreezeStateLSP(app, oomAdjReson);
if (state.getReportedProcState() != state.getCurProcState()) {
state.setReportedProcState(state.getCurProcState());
if (app.getThread() != null) {
try {
if (false) {
//RuntimeException h = new RuntimeException("here");
Slog.i(TAG, "Sending new process state " + state.getReportedProcState()
+ " to " + app /*, h*/);
}
app.getThread().setProcessState(state.getReportedProcState());
} catch (RemoteException e) {
}
}
}
boolean forceUpdatePssTime = false;
if (state.getSetProcState() == PROCESS_STATE_NONEXISTENT
|| ProcessList.procStatesDifferForMem(
state.getCurProcState(), state.getSetProcState())) {
state.setLastStateTime(now);
forceUpdatePssTime = true;
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Process state change from "
+ ProcessList.makeProcStateString(state.getSetProcState()) + " to "
+ ProcessList.makeProcStateString(state.getCurProcState()) + " next pss in "
+ (app.mProfile.getNextPssTime() - now) + ": " + app);
}
}
synchronized (mService.mAppProfiler.mProfilerLock) {
app.mProfile.updateProcState(app.mState);
mService.mAppProfiler.updateNextPssTimeLPf(
state.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
}
if (state.getSetProcState() != state.getCurProcState()) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
String msg = "Proc state change of " + app.processName
+ " to " + ProcessList.makeProcStateString(state.getCurProcState())
+ " (" + state.getCurProcState() + ")" + ": " + state.getAdjType();
reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
}
boolean setImportant = state.getSetProcState() < PROCESS_STATE_SERVICE;
boolean curImportant = state.getCurProcState() < PROCESS_STATE_SERVICE;
if (setImportant && !curImportant) {
// This app is no longer something we consider important enough to allow to use
// arbitrary amounts of battery power. Note its current CPU time to later know to
// kill it if it is not behaving well.
state.setWhenUnimportant(now);
app.mProfile.mLastCpuTime.set(0);
}
// Inform UsageStats of important process state change
// Must be called before updating setProcState
maybeUpdateUsageStatsLSP(app, nowElapsed);
maybeUpdateLastTopTime(state, now);
state.setSetProcState(state.getCurProcState());
if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) {
state.setNotCachedSinceIdle(false);
}
if (!doingAll) {
synchronized (mService.mProcessStats.mLock) {
mService.setProcessTrackerStateLOSP(app,
mService.mProcessStats.getMemFactorLocked());
}
} else {
state.setProcStateChanged(true);
}
} else if (state.hasReportedInteraction()) {
final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange(
CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME);
final long interactionThreshold = fgsInteractionChangeEnabled
? mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S
: mConstants.USAGE_STATS_INTERACTION_INTERVAL_PRE_S;
// For apps that sit around for a long time in the interactive state, we need
// to report this at least once a day so they don't go idle.
if ((nowElapsed - state.getInteractionEventTime()) > interactionThreshold) {
maybeUpdateUsageStatsLSP(app, nowElapsed);
}
} else {
final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange(
CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME);
final long interactionThreshold = fgsInteractionChangeEnabled
? mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S
: mConstants.SERVICE_USAGE_INTERACTION_TIME_PRE_S;
// For foreground services that sit around for a long time but are not interacted with.
if ((nowElapsed - state.getFgInteractionTime()) > interactionThreshold) {
maybeUpdateUsageStatsLSP(app, nowElapsed);
}
}
if (state.getCurCapability() != state.getSetCapability()) {
state.setSetCapability(state.getCurCapability());
}
final boolean curBoundByNonBgRestrictedApp = state.isCurBoundByNonBgRestrictedApp();
if (curBoundByNonBgRestrictedApp != state.isSetBoundByNonBgRestrictedApp()) {
state.setSetBoundByNonBgRestrictedApp(curBoundByNonBgRestrictedApp);
if (!curBoundByNonBgRestrictedApp && state.isBackgroundRestricted()) {
mService.mHandler.post(() -> {
synchronized (mService) {
mService.mServices.stopAllForegroundServicesLocked(
app.uid, app.info.packageName);
}
});
}
}
if (changes != 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Changes in " + app + ": " + changes);
ActivityManagerService.ProcessChangeItem item =
mProcessList.enqueueProcessChangeItemLocked(app.getPid(), app.info.uid);
item.changes |= changes;
item.foregroundActivities = state.hasRepForegroundActivities();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Item " + Integer.toHexString(System.identityHashCode(item))
+ " " + app.toShortString() + ": changes=" + item.changes
+ " foreground=" + item.foregroundActivities
+ " type=" + state.getAdjType() + " source=" + state.getAdjSource()
+ " target=" + state.getAdjTarget());
}
if (state.isCached() && !state.shouldNotKillOnBgRestrictedAndIdle()) {
// It's eligible to get killed when in UID idle and bg restricted mode,
// check if these states are just flipped.
if (!state.isSetCached() || state.isSetNoKillOnBgRestrictedAndIdle()) {
// Take the timestamp, we'd hold the killing for the background settle time
// (for states debouncing to avoid from thrashing).
state.setLastCanKillOnBgRestrictedAndIdleTime(nowElapsed);
// Kick off the delayed checkup message if needed.
if (mService.mDeterministicUidIdle
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs);
}
}
}
state.setSetCached(state.isCached());
state.setSetNoKillOnBgRestrictedAndIdle(state.shouldNotKillOnBgRestrictedAndIdle());
return success;
}
@GuardedBy({"mService", "mProcLock"})
void setAttachingProcessStatesLSP(ProcessRecord app) {
int initialSchedGroup = SCHED_GROUP_DEFAULT;
int initialProcState = PROCESS_STATE_CACHED_EMPTY;
int initialCapability = PROCESS_CAPABILITY_NONE;
boolean initialCached = true;
final ProcessStateRecord state = app.mState;
final int prevProcState = PROCESS_STATE_UNKNOWN;
final int prevAdj = UNKNOWN_ADJ;
// If the process has been marked as foreground, it is starting as the top app (with
// Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread.
if (state.hasForegroundActivities()) {
try {
// The priority must be the same as how does {@link #applyOomAdjLSP} set for
// {@link SCHED_GROUP_TOP_APP}. We don't check render thread because it
// is not ready when attaching.
app.getWindowProcessController().onTopProcChanged();
if (mService.mUseFifoUiScheduling) {
mService.scheduleAsFifoPriority(app.getPid(), true);
} else {
setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
}
if (isScreenOnOrAnimatingLocked(state)) {
initialSchedGroup = SCHED_GROUP_TOP_APP;
initialProcState = PROCESS_STATE_TOP;
}
initialCapability = PROCESS_CAPABILITY_ALL;
initialCached = false;
} catch (Exception e) {
Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e);
}
}
state.setCurrentSchedulingGroup(initialSchedGroup);
state.setCurProcState(initialProcState);
state.setCurRawProcState(initialProcState);
state.setCurCapability(initialCapability);
state.setCached(initialCached);
state.setCurAdj(ProcessList.FOREGROUND_APP_ADJ);
state.setCurRawAdj(ProcessList.FOREGROUND_APP_ADJ);
state.setForcingToImportant(null);
state.setHasShownUi(false);
onProcessStateChanged(app, prevProcState);
onProcessOomAdjChanged(app, prevAdj);
}
// ONLY used for unit testing in OomAdjusterTests.java
@VisibleForTesting
void maybeUpdateUsageStats(ProcessRecord app, long nowElapsed) {
synchronized (mService) {
synchronized (mProcLock) {
maybeUpdateUsageStatsLSP(app, nowElapsed);
}
}
}
@GuardedBy({"mService", "mProcLock"})
private void maybeUpdateUsageStatsLSP(ProcessRecord app, long nowElapsed) {
final ProcessStateRecord state = app.mState;
if (DEBUG_USAGE_STATS) {
Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
+ "] state changes: old = " + state.getSetProcState() + ", new = "
+ state.getCurProcState());
}
if (mService.mUsageStatsService == null) {
return;
}
final boolean fgsInteractionChangeEnabled = state.getCachedCompatChange(
CACHED_COMPAT_CHANGE_USE_SHORT_FGS_USAGE_INTERACTION_TIME);
boolean isInteraction;
// To avoid some abuse patterns, we are going to be careful about what we consider
// to be an app interaction. Being the top activity doesn't count while the display
// is sleeping, nor do short foreground services.
if (ActivityManager.isProcStateConsideredInteraction(state.getCurProcState())) {
isInteraction = true;
state.setFgInteractionTime(0);
} else if (state.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
if (state.getFgInteractionTime() == 0) {
state.setFgInteractionTime(nowElapsed);
isInteraction = false;
} else {
final long interactionTime = fgsInteractionChangeEnabled
? mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S
: mConstants.SERVICE_USAGE_INTERACTION_TIME_PRE_S;
isInteraction = nowElapsed > state.getFgInteractionTime() + interactionTime;
}
} else {
isInteraction =
state.getCurProcState() <= PROCESS_STATE_IMPORTANT_FOREGROUND;
state.setFgInteractionTime(0);
}
final long interactionThreshold = fgsInteractionChangeEnabled
? mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S
: mConstants.USAGE_STATS_INTERACTION_INTERVAL_PRE_S;
if (isInteraction
&& (!state.hasReportedInteraction()
|| (nowElapsed - state.getInteractionEventTime()) > interactionThreshold)) {
state.setInteractionEventTime(nowElapsed);
String[] packages = app.getPackageList();
if (packages != null) {
for (int i = 0; i < packages.length; i++) {
mService.mUsageStatsService.reportEvent(packages[i], app.userId,
UsageEvents.Event.SYSTEM_INTERACTION);
}
}
}
state.setReportedInteraction(isInteraction);
if (!isInteraction) {
state.setInteractionEventTime(0);
}
}
private void maybeUpdateLastTopTime(ProcessStateRecord state, long nowUptime) {
if (state.getSetProcState() <= PROCESS_STATE_TOP
&& state.getCurProcState() > PROCESS_STATE_TOP) {
state.setLastTopTime(nowUptime);
}
}
/**
* Look for recently inactive apps and mark them idle after a grace period. If idled, stop
* any background services and inform listeners.
*/
@GuardedBy("mService")
void idleUidsLocked() {
final int N = mActiveUids.size();
mService.mHandler.removeMessages(IDLE_UIDS_MSG);
if (N <= 0) {
return;
}
final long nowElapsed = SystemClock.elapsedRealtime();
final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
long nextTime = 0;
if (mService.mLocalPowerManager != null) {
mService.mLocalPowerManager.startUidChanges();
}
for (int i = N - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
final long bgTime = uidRec.getLastBackgroundTime();
if (bgTime > 0 && !uidRec.isIdle()) {
if (bgTime <= maxBgTime) {
EventLogTags.writeAmUidIdle(uidRec.getUid());
synchronized (mProcLock) {
uidRec.setIdle(true);
uidRec.setSetIdle(true);
}
mService.doStopUidLocked(uidRec.getUid(), uidRec);
} else {
if (nextTime == 0 || nextTime > bgTime) {
nextTime = bgTime;
}
}
}
}
if (mService.mLocalPowerManager != null) {
mService.mLocalPowerManager.finishUidChanges();
}
// Also check if there are any apps in cached and background restricted mode,
// if so, kill it if it's been there long enough, or kick off a msg to check
// it later.
if (mService.mConstants.mKillBgRestrictedAndCachedIdle) {
final ArraySet<ProcessRecord> apps = mProcessList.mAppsInBackgroundRestricted;
for (int i = 0, size = apps.size(); i < size; i++) {
// Check to see if needs to be killed.
final long bgTime = mProcessList.killAppIfBgRestrictedAndCachedIdleLocked(
apps.valueAt(i), nowElapsed) - mConstants.BACKGROUND_SETTLE_TIME;
if (bgTime > 0 && (nextTime == 0 || nextTime > bgTime)) {
nextTime = bgTime;
}
}
}
if (nextTime > 0) {
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
}
}
@GuardedBy({"mService", "mProcLock"})
void setAppIdTempAllowlistStateLSP(int uid, boolean onAllowlist) {
boolean changed = false;
for (int i = mActiveUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
if (uidRec.getUid() == uid && uidRec.isCurAllowListed() != onAllowlist) {
uidRec.setCurAllowListed(onAllowlist);
changed = true;
}
}
if (changed) {
updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST);
}
}
@GuardedBy({"mService", "mProcLock"})
void setUidTempAllowlistStateLSP(int uid, boolean onAllowlist) {
boolean changed = false;
final UidRecord uidRec = mActiveUids.get(uid);
if (uidRec != null && uidRec.isCurAllowListed() != onAllowlist) {
uidRec.setCurAllowListed(onAllowlist);
updateOomAdjLSP(OOM_ADJ_REASON_ALLOWLIST);
}
}
@GuardedBy("mService")
void dumpProcessListVariablesLocked(ProtoOutputStream proto) {
proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.getLruSeqLOSP());
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS,
mNumNonCachedProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS,
mNewNumServiceProcs);
}
@GuardedBy("mService")
void dumpSequenceNumbersLocked(PrintWriter pw) {
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.getLruSeqLOSP());
}
@GuardedBy("mService")
void dumpProcCountsLocked(PrintWriter pw) {
pw.println(" mNumNonCachedProcs=" + mNumNonCachedProcs
+ " (" + mProcessList.getLruSizeLOSP() + " total)"
+ " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+ " mNumServiceProcs=" + mNumServiceProcs
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
}
@GuardedBy("mProcLock")
void dumpCachedAppOptimizerSettings(PrintWriter pw) {
mCachedAppOptimizer.dump(pw);
}
@GuardedBy("mService")
void dumpCacheOomRankerSettings(PrintWriter pw) {
mCacheOomRanker.dump(pw);
}
@GuardedBy({"mService", "mProcLock"})
private void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
if (!mCachedAppOptimizer.useFreezer()) {
return;
}
if (app.mOptRecord.isFreezeExempt()) {
return;
}
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
// if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze
if (opt.isFrozen() && opt.shouldNotFreeze()) {
mCachedAppOptimizer.unfreezeAppLSP(app,
CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
return;
}
final ProcessStateRecord state = app.mState;
// Use current adjustment when freezing, set adjustment when unfreezing.
if (state.getCurAdj() >= CACHED_APP_MIN_ADJ && !opt.isFrozen()
&& !opt.shouldNotFreeze()) {
mCachedAppOptimizer.freezeAppAsyncLSP(app);
} else if (state.getSetAdj() < CACHED_APP_MIN_ADJ) {
mCachedAppOptimizer.unfreezeAppLSP(app,
CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
}
}
@GuardedBy("mService")
void unfreezeTemporarily(ProcessRecord app, @OomAdjReason int reason) {
if (!mCachedAppOptimizer.useFreezer()) {
return;
}
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (!opt.isFrozen() && !opt.isPendingFreeze()) {
return;
}
final ArrayList<ProcessRecord> processes = mTmpProcessList;
final ActiveUids uids = mTmpUidRecords;
mTmpProcessSet.add(app);
collectReachableProcessesLocked(mTmpProcessSet, processes, uids);
mTmpProcessSet.clear();
// Now processes contains app's downstream and app
final int size = processes.size();
for (int i = 0; i < size; i++) {
ProcessRecord proc = processes.get(i);
mCachedAppOptimizer.unfreezeTemporarily(proc, reason);
}
processes.clear();
}
@GuardedBy("mService")
void onProcessBeginLocked(@NonNull ProcessRecord app) {
// Empty, the OomAdjusterModernImpl will have an implementation.
}
@GuardedBy("mService")
void onProcessEndLocked(@NonNull ProcessRecord app) {
// Empty, the OomAdjusterModernImpl will have an implementation.
}
/**
* Called when the process state is changed outside of the OomAdjuster.
*/
@GuardedBy("mService")
void onProcessStateChanged(@NonNull ProcessRecord app, int prevProcState) {
// Empty, the OomAdjusterModernImpl will have an implementation.
}
/**
* Called when the oom adj is changed outside of the OomAdjuster.
*/
@GuardedBy("mService")
void onProcessOomAdjChanged(@NonNull ProcessRecord app, int prevAdj) {
// Empty, the OomAdjusterModernImpl will have an implementation.
}
@VisibleForTesting
void resetInternal() {
// Empty, the OomAdjusterModernImpl will have an implementation.
}
@GuardedBy("mService")
protected int getInitialAdj(@NonNull ProcessRecord app) {
return app.mState.getCurAdj();
}
@GuardedBy("mService")
protected int getInitialProcState(@NonNull ProcessRecord app) {
return app.mState.getCurProcState();
}
@GuardedBy("mService")
protected int getInitialCapability(@NonNull ProcessRecord app) {
return app.mState.getCurCapability();
}
@GuardedBy("mService")
protected boolean getInitialIsCurBoundByNonBgRestrictedApp(@NonNull ProcessRecord app) {
// The caller will set the initial value in this implementation.
return app.mState.isCurBoundByNonBgRestrictedApp();
}
}