blob: 3eac4066dc8663353c261854c09e44da567c5ad7 [file] [log] [blame]
/*
* Copyright (C) 2011 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_NONE;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESSES_CHANGED_UI_MSG;
import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESS_DIED_UI_MSG;
import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_DELAY_MS;
import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_MSG;
import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT;
import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MSG;
import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_WITH_WRAPPER;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
import static com.android.server.am.ActivityManagerService.TAG_NETWORK;
import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppProtoEnums;
import android.app.ApplicationExitInfo;
import android.app.ApplicationExitInfo.Reason;
import android.app.ApplicationExitInfo.SubReason;
import android.app.IApplicationThread;
import android.app.IProcessObserver;
import android.app.IUidObserver;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.graphics.Point;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.AppZygote;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManagerInternal;
import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ProcessChangeItem;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;
import com.android.server.wm.WindowProcessController;
import dalvik.system.VMRuntime;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Activity manager code dealing with processes.
*/
public final class ProcessList {
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
// A system property to control if app data isolation is enabled.
static final String ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.zygote.app_data_isolation";
// A system property to control if obb app data isolation is enabled in vold.
static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
"persist.sys.vold_app_data_isolation_enabled";
// OOM adjustments for processes in various states:
// Uninitialized value for any major or minor adj fields
public static final int INVALID_ADJ = -10000;
// Adjustment used in certain places where we don't know it yet.
// (Generally this is something that is going to be cached, but we
// don't know the exact value in the cached range to assign yet.)
public static final int UNKNOWN_ADJ = 1001;
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
public static final int CACHED_APP_MAX_ADJ = 999;
public static final int CACHED_APP_MIN_ADJ = 900;
// This is the oom_adj level that we allow to die first. This cannot be equal to
// CACHED_APP_MAX_ADJ unless processes are actively being assigned an oom_score_adj of
// CACHED_APP_MAX_ADJ.
public static final int CACHED_APP_LMK_FIRST_ADJ = 950;
// Number of levels we have available for different service connection group importance
// levels.
static final int CACHED_APP_IMPORTANCE_LEVELS = 5;
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
public static final int SERVICE_B_ADJ = 800;
// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
// switch back to the previous app. This is important both for recent
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
public static final int PREVIOUS_APP_ADJ = 700;
// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
// because the user interacts with it so much.
public static final int HOME_APP_ADJ = 600;
// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
public static final int SERVICE_ADJ = 500;
// This is a process with a heavy-weight application. It is in the
// background, but we want to try to avoid killing it. Value set in
// system/rootdir/init.rc on startup.
public static final int HEAVY_WEIGHT_APP_ADJ = 400;
// This is a process currently hosting a backup operation. Killing it
// is not entirely fatal but is generally a bad idea.
public static final int BACKUP_APP_ADJ = 300;
// This is a process bound by the system (or other app) that's more important than services but
// not so perceptible that it affects the user immediately if killed.
public static final int PERCEPTIBLE_LOW_APP_ADJ = 250;
// This is a process hosting services that are not perceptible to the user but the
// client (system) binding to it requested to treat it as if it is perceptible and avoid killing
// it if possible.
public static final int PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
public static final int PERCEPTIBLE_APP_ADJ = 200;
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
public static final int VISIBLE_APP_ADJ = 100;
static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
// This is a process that was recently TOP and moved to FGS. Continue to treat it almost
// like a foreground app for a while.
// @see TOP_TO_FGS_GRACE_PERIOD
public static final int PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
// This is the process running the current foreground app. We'd really
// rather not kill it!
public static final int FOREGROUND_APP_ADJ = 0;
// This is a process that the system or a persistent process has bound to,
// and indicated it is important.
public static final int PERSISTENT_SERVICE_ADJ = -700;
// This is a system persistent process, such as telephony. Definitely
// don't want to kill it, but doing so is not completely fatal.
public static final int PERSISTENT_PROC_ADJ = -800;
// The system process runs at the default adjustment.
public static final int SYSTEM_ADJ = -900;
// Special code for native processes that are not being managed by the system (so
// don't have an oom adj assigned by the system).
public static final int NATIVE_ADJ = -1000;
// Memory pages are 4K.
static final int PAGE_SIZE = 4 * 1024;
// Activity manager's version of Process.THREAD_GROUP_BACKGROUND
static final int SCHED_GROUP_BACKGROUND = 0;
// Activity manager's version of Process.THREAD_GROUP_RESTRICTED
static final int SCHED_GROUP_RESTRICTED = 1;
// Activity manager's version of Process.THREAD_GROUP_DEFAULT
static final int SCHED_GROUP_DEFAULT = 2;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
public static final int SCHED_GROUP_TOP_APP = 3;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
// Disambiguate between actual top app and processes bound to the top app
static final int SCHED_GROUP_TOP_APP_BOUND = 4;
// The minimum number of cached apps we want to be able to keep around,
// without empty apps being able to push them out of memory.
static final int MIN_CACHED_APPS = 2;
// Threshold of number of cached+empty where we consider memory critical.
static final int TRIM_CRITICAL_THRESHOLD = 3;
// Threshold of number of cached+empty where we consider memory critical.
static final int TRIM_LOW_THRESHOLD = 5;
/**
* State indicating that there is no need for any blocking for network.
*/
@VisibleForTesting
static final int NETWORK_STATE_NO_CHANGE = 0;
/**
* State indicating that the main thread needs to be informed about the network wait.
*/
@VisibleForTesting
static final int NETWORK_STATE_BLOCK = 1;
/**
* State indicating that any threads waiting for network state to get updated can be unblocked.
*/
@VisibleForTesting
static final int NETWORK_STATE_UNBLOCK = 2;
// If true, then we pass the flag to ART to load the app image startup cache.
private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE =
"persist.device_config.runtime_native.use_app_image_startup_cache";
// The socket path for zygote to send unsolicited msg.
// Must keep sync with com_android_internal_os_Zygote.cpp.
private static final String UNSOL_ZYGOTE_MSG_SOCKET_PATH = "/data/system/unsolzygotesocket";
// Low Memory Killer Daemon command codes.
// These must be kept in sync with lmk_cmd definitions in lmkd.h
//
// LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
// LMK_PROCPRIO <pid> <uid> <prio>
// LMK_PROCREMOVE <pid>
// LMK_PROCPURGE
// LMK_GETKILLCNT
// LMK_SUBSCRIBE
// LMK_PROCKILL
// LMK_UPDATE_PROPS
// LMK_KILL_OCCURRED
// LMK_STATE_CHANGED
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
static final byte LMK_PROCPURGE = 3;
static final byte LMK_GETKILLCNT = 4;
static final byte LMK_SUBSCRIBE = 5;
static final byte LMK_PROCKILL = 6; // Note: this is an unsolicited command
static final byte LMK_UPDATE_PROPS = 7;
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
// Low Memory Killer Daemon command codes.
// These must be kept in sync with async_event_type definitions in lmkd.h
//
static final int LMK_ASYNC_EVENT_KILL = 0;
static final int LMK_ASYNC_EVENT_STAT = 1;
// lmkd reconnect delay in msecs
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
/**
* Apps have no access to the private data directories of any other app, even if the other
* app has made them world-readable.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long APP_DATA_DIRECTORY_ISOLATION = 143937733; // See b/143937733
ActivityManagerService mService = null;
// To kill process groups asynchronously
static KillHandler sKillHandler = null;
static ServiceThread sKillThread = null;
// These are the various interesting memory levels that we will give to
// the OOM killer. Note that the OOM killer only supports 6 slots, so we
// can't give it a different value for every possible kind of process.
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
PERCEPTIBLE_LOW_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_LMK_FIRST_ADJ
};
// These are the low-end OOM level limits. This is appropriate for an
// HVGA or smaller phone with less than 512MB. Values are in KB.
private final int[] mOomMinFreeLow = new int[] {
12288, 18432, 24576,
36864, 43008, 49152
};
// These are the high-end OOM level limits. This is appropriate for a
// 1280x800 or larger screen with around 1GB RAM. Values are in KB.
private final int[] mOomMinFreeHigh = new int[] {
73728, 92160, 110592,
129024, 147456, 184320
};
// The actual OOM killer memory levels we are using.
private final int[] mOomMinFree = new int[mOomAdj.length];
private final long mTotalMemMb;
private long mCachedRestoreLevel;
private boolean mHaveDisplaySize;
private static LmkdConnection sLmkdConnection = null;
private boolean mOomLevelsSet = false;
private boolean mAppDataIsolationEnabled = false;
private boolean mVoldAppDataIsolationEnabled = false;
private ArrayList<String> mAppDataIsolationAllowlistedApps;
/**
* Temporary to avoid allocations. Protected by main lock.
*/
@GuardedBy("mService")
final StringBuilder mStringBuilder = new StringBuilder(256);
/**
* A global counter for generating sequence numbers.
* This value will be used when incrementing sequence numbers in individual uidRecords.
*
* Having a global counter ensures that seq numbers are monotonically increasing for a
* particular uid even when the uidRecord is re-created.
*/
@VisibleForTesting
volatile long mProcStateSeqCounter = 0;
/**
* A global counter for generating sequence numbers to uniquely identify pending process starts.
*/
@GuardedBy("mService")
private long mProcStartSeqCounter = 0;
/**
* Contains {@link ProcessRecord} objects for pending process starts.
*
* Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
*/
@GuardedBy("mService")
final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
/**
* List of running applications, sorted by recent usage.
* The first entry in the list is the least recently used.
*/
@CompositeRWLock({"mService", "mProcLock"})
private final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
/**
* Where in mLruProcesses that the processes hosting activities start.
*/
@CompositeRWLock({"mService", "mProcLock"})
private int mLruProcessActivityStart = 0;
/**
* Where in mLruProcesses that the processes hosting services start.
* This is after (lower index) than mLruProcessesActivityStart.
*/
@CompositeRWLock({"mService", "mProcLock"})
private int mLruProcessServiceStart = 0;
/**
* Current sequence id for process LRU updating.
*/
@CompositeRWLock({"mService", "mProcLock"})
private int mLruSeq = 0;
@CompositeRWLock({"mService", "mProcLock"})
ActiveUids mActiveUids;
/**
* The currently running isolated processes.
*/
@GuardedBy("mService")
final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
/**
* The currently running application zygotes.
*/
@GuardedBy("mService")
final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();
/**
* Managees the {@link android.app.ApplicationExitInfo} records.
*/
@GuardedBy("mAppExitInfoTracker")
final AppExitInfoTracker mAppExitInfoTracker = new AppExitInfoTracker();
/**
* The processes that are forked off an application zygote.
*/
@GuardedBy("mService")
final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
/**
* The list of apps in background restricted mode.
*/
@GuardedBy("mService")
final ArraySet<ProcessRecord> mAppsInBackgroundRestricted = new ArraySet<>();
private PlatformCompat mPlatformCompat = null;
/**
* The server socket in system_server, zygote will connect to it
* in order to send unsolicited messages to system_server.
*/
private LocalSocket mSystemServerSocketForZygote;
/**
* Maximum number of bytes that an incoming unsolicited zygote message could be.
* To be updated if new message type needs to be supported.
*/
private static final int MAX_ZYGOTE_UNSOLICITED_MESSAGE_SIZE = 16;
/**
* The buffer to be used to receive the incoming unsolicited zygote message.
*/
private final byte[] mZygoteUnsolicitedMessage = new byte[MAX_ZYGOTE_UNSOLICITED_MESSAGE_SIZE];
/**
* The buffer to be used to receive the SIGCHLD data, it includes pid/uid/status.
*/
private final int[] mZygoteSigChldMessage = new int[3];
ActivityManagerGlobalLock mProcLock;
final class IsolatedUidRange {
@VisibleForTesting
public final int mFirstUid;
@VisibleForTesting
public final int mLastUid;
@GuardedBy("ProcessList.this.mService")
private final SparseBooleanArray mUidUsed = new SparseBooleanArray();
@GuardedBy("ProcessList.this.mService")
private int mNextUid;
IsolatedUidRange(int firstUid, int lastUid) {
mFirstUid = firstUid;
mLastUid = lastUid;
mNextUid = firstUid;
}
@GuardedBy("ProcessList.this.mService")
int allocateIsolatedUidLocked(int userId) {
int uid;
int stepsLeft = (mLastUid - mFirstUid + 1);
for (int i = 0; i < stepsLeft; ++i) {
if (mNextUid < mFirstUid || mNextUid > mLastUid) {
mNextUid = mFirstUid;
}
uid = UserHandle.getUid(userId, mNextUid);
mNextUid++;
if (!mUidUsed.get(uid, false)) {
mUidUsed.put(uid, true);
return uid;
}
}
return -1;
}
@GuardedBy("ProcessList.this.mService")
void freeIsolatedUidLocked(int uid) {
mUidUsed.delete(uid);
}
};
/**
* A class that allocates ranges of isolated UIDs per application, and keeps track of them.
*/
final class IsolatedUidRangeAllocator {
private final int mFirstUid;
private final int mNumUidRanges;
private final int mNumUidsPerRange;
/**
* We map the uid range [mFirstUid, mFirstUid + mNumUidRanges * mNumUidsPerRange)
* back to an underlying bitset of [0, mNumUidRanges) and allocate out of that.
*/
@GuardedBy("ProcessList.this.mService")
private final BitSet mAvailableUidRanges;
@GuardedBy("ProcessList.this.mService")
private final ProcessMap<IsolatedUidRange> mAppRanges = new ProcessMap<IsolatedUidRange>();
IsolatedUidRangeAllocator(int firstUid, int lastUid, int numUidsPerRange) {
mFirstUid = firstUid;
mNumUidsPerRange = numUidsPerRange;
mNumUidRanges = (lastUid - firstUid + 1) / numUidsPerRange;
mAvailableUidRanges = new BitSet(mNumUidRanges);
// Mark all as available
mAvailableUidRanges.set(0, mNumUidRanges);
}
@GuardedBy("ProcessList.this.mService")
IsolatedUidRange getIsolatedUidRangeLocked(String processName, int uid) {
return mAppRanges.get(processName, uid);
}
@GuardedBy("ProcessList.this.mService")
IsolatedUidRange getOrCreateIsolatedUidRangeLocked(String processName, int uid) {
IsolatedUidRange range = getIsolatedUidRangeLocked(processName, uid);
if (range == null) {
int uidRangeIndex = mAvailableUidRanges.nextSetBit(0);
if (uidRangeIndex < 0) {
// No free range
return null;
}
mAvailableUidRanges.clear(uidRangeIndex);
int actualUid = mFirstUid + uidRangeIndex * mNumUidsPerRange;
range = new IsolatedUidRange(actualUid, actualUid + mNumUidsPerRange - 1);
mAppRanges.put(processName, uid, range);
}
return range;
}
@GuardedBy("ProcessList.this.mService")
void freeUidRangeLocked(ApplicationInfo info) {
// Find the UID range
IsolatedUidRange range = mAppRanges.get(info.processName, info.uid);
if (range != null) {
// Map back to starting uid
final int uidRangeIndex = (range.mFirstUid - mFirstUid) / mNumUidsPerRange;
// Mark it as available in the underlying bitset
mAvailableUidRanges.set(uidRangeIndex);
// And the map
mAppRanges.remove(info.processName, info.uid);
}
}
}
/**
* The available isolated UIDs for processes that are not spawned from an application zygote.
*/
@VisibleForTesting
@GuardedBy("mService")
IsolatedUidRange mGlobalIsolatedUids = new IsolatedUidRange(Process.FIRST_ISOLATED_UID,
Process.LAST_ISOLATED_UID);
/**
* An allocator for isolated UID ranges for apps that use an application zygote.
*/
@VisibleForTesting
@GuardedBy("mService")
IsolatedUidRangeAllocator mAppIsolatedUidRangeAllocator =
new IsolatedUidRangeAllocator(Process.FIRST_APP_ZYGOTE_ISOLATED_UID,
Process.LAST_APP_ZYGOTE_ISOLATED_UID, Process.NUM_UIDS_PER_APP_ZYGOTE);
/**
* Processes that are being forcibly torn down.
*/
@GuardedBy("mService")
final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
/**
* Processes that are killed by us and being waiting for the death notification.
*/
@GuardedBy("mService")
final ProcessMap<ProcessRecord> mDyingProcesses = new ProcessMap<>();
// Self locked with the inner lock within the RemoteCallbackList
private final RemoteCallbackList<IProcessObserver> mProcessObservers =
new RemoteCallbackList<>();
// No lock is needed as it's accessed from single thread only
private ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@GuardedBy("mProcessChangeLock")
private final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>();
@GuardedBy("mProcessChangeLock")
final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>();
/**
* A dedicated lock for dispatching the process changes as it occurs frequently
*/
private final Object mProcessChangeLock = new Object();
/**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
@CompositeRWLock({"mService", "mProcLock"})
private final MyProcessMap mProcessNames = new MyProcessMap();
final class MyProcessMap extends ProcessMap<ProcessRecord> {
@Override
public ProcessRecord put(String name, int uid, ProcessRecord value) {
final ProcessRecord r = super.put(name, uid, value);
mService.mAtmInternal.onProcessAdded(r.getWindowProcessController());
return r;
}
@Override
public ProcessRecord remove(String name, int uid) {
final ProcessRecord r = super.remove(name, uid);
mService.mAtmInternal.onProcessRemoved(name, uid);
return r;
}
}
final class KillHandler extends Handler {
static final int KILL_PROCESS_GROUP_MSG = 4000;
static final int LMKD_RECONNECT_MSG = 4001;
public KillHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case KILL_PROCESS_GROUP_MSG:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case LMKD_RECONNECT_MSG:
if (!sLmkdConnection.connect()) {
Slog.i(TAG, "Failed to connect to lmkd, retry after " +
LMKD_RECONNECT_DELAY_MS + " ms");
// retry after LMKD_RECONNECT_DELAY_MS
sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage(
KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS);
}
break;
default:
super.handleMessage(msg);
}
}
}
/**
* A runner to handle the imperceptible killings.
*/
ImperceptibleKillRunner mImperceptibleKillRunner;
//////////////////// END FIELDS ////////////////////
ProcessList() {
MemInfoReader minfo = new MemInfoReader();
minfo.readMemInfo();
mTotalMemMb = minfo.getTotalSize()/(1024*1024);
updateOomLevels(0, 0, false);
}
void init(ActivityManagerService service, ActiveUids activeUids,
PlatformCompat platformCompat) {
mService = service;
mActiveUids = activeUids;
mPlatformCompat = platformCompat;
mProcLock = service.mProcLock;
// Get this after boot, and won't be changed until it's rebooted, as we don't
// want some apps enabled while some apps disabled
mAppDataIsolationEnabled =
SystemProperties.getBoolean(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true);
mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mAppDataIsolationAllowlistedApps = new ArrayList<>(
SystemConfig.getInstance().getAppDataIsolationWhitelistedApps());
if (sKillHandler == null) {
sKillThread = new ServiceThread(TAG + ":kill",
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
sKillThread.start();
sKillHandler = new KillHandler(sKillThread.getLooper());
sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(),
new LmkdConnection.LmkdConnectionListener() {
@Override
public boolean onConnect(OutputStream ostream) {
Slog.i(TAG, "Connection with lmkd established");
return onLmkdConnect(ostream);
}
@Override
public void onDisconnect() {
Slog.w(TAG, "Lost connection to lmkd");
// start reconnection after delay to let lmkd restart
sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage(
KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS);
}
@Override
public boolean isReplyExpected(ByteBuffer replyBuf,
ByteBuffer dataReceived, int receivedLen) {
// compare the preambule (currently one integer) to check if
// this is the reply packet we are waiting for
return (receivedLen == replyBuf.array().length &&
dataReceived.getInt(0) == replyBuf.getInt(0));
}
@Override
public boolean handleUnsolicitedMessage(DataInputStream inputData,
int receivedLen) {
if (receivedLen < 4) {
return false;
}
try {
switch (inputData.readInt()) {
case LMK_PROCKILL:
if (receivedLen != 12) {
return false;
}
final int pid = inputData.readInt();
final int uid = inputData.readInt();
mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid);
return true;
case LMK_KILL_OCCURRED:
if (receivedLen
< LmkdStatsReporter.KILL_OCCURRED_MSG_SIZE) {
return false;
}
// Note: directly access
// ActiveServices.sNumForegroundServices, do not try to
// hold AMS lock here, otherwise it is a potential deadlock.
Pair<Integer, Integer> foregroundServices =
ActiveServices.sNumForegroundServices.get();
LmkdStatsReporter.logKillOccurred(inputData,
foregroundServices.first,
foregroundServices.second);
return true;
case LMK_STATE_CHANGED:
if (receivedLen
!= LmkdStatsReporter.STATE_CHANGED_MSG_SIZE) {
return false;
}
final int state = inputData.readInt();
LmkdStatsReporter.logStateChanged(state);
return true;
default:
return false;
}
} catch (IOException e) {
Slog.e(TAG, "Invalid buffer data. Failed to log LMK_KILL_OCCURRED");
}
return false;
}
}
);
// Start listening on incoming connections from zygotes.
mSystemServerSocketForZygote = createSystemServerSocketForZygote();
if (mSystemServerSocketForZygote != null) {
sKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
mSystemServerSocketForZygote.getFileDescriptor(),
EVENT_INPUT, this::handleZygoteMessages);
}
mAppExitInfoTracker.init(mService);
mImperceptibleKillRunner = new ImperceptibleKillRunner(sKillThread.getLooper());
}
}
void onSystemReady() {
mAppExitInfoTracker.onSystemReady();
}
void applyDisplaySize(WindowManagerService wm) {
if (!mHaveDisplaySize) {
Point p = new Point();
// TODO(multi-display): Compute based on sum of all connected displays' resolutions.
wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);
if (p.x != 0 && p.y != 0) {
updateOomLevels(p.x, p.y, true);
mHaveDisplaySize = true;
}
}
}
/**
* Get a map of pid and package name that process of that pid Android/data and Android/obb
* directory is not mounted to lowerfs to speed up access.
*/
Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
final Map<Integer, String> pidPackageMap = new HashMap<>();
synchronized (mProcLock) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord record = mLruProcesses.get(i);
if (record.userId != userId || !record.isBindMountPending()) {
continue;
}
final int pid = record.getPid();
// It can happen when app process is starting, but zygote work is not done yet so
// system does not this pid record yet.
if (pid == 0) {
throw new IllegalStateException("Pending process is not started yet,"
+ "retry later");
}
pidPackageMap.put(pid, record.info.packageName);
}
}
return pidPackageMap;
}
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
// Scale buckets from avail memory: at 300MB we use the lowest values to
// 700MB or more for the top values.
float scaleMem = ((float) (mTotalMemMb - 350)) / (700 - 350);
// Scale buckets from screen size.
int minSize = 480 * 800; // 384000
int maxSize = 1280 * 800; // 1024000 230400 870400 .264
float scaleDisp = ((float)(displayWidth * displayHeight) - minSize) / (maxSize - minSize);
if (false) {
Slog.i("XXXXXX", "scaleMem=" + scaleMem);
Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
+ " dh=" + displayHeight);
}
float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
if (scale < 0) scale = 0;
else if (scale > 1) scale = 1;
int minfree_adj = Resources.getSystem().getInteger(
com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);
int minfree_abs = Resources.getSystem().getInteger(
com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
if (false) {
Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
}
final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
for (int i = 0; i < mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
if (is64bit) {
// Increase the high min-free levels for cached processes for 64-bit
if (i == 4) high = (high * 3) / 2;
else if (i == 5) high = (high * 7) / 4;
}
mOomMinFree[i] = (int)(low + ((high - low) * scale));
}
if (minfree_abs >= 0) {
for (int i = 0; i < mOomAdj.length; i++) {
mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
/ mOomMinFree[mOomAdj.length - 1]);
}
}
if (minfree_adj != 0) {
for (int i = 0; i < mOomAdj.length; i++) {
mOomMinFree[i] += (int)((float) minfree_adj * mOomMinFree[i]
/ mOomMinFree[mOomAdj.length - 1]);
if (mOomMinFree[i] < 0) {
mOomMinFree[i] = 0;
}
}
}
// The maximum size we will restore a process from cached to background, when under
// memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
// before killing background processes.
mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024) / 3;
// Ask the kernel to try to keep enough memory free to allocate 3 full
// screen 32bpp buffers without entering direct reclaim.
int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
int reserve_adj = Resources.getSystem().getInteger(
com.android.internal.R.integer.config_extraFreeKbytesAdjust);
int reserve_abs = Resources.getSystem().getInteger(
com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
if (reserve_abs >= 0) {
reserve = reserve_abs;
}
if (reserve_adj != 0) {
reserve += reserve_adj;
if (reserve < 0) {
reserve = 0;
}
}
if (write) {
ByteBuffer buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1));
buf.putInt(LMK_TARGET);
for (int i = 0; i < mOomAdj.length; i++) {
buf.putInt((mOomMinFree[i] * 1024)/PAGE_SIZE);
buf.putInt(mOomAdj[i]);
}
writeLmkd(buf, null);
SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
mOomLevelsSet = true;
}
// GB: 2048,3072,4096,6144,7168,8192
// HC: 8192,10240,12288,14336,16384,20480
}
public static int computeEmptyProcessLimit(int totalProcessLimit) {
return totalProcessLimit/2;
}
private static String buildOomTag(String prefix, String compactPrefix, String space, int val,
int base, boolean compact) {
final int diff = val - base;
if (diff == 0) {
if (compact) {
return compactPrefix;
}
if (space == null) return prefix;
return prefix + space;
}
if (diff < 10) {
return prefix + (compact ? "+" : "+ ") + Integer.toString(diff);
}
return prefix + "+" + Integer.toString(diff);
}
public static String makeOomAdjString(int setAdj, boolean compact) {
if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
return buildOomTag("cch", "cch", " ", setAdj,
ProcessList.CACHED_APP_MIN_ADJ, compact);
} else if (setAdj >= ProcessList.SERVICE_B_ADJ) {
return buildOomTag("svcb ", "svcb", null, setAdj,
ProcessList.SERVICE_B_ADJ, compact);
} else if (setAdj >= ProcessList.PREVIOUS_APP_ADJ) {
return buildOomTag("prev ", "prev", null, setAdj,
ProcessList.PREVIOUS_APP_ADJ, compact);
} else if (setAdj >= ProcessList.HOME_APP_ADJ) {
return buildOomTag("home ", "home", null, setAdj,
ProcessList.HOME_APP_ADJ, compact);
} else if (setAdj >= ProcessList.SERVICE_ADJ) {
return buildOomTag("svc ", "svc", null, setAdj,
ProcessList.SERVICE_ADJ, compact);
} else if (setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
return buildOomTag("hvy ", "hvy", null, setAdj,
ProcessList.HEAVY_WEIGHT_APP_ADJ, compact);
} else if (setAdj >= ProcessList.BACKUP_APP_ADJ) {
return buildOomTag("bkup ", "bkup", null, setAdj,
ProcessList.BACKUP_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
return buildOomTag("prcl ", "prcl", null, setAdj,
ProcessList.PERCEPTIBLE_LOW_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) {
return buildOomTag("prcm ", "prcm", null, setAdj,
ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
return buildOomTag("prcp ", "prcp", null, setAdj,
ProcessList.PERCEPTIBLE_APP_ADJ, compact);
} else if (setAdj >= ProcessList.VISIBLE_APP_ADJ) {
return buildOomTag("vis", "vis", " ", setAdj,
ProcessList.VISIBLE_APP_ADJ, compact);
} else if (setAdj >= ProcessList.FOREGROUND_APP_ADJ) {
return buildOomTag("fg ", "fg ", " ", setAdj,
ProcessList.FOREGROUND_APP_ADJ, compact);
} else if (setAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
return buildOomTag("psvc ", "psvc", null, setAdj,
ProcessList.PERSISTENT_SERVICE_ADJ, compact);
} else if (setAdj >= ProcessList.PERSISTENT_PROC_ADJ) {
return buildOomTag("pers ", "pers", null, setAdj,
ProcessList.PERSISTENT_PROC_ADJ, compact);
} else if (setAdj >= ProcessList.SYSTEM_ADJ) {
return buildOomTag("sys ", "sys", null, setAdj,
ProcessList.SYSTEM_ADJ, compact);
} else if (setAdj >= ProcessList.NATIVE_ADJ) {
return buildOomTag("ntv ", "ntv", null, setAdj,
ProcessList.NATIVE_ADJ, compact);
} else {
return Integer.toString(setAdj);
}
}
public static String makeProcStateString(int curProcState) {
return ActivityManager.procStateToString(curProcState);
}
public static int makeProcStateProtoEnum(int curProcState) {
switch (curProcState) {
case ActivityManager.PROCESS_STATE_PERSISTENT:
return AppProtoEnums.PROCESS_STATE_PERSISTENT;
case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
return AppProtoEnums.PROCESS_STATE_PERSISTENT_UI;
case ActivityManager.PROCESS_STATE_TOP:
return AppProtoEnums.PROCESS_STATE_TOP;
case ActivityManager.PROCESS_STATE_BOUND_TOP:
return AppProtoEnums.PROCESS_STATE_BOUND_TOP;
case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
return AppProtoEnums.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
return AppProtoEnums.PROCESS_STATE_TOP_SLEEPING;
case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
return AppProtoEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
return AppProtoEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
return AppProtoEnums.PROCESS_STATE_TRANSIENT_BACKGROUND;
case ActivityManager.PROCESS_STATE_BACKUP:
return AppProtoEnums.PROCESS_STATE_BACKUP;
case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
return AppProtoEnums.PROCESS_STATE_HEAVY_WEIGHT;
case ActivityManager.PROCESS_STATE_SERVICE:
return AppProtoEnums.PROCESS_STATE_SERVICE;
case ActivityManager.PROCESS_STATE_RECEIVER:
return AppProtoEnums.PROCESS_STATE_RECEIVER;
case ActivityManager.PROCESS_STATE_HOME:
return AppProtoEnums.PROCESS_STATE_HOME;
case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
return AppProtoEnums.PROCESS_STATE_LAST_ACTIVITY;
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY;
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
return AppProtoEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
case ActivityManager.PROCESS_STATE_CACHED_RECENT:
return AppProtoEnums.PROCESS_STATE_CACHED_RECENT;
case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
return AppProtoEnums.PROCESS_STATE_CACHED_EMPTY;
case ActivityManager.PROCESS_STATE_NONEXISTENT:
return AppProtoEnums.PROCESS_STATE_NONEXISTENT;
case ActivityManager.PROCESS_STATE_UNKNOWN:
return AppProtoEnums.PROCESS_STATE_UNKNOWN;
default:
return AppProtoEnums.PROCESS_STATE_UNKNOWN_TO_PROTO;
}
}
public static void appendRamKb(StringBuilder sb, long ramKb) {
for (int j = 0, fact = 10; j < 6; j++, fact *= 10) {
if (ramKb < fact) {
sb.append(' ');
}
}
sb.append(ramKb);
}
// How long after a state change that it is safe to collect PSS without it being dirty.
public static final int PSS_SAFE_TIME_FROM_STATE_CHANGE = 1000;
// The minimum time interval after a state change it is safe to collect PSS.
public static final int PSS_MIN_TIME_FROM_STATE_CHANGE = 15*1000;
// The maximum amount of time we want to go between PSS collections.
public static final int PSS_MAX_INTERVAL = 60*60*1000;
// The minimum amount of time between successive PSS requests for *all* processes.
public static final int PSS_ALL_INTERVAL = 20*60*1000;
// The amount of time until PSS when a persistent process first appears.
private static final int PSS_FIRST_PERSISTENT_INTERVAL = 30*1000;
// The amount of time until PSS when a process first becomes top.
private static final int PSS_FIRST_TOP_INTERVAL = 10*1000;
// The amount of time until PSS when a process first goes into the background.
private static final int PSS_FIRST_BACKGROUND_INTERVAL = 20*1000;
// The amount of time until PSS when a process first becomes cached.
private static final int PSS_FIRST_CACHED_INTERVAL = 20*1000;
// The amount of time until PSS when an important process stays in the same state.
private static final int PSS_SAME_PERSISTENT_INTERVAL = 10*60*1000;
// The amount of time until PSS when the top process stays in the same state.
private static final int PSS_SAME_TOP_INTERVAL = 1*60*1000;
// The amount of time until PSS when an important process stays in the same state.
private static final int PSS_SAME_IMPORTANT_INTERVAL = 10*60*1000;
// The amount of time until PSS when a service process stays in the same state.
private static final int PSS_SAME_SERVICE_INTERVAL = 5*60*1000;
// The amount of time until PSS when a cached process stays in the same state.
private static final int PSS_SAME_CACHED_INTERVAL = 10*60*1000;
// The amount of time until PSS when a persistent process first appears.
private static final int PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL = 1*60*1000;
// The amount of time until PSS when a process first becomes top.
private static final int PSS_FIRST_ASLEEP_TOP_INTERVAL = 20*1000;
// The amount of time until PSS when a process first goes into the background.
private static final int PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL = 30*1000;
// The amount of time until PSS when a process first becomes cached.
private static final int PSS_FIRST_ASLEEP_CACHED_INTERVAL = 1*60*1000;
// The minimum time interval after a state change it is safe to collect PSS.
public static final int PSS_TEST_MIN_TIME_FROM_STATE_CHANGE = 10*1000;
// The amount of time during testing until PSS when a process first becomes top.
private static final int PSS_TEST_FIRST_TOP_INTERVAL = 3*1000;
// The amount of time during testing until PSS when a process first goes into the background.
private static final int PSS_TEST_FIRST_BACKGROUND_INTERVAL = 5*1000;
// The amount of time during testing until PSS when an important process stays in same state.
private static final int PSS_TEST_SAME_IMPORTANT_INTERVAL = 10*1000;
// The amount of time during testing until PSS when a background process stays in same state.
private static final int PSS_TEST_SAME_BACKGROUND_INTERVAL = 15*1000;
public static final int PROC_MEM_PERSISTENT = 0;
public static final int PROC_MEM_TOP = 1;
public static final int PROC_MEM_IMPORTANT = 2;
public static final int PROC_MEM_SERVICE = 3;
public static final int PROC_MEM_CACHED = 4;
public static final int PROC_MEM_NUM = 5;
// Map large set of system process states to
private static final int[] sProcStateToProcMem = new int[] {
PROC_MEM_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT
PROC_MEM_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_TOP
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_BOUND_TOP
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_BACKUP
PROC_MEM_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_RECEIVER
PROC_MEM_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
PROC_MEM_IMPORTANT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_HOME
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_CACHED_RECENT
PROC_MEM_CACHED, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
};
private static final long[] sFirstAwakePssTimes = new long[] {
PSS_FIRST_PERSISTENT_INTERVAL, // PROC_MEM_PERSISTENT
PSS_FIRST_TOP_INTERVAL, // PROC_MEM_TOP
PSS_FIRST_BACKGROUND_INTERVAL, // PROC_MEM_IMPORTANT
PSS_FIRST_BACKGROUND_INTERVAL, // PROC_MEM_SERVICE
PSS_FIRST_CACHED_INTERVAL, // PROC_MEM_CACHED
};
private static final long[] sSameAwakePssTimes = new long[] {
PSS_SAME_PERSISTENT_INTERVAL, // PROC_MEM_PERSISTENT
PSS_SAME_TOP_INTERVAL, // PROC_MEM_TOP
PSS_SAME_IMPORTANT_INTERVAL, // PROC_MEM_IMPORTANT
PSS_SAME_SERVICE_INTERVAL, // PROC_MEM_SERVICE
PSS_SAME_CACHED_INTERVAL, // PROC_MEM_CACHED
};
private static final long[] sFirstAsleepPssTimes = new long[] {
PSS_FIRST_ASLEEP_PERSISTENT_INTERVAL, // PROC_MEM_PERSISTENT
PSS_FIRST_ASLEEP_TOP_INTERVAL, // PROC_MEM_TOP
PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // PROC_MEM_IMPORTANT
PSS_FIRST_ASLEEP_BACKGROUND_INTERVAL, // PROC_MEM_SERVICE
PSS_FIRST_ASLEEP_CACHED_INTERVAL, // PROC_MEM_CACHED
};
private static final long[] sSameAsleepPssTimes = new long[] {
PSS_SAME_PERSISTENT_INTERVAL, // PROC_MEM_PERSISTENT
PSS_SAME_TOP_INTERVAL, // PROC_MEM_TOP
PSS_SAME_IMPORTANT_INTERVAL, // PROC_MEM_IMPORTANT
PSS_SAME_SERVICE_INTERVAL, // PROC_MEM_SERVICE
PSS_SAME_CACHED_INTERVAL, // PROC_MEM_CACHED
};
private static final long[] sTestFirstPssTimes = new long[] {
PSS_TEST_FIRST_TOP_INTERVAL, // PROC_MEM_PERSISTENT
PSS_TEST_FIRST_TOP_INTERVAL, // PROC_MEM_TOP
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // PROC_MEM_IMPORTANT
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // PROC_MEM_SERVICE
PSS_TEST_FIRST_BACKGROUND_INTERVAL, // PROC_MEM_CACHED
};
private static final long[] sTestSamePssTimes = new long[] {
PSS_TEST_SAME_BACKGROUND_INTERVAL, // PROC_MEM_PERSISTENT
PSS_TEST_SAME_IMPORTANT_INTERVAL, // PROC_MEM_TOP
PSS_TEST_SAME_IMPORTANT_INTERVAL, // PROC_MEM_IMPORTANT
PSS_TEST_SAME_BACKGROUND_INTERVAL, // PROC_MEM_SERVICE
PSS_TEST_SAME_BACKGROUND_INTERVAL, // PROC_MEM_CACHED
};
public static final class ProcStateMemTracker {
final int[] mHighestMem = new int[PROC_MEM_NUM];
final float[] mScalingFactor = new float[PROC_MEM_NUM];
int mTotalHighestMem = PROC_MEM_CACHED;
int mPendingMemState;
int mPendingHighestMemState;
float mPendingScalingFactor;
public ProcStateMemTracker() {
for (int i = PROC_MEM_PERSISTENT; i < PROC_MEM_NUM; i++) {
mHighestMem[i] = PROC_MEM_NUM;
mScalingFactor[i] = 1.0f;
}
mPendingMemState = -1;
}
public void dumpLine(PrintWriter pw) {
pw.print("best=");
pw.print(mTotalHighestMem);
pw.print(" (");
boolean needSep = false;
for (int i = 0; i < PROC_MEM_NUM; i++) {
if (mHighestMem[i] < PROC_MEM_NUM) {
if (needSep) {
pw.print(", ");
needSep = false;
}
pw.print(i);
pw.print("=");
pw.print(mHighestMem[i]);
pw.print(" ");
pw.print(mScalingFactor[i]);
pw.print("x");
needSep = true;
}
}
pw.print(")");
if (mPendingMemState >= 0) {
pw.print(" / pending state=");
pw.print(mPendingMemState);
pw.print(" highest=");
pw.print(mPendingHighestMemState);
pw.print(" ");
pw.print(mPendingScalingFactor);
pw.print("x");
}
pw.println();
}
}
public static boolean procStatesDifferForMem(int procState1, int procState2) {
return sProcStateToProcMem[procState1] != sProcStateToProcMem[procState2];
}
public static long minTimeFromStateChange(boolean test) {
return test ? PSS_TEST_MIN_TIME_FROM_STATE_CHANGE : PSS_MIN_TIME_FROM_STATE_CHANGE;
}
public static long computeNextPssTime(int procState, ProcStateMemTracker tracker, boolean test,
boolean sleeping, long now) {
boolean first;
float scalingFactor;
final int memState = sProcStateToProcMem[procState];
if (tracker != null) {
final int highestMemState = memState < tracker.mTotalHighestMem
? memState : tracker.mTotalHighestMem;
first = highestMemState < tracker.mHighestMem[memState];
tracker.mPendingMemState = memState;
tracker.mPendingHighestMemState = highestMemState;
if (first) {
tracker.mPendingScalingFactor = scalingFactor = 1.0f;
} else {
scalingFactor = tracker.mScalingFactor[memState];
tracker.mPendingScalingFactor = scalingFactor * 1.5f;
}
} else {
first = true;
scalingFactor = 1.0f;
}
final long[] table = test
? (first
? sTestFirstPssTimes
: sTestSamePssTimes)
: (first
? (sleeping ? sFirstAsleepPssTimes : sFirstAwakePssTimes)
: (sleeping ? sSameAsleepPssTimes : sSameAwakePssTimes));
long delay = (long)(table[memState] * scalingFactor);
if (delay > PSS_MAX_INTERVAL) {
delay = PSS_MAX_INTERVAL;
}
return now + delay;
}
long getMemLevel(int adjustment) {
for (int i = 0; i < mOomAdj.length; i++) {
if (adjustment <= mOomAdj[i]) {
return mOomMinFree[i] * 1024;
}
}
return mOomMinFree[mOomAdj.length - 1] * 1024;
}
/**
* Return the maximum pss size in kb that we consider a process acceptable to
* restore from its cached state for running in the background when RAM is low.
*/
long getCachedRestoreThresholdKb() {
return mCachedRestoreLevel;
}
/**
* Set the out-of-memory badness adjustment for a process.
* If {@code pid <= 0}, this method will be a no-op.
*
* @param pid The process identifier to set.
* @param uid The uid of the app
* @param amt Adjustment value -- lmkd allows -1000 to +1000
*
* {@hide}
*/
public static void setOomAdj(int pid, int uid, int amt) {
// This indicates that the process is not started yet and so no need to proceed further.
if (pid <= 0) {
return;
}
if (amt == UNKNOWN_ADJ)
return;
long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 * 4);
buf.putInt(LMK_PROCPRIO);
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
writeLmkd(buf, null);
long now = SystemClock.elapsedRealtime();
if ((now-start) > 250) {
Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
+ " = " + amt);
}
}
/*
* {@hide}
*/
public static final void remove(int pid) {
// This indicates that the process is not started yet and so no need to proceed further.
if (pid <= 0) {
return;
}
ByteBuffer buf = ByteBuffer.allocate(4 * 2);
buf.putInt(LMK_PROCREMOVE);
buf.putInt(pid);
writeLmkd(buf, null);
}
/*
* {@hide}
*/
public static final Integer getLmkdKillCount(int min_oom_adj, int max_oom_adj) {
ByteBuffer buf = ByteBuffer.allocate(4 * 3);
ByteBuffer repl = ByteBuffer.allocate(4 * 2);
buf.putInt(LMK_GETKILLCNT);
buf.putInt(min_oom_adj);
buf.putInt(max_oom_adj);
// indicate what we are waiting for
repl.putInt(LMK_GETKILLCNT);
repl.rewind();
if (writeLmkd(buf, repl) && repl.getInt() == LMK_GETKILLCNT) {
return new Integer(repl.getInt());
}
return null;
}
public boolean onLmkdConnect(OutputStream ostream) {
try {
// Purge any previously registered pids
ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(LMK_PROCPURGE);
ostream.write(buf.array(), 0, buf.position());
if (mOomLevelsSet) {
// Reset oom_adj levels
buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1));
buf.putInt(LMK_TARGET);
for (int i = 0; i < mOomAdj.length; i++) {
buf.putInt((mOomMinFree[i] * 1024)/PAGE_SIZE);
buf.putInt(mOomAdj[i]);
}
ostream.write(buf.array(), 0, buf.position());
}
// Subscribe for kill event notifications
buf = ByteBuffer.allocate(4 * 2);
buf.putInt(LMK_SUBSCRIBE);
buf.putInt(LMK_ASYNC_EVENT_KILL);
ostream.write(buf.array(), 0, buf.position());
// Subscribe for stats event notifications
buf = ByteBuffer.allocate(4 * 2);
buf.putInt(LMK_SUBSCRIBE);
buf.putInt(LMK_ASYNC_EVENT_STAT);
ostream.write(buf.array(), 0, buf.position());
} catch (IOException ex) {
return false;
}
return true;
}
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
if (!sLmkdConnection.isConnected()) {
// try to connect immediately and then keep retrying
sKillHandler.sendMessage(
sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG));
// wait for connection retrying 3 times (up to 3 seconds)
if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) {
return false;
}
}
return sLmkdConnection.exchange(buf, repl);
}
static void killProcessGroup(int uid, int pid) {
/* static; one-time init here */
if (sKillHandler != null) {
sKillHandler.sendMessage(
sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
} else {
Slog.w(TAG, "Asked to kill process group before system bringup!");
Process.killProcessGroup(uid, pid);
}
}
@GuardedBy("mService")
ProcessRecord getProcessRecordLocked(String processName, int uid) {
if (uid == SYSTEM_UID) {
// The system gets to run in any process. If there are multiple
// processes with the same uid, just pick the first (this
// should never happen).
SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
if (procs == null) return null;
final int procCount = procs.size();
for (int i = 0; i < procCount; i++) {
final int procUid = procs.keyAt(i);
if (!UserHandle.isCore(procUid) || !UserHandle.isSameUser(procUid, uid)) {
// Don't use an app process or different user process for system component.
continue;
}
return procs.valueAt(i);
}
}
return mProcessNames.get(processName, uid);
}
void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
final long homeAppMem = getMemLevel(HOME_APP_ADJ);
final long cachedAppMem = getMemLevel(CACHED_APP_MIN_ADJ);
outInfo.availMem = getFreeMemory();
outInfo.totalMem = getTotalMemory();
outInfo.threshold = homeAppMem;
outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
outInfo.hiddenAppThreshold = cachedAppMem;
outInfo.secondaryServerThreshold = getMemLevel(SERVICE_ADJ);
outInfo.visibleAppThreshold = getMemLevel(VISIBLE_APP_ADJ);
outInfo.foregroundAppThreshold = getMemLevel(FOREGROUND_APP_ADJ);
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
ProcessRecord findAppProcessLOSP(IBinder app, String reason) {
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
ProcessRecord p = apps.valueAt(ia);
final IApplicationThread thread = p.getThread();
if (thread != null && thread.asBinder() == app) {
return p;
}
}
}
Slog.w(TAG, "Can't find mystery application for " + reason
+ " from pid=" + Binder.getCallingPid()
+ " uid=" + Binder.getCallingUid() + ": " + app);
return null;
}
private void checkSlow(long startTime, String where) {
long now = SystemClock.uptimeMillis();
if ((now - startTime) > 50) {
// If we are taking more than 50ms, log about it.
Slog.w(TAG, "Slow operation: " + (now - startTime) + "ms so far, now at " + where);
}
}
private int[] computeGidsForProcess(int mountExternal, int uid, int[] permGids,
boolean externalStorageAccess) {
ArrayList<Integer> gidList = new ArrayList<>(permGids.length + 5);
final int sharedAppGid = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
final int cacheAppGid = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
final int userGid = UserHandle.getUserGid(UserHandle.getUserId(uid));
// Add shared application and profile GIDs so applications can share some
// resources like shared libraries and access user-wide resources
for (int permGid : permGids) {
gidList.add(permGid);
}
if (sharedAppGid != UserHandle.ERR_GID) {
gidList.add(sharedAppGid);
}
if (cacheAppGid != UserHandle.ERR_GID) {
gidList.add(cacheAppGid);
}
if (userGid != UserHandle.ERR_GID) {
gidList.add(userGid);
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
|| mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
// For DownloadProviders and MTP: To grant access to /sdcard/Android/
// And a special case for the FUSE daemon since it runs an MTP server and should have
// access to Android/
// Note that we must add in the user id, because sdcardfs synthesizes this permission
// based on the user
gidList.add(UserHandle.getUid(UserHandle.getUserId(uid), Process.SDCARD_RW_GID));
// For devices without sdcardfs, these GIDs are needed instead; note that we
// consciously don't add the user_id in the GID, since these apps are anyway
// isolated to only their own user
gidList.add(Process.EXT_DATA_RW_GID);
gidList.add(Process.EXT_OBB_RW_GID);
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
// For devices without sdcardfs, this GID is needed to allow installers access to OBBs
gidList.add(Process.EXT_OBB_RW_GID);
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
// For the FUSE daemon: To grant access to the lower filesystem.
// EmulatedVolumes: /data/media and /mnt/expand/<volume>/data/media
// PublicVolumes: /mnt/media_rw/<volume>
gidList.add(Process.MEDIA_RW_GID);
}
if (externalStorageAccess) {
// Apps with MANAGE_EXTERNAL_STORAGE PERMISSION need the external_storage gid to access
// USB OTG (unreliable) volumes on /mnt/media_rw/<vol name>
gidList.add(Process.EXTERNAL_STORAGE_GID);
}
int[] gidArray = new int[gidList.size()];
for (int i = 0; i < gidArray.length; i++) {
gidArray[i] = gidList.get(i);
}
return gidArray;
}
/**
* @return {@code true} if process start is successful, false otherwise.
*/
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
String abiOverride) {
if (app.isPendingStart()) {
return true;
}
final long startUptime = SystemClock.uptimeMillis();
final long startElapsedTime = SystemClock.elapsedRealtime();
if (app.getPid() > 0 && app.getPid() != ActivityManagerService.MY_PID) {
checkSlow(startUptime, "startProcess: removing from pids map");
mService.removePidLocked(app.getPid(), app);
app.setBindMountPending(false);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
checkSlow(startUptime, "startProcess: done removing from pids map");
app.setPid(0);
app.setStartSeq(0);
}
// Clear any residual death recipient link as the ProcessRecord could be reused.
app.unlinkDeathRecipient();
app.setDyingPid(0);
if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
TAG_PROCESSES,
"startProcessLocked removing on hold: " + app);
mService.mProcessesOnHold.remove(app);
checkSlow(startUptime, "startProcess: starting to update cpu stats");
mService.updateCpuStats();
checkSlow(startUptime, "startProcess: done updating cpu stats");
try {
final int userId = UserHandle.getUserId(app.uid);
try {
AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
int uid = app.uid;
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
boolean externalStorageAccess = false;
if (!app.isolated) {
int[] permGids = null;
try {
checkSlow(startUptime, "startProcess: getting gids from package manager");
final IPackageManager pm = AppGlobals.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName,
MATCH_DIRECT_BOOT_AUTO, app.userId);
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
app.info.packageName);
externalStorageAccess = storageManagerInternal.hasExternalStorageAccess(uid,
app.info.packageName);
if (pm.checkPermission(Manifest.permission.INSTALL_PACKAGES,
app.info.packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
Slog.i(TAG, app.info.packageName + " is exempt from freezer");
app.mOptRecord.setFreezeExempt(true);
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
// Remove any gids needed if the process has been denied permissions.
// NOTE: eventually we should probably have the package manager pre-compute
// this for us?
if (app.processInfo != null && app.processInfo.deniedPermissions != null) {
for (int i = app.processInfo.deniedPermissions.size() - 1; i >= 0; i--) {
int[] denyGids = mService.mPackageManagerInt.getPermissionGids(
app.processInfo.deniedPermissions.valueAt(i), app.userId);
if (denyGids != null) {
for (int gid : denyGids) {
permGids = ArrayUtils.removeInt(permGids, gid);
}
}
}
}
gids = computeGidsForProcess(mountExternal, uid, permGids, externalStorageAccess);
}
app.setMountMode(mountExternal);
checkSlow(startUptime, "startProcess: building args");
if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
uid = 0;
}
int runtimeFlags = 0;
boolean debuggableFlag = (app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
boolean isProfileableByShell = app.info.isProfileableByShell();
boolean isProfileable = app.info.isProfileable();
if (app.isSdkSandbox) {
ApplicationInfo clientInfo = app.getClientInfoForSdkSandbox();
if (clientInfo != null) {
debuggableFlag |= (clientInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
isProfileableByShell |= clientInfo.isProfileableByShell();
isProfileable |= clientInfo.isProfileable();
}
}
if (debuggableFlag) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
// Also turn on CheckJNI for debuggable apps. It's quite
// awkward to turn on otherwise.
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
// Check if the developer does not want ART verification
if (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(),
android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) {
runtimeFlags |= Zygote.DISABLE_VERIFIER;
Slog.w(TAG_PROCESSES, app + ": ART verification disabled");
}
}
// Run the app in safe mode if its manifest requests so or the
// system is booted in safe mode.
if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 || mService.mSafeMode) {
runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
}
if (isProfileableByShell) {
runtimeFlags |= Zygote.PROFILE_FROM_SHELL;
}
if (isProfileable) {
runtimeFlags |= Zygote.PROFILEABLE;
}
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
}
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
if ("1".equals(SystemProperties.get("debug.assert"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
if ("1".equals(SystemProperties.get("debug.ignoreappsignalhandler"))) {
runtimeFlags |= Zygote.DEBUG_IGNORE_APP_SIGNAL_HANDLER;
}
if (mService.mNativeDebuggingApp != null
&& mService.mNativeDebuggingApp.equals(app.processName)) {
// Enable all debug flags required by the native debugger.
runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations
mService.mNativeDebuggingApp = null;
}
if (app.info.isEmbeddedDexUsed()) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
} else if (app.info.isPrivilegedApp()) {
final PackageList pkgList = app.getPkgList();
synchronized (pkgList) {
if (DexManager.isPackageSelectedToRunOob(
pkgList.getPackageListLocked().keySet())) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
}
}
if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
app.info.maybeUpdateHiddenApiEnforcementPolicy(
mService.mHiddenApiBlacklist.getPolicy());
@ApplicationInfo.HiddenApiEnforcementPolicy int policy =
app.info.getHiddenApiEnforcementPolicy();
int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
if (disableTestApiChecks) {
runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
}
}
String useAppImageCache = SystemProperties.get(
PROPERTY_USE_APP_IMAGE_STARTUP_CACHE, "");
// Property defaults to true currently.
if (!TextUtils.isEmpty(useAppImageCache) && !useAppImageCache.equals("false")) {
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
String invokeWith = null;
if (debuggableFlag) {
// Debuggable apps may include a wrapper script with their library directory.
String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
if (new File(wrapperFileName).exists()) {
invokeWith = "/system/bin/logwrapper " + wrapperFileName;
}
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
}
String instructionSet = null;
if (app.info.primaryCpuAbi != null) {
// If ABI override is specified, use the isa derived from the value of ABI override.
// Otherwise, use the isa derived from primary ABI
instructionSet = VMRuntime.getInstructionSet(requiredAbi);
}
app.setGids(gids);
app.setRequiredAbi(requiredAbi);
app.setInstructionSet(instructionSet);
// If this was an external service, the package name and uid in the passed in
// ApplicationInfo have been changed to match those of the calling package;
// that will incorrectly apply compat feature overrides for the calling package instead
// of the defining one.
ApplicationInfo definingAppInfo;
if (hostingRecord.getDefiningPackageName() != null) {
definingAppInfo = new ApplicationInfo(app.info);
definingAppInfo.packageName = hostingRecord.getDefiningPackageName();
definingAppInfo.uid = uid;
} else {
definingAppInfo = app.info;
}
runtimeFlags |= Zygote.getMemorySafetyRuntimeFlags(
definingAppInfo, app.processInfo, instructionSet, mPlatformCompat);
// the per-user SELinux context must be set
if (TextUtils.isEmpty(app.info.seInfoUser)) {
Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",
new IllegalStateException("SELinux tag not defined for "
+ app.info.packageName + " (uid " + app.uid + ")"));
}
final String seInfo = app.info.seInfo
+ (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
final String entryPoint = "android.app.ActivityThread";
return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
instructionSet, invokeWith, startUptime, startElapsedTime);
} catch (RuntimeException e) {
Slog.e(ActivityManagerService.TAG, "Failure starting process " + app.processName, e);
// Something went very wrong while trying to start this process; one
// common case is when the package is frozen due to an active
// upgrade. To recover, clean up any active bookkeeping related to
// starting this process. (We already invoked this method once when
// the package was initially frozen through KILL_APPLICATION_MSG, so
// it doesn't hurt to use it again.)
mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
false, false, true, false, false, app.userId, "start failure");
return false;
}
}
@GuardedBy("mService")
boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startUptime, long startElapsedTime) {
app.setPendingStart(true);
app.setRemoved(false);
synchronized (mProcLock) {
app.setKilledByAm(false);
app.setKilled(false);
}
if (app.getStartSeq() != 0) {
Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+ " with non-zero startSeq:" + app.getStartSeq());
}
if (app.getPid() != 0) {
Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
+ " with non-zero pid:" + app.getPid());
}
app.setDisabledCompatChanges(null);
if (mPlatformCompat != null) {
app.setDisabledCompatChanges(mPlatformCompat.getDisabledChanges(app.info));
}
final long startSeq = ++mProcStartSeqCounter;
app.setStartSeq(startSeq);
app.setStartParams(uid, hostingRecord, seInfo, startUptime, startElapsedTime);
app.setUsingWrapper(invokeWith != null
|| Zygote.getWrapProperty(app.processName) != null);
mPendingStarts.put(startSeq, app);
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
"Posting procStart msg for " + app.toShortString());
mService.mProcStartHandler.post(() -> handleProcessStart(
app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
requiredAbi, instructionSet, invokeWith, startSeq));
return true;
} else {
try {
final Process.ProcessStartResult startResult = startProcess(hostingRecord,
entryPoint, app,
uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
requiredAbi, instructionSet, invokeWith, startUptime);
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
} catch (RuntimeException e) {
Slog.e(ActivityManagerService.TAG, "Failure starting process "
+ app.processName, e);
app.setPendingStart(false);
mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
false, false, true, false, false, app.userId, "start failure");
}
return app.getPid() > 0;
}
}
/**
* Main handler routine to start the given process from the ProcStartHandler.
*
* <p>Note: this function doesn't hold the global AM lock intentionally.</p>
*/
private void handleProcessStart(final ProcessRecord app, final String entryPoint,
final int[] gids, final int runtimeFlags, int zygotePolicyFlags,
final int mountExternal, final String requiredAbi, final String instructionSet,
final String invokeWith, final long startSeq) {
final Runnable startRunnable = () -> {
try {
final Process.ProcessStartResult startResult = startProcess(app.getHostingRecord(),
entryPoint, app, app.getStartUid(), gids, runtimeFlags, zygotePolicyFlags,
mountExternal, app.getSeInfo(), requiredAbi, instructionSet, invokeWith,
app.getStartTime());
synchronized (mService) {
handleProcessStartedLocked(app, startResult, startSeq);
}
} catch (RuntimeException e) {
synchronized (mService) {
Slog.e(ActivityManagerService.TAG, "Failure starting process "
+ app.processName, e);
mPendingStarts.remove(startSeq);
app.setPendingStart(false);
mService.forceStopPackageLocked(app.info.packageName,
UserHandle.getAppId(app.uid),
false, false, true, false, false, app.userId, "start failure");
}
}
};
// Use local reference since we are not using locks here
final ProcessRecord predecessor = app.mPredecessor;
if (predecessor != null && predecessor.getDyingPid() > 0) {
handleProcessStartWithPredecessor(predecessor, startRunnable);
} else {
// Kick off the process start for real.
startRunnable.run();
}
}
/**
* Handle the case where the given process is killed but still not gone, but we'd need to start
* the new instance of it.
*/
private void handleProcessStartWithPredecessor(final ProcessRecord predecessor,
final Runnable successorStartRunnable) {
// If there is a preceding instance of the process, wait for its death with a timeout.
if (predecessor.mSuccessorStartRunnable != null) {
// It's been watched already, this shouldn't happen.
Slog.wtf(TAG, "We've been watching for the death of " + predecessor);
return;
}
predecessor.mSuccessorStartRunnable = successorStartRunnable;
mService.mProcStartHandler.sendMessageDelayed(mService.mProcStartHandler.obtainMessage(
ProcStartHandler.MSG_PROCESS_KILL_TIMEOUT, predecessor),
mService.mConstants.mProcessKillTimeoutMs);
}
static final class ProcStartHandler extends Handler {
static final int MSG_PROCESS_DIED = 1;
static final int MSG_PROCESS_KILL_TIMEOUT = 2;
private final ActivityManagerService mService;
ProcStartHandler(ActivityManagerService service, Looper looper) {
super(looper);
mService = service;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PROCESS_DIED:
mService.mProcessList.handlePredecessorProcDied((ProcessRecord) msg.obj);
break;
case MSG_PROCESS_KILL_TIMEOUT:
synchronized (mService) {
mService.handleProcessStartOrKillTimeoutLocked((ProcessRecord) msg.obj,
/* isKillTimeout */ true);
}
break;
}
}
}
/**
* Called when the dying process we're waiting for is really gone.
*/
private void handlePredecessorProcDied(ProcessRecord app) {
if (DEBUG_PROCESSES) {
Slog.i(TAG, app.toString() + " is really gone now");
}
// Now kick off the subsequent process start if there is any.
final Runnable start = app.mSuccessorStartRunnable;
if (start != null) {
app.mSuccessorStartRunnable = null;
start.run();
}
}
@GuardedBy("mService")
public void killAppZygoteIfNeededLocked(AppZygote appZygote, boolean force) {
final ApplicationInfo appInfo = appZygote.getAppInfo();
ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
if (zygoteProcesses != null && (force || zygoteProcesses.size() == 0)) {
// Only remove if no longer in use now, or forced kill
mAppZygotes.remove(appInfo.processName, appInfo.uid);
mAppZygoteProcesses.remove(appZygote);
mAppIsolatedUidRangeAllocator.freeUidRangeLocked(appInfo);
appZygote.stopZygote();
}
}
@GuardedBy("mService")
private void removeProcessFromAppZygoteLocked(final ProcessRecord app) {
// Free the isolated uid for this process
final IsolatedUidRange appUidRange =
mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(app.info.processName,
app.getHostingRecord().getDefiningUid());
if (appUidRange != null) {
appUidRange.freeIsolatedUidLocked(app.uid);
}
final AppZygote appZygote = mAppZygotes.get(app.info.processName,
app.getHostingRecord().getDefiningUid());
if (appZygote != null) {
ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
zygoteProcesses.remove(app);
if (zygoteProcesses.size() == 0) {
mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG);
if (app.isRemoved()) {
// If we stopped this process because the package hosting it was removed,
// there's no point in delaying the app zygote kill.
killAppZygoteIfNeededLocked(appZygote, false /* force */);
} else {
Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG);
msg.obj = appZygote;
mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS);
}
}
}
}
private AppZygote createAppZygoteForProcessIfNeeded(final ProcessRecord app) {
synchronized (mService) {
// The UID for the app zygote should be the UID of the application hosting
// the service.
final int uid = app.getHostingRecord().getDefiningUid();
AppZygote appZygote = mAppZygotes.get(app.info.processName, uid);
final ArrayList<ProcessRecord> zygoteProcessList;
if (appZygote == null) {
if (DEBUG_PROCESSES) {
Slog.d(TAG_PROCESSES, "Creating new app zygote.");
}
final IsolatedUidRange uidRange =
mAppIsolatedUidRangeAllocator.getIsolatedUidRangeLocked(
app.info.processName, app.getHostingRecord().getDefiningUid());
final int userId = UserHandle.getUserId(uid);
// Create the app-zygote and provide it with the UID-range it's allowed
// to setresuid/setresgid to.
final int firstUid = UserHandle.getUid(userId, uidRange.mFirstUid);
final int lastUid = UserHandle.getUid(userId, uidRange.mLastUid);
ApplicationInfo appInfo = new ApplicationInfo(app.info);
// If this was an external service, the package name and uid in the passed in
// ApplicationInfo have been changed to match those of the calling package;
// that is not what we want for the AppZygote though, which needs to have the
// packageName and uid of the defining application. This is because the
// preloading only makes sense in the context of the defining application,
// not the calling one.
appInfo.packageName = app.getHostingRecord().getDefiningPackageName();
appInfo.uid = uid;
appZygote = new AppZygote(appInfo, app.processInfo, uid, firstUid, lastUid);
mAppZygotes.put(app.info.processName, uid, appZygote);
zygoteProcessList = new ArrayList<ProcessRecord>();
mAppZygoteProcesses.put(appZygote, zygoteProcessList);
} else {
if (DEBUG_PROCESSES) {
Slog.d(TAG_PROCESSES, "Reusing existing app zygote.");
}
mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG, appZygote);
zygoteProcessList = mAppZygoteProcesses.get(appZygote);
}
// Note that we already add the app to mAppZygoteProcesses here;
// this is so that another thread can't come in and kill the zygote
// before we've even tried to start the process. If the process launch
// goes wrong, we'll clean this up in removeProcessNameLocked()
zygoteProcessList.add(app);
return appZygote;
}
}
private Map<String, Pair<String, Long>> getPackageAppDataInfoMap(PackageManagerInternal pmInt,
String[] packages, int uid) {
Map<String, Pair<String, Long>> result = new ArrayMap<>(packages.length);
int userId = UserHandle.getUserId(uid);
for (String packageName : packages) {
final PackageStateInternal packageState = pmInt.getPackageStateInternal(packageName);
if (packageState == null) {
Slog.w(TAG, "Unknown package:" + packageName);
continue;
}
String volumeUuid = packageState.getVolumeUuid();
long inode = packageState.getUserStateOrDefault(userId).getCeDataInode();
if (inode == 0) {
Slog.w(TAG, packageName + " inode == 0 (b/152760674)");
return null;
}
result.put(packageName, Pair.create(volumeUuid, inode));
}
return result;
}
private boolean needsStorageDataIsolation(StorageManagerInternal storageManagerInternal,
ProcessRecord app) {
final int mountMode = app.getMountMode();
return mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid)
&& !storageManagerInternal.isExternalStorageService(app.uid)
// Special mounting mode doesn't need to have data isolation as they won't
// access /mnt/user anyway.
&& mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
&& mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH
&& mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER
&& mountMode != Zygote.MOUNT_EXTERNAL_NONE;
}
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
int mountExternal, String seInfo, String requiredAbi, String instructionSet,
String invokeWith, long startTime) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkSlow(startTime, "startProcess: asking zygote to start proc");
final boolean isTopApp = hostingRecord.isTopApp();
if (isTopApp) {
// Use has-foreground-activities as a temporary hint so the current scheduling
// group won't be lost when the process is attaching. The actual state will be
// refreshed when computing oom-adj.
app.mState.setHasForegroundActivities(true);
}
Map<String, Pair<String, Long>> pkgDataInfoMap;
Map<String, Pair<String, Long>> allowlistedAppDataInfoMap;
boolean bindMountAppStorageDirs = false;
boolean bindMountAppsData = mAppDataIsolationEnabled
&& (UserHandle.isApp(app.uid) || UserHandle.isIsolated(app.uid))
&& mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info);
// Get all packages belongs to the same shared uid. sharedPackages is empty array
// if it doesn't have shared uid.
final PackageManagerInternal pmInt = mService.getPackageManagerInternal();
final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
app.info.packageName, app.userId);
final String[] targetPackagesList = sharedPackages.length == 0
? new String[]{app.info.packageName} : sharedPackages;
final boolean hasAppStorage = hasAppStorage(pmInt, app.info.packageName);
pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
if (pkgDataInfoMap == null) {
// TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
// tmp free pass.
bindMountAppsData = false;
}
// Remove all packages in pkgDataInfoMap from mAppDataIsolationAllowlistedApps, so
// it won't be mounted twice.
final Set<String> allowlistedApps = new ArraySet<>(mAppDataIsolationAllowlistedApps);
for (String pkg : targetPackagesList) {
allowlistedApps.remove(pkg);
}
allowlistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
allowlistedApps.toArray(new String[0]), uid);
if (allowlistedAppDataInfoMap == null) {
// TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
// tmp free pass.
bindMountAppsData = false;
}
if (!hasAppStorage) {
bindMountAppsData = false;
pkgDataInfoMap = null;
allowlistedAppDataInfoMap = null;
}
int userId = UserHandle.getUserId(uid);
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
if (needsStorageDataIsolation(storageManagerInternal, app)) {
// We will run prepareStorageDirs() after we trigger zygote fork, so it won't
// slow down app starting speed as those dirs might not be cached.
if (pkgDataInfoMap != null && storageManagerInternal.isFuseMounted(userId)) {
bindMountAppStorageDirs = true;
} else {
// Fuse is not mounted or inode == 0,
// so we won't mount it in zygote, but resume the mount after unlocking device.
app.setBindMountPending(true);
bindMountAppStorageDirs = false;
}
}
// If it's an isolated process, it should not even mount its own app data directories,
// since it has no access to them anyway.
if (app.isolated) {
pkgDataInfoMap = null;
allowlistedAppDataInfoMap = null;
}
AppStateTracker ast = mService.mServices.mAppStateTracker;
if (ast != null) {
final boolean inBgRestricted = ast.isAppBackgroundRestricted(
app.info.uid, app.info.packageName);
if (inBgRestricted) {
synchronized (mService) {
mAppsInBackgroundRestricted.add(app);
}
}
app.mState.setBackgroundRestricted(inBgRestricted);
}
final Process.ProcessStartResult startResult;
boolean regularZygote = false;
if (hostingRecord.usesWebviewZygote()) {
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
app.getDisabledCompatChanges(),
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else if (hostingRecord.usesAppZygote()) {
final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
// We can't isolate app data and storage data as parent zygote already did that.
startResult = appZygote.getProcess().start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
/*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
false, false,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
} else {
regularZygote = true;
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap,
allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
}
if (!regularZygote) {
// webview and app zygote don't have the permission to create the nodes
if (Process.createProcessGroup(uid, startResult.pid) < 0) {
Slog.e(ActivityManagerService.TAG, "Unable to create process group for "
+ app.processName + " (" + startResult.pid + ")");
}
}
// This runs after Process.start() as this method may block app process starting time
// if dir is not cached. Running this method after Process.start() can make it
// cache the dir asynchronously, so zygote can use it without waiting for it.
if (bindMountAppStorageDirs) {
storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
app.processName);
}
checkSlow(startTime, "startProcess: returned from zygote!");
return startResult;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
private boolean hasAppStorage(PackageManagerInternal pmInt, String packageName) {
final AndroidPackage pkg = pmInt.getPackage(packageName);
if (pkg == null) {
Slog.w(TAG, "Unknown package " + packageName);
return false;
}
final PackageManager.Property noAppStorageProp =
pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
return noAppStorageProp == null || !noAppStorageProp.getBoolean();
}
@GuardedBy("mService")
void startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, int zygotePolicyFlags) {
startProcessLocked(app, hostingRecord, zygotePolicyFlags, null /* abiOverride */);
}
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
int zygotePolicyFlags, String abiOverride) {
return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
abiOverride);
}
@GuardedBy("mService")
ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
boolean isSdkSandbox, int sdkSandboxUid, String sdkSandboxClientAppPackage,
String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(processName, info.uid);
checkSlow(startTime, "startProcess: after getProcessRecord");
if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
// If we are in the background, then check to see if this process
// is bad. If so, we will just silently fail.
if (mService.mAppErrors.isBadProcess(processName, info.uid)) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+ "/" + processName);
return null;
}
} else {
// When the user is explicitly starting a process, then clear its
// crash count so that we won't make it bad until they see at
// least one crash dialog again, and make the process good again
// if it had been bad.
if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+ "/" + processName);
mService.mAppErrors.resetProcessCrashTime(processName, info.uid);
if (mService.mAppErrors.isBadProcess(processName, info.uid)) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
UserHandle.getUserId(info.uid), info.uid,
info.processName);
mService.mAppErrors.clearBadProcess(processName, info.uid);
if (app != null) {
app.mErrorState.setBad(false);
}
}
}
} else {
// If this is an isolated process, it can't re-use an existing process.
app = null;
}
// We don't have to do anything more if:
// (1) There is an existing application record; and
// (2) The caller doesn't think it is dead, OR there is no thread
// object attached to it so we know it couldn't have crashed; and
// (3) There is a pid assigned to it, so it is either starting or
// already running.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
+ " app=" + app + " knownToBeDead=" + knownToBeDead
+ " thread=" + (app != null ? app.getThread() : null)
+ " pid=" + (app != null ? app.getPid() : -1));
ProcessRecord predecessor = null;
if (app != null && app.getPid() > 0) {
if ((!knownToBeDead && !app.isKilled()) || app.getThread() == null) {
// We already have the app running, or are waiting for it to
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName, info.longVersionCode, mService.mProcessStats);
checkSlow(startTime, "startProcess: done, added package to proc");
return app;
}
// An application record is attached to a previous process,
// clean it up now.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
checkSlow(startTime, "startProcess: bad proc running, killing");
ProcessList.killProcessGroup(app.uid, app.getPid());
checkSlow(startTime, "startProcess: done killing old proc");
if (!app.isKilled()) {
// Throw a wtf if it's not killed
Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
} else {
Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
}
// We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
// routine of it yet, but we'd set it as the predecessor of the new process.
predecessor = app;
app = null;
} else if (!isolated) {
// This app may have been removed from process name maps, probably because we killed it
// and did the cleanup before the actual death notification. Check the dying processes.
predecessor = mDyingProcesses.get(processName, info.uid);
if (predecessor != null) {
if (app != null) {
app.mPredecessor = predecessor;
predecessor.mSuccessor = app;
}
Slog.w(TAG_PROCESSES, predecessor.toString() + " is attached to a previous process "
+ predecessor.getDyingPid());
}
}
if (app == null) {
checkSlow(startTime, "startProcess: creating new process record");
app = newProcessRecordLocked(info, processName, isolated, isolatedUid, isSdkSandbox,
sdkSandboxUid, sdkSandboxClientAppPackage, hostingRecord);
if (app == null) {
Slog.w(TAG, "Failed making new process record for "
+ processName + "/" + info.uid + " isolated=" + isolated);
return null;
}
app.mErrorState.setCrashHandler(crashHandler);
app.setIsolatedEntryPoint(entryPoint);
app.setIsolatedEntryPointArgs(entryPointArgs);
if (predecessor != null) {
app.mPredecessor = predecessor;
predecessor.mSuccessor = app;
}
checkSlow(startTime, "startProcess: done creating new process record");
} else {
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName, info.longVersionCode, mService.mProcessStats);
checkSlow(startTime, "startProcess: added package to existing proc");
}
// If the system is not ready yet, then hold off on starting this
// process until it is.
if (!mService.mProcessesReady
&& !mService.isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mService.mProcessesOnHold.contains(app)) {
mService.mProcessesOnHold.add(app);
}
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
"System not ready, putting on hold: " + app);
checkSlow(startTime, "startProcess: returning with proc on hold");
return app;
}
checkSlow(startTime, "startProcess: stepping in to startProcess");
final boolean success =
startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
checkSlow(startTime, "startProcess: done starting proc!");
return success ? app : null;
}
@GuardedBy("mService")
private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
StringBuilder sb = null;
if (app.isKilledByAm()) {
if (sb == null) sb = new StringBuilder();
sb.append("killedByAm=true;");
}
if (mProcessNames.get(app.processName, app.uid) != app) {
if (sb == null) sb = new StringBuilder();
sb.append("No entry in mProcessNames;");
}
if (!app.isPendingStart()) {
if (sb == null) sb = new StringBuilder();
sb.append("pendingStart=false;");
}
if (app.getStartSeq() > expectedStartSeq) {
if (sb == null) sb = new StringBuilder();
sb.append("seq=" + app.getStartSeq() + ",expected=" + expectedStartSeq + ";");
}
try {
AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, app.userId);
} catch (RemoteException e) {
// unexpected; ignore
} catch (SecurityException e) {
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
if (sb == null) sb = new StringBuilder();
sb.append("Package is frozen;");
} else {
// we're not being started async and so should throw to the caller.
throw e;
}
}
return sb == null ? null : sb.toString();
}
@GuardedBy("mService")
private boolean handleProcessStartedLocked(ProcessRecord pending,
Process.ProcessStartResult startResult, long expectedStartSeq) {
// Indicates that this process start has been taken care of.
if (mPendingStarts.get(expectedStartSeq) == null) {
if (pending.getPid() == startResult.pid) {
pending.setUsingWrapper(startResult.usingWrapper);
// TODO: Update already existing clients of usingWrapper
}
return false;
}
return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
expectedStartSeq, false);
}
@GuardedBy("mService")
boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
long expectedStartSeq, boolean procAttached) {
mPendingStarts.remove(expectedStartSeq);
final String reason = isProcStartValidLocked(app, expectedStartSeq);
if (reason != null) {
Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" +
pid
+ ", " + reason);
app.setPendingStart(false);
killProcessQuiet(pid);
Process.killProcessGroup(app.uid, app.getPid());
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_INVALID_START, reason);
return false;
}
mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
checkSlow(app.getStartTime(), "startProcess: done updating battery stats");
EventLog.writeEvent(EventLogTags.AM_PROC_START,
UserHandle.getUserId(app.getStartUid()), pid, app.getStartUid(),
app.processName, app.getHostingRecord().getType(),
app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "");
try {
AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.info.packageName,
app.processName, app.uid, app.getSeInfo(), app.info.sourceDir, pid);
} catch (RemoteException ex) {
// Ignore
}
Watchdog.getInstance().processStarted(app.processName, pid);
checkSlow(app.getStartTime(), "startProcess: building log message");
StringBuilder buf = mStringBuilder;
buf.setLength(0);
buf.append("Start proc ");
buf.append(pid);
buf.append(':');
buf.append(app.processName);
buf.append('/');
UserHandle.formatUid(buf, app.getStartUid());
if (app.getIsolatedEntryPoint() != null) {
buf.append(" [");
buf.append(app.getIsolatedEntryPoint());
buf.append("]");
}
buf.append(" for ");
buf.append(app.getHostingRecord().getType());
if (app.getHostingRecord().getName() != null) {
buf.append(" ");
buf.append(app.getHostingRecord().getName());
}
mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.getStartUid());
synchronized (mProcLock) {
app.setPid(pid);
app.setUsingWrapper(usingWrapper);
app.setPendingStart(false);
}
checkSlow(app.getStartTime(), "startProcess: starting to update pids map");
ProcessRecord oldApp;
synchronized (mService.mPidsSelfLocked) {
oldApp = mService.mPidsSelfLocked.get(pid);
}
// If there is already an app occupying that pid that hasn't been cleaned up
if (oldApp != null && !app.isolated) {
// Clean up anything relating to this pid first
Slog.wtf(TAG, "handleProcessStartedLocked process:" + app.processName
+ " startSeq:" + app.getStartSeq()
+ " pid:" + pid
+ " belongs to another existing app:" + oldApp.processName
+ " startSeq:" + oldApp.getStartSeq());
mService.cleanUpApplicationRecordLocked(oldApp, pid, false, false, -1,
true /*replacingPid*/, false /* fromBinderDied */);
}
mService.addPidLocked(app);
synchronized (mService.mPidsSelfLocked) {
if (!procAttached) {
Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mService.mHandler.sendMessageDelayed(msg, usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
checkSlow(app.getStartTime(), "startProcess: done updating pids map");
return true;
}
@GuardedBy("mService")
void removeLruProcessLocked(ProcessRecord app) {
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui >= 0) {
synchronized (mProcLock) {
if (!app.isKilled()) {
if (app.isPersistent()) {
Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
} else {
Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
if (app.getPid() > 0) {
killProcessQuiet(app.getPid());
ProcessList.killProcessGroup(app.uid, app.getPid());
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_REMOVE_LRU, "hasn't been killed");
} else {
app.setPendingStart(false);
}
}
}
if (lrui < mLruProcessActivityStart) {
mLruProcessActivityStart--;
}
if (lrui < mLruProcessServiceStart) {
mLruProcessServiceStart--;
}
mLruProcesses.remove(lrui);
}
}
mService.removeOomAdjTargetLocked(app, true);
}
@GuardedBy({"mService", "mProcLock"})
boolean killPackageProcessesLSP(String packageName, int appId, int userId, int minOomAdj,
int reasonCode, int subReason, String reason) {
return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */,
reasonCode, subReason, reason);
}
@GuardedBy("mService")
void killAppZygotesLocked(String packageName, int appId, int userId, boolean force) {
// See if there are any app zygotes running for this packageName / UID combination,
// and kill it if so.
final ArrayList<AppZygote> zygotesToKill = new ArrayList<>();
for (SparseArray<AppZygote> appZygotes : mAppZygotes.getMap().values()) {
for (int i = 0; i < appZygotes.size(); ++i) {
final int appZygoteUid = appZygotes.keyAt(i);
if (userId != UserHandle.USER_ALL && UserHandle.getUserId(appZygoteUid) != userId) {
continue;
}
if (appId >= 0 && UserHandle.getAppId(appZygoteUid) != appId) {
continue;
}
final AppZygote appZygote = appZygotes.valueAt(i);
if (packageName != null
&& !packageName.equals(appZygote.getAppInfo().packageName)) {
continue;
}
zygotesToKill.add(appZygote);
}
}
for (AppZygote appZygote : zygotesToKill) {
killAppZygoteIfNeededLocked(appZygote, force);
}
}
@GuardedBy({"mService", "mProcLock"})
boolean killPackageProcessesLSP(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling,
int reasonCode, int subReason, String reason) {
final PackageManagerInternal pm = mService.getPackageManagerInternal();
final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>();
// Remove all processes this package may have touched: all with the
// same UID (except for the system or root user), and all whose name
// matches the package name.
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
ProcessRecord app = apps.valueAt(ia);
if (app.isPersistent() && !evenPersistent) {
// we don't kill persistent processes
continue;
}
if (app.isRemoved()) {
if (doit) {
boolean shouldAllowRestart = false;
if (!uninstalling && packageName != null) {
// This package has a dependency on the given package being stopped,
// while it's not being frozen nor uninstalled, allow to restart it.
shouldAllowRestart = !app.getPkgList().containsKey(packageName)
&& app.getPkgDeps() != null
&& app.getPkgDeps().contains(packageName)
&& app.info != null
&& !pm.isPackageFrozen(app.info.packageName, app.uid,
app.userId);
}
procs.add(new Pair<>(app, shouldAllowRestart));
}
continue;
}
// Skip process if it doesn't meet our oom adj requirement.
if (app.mState.getSetAdj() < minOomAdj) {
// Note it is still possible to have a process with oom adj 0 in the killed
// processes, but it does not mean misjudgment. E.g. a bound service process
// and its client activity process are both in the background, so they are
// collected to be killed. If the client activity is killed first, the service
// may be scheduled to unbind and become an executing service (oom adj 0).
continue;
}
boolean shouldAllowRestart = false;
// If no package is specified, we call all processes under the
// give user id.
if (packageName == null) {
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
continue;
}
// Package has been specified, we want to hit all processes
// that match it. We need to qualify this by the processes
// that are running under the specified app and user ID.
} else {
final boolean isDep = app.getPkgDeps() != null
&& app.getPkgDeps().contains(packageName);
if (!isDep && UserHandle.getAppId(app.uid) != appId) {
continue;
}
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
final boolean isInPkgList = app.getPkgList().containsKey(packageName);
if (!isInPkgList && !isDep) {
continue;
}
if (!isInPkgList && isDep && !uninstalling && app.info != null
&& !pm.isPackageFrozen(app.info.packageName, app.uid, app.userId)) {
// This package has a dependency on the given package being stopped,
// while it's not being frozen nor uninstalled, allow to restart it.
shouldAllowRestart = true;
}
}
// Process has passed all conditions, kill it!
if (!doit) {
return true;
}
if (setRemoved) {
app.setRemoved(true);
}
procs.add(new Pair<>(app, shouldAllowRestart));
}
}
int N = procs.size();
for (int i=0; i<N; i++) {
final Pair<ProcessRecord, Boolean> proc = procs.get(i);
removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
reasonCode, subReason, reason);
}
killAppZygotesLocked(packageName, appId, userId, false /* force */);
mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
return N > 0;
}
@GuardedBy("mService")
boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, int reasonCode, String reason) {
return removeProcessLocked(app, callerWillRestart, allowRestart, reasonCode,
ApplicationExitInfo.SUBREASON_UNKNOWN, reason);
}
@GuardedBy("mService")
boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart,
boolean allowRestart, int reasonCode, int subReason, String reason) {
final String name = app.processName;
final int uid = app.uid;
if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
"Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
ProcessRecord old = mProcessNames.get(name, uid);
if (old != app) {
// This process is no longer active, so nothing to do.
Slog.w(TAG, "Ignoring remove of inactive process: " + app);
return false;
}
removeProcessNameLocked(name, uid);
mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
boolean needRestart = false;
final int pid = app.getPid();
if ((pid > 0 && pid != ActivityManagerService.MY_PID)
|| (pid == 0 && app.isPendingStart())) {
if (pid > 0) {
mService.removePidLocked(pid, app);
app.setBindMountPending(false);
mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
mService.getPackageManagerInternal().removeIsolatedUid(app.uid);
}
}
boolean willRestart = false;
if (app.isPersistent() && !app.isolated) {
if (!callerWillRestart) {
willRestart = true;
} else {
needRestart = true;
}
}
app.killLocked(reason, reasonCode, subReason, true);
mService.handleAppDiedLocked(app, pid, willRestart, allowRestart,
false /* fromBinderDied */);
if (willRestart) {
removeLruProcessLocked(app);
mService.addAppLocked(app.info, null, false, null /* ABI override */,
ZYGOTE_POLICY_FLAG_EMPTY);
}
} else {
mRemovedProcesses.add(app);
}
return needRestart;
}
@GuardedBy("mService")
void addProcessNameLocked(ProcessRecord proc) {
// We shouldn't already have a process under this name, but just in case we
// need to clean up whatever may be there now.
synchronized (mProcLock) {
ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
if (old == proc && proc.isPersistent()) {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
if (old.isKilled()) {
// The old process has been killed, we probably haven't had
// a chance to clean up the old record, just log a warning
Slog.w(TAG, "Existing proc " + old + " was killed "
+ (SystemClock.uptimeMillis() - old.getKillTime())
+ "ms ago when adding " + proc);
} else {
Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
}
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
uidRec = new UidRecord(proc.uid, mService);
// This is the first appearance of the uid, report it now!
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "Creating new process uid: " + uidRec);
}
if (Arrays.binarySearch(mService.mDeviceIdleTempAllowlist,
UserHandle.getAppId(proc.uid)) >= 0
|| mService.mPendingTempAllowlist.indexOfKey(proc.uid) >= 0) {
uidRec.setCurAllowListed(true);
uidRec.setSetAllowListed(true);
}
uidRec.updateHasInternetPermission();
mActiveUids.put(proc.uid, uidRec);
EventLogTags.writeAmUidRunning(uidRec.getUid());
mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
uidRec.getCurCapability());
}
proc.setUidRecord(uidRec);
uidRec.addProcess(proc);
// Reset render thread tid if it was already set, so new process can set it again.
proc.setRenderThreadTid(0);
mProcessNames.put(proc.processName, proc.uid, proc);
}
if (proc.isolated) {
mIsolatedProcesses.put(proc.uid, proc);
}
}
@GuardedBy("mService")
private IsolatedUidRange getOrCreateIsolatedUidRangeLocked(ApplicationInfo info,
HostingRecord hostingRecord) {
if (hostingRecord == null || !hostingRecord.usesAppZygote()) {
// Allocate an isolated UID from the global range
return mGlobalIsolatedUids;
} else {
return mAppIsolatedUidRangeAllocator.getOrCreateIsolatedUidRangeLocked(
info.processName, hostingRecord.getDefiningUid());
}
}
@Nullable
@GuardedBy("mService")
List<Integer> getIsolatedProcessesLocked(int uid) {
List<Integer> ret = null;
for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
final ProcessRecord app = mIsolatedProcesses.valueAt(i);
if (app.info.uid == uid) {
if (ret == null) {
ret = new ArrayList<>();
}
ret.add(app.getPid());
}
}
return ret;
}
@GuardedBy("mService")
ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid, boolean isSdkSandbox, int sdkSandboxUid,
String sdkSandboxClientAppPackage, HostingRecord hostingRecord) {
String proc = customProcess != null ? customProcess : info.processName;
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
if (isSdkSandbox) {
uid = sdkSandboxUid;
}
if (isolated) {
if (isolatedUid == 0) {
IsolatedUidRange uidRange = getOrCreateIsolatedUidRangeLocked(info, hostingRecord);
if (uidRange == null) {
return null;
}
uid = uidRange.allocateIsolatedUidLocked(userId);
if (uid == -1) {
return null;
}
} else {
// Special case for startIsolatedProcess (internal only), where
// the uid of the isolated process is specified by the caller.
uid = isolatedUid;
}
mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(uid, info.uid);
mService.getPackageManagerInternal().addIsolatedUid(uid, info.uid);
// Register the isolated UID with this application so BatteryStats knows to
// attribute resource usage to the application.
//
// NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
// about the process state of the isolated UID *before* it is registered with the
// owning application.
mService.mBatteryStatsService.addIsolatedUid(uid, info.uid);
FrameworkStatsLog.write(FrameworkStatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
FrameworkStatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
}
final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
sdkSandboxClientAppPackage,
hostingRecord.getDefiningUid(), hostingRecord.getDefiningProcessName());
final ProcessStateRecord state = r.mState;
if (!mService.mBooted && !mService.mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
state.setSetSchedGroup(ProcessList.SCHED_GROUP_DEFAULT);
r.setPersistent(true);
state.setMaxAdj(ProcessList.PERSISTENT_PROC_ADJ);
}
if (isolated && isolatedUid != 0) {
// Special case for startIsolatedProcess (internal only) - assume the process
// is required by the system server to prevent it being killed.
state.setMaxAdj(ProcessList.PERSISTENT_SERVICE_ADJ);
}
addProcessNameLocked(r);
return r;
}
@GuardedBy("mService")
ProcessRecord removeProcessNameLocked(final String name, final int uid) {
return removeProcessNameLocked(name, uid, null);
}
@GuardedBy("mService")
ProcessRecord removeProcessNameLocked(final String name, final int uid,
final ProcessRecord expecting) {
ProcessRecord old = mProcessNames.get(name, uid);
final ProcessRecord record = expecting != null ? expecting : old;
synchronized (mProcLock) {
// Only actually remove when the currently recorded value matches the
// record that we expected; if it doesn't match then we raced with a
// newly created process and we don't want to destroy the new one.
if ((expecting == null) || (old == expecting)) {
mProcessNames.remove(name, uid);
}
if (record != null) {
final UidRecord uidRecord = record.getUidRecord();
if (uidRecord != null) {
uidRecord.removeProcess(record);
if (uidRecord.getNumOfProcs() == 0) {
// No more processes using this uid, tell clients it is gone.
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "No more processes in " + uidRecord);
}
mService.enqueueUidChangeLocked(uidRecord, -1,
UidRecord.CHANGE_GONE | UidRecord.CHANGE_PROCSTATE);
EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
mService.mFgsStartTempAllowList.removeUid(record.info.uid);
mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
ActivityManager.PROCESS_CAPABILITY_NONE);
}
record.setUidRecord(null);
}
}
}
mIsolatedProcesses.remove(uid);
mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
// Remove the (expected) ProcessRecord from the app zygote
if (record != null && record.appZygote) {
removeProcessFromAppZygoteLocked(record);
}
mAppsInBackgroundRestricted.remove(record);
return old;
}
/** Call setCoreSettings on all LRU processes, with the new settings. */
@GuardedBy(anyOf = {"mService", "mProcLock"})
void updateCoreSettingsLOSP(Bundle settings) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord processRecord = mLruProcesses.get(i);
final IApplicationThread thread = processRecord.getThread();
try {
if (thread != null) {
thread.setCoreSettings(settings);
}
} catch (RemoteException re) {
/* ignore */
}
}
}
/**
* Kill all background processes except for ones with targetSdk lower than minTargetSdk and
* procstate lower than maxProcState.
* @param minTargetSdk
* @param maxProcState
*/
@GuardedBy({"mService", "mProcLock"})
void killAllBackgroundProcessesExceptLSP(int minTargetSdk, int maxProcState) {
final ArrayList<ProcessRecord> procs = new ArrayList<>();
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
if (app.isRemoved()
|| ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
&& (maxProcState < 0 || app.mState.getSetProcState() > maxProcState))) {
procs.add(app);
}
}
}
final int N = procs.size();
for (int i = 0; i < N; i++) {
removeProcessLocked(procs.get(i), false, true, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_KILL_ALL_BG_EXCEPT, "kill all background except");
}
}
/**
* Call updateTimePrefs on all LRU processes
* @param timePref The time pref to pass to each process
*/
@GuardedBy(anyOf = {"mService", "mProcLock"})
void updateAllTimePrefsLOSP(int timePref) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
final IApplicationThread thread = r.getThread();
if (thread != null) {
try {
thread.updateTimePrefs(timePref);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update preferences for: "
+ r.info.processName);
}
}
}
}
void setAllHttpProxy() {
// Update the HTTP proxy for each application thread.
synchronized (mProcLock) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
IApplicationThread thread = r.getThread();
// Don't dispatch to isolated processes as they can't access ConnectivityManager and
// don't have network privileges anyway. Exclude system server and update it
// separately outside the AMS lock, to avoid deadlock with Connectivity Service.
if (r.getPid() != ActivityManagerService.MY_PID && thread != null && !r.isolated) {
try {
thread.updateHttpProxy();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update http proxy for: "
+ r.info.processName);
}
}
}
}
ActivityThread.updateHttpProxy(mService.mContext);
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
void clearAllDnsCacheLOSP() {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
final IApplicationThread thread = r.getThread();
if (thread != null) {
try {
thread.clearDnsCache();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
}
}
}
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
void handleAllTrustStorageUpdateLOSP() {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
final IApplicationThread thread = r.getThread();
if (thread != null) {
try {
thread.handleTrustStorageUpdate();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to handle trust storage update for: " +
r.info.processName);
}
}
}
}
@GuardedBy({"mService", "mProcLock"})
private int updateLruProcessInternalLSP(ProcessRecord app, long now, int index,
int lruSeq, String what, Object obj, ProcessRecord srcApp) {
app.setLastActivityTime(now);
if (app.hasActivitiesOrRecentTasks()) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui < 0) {
Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
+ what + " " + obj + " from " + srcApp);
return index;
}
if (lrui >= index) {
// Don't want to cause this to move dependent processes *back* in the
// list as if they were less frequently used.
return index;
}
if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
mLruProcesses.remove(lrui);
if (index > 0) {
index--;
}
if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ " in LRU list: " + app);
mLruProcesses.add(index, app);
app.setLruSeq(lruSeq);
return index;
}
/**
* Handle the case where we are inserting a process hosting client activities:
* Make sure any groups have their order match their importance, and take care of
* distributing old clients across other activity processes so they can't spam
* the LRU list. Processing of the list will be restricted by the indices provided,
* and not extend out of them.
*
* @param topApp The app at the top that has just been inserted in to the list.
* @param topI The position in the list where topApp was inserted; this is the start (at the
* top) where we are going to do our processing.
* @param bottomI The last position at which we will be processing; this is the end position
* of whichever section of the LRU list we are in. Nothing past it will be
* touched.
* @param endIndex The current end of the top being processed. Typically topI - 1. That is,
* where we are going to start potentially adjusting other entries in the list.
*/
@GuardedBy({"mService", "mProcLock"})
private void updateClientActivitiesOrderingLSP(final ProcessRecord topApp, final int topI,
final int bottomI, int endIndex) {
final ProcessServiceRecord topPsr = topApp.mServices;
if (topApp.hasActivitiesOrRecentTasks() || topPsr.isTreatedLikeActivity()
|| !topPsr.hasClientActivities()) {
// If this is not a special process that has client activities, then there is
// nothing to do.
return;
}
final int uid = topApp.info.uid;
final int topConnectionGroup = topPsr.getConnectionGroup();
if (topConnectionGroup > 0) {
int endImportance = topPsr.getConnectionImportance();
for (int i = endIndex; i >= bottomI; i--) {
final ProcessRecord subProc = mLruProcesses.get(i);
final ProcessServiceRecord subPsr = subProc.mServices;
final int subConnectionGroup = subPsr.getConnectionGroup();
final int subConnectionImportance = subPsr.getConnectionImportance();
if (subProc.info.uid == uid
&& subConnectionGroup == topConnectionGroup) {
if (i == endIndex && subConnectionImportance >= endImportance) {
// This process is already in the group, and its importance
// is not as strong as the process before it, so keep it
// correctly positioned in the group.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Keeping in-place above " + subProc
+ " endImportance=" + endImportance
+ " group=" + subConnectionGroup
+ " importance=" + subConnectionImportance);
endIndex--;
endImportance = subConnectionImportance;
} else {
// We want to pull this up to be with the rest of the group,
// and order within the group by importance.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Pulling up " + subProc
+ " to position in group with importance="
+ subConnectionImportance);
boolean moved = false;
for (int pos = topI; pos > endIndex; pos--) {
final ProcessRecord posProc = mLruProcesses.get(pos);
if (subConnectionImportance
<= posProc.mServices.getConnectionImportance()) {
mLruProcesses.remove(i);
mLruProcesses.add(pos, subProc);
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Moving " + subProc
+ " from position " + i + " to above " + posProc
+ " @ " + pos);
moved = true;
endIndex--;
break;
}
}
if (!moved) {
// Goes to the end of the group.
mLruProcesses.remove(i);
mLruProcesses.add(endIndex, subProc);
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Moving " + subProc
+ " from position " + i + " to end of group @ "
+ endIndex);
endIndex--;
endImportance = subConnectionImportance;
}
}
}
}
}
// To keep it from spamming the LRU list (by making a bunch of clients),
// we will distribute other entries owned by it to be in-between other apps.
int i = endIndex;
while (i >= bottomI) {
ProcessRecord subProc = mLruProcesses.get(i);
final ProcessServiceRecord subPsr = subProc.mServices;
final int subConnectionGroup = subPsr.getConnectionGroup();
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Looking to spread old procs, at " + subProc + " @ " + i);
if (subProc.info.uid != uid) {
// This is a different app... if we have gone through some of the
// target app, pull this up to be before them. We want to pull up
// one activity process, but any number of non-activity processes.
if (i < endIndex) {
boolean hasActivity = false;
int connUid = 0;
int connGroup = 0;
while (i >= bottomI) {
mLruProcesses.remove(i);
mLruProcesses.add(endIndex, subProc);
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Different app, moving to " + endIndex);
i--;
if (i < bottomI) {
break;
}
subProc = mLruProcesses.get(i);
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Looking at next app at " + i + ": " + subProc);
if (subProc.hasActivitiesOrRecentTasks()
|| subPsr.isTreatedLikeActivity()) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"This is hosting an activity!");
if (hasActivity) {
// Already found an activity, done.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Already found an activity, done");
break;
}
hasActivity = true;
} else if (subPsr.hasClientActivities()) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"This is a client of an activity");
if (hasActivity) {
if (connUid == 0 || connUid != subProc.info.uid) {
// Already have an activity that is not from from a client
// connection or is a different client connection, done.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Already found a different activity: connUid="
+ connUid + " uid=" + subProc.info.uid);
break;
} else if (connGroup == 0 || connGroup != subConnectionGroup) {
// Previously saw a different group or not from a group,
// want to treat these as different things.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Already found a different group: connGroup="
+ connGroup + " group=" + subConnectionGroup);
break;
}
} else {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"This is an activity client! uid="
+ subProc.info.uid + " group=" + subConnectionGroup);
hasActivity = true;
connUid = subProc.info.uid;
connGroup = subConnectionGroup;
}
}
endIndex--;
}
}
// Find the end of the next group of processes for target app. This
// is after any entries of different apps (so we don't change the existing
// relative order of apps) and then after the next last group of processes
// of the target app.
for (endIndex--; endIndex >= bottomI; endIndex--) {
final ProcessRecord endProc = mLruProcesses.get(endIndex);
if (endProc.info.uid == uid) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Found next group of app: " + endProc + " @ "
+ endIndex);
break;
}
}
if (endIndex >= bottomI) {
final ProcessRecord endProc = mLruProcesses.get(endIndex);
final ProcessServiceRecord endPsr = endProc.mServices;
final int endConnectionGroup = endPsr.getConnectionGroup();
for (endIndex--; endIndex >= bottomI; endIndex--) {
final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
final int nextConnectionGroup = nextEndProc.mServices.getConnectionGroup();
if (nextEndProc.info.uid != uid
|| nextConnectionGroup != endConnectionGroup) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Found next group or app: " + nextEndProc + " @ "
+ endIndex + " group=" + nextConnectionGroup);
break;
}
}
}
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Bumping scan position to " + endIndex);
i = endIndex;
} else {
i--;
}
}
}
@GuardedBy("mService")
void updateLruProcessLocked(ProcessRecord app, boolean activityChange, ProcessRecord client) {
final ProcessServiceRecord psr = app.mServices;
final boolean hasActivity = app.hasActivitiesOrRecentTasks() || psr.hasClientActivities()
|| psr.isTreatedLikeActivity();
final boolean hasService = false; // not impl yet. app.services.size() > 0;
if (!activityChange && hasActivity) {
// The process has activities, so we are only allowing activity-based adjustments
// to move it. It should be kept in the front of the list with other
// processes that have activities, and we don't want those to change their
// order except due to activity operations.
return;
}
if (app.getPid() == 0 && !app.isPendingStart()) {
// This process has been killed and its cleanup is done, don't proceed the LRU update.
return;
}
synchronized (mProcLock) {
updateLruProcessLSP(app, client, hasActivity, hasService);
}
}
@GuardedBy({"mService", "mProcLock"})
private void updateLruProcessLSP(ProcessRecord app, ProcessRecord client,
boolean hasActivity, boolean hasService) {
mLruSeq++;
final long now = SystemClock.uptimeMillis();
final ProcessServiceRecord psr = app.mServices;
app.setLastActivityTime(now);
// First a quick reject: if the app is already at the position we will
// put it, then there is nothing to do.
if (hasActivity) {
final int N = mLruProcesses.size();
if (N > 0 && mLruProcesses.get(N - 1) == app) {
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
return;
}
} else {
if (mLruProcessServiceStart > 0
&& mLruProcesses.get(mLruProcessServiceStart-1) == app) {
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
return;
}
}
int lrui = mLruProcesses.lastIndexOf(app);
if (app.isPersistent() && lrui >= 0) {
// We don't care about the position of persistent processes, as long as
// they are in the list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
return;
}
/* In progress: compute new position first, so we can avoid doing work
if the process is not actually going to move. Not yet working.
int addIndex;
int nextIndex;
boolean inActivity = false, inService = false;
if (hasActivity) {
// Process has activities, put it at the very tipsy-top.
addIndex = mLruProcesses.size();
nextIndex = mLruProcessServiceStart;
inActivity = true;
} else if (hasService) {
// Process has services, put it at the top of the service list.
addIndex = mLruProcessActivityStart;
nextIndex = mLruProcessServiceStart;
inActivity = true;
inService = true;
} else {
// Process not otherwise of interest, it goes to the top of the non-service area.
addIndex = mLruProcessServiceStart;
if (client != null) {
int clientIndex = mLruProcesses.lastIndexOf(client);
if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
+ app);
if (clientIndex >= 0 && addIndex > clientIndex) {
addIndex = clientIndex;
}
}
nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
}
Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
+ mLruProcessActivityStart + "): " + app);
*/
if (lrui >= 0) {
if (lrui < mLruProcessActivityStart) {
mLruProcessActivityStart--;
}
if (lrui < mLruProcessServiceStart) {
mLruProcessServiceStart--;
}
/*
if (addIndex > lrui) {
addIndex--;
}
if (nextIndex > lrui) {
nextIndex--;
}
*/
mLruProcesses.remove(lrui);
}
/*
mLruProcesses.add(addIndex, app);
if (inActivity) {
mLruProcessActivityStart++;
}
if (inService) {
mLruProcessActivityStart++;
}
*/
int nextIndex;
int nextActivityIndex = -1;
if (hasActivity) {
final int N = mLruProcesses.size();
nextIndex = mLruProcessServiceStart;
if (!app.hasActivitiesOrRecentTasks() && !psr.isTreatedLikeActivity()
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but below the app that is binding to it.
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Adding to second-top of LRU activity list: " + app
+ " group=" + psr.getConnectionGroup()
+ " importance=" + psr.getConnectionImportance());
int pos = N - 1;
while (pos > mLruProcessActivityStart) {
final ProcessRecord posproc = mLruProcesses.get(pos);
if (posproc.info.uid == app.info.uid) {
// Technically this app could have multiple processes with different
// activities and so we should be looking for the actual process that
// is bound to the target proc... but I don't really care, do you?
break;
}
pos--;
}
mLruProcesses.add(pos, app);
// If this process is part of a group, need to pull up any other processes
// in that group to be with it.
int endIndex = pos - 1;
if (endIndex < mLruProcessActivityStart) {
endIndex = mLruProcessActivityStart;
}
nextActivityIndex = endIndex;
updateClientActivitiesOrderingLSP(app, pos, mLruProcessActivityStart, endIndex);
} else {
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
mLruProcesses.add(app);
nextActivityIndex = mLruProcesses.size() - 1;
}
} else if (hasService) {
// Process has services, put it at the top of the service list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
mLruProcesses.add(mLruProcessActivityStart, app);
nextIndex = mLruProcessServiceStart;
mLruProcessActivityStart++;
} else {
// Process not otherwise of interest, it goes to the top of the non-service area.
int index = mLruProcessServiceStart;
if (client != null) {
// If there is a client, don't allow the process to be moved up higher
// in the list than that client.
int clientIndex = mLruProcesses.lastIndexOf(client);
if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
+ " when updating " + app);
if (clientIndex <= lrui) {
// Don't allow the client index restriction to push it down farther in the
// list than it already is.
clientIndex = lrui;
}
if (clientIndex >= 0 && index > clientIndex) {
index = clientIndex;
}
}
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
mLruProcesses.add(index, app);
nextIndex = index - 1;
mLruProcessActivityStart++;
mLruProcessServiceStart++;
if (index > 1) {
updateClientActivitiesOrderingLSP(app, mLruProcessServiceStart - 1, 0, index - 1);
}
}
app.setLruSeq(mLruSeq);
// If the app is currently using a content provider or service,
// bump those processes as well.
for (int j = psr.numberOfConnections() - 1; j >= 0; j--) {
ConnectionRecord cr = psr.getConnectionAt(j);
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.getLruSeq() != mLruSeq
&& (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
&& !cr.binding.service.app.isPersistent()) {
if (cr.binding.service.app.mServices.hasClientActivities()) {
if (nextActivityIndex >= 0) {
nextActivityIndex = updateLruProcessInternalLSP(cr.binding.service.app,
now,
nextActivityIndex, mLruSeq,
"service connection", cr, app);
}
} else {
nextIndex = updateLruProcessInternalLSP(cr.binding.service.app,
now,
nextIndex, mLruSeq,
"service connection", cr, app);
}
}
}
final ProcessProviderRecord ppr = app.mProviders;
for (int j = ppr.numberOfProviderConnections() - 1; j >= 0; j--) {
ContentProviderRecord cpr = ppr.getProviderConnectionAt(j).provider;
if (cpr.proc != null && cpr.proc.getLruSeq() != mLruSeq && !cpr.proc.isPersistent()) {
nextIndex = updateLruProcessInternalLSP(cpr.proc, now, nextIndex, mLruSeq,
"provider reference", cpr, app);
}
}
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
ProcessRecord getLRURecordForAppLOSP(IApplicationThread thread) {
if (thread == null) {
return null;
}
final IBinder threadBinder = thread.asBinder();
// Find the application record.
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord rec = mLruProcesses.get(i);
final IApplicationThread t = rec.getThread();
if (t != null && t.asBinder() == threadBinder) {
return rec;
}
}
return null;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
boolean haveBackgroundProcessLOSP() {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord rec = mLruProcesses.get(i);
if (rec.getThread() != null
&& rec.mState.getSetProcState() >= PROCESS_STATE_CACHED_ACTIVITY) {
return true;
}
}
return false;
}
private static int procStateToImportance(int procState, int memAdj,
ActivityManager.RunningAppProcessInfo currApp,
int clientTargetSdk) {
int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
procState, clientTargetSdk);
if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
currApp.lru = memAdj;
} else {
currApp.lru = 0;
}
return imp;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
void fillInProcMemInfoLOSP(ProcessRecord app,
ActivityManager.RunningAppProcessInfo outInfo,
int clientTargetSdk) {
outInfo.pid = app.getPid();
outInfo.uid = app.info.uid;
if (app.getWindowProcessController().isHeavyWeightProcess()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
}
if (app.isPersistent()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
}
if (app.hasActivities()) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
}
outInfo.lastTrimLevel = app.mProfile.getTrimMemoryLevel();
final ProcessStateRecord state = app.mState;
int adj = state.getCurAdj();
int procState = state.getCurProcState();
outInfo.importance = procStateToImportance(procState, adj, outInfo,
clientTargetSdk);
outInfo.importanceReasonCode = state.getAdjTypeCode();
outInfo.processState = procState;
outInfo.isFocused = (app == mService.getTopApp());
outInfo.lastActivityTime = app.getLastActivityTime();
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLOSP(boolean allUsers,
int userId, boolean allUids, int callingUid, int clientTargetSdk) {
// Lazy instantiation of list
List<ActivityManager.RunningAppProcessInfo> runList = null;
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mLruProcesses.get(i);
final ProcessStateRecord state = app.mState;
final ProcessErrorStateRecord errState = app.mErrorState;
if ((!allUsers && app.userId != userId)
|| (!allUids && app.uid != callingUid)) {
continue;
}
if ((app.getThread() != null)
&& (!errState.isCrashing() && !errState.isNotResponding())) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.getPid(), app.getPackageList());
fillInProcMemInfoLOSP(app, currApp, clientTargetSdk);
if (state.getAdjSource() instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord) state.getAdjSource()).getPid();
currApp.importanceReasonImportance =
ActivityManager.RunningAppProcessInfo.procStateToImportance(
state.getAdjSourceProcState());
} else if (state.getAdjSource() instanceof ActivityServiceConnectionsHolder) {
final ActivityServiceConnectionsHolder r =
(ActivityServiceConnectionsHolder) state.getAdjSource();
final int pid = r.getActivityPid();
if (pid != -1) {
currApp.importanceReasonPid = pid;
}
}
if (state.getAdjTarget() instanceof ComponentName) {
currApp.importanceReasonComponent = (ComponentName) state.getAdjTarget();
}
//Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
// + " lru=" + currApp.lru);
if (runList == null) {
runList = new ArrayList<>();
}
runList.add(currApp);
}
}
return runList;
}
@GuardedBy(anyOf = {"mService", "mProfileLock"})
int getLruSizeLOSP() {
return mLruProcesses.size();
}
/**
* Return the reference to the LRU list, call this function for read-only access
*/
@GuardedBy(anyOf = {"mService", "mProfileLock"})
ArrayList<ProcessRecord> getLruProcessesLOSP() {
return mLruProcesses;
}
/**
* Return the reference to the LRU list, call this function for read/write access
*/
@GuardedBy({"mService", "mProfileLock"})
ArrayList<ProcessRecord> getLruProcessesLSP() {
return mLruProcesses;
}
/**
* For test only
*/
@VisibleForTesting
@GuardedBy({"mService", "mProfileLock"})
void setLruProcessServiceStartLSP(int pos) {
mLruProcessServiceStart = pos;
}
@GuardedBy(anyOf = {"mService", "mProfileLock"})
int getLruProcessServiceStartLOSP() {
return mLruProcessServiceStart;
}
/**
* Iterate the whole LRU list, invoke the given {@code callback} with each of the ProcessRecord
* in that list.
*
* @param iterateForward If {@code true}, iterate the LRU list from the least recent used
* to most recent used ProcessRecord.
* @param callback The callback interface to accept the current ProcessRecord.
*/
@GuardedBy(anyOf = {"mService", "mProfileLock"})
void forEachLruProcessesLOSP(boolean iterateForward,
@NonNull Consumer<ProcessRecord> callback) {
if (iterateForward) {
for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
callback.accept(mLruProcesses.get(i));
}
} else {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
callback.accept(mLruProcesses.get(i));
}
}
}
/**
* Search in the LRU list, invoke the given {@code callback} with each of the ProcessRecord
* in that list; if the callback returns a non-null object, halt the search, return that
* object as the return value of this search function.
*
* @param iterateForward If {@code true}, iterate the LRU list from the least recent used
* to most recent used ProcessRecord.
* @param callback The callback interface to accept the current ProcessRecord; if it returns
* a non-null object, the search will be halted and this object will be used
* as the return value of this search function.
*/
@GuardedBy(anyOf = {"mService", "mProfileLock"})
<R> R searchEachLruProcessesLOSP(boolean iterateForward,
@NonNull Function<ProcessRecord, R> callback) {
if (iterateForward) {
for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
R r;
if ((r = callback.apply(mLruProcesses.get(i))) != null) {
return r;
}
}
} else {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
R r;
if ((r = callback.apply(mLruProcesses.get(i))) != null) {
return r;
}
}
}
return null;
}
@GuardedBy(anyOf = {"mService", "mProfileLock"})
boolean isInLruListLOSP(ProcessRecord app) {
return mLruProcesses.contains(app);
}
@GuardedBy(anyOf = {"mService", "mProfileLock"})
int getLruSeqLOSP() {
return mLruSeq;
}
@GuardedBy(anyOf = {"mService", "mProfileLock"})
MyProcessMap getProcessNamesLOSP() {
return mProcessNames;
}
@GuardedBy("mService")
void dumpLruListHeaderLocked(PrintWriter pw) {
pw.print(" Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
pw.print(" total, non-act at ");
pw.print(mLruProcesses.size() - mLruProcessActivityStart);
pw.print(", non-svc at ");
pw.print(mLruProcesses.size() - mLruProcessServiceStart);
pw.println("):");
}
@GuardedBy("mService")
private void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc, String prefix) {
pw.print(prefix);
pw.print('#');
if (index < 10) {
pw.print(' ');
}
pw.print(index);
pw.print(": ");
pw.print(makeOomAdjString(proc.mState.getSetAdj(), false));
pw.print(' ');
pw.print(makeProcStateString(proc.mState.getCurProcState()));
pw.print(' ');
ActivityManager.printCapabilitiesSummary(pw, proc.mState.getCurCapability());
pw.print(' ');
pw.print(proc.toShortString());
final ProcessServiceRecord psr = proc.mServices;
if (proc.hasActivitiesOrRecentTasks() || psr.hasClientActivities()
|| psr.isTreatedLikeActivity()) {
pw.print(" act:");
boolean printed = false;
if (proc.hasActivities()) {
pw.print("activities");
printed = true;
}
if (proc.hasRecentTasks()) {
if (printed) {
pw.print("|");
}
pw.print("recents");
printed = true;
}
if (psr.hasClientActivities()) {
if (printed) {
pw.print("|");
}
pw.print("client");
printed = true;
}
if (psr.isTreatedLikeActivity()) {
if (printed) {
pw.print("|");
}
pw.print("treated");
}
}
pw.println();
}
@GuardedBy("mService")
boolean dumpLruLocked(PrintWriter pw, String dumpPackage, String prefix) {
final int lruSize = mLruProcesses.size();
final String innerPrefix;
if (prefix == null) {
pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
innerPrefix = " ";
} else {
boolean haveAny = false;
for (int i = lruSize - 1; i >= 0; i--) {
final ProcessRecord r = mLruProcesses.get(i);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
haveAny = true;
break;
}
if (!haveAny) {
return false;
}
pw.print(prefix);
pw.println("Raw LRU list (dumpsys activity lru):");
innerPrefix = prefix + " ";
}
int i;
boolean first = true;
for (i = lruSize - 1; i >= mLruProcessActivityStart; i--) {
final ProcessRecord r = mLruProcesses.get(i);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
if (first) {
pw.print(innerPrefix);
pw.println("Activities:");
first = false;
}
dumpLruEntryLocked(pw, i, r, innerPrefix);
}
first = true;
for (; i >= mLruProcessServiceStart; i--) {
final ProcessRecord r = mLruProcesses.get(i);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
if (first) {
pw.print(innerPrefix);
pw.println("Services:");
first = false;
}
dumpLruEntryLocked(pw, i, r, innerPrefix);
}
first = true;
for (; i >= 0; i--) {
final ProcessRecord r = mLruProcesses.get(i);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
if (first) {
pw.print(innerPrefix);
pw.println("Other:");
first = false;
}
dumpLruEntryLocked(pw, i, r, innerPrefix);
}
return true;
}
@GuardedBy({"mService", "mProcLock"})
void dumpProcessesLSP(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
boolean needSep = false;
int numPers = 0;
pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
if (dumpAll || dumpPackage != null) {
final int numOfNames = mProcessNames.getMap().size();
for (int ip = 0; ip < numOfNames; ip++) {
SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
for (int ia = 0, size = procs.size(); ia < size; ia++) {
ProcessRecord r = procs.valueAt(ia);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
if (!needSep) {
pw.println(" All known processes:");
needSep = true;
}
pw.print(r.isPersistent() ? " *PERS*" : " *APP*");
pw.print(" UID "); pw.print(procs.keyAt(ia));
pw.print(" "); pw.println(r);
r.dump(pw, " ");
if (r.isPersistent()) {
numPers++;
}
}
}
}
if (mIsolatedProcesses.size() > 0) {
boolean printed = false;
for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
ProcessRecord r = mIsolatedProcesses.valueAt(i);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
if (!printed) {
if (needSep) {
pw.println();
}
pw.println(" Isolated process list (sorted by uid):");
printed = true;
needSep = true;
}
pw.print(" Isolated #"); pw.print(i); pw.print(": ");
pw.println(r);
}
}
needSep = mService.dumpActiveInstruments(pw, dumpPackage, needSep);
if (dumpOomLocked(fd, pw, needSep, args, opti, dumpAll, dumpPackage, false)) {
needSep = true;
}
if (mActiveUids.size() > 0) {
needSep |= mActiveUids.dump(pw, dumpPackage, dumpAppId,
"UID states:", needSep);
}
if (dumpAll) {
needSep |= mService.mUidObserverController.dumpValidateUids(pw,
dumpPackage, dumpAppId, "UID validation:", needSep);
}
if (needSep) {
pw.println();
}
if (dumpLruLocked(pw, dumpPackage, " ")) {
needSep = true;
}
if (getLruSizeLOSP() > 0) {
if (needSep) {
pw.println();
}
dumpLruListHeaderLocked(pw);
dumpProcessOomList(pw, mService, mLruProcesses, " ", "Proc", "PERS", false,
dumpPackage);
needSep = true;
}
mService.dumpOtherProcessesInfoLSP(fd, pw, dumpAll, dumpPackage, dumpAppId, numPers,
needSep);
}
@GuardedBy({"mService", "mProcLock"})
void writeProcessesToProtoLSP(ProtoOutputStream proto, String dumpPackage) {
int numPers = 0;
final int numOfNames = mProcessNames.getMap().size();
for (int ip = 0; ip < numOfNames; ip++) {
SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
for (int ia = 0, size = procs.size(); ia < size; ia++) {
ProcessRecord r = procs.valueAt(ia);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.PROCS,
mLruProcesses.indexOf(r)
);
if (r.isPersistent()) {
numPers++;
}
}
}
for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
ProcessRecord r = mIsolatedProcesses.valueAt(i);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
r.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS,
mLruProcesses.indexOf(r)
);
}
final int dumpAppId = mService.getAppId(dumpPackage);
mActiveUids.dumpProto(proto, dumpPackage, dumpAppId,
ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS);
if (getLruSizeLOSP() > 0) {
long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
int total = getLruSizeLOSP();
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
total - mLruProcessActivityStart);
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT,
total - mLruProcessServiceStart);
writeProcessOomListToProto(proto,
ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, mService,
mLruProcesses, false, dumpPackage);
proto.end(lruToken);
}
mService.writeOtherProcessesInfoToProtoLSP(proto, dumpPackage, dumpAppId, numPers);
}
private static ArrayList<Pair<ProcessRecord, Integer>> sortProcessOomList(
List<ProcessRecord> origList, String dumpPackage) {
ArrayList<Pair<ProcessRecord, Integer>> list =
new ArrayList<Pair<ProcessRecord, Integer>>(origList.size());
for (int i = 0, size = origList.size(); i < size; i++) {
ProcessRecord r = origList.get(i);
if (dumpPackage != null && !r.getPkgList().containsKey(dumpPackage)) {
continue;
}
list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i));
}
Comparator<Pair<ProcessRecord, Integer>> comparator =
new Comparator<Pair<ProcessRecord, Integer>>() {
@Override
public int compare(Pair<ProcessRecord, Integer> object1,
Pair<ProcessRecord, Integer> object2) {
final int adj = object2.first.mState.getSetAdj() - object1.first.mState.getSetAdj();
if (adj != 0) {
return adj;
}
final int procState = object2.first.mState.getSetProcState()
- object1.first.mState.getSetProcState();
if (procState != 0) {
return procState;
}
final int val = object2.second - object1.second;
if (val != 0) {
return val;
}
return 0;
}
};
Collections.sort(list, comparator);
return list;
}
private static boolean writeProcessOomListToProto(ProtoOutputStream proto, long fieldId,
ActivityManagerService service, List<ProcessRecord> origList,
boolean inclDetails, String dumpPackage) {
ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
if (list.isEmpty()) return false;
final long curUptime = SystemClock.uptimeMillis();
for (int i = list.size() - 1; i >= 0; i--) {
ProcessRecord r = list.get(i).first;
final ProcessStateRecord state = r.mState;
final ProcessServiceRecord psr = r.mServices;
long token = proto.start(fieldId);
String oomAdj = makeOomAdjString(state.getSetAdj(), true);
proto.write(ProcessOomProto.PERSISTENT, r.isPersistent());
proto.write(ProcessOomProto.NUM, (origList.size() - 1) - list.get(i).second);
proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
switch (state.getSetSchedGroup()) {
case SCHED_GROUP_BACKGROUND:
schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND;
break;
case SCHED_GROUP_DEFAULT:
schedGroup = ProcessOomProto.SCHED_GROUP_DEFAULT;
break;
case SCHED_GROUP_TOP_APP:
schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP;
break;
case SCHED_GROUP_TOP_APP_BOUND:
schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP_BOUND;
break;
}
if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
}
if (state.hasForegroundActivities()) {
proto.write(ProcessOomProto.ACTIVITIES, true);
} else if (psr.hasForegroundServices()) {
proto.write(ProcessOomProto.SERVICES, true);
}
proto.write(ProcessOomProto.STATE,
makeProcStateProtoEnum(state.getCurProcState()));
proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.mProfile.getTrimMemoryLevel());
r.dumpDebug(proto, ProcessOomProto.PROC);
proto.write(ProcessOomProto.ADJ_TYPE, state.getAdjType());
if (state.getAdjSource() != null || state.getAdjTarget() != null) {
if (state.getAdjTarget() instanceof ComponentName) {
ComponentName cn = (ComponentName) state.getAdjTarget();
cn.dumpDebug(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME);
} else if (state.getAdjTarget() != null) {
proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, state.getAdjTarget().toString());
}
if (state.getAdjSource() instanceof ProcessRecord) {
ProcessRecord p = (ProcessRecord) state.getAdjSource();
p.dumpDebug(proto, ProcessOomProto.ADJ_SOURCE_PROC);
} else if (state.getAdjSource() != null) {
proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, state.getAdjSource().toString());
}
}
if (inclDetails) {
long detailToken = proto.start(ProcessOomProto.DETAIL);
proto.write(ProcessOomProto.Detail.MAX_ADJ, state.getMaxAdj());
proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, state.getCurRawAdj());
proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, state.getSetRawAdj());
proto.write(ProcessOomProto.Detail.CUR_ADJ, state.getCurAdj());
proto.write(ProcessOomProto.Detail.SET_ADJ, state.getSetAdj());
proto.write(ProcessOomProto.Detail.CURRENT_STATE,
makeProcStateProtoEnum(state.getCurProcState()));
proto.write(ProcessOomProto.Detail.SET_STATE,
makeProcStateProtoEnum(state.getSetProcState()));
proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastPss() * 1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastSwapPss() * 1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
r.mProfile.getLastCachedPss() * 1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.CACHED, state.isCached());
proto.write(ProcessOomProto.Detail.EMPTY, state.isEmpty());
proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, psr.hasAboveClient());
if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
long lastCpuTime = r.mProfile.mLastCpuTime.get();
long uptimeSince = curUptime - service.mLastPowerCheckUptime;
if (lastCpuTime != 0 && uptimeSince > 0) {
long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime;
long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME);
proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince);
proto.write(ProcessOomProto.Detail.CpuRunTime.USED_MS, timeUsed);
proto.write(ProcessOomProto.Detail.CpuRunTime.ULTILIZATION,
(100.0 * timeUsed) / uptimeSince);
proto.end(cpuTimeToken);
}
}
proto.end(detailToken);
}
proto.end(token);
}
return true;
}
private static boolean dumpProcessOomList(PrintWriter pw,
ActivityManagerService service, List<ProcessRecord> origList,
String prefix, String normalLabel, String persistentLabel,
boolean inclDetails, String dumpPackage) {
ArrayList<Pair<ProcessRecord, Integer>> list = sortProcessOomList(origList, dumpPackage);
if (list.isEmpty()) return false;
final long curUptime = SystemClock.uptimeMillis();
final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
for (int i = list.size() - 1; i >= 0; i--) {
ProcessRecord r = list.get(i).first;
final ProcessStateRecord state = r.mState;
final ProcessServiceRecord psr = r.mServices;
String oomAdj = makeOomAdjString(state.getSetAdj(), false);
char schedGroup;
switch (state.getSetSchedGroup()) {
case SCHED_GROUP_BACKGROUND:
schedGroup = 'b';
break;
case SCHED_GROUP_DEFAULT:
schedGroup = 'F';
break;
case SCHED_GROUP_TOP_APP:
schedGroup = 'T';
break;
case SCHED_GROUP_RESTRICTED:
schedGroup = 'R';
break;
case SCHED_GROUP_TOP_APP_BOUND:
schedGroup = 'B';
break;
default:
schedGroup = '?';
break;
}
char foreground;
if (state.hasForegroundActivities()) {
foreground = 'A';
} else if (psr.hasForegroundServices()) {
foreground = 'S';
} else {
foreground = ' ';
}
String procState = makeProcStateString(state.getCurProcState());
pw.print(prefix);
pw.print(r.isPersistent() ? persistentLabel : normalLabel);
pw.print(" #");
int num = (origList.size() - 1) - list.get(i).second;
if (num < 10) pw.print(' ');
pw.print(num);
pw.print(": ");
pw.print(oomAdj);
pw.print(' ');
pw.print(schedGroup);
pw.print('/');
pw.print(foreground);
pw.print('/');
pw.print(procState);
pw.print(' ');
ActivityManager.printCapabilitiesSummary(pw, state.getCurCapability());
pw.print(' ');
pw.print(" t:");
if (r.mProfile.getTrimMemoryLevel() < 10) pw.print(' ');
pw.print(r.mProfile.getTrimMemoryLevel());
pw.print(' ');
pw.print(r.toShortString());
pw.print(" (");
pw.print(state.getAdjType());
pw.println(')');
if (state.getAdjSource() != null || state.getAdjTarget() != null) {
pw.print(prefix);
pw.print(" ");
if (state.getAdjTarget() instanceof ComponentName) {
pw.print(((ComponentName) state.getAdjTarget()).flattenToShortString());
} else if (state.getAdjTarget() != null) {
pw.print(state.getAdjTarget().toString());
} else {
pw.print("{null}");
}
pw.print("<=");
if (state.getAdjSource() instanceof ProcessRecord) {
pw.print("Proc{");
pw.print(((ProcessRecord) state.getAdjSource()).toShortString());
pw.println("}");
} else if (state.getAdjSource() != null) {
pw.println(state.getAdjSource().toString());
} else {
pw.println("{null}");
}
}
if (inclDetails) {
pw.print(prefix);
pw.print(" ");
pw.print("oom: max="); pw.print(state.getMaxAdj());
pw.print(" curRaw="); pw.print(state.getCurRawAdj());
pw.print(" setRaw="); pw.print(state.getSetRawAdj());
pw.print(" cur="); pw.print(state.getCurAdj());
pw.print(" set="); pw.println(state.getSetAdj());
pw.print(prefix);
pw.print(" ");
pw.print("state: cur="); pw.print(makeProcStateString(state.getCurProcState()));
pw.print(" set="); pw.print(makeProcStateString(state.getSetProcState()));
pw.print(" lastPss=");
DebugUtils.printSizeValue(pw, r.mProfile.getLastPss() * 1024);
pw.print(" lastSwapPss=");
DebugUtils.printSizeValue(pw, r.mProfile.getLastSwapPss() * 1024);
pw.print(" lastCachedPss=");
DebugUtils.printSizeValue(pw, r.mProfile.getLastCachedPss() * 1024);
pw.println();
pw.print(prefix);
pw.print(" ");
pw.print("cached="); pw.print(state.isCached());
pw.print(" empty="); pw.print(state.isEmpty());
pw.print(" hasAboveClient="); pw.println(psr.hasAboveClient());
if (state.getSetProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
long lastCpuTime = r.mProfile.mLastCpuTime.get();
if (lastCpuTime != 0 && uptimeSince > 0) {
long timeUsed = r.mProfile.mCurCpuTime.get() - lastCpuTime;
pw.print(prefix);
pw.print(" ");
pw.print("run cpu over ");
TimeUtils.formatDuration(uptimeSince, pw);
pw.print(" used ");
TimeUtils.formatDuration(timeUsed, pw);
pw.print(" (");
pw.print((timeUsed * 100) / uptimeSince);
pw.println("%)");
}
}
}
}
return true;
}
private void printOomLevel(PrintWriter pw, String name, int adj) {
pw.print(" ");
if (adj >= 0) {
pw.print(' ');
if (adj < 10) pw.print(' ');
} else {
if (adj > -10) pw.print(' ');
}
pw.print(adj);
pw.print(": ");
pw.print(name);
pw.print(" (");
pw.print(ActivityManagerService.stringifySize(getMemLevel(adj), 1024));
pw.println(")");
}
@GuardedBy("mService")
boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean inclGc) {
if (getLruSizeLOSP() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" OOM levels:");
printOomLevel(pw, "SYSTEM_ADJ", SYSTEM_ADJ);
printOomLevel(pw, "PERSISTENT_PROC_ADJ", PERSISTENT_PROC_ADJ);
printOomLevel(pw, "PERSISTENT_SERVICE_ADJ", PERSISTENT_SERVICE_ADJ);
printOomLevel(pw, "FOREGROUND_APP_ADJ", FOREGROUND_APP_ADJ);
printOomLevel(pw, "VISIBLE_APP_ADJ", VISIBLE_APP_ADJ);
printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", PERCEPTIBLE_APP_ADJ);
printOomLevel(pw, "PERCEPTIBLE_MEDIUM_APP_ADJ", PERCEPTIBLE_MEDIUM_APP_ADJ);
printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", PERCEPTIBLE_LOW_APP_ADJ);
printOomLevel(pw, "BACKUP_APP_ADJ", BACKUP_APP_ADJ);
printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", HEAVY_WEIGHT_APP_ADJ);
printOomLevel(pw, "SERVICE_ADJ", SERVICE_ADJ);
printOomLevel(pw, "HOME_APP_ADJ", HOME_APP_ADJ);
printOomLevel(pw, "PREVIOUS_APP_ADJ", PREVIOUS_APP_ADJ);
printOomLevel(pw, "SERVICE_B_ADJ", SERVICE_B_ADJ);
printOomLevel(pw, "CACHED_APP_MIN_ADJ", CACHED_APP_MIN_ADJ);
printOomLevel(pw, "CACHED_APP_MAX_ADJ", CACHED_APP_MAX_ADJ);
if (needSep) pw.println();
pw.print(" Process OOM control ("); pw.print(getLruSizeLOSP());
pw.print(" total, non-act at ");
pw.print(getLruSizeLOSP() - mLruProcessActivityStart);
pw.print(", non-svc at ");
pw.print(getLruSizeLOSP() - mLruProcessServiceStart);
pw.println("):");
dumpProcessOomList(pw, mService, mLruProcesses,
" ", "Proc", "PERS", true, dumpPackage);
needSep = true;
}
synchronized (mService.mAppProfiler.mProfilerLock) {
mService.mAppProfiler.dumpProcessesToGc(pw, needSep, dumpPackage);
}
pw.println();
mService.mAtmInternal.dumpForOom(pw);
return true;
}
void registerProcessObserver(IProcessObserver observer) {
mProcessObservers.register(observer);
}
void unregisterProcessObserver(IProcessObserver observer) {
mProcessObservers.unregister(observer);
}
void dispatchProcessesChanged() {
int numOfChanges;
synchronized (mProcessChangeLock) {
numOfChanges = mPendingProcessChanges.size();
if (mActiveProcessChanges.length < numOfChanges) {
mActiveProcessChanges = new ProcessChangeItem[numOfChanges];
}
mPendingProcessChanges.toArray(mActiveProcessChanges);
mPendingProcessChanges.clear();
if (DEBUG_PROCESS_OBSERVERS) {
Slog.i(TAG_PROCESS_OBSERVERS,
"*** Delivering " + numOfChanges + " process changes");
}
}
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
i--;
final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
if (observer != null) {
try {
for (int j = 0; j < numOfChanges; j++) {
ProcessChangeItem item = mActiveProcessChanges[j];
if ((item.changes & ProcessChangeItem.CHANGE_ACTIVITIES) != 0) {
if (DEBUG_PROCESS_OBSERVERS) {
Slog.i(TAG_PROCESS_OBSERVERS,
"ACTIVITIES CHANGED pid=" + item.pid + " uid="
+ item.uid + ": " + item.foregroundActivities);
}
observer.onForegroundActivitiesChanged(item.pid, item.uid,
item.foregroundActivities);
}
if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) {
if (DEBUG_PROCESS_OBSERVERS) {
Slog.i(TAG_PROCESS_OBSERVERS,
"FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid="
+ item.uid + ": " + item.foregroundServiceTypes);
}
observer.onForegroundServicesChanged(item.pid, item.uid,
item.foregroundServiceTypes);
}
}
} catch (RemoteException e) {
}
}
}
mProcessObservers.finishBroadcast();
synchronized (mProcessChangeLock) {
for (int j = 0; j < numOfChanges; j++) {
mAvailProcessChanges.add(mActiveProcessChanges[j]);
}
}
}
@GuardedBy("mService")
ProcessChangeItem enqueueProcessChangeItemLocked(int pid, int uid) {
synchronized (mProcessChangeLock) {
int i = mPendingProcessChanges.size() - 1;
ActivityManagerService.ProcessChangeItem item = null;
while (i >= 0) {
item = mPendingProcessChanges.get(i);
if (item.pid == pid) {
if (DEBUG_PROCESS_OBSERVERS) {
Slog.i(TAG_PROCESS_OBSERVERS, "Re-using existing item: " + item);
}
break;
}
i--;
}
if (i < 0) {
// No existing item in pending changes; need a new one.
final int num = mAvailProcessChanges.size();
if (num > 0) {
item = mAvailProcessChanges.remove(num - 1);
if (DEBUG_PROCESS_OBSERVERS) {
Slog.i(TAG_PROCESS_OBSERVERS, "Retrieving available item: " + item);
}
} else {
item = new ActivityManagerService.ProcessChangeItem();
if (DEBUG_PROCESS_OBSERVERS) {
Slog.i(TAG_PROCESS_OBSERVERS, "Allocating new item: " + item);
}
}
item.changes = 0;
item.pid = pid;
item.uid = uid;
if (mPendingProcessChanges.size() == 0) {
if (DEBUG_PROCESS_OBSERVERS) {
Slog.i(TAG_PROCESS_OBSERVERS, "*** Enqueueing dispatch processes changed!");
}
mService.mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG)
.sendToTarget();
}
mPendingProcessChanges.add(item);
}
return item;
}
}
@GuardedBy("mService")
void scheduleDispatchProcessDiedLocked(int pid, int uid) {
synchronized (mProcessChangeLock) {
for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
ProcessChangeItem item = mPendingProcessChanges.get(i);
if (pid > 0 && item.pid == pid) {
mPendingProcessChanges.remove(i);
mAvailProcessChanges.add(item);
}
}
mService.mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, pid, uid,
null).sendToTarget();
}
}
void dispatchProcessDied(int pid, int uid) {
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
i--;
final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
if (observer != null) {
try {
observer.onProcessDied(pid, uid);
} catch (RemoteException e) {
}
}
}
mProcessObservers.finishBroadcast();
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
ArrayList<ProcessRecord> collectProcessesLOSP(int start, boolean allPkgs, String[] args) {
ArrayList<ProcessRecord> procs;
if (args != null && args.length > start
&& args[start].charAt(0) != '-') {
procs = new ArrayList<ProcessRecord>();
int pid = -1;
try {
pid = Integer.parseInt(args[start]);
} catch (NumberFormatException e) {
}
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord proc = mLruProcesses.get(i);
if (proc.getPid() > 0 && proc.getPid() == pid) {
procs.add(proc);
} else if (allPkgs && proc.getPkgList() != null
&& proc.getPkgList().containsKey(args[start])) {
procs.add(proc);
} else if (proc.processName.equals(args[start])) {
procs.add(proc);
}
}
if (procs.size() <= 0) {
return null;
}
} else {
procs = new ArrayList<ProcessRecord>(mLruProcesses);
}
return procs;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
void updateApplicationInfoLOSP(List<String> packagesToUpdate, int userId,
boolean updateFrameworkRes) {
final ArrayList<WindowProcessController> targetProcesses = new ArrayList<>();
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord app = mLruProcesses.get(i);
if (app.getThread() == null) {
continue;
}
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
app.getPkgList().forEachPackage(packageName -> {
if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
try {
final ApplicationInfo ai = AppGlobals.getPackageManager()
.getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
if (ai != null) {
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
PlatformCompatCache.getInstance()
.onApplicationInfoChanged(ai);
}
app.getThread().scheduleApplicationInfoChanged(ai);
targetProcesses.add(app.getWindowProcessController());
}
} catch (RemoteException e) {
Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
packageName, app));
}
}
});
}
mService.mActivityTaskManager.updateAssetConfiguration(targetProcesses, updateFrameworkRes);
}
@GuardedBy("mService")
void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
boolean foundProcess = false;
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
final IApplicationThread thread = r.getThread();
if (thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
try {
for (int index = packages.length - 1; index >= 0 && !foundProcess; index--) {
if (packages[index].equals(r.info.packageName)) {
foundProcess = true;
}
}
thread.dispatchPackageBroadcast(cmd, packages);
} catch (RemoteException ex) {
}
}
}
if (!foundProcess) {
try {
AppGlobals.getPackageManager().notifyPackagesReplacedReceived(packages);
} catch (RemoteException ignored) {
}
}
}
/**
* Returns the uid's process state or {@link ActivityManager#PROCESS_STATE_NONEXISTENT}
* if not running
*/
@GuardedBy(anyOf = {"mService", "mProcLock"})
int getUidProcStateLOSP(int uid) {
UidRecord uidRec = mActiveUids.get(uid);
return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
}
/**
* Returns the uid's process capability or {@link ActivityManager#PROCESS_CAPABILITY_NONE}
* if not running
*/
@GuardedBy(anyOf = {"mService", "mProcLock"})
@ProcessCapability int getUidProcessCapabilityLOSP(int uid) {
UidRecord uidRec = mActiveUids.get(uid);
return uidRec == null ? PROCESS_CAPABILITY_NONE : uidRec.getCurCapability();
}
/** Returns the UidRecord for the given uid, if it exists. */
@GuardedBy(anyOf = {"mService", "mProcLock"})
UidRecord getUidRecordLOSP(int uid) {
return mActiveUids.get(uid);
}
/**
* Call {@link ActivityManagerService#doStopUidLocked}
* (which will also stop background services) for all idle UIDs.
*/
@GuardedBy("mService")
void doStopUidForIdleUidsLocked() {
final int size = mActiveUids.size();
for (int i = 0; i < size; i++) {
final int uid = mActiveUids.keyAt(i);
if (UserHandle.isCore(uid)) {
continue;
}
final UidRecord uidRec = mActiveUids.valueAt(i);
if (!uidRec.isIdle()) {
continue;
}
mService.doStopUidLocked(uidRec.getUid(), uidRec);
}
}
/**
* Checks if the uid is coming from background to foreground or vice versa and returns
* appropriate block state based on this.
*
* @return blockState based on whether the uid is coming from background to foreground or
* vice versa. If bg->fg or fg->bg, then {@link #NETWORK_STATE_BLOCK} or
* {@link #NETWORK_STATE_UNBLOCK} respectively, otherwise
* {@link #NETWORK_STATE_NO_CHANGE}.
*/
@VisibleForTesting
@GuardedBy(anyOf = {"mService", "mProcLock"})
int getBlockStateForUid(UidRecord uidRec) {
// Denotes whether uid's process state is currently allowed network access.
final boolean isAllowed =
isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState(),
uidRec.getCurCapability())
|| isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
// Denotes whether uid's process state was previously allowed network access.
final boolean wasAllowed =
isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState(),
uidRec.getSetCapability())
|| isProcStateAllowedWhileOnRestrictBackground(uidRec.getSetProcState());
// When the uid is coming to foreground, AMS should inform the app thread that it should
// block for the network rules to get updated before launching an activity.
if (!wasAllowed && isAllowed) {
return NETWORK_STATE_BLOCK;
}
// When the uid is going to background, AMS should inform the app thread that if an
// activity launch is blocked for the network rules to get updated, it should be unblocked.
if (wasAllowed && !isAllowed) {
return NETWORK_STATE_UNBLOCK;
}
return NETWORK_STATE_NO_CHANGE;
}
/**
* Increments the {@link UidRecord#curProcStateSeq} for all uids using global seq counter
* {@link ProcessList#mProcStateSeqCounter} and checks if any uid is coming
* from background to foreground or vice versa and if so, notifies the app if it needs to block.
*/
@VisibleForTesting
@GuardedBy(anyOf = {"mService", "mProcLock"})
void incrementProcStateSeqAndNotifyAppsLOSP(ActiveUids activeUids) {
for (int i = activeUids.size() - 1; i >= 0; --i) {
final UidRecord uidRec = activeUids.valueAt(i);
uidRec.curProcStateSeq = getNextProcStateSeq();
}
if (mService.mConstants.mNetworkAccessTimeoutMs <= 0) {
return;
}
// Used for identifying which uids need to block for network.
ArrayList<Integer> blockingUids = null;
for (int i = activeUids.size() - 1; i >= 0; --i) {
final UidRecord uidRec = activeUids.valueAt(i);
// If the network is not restricted for uid, then nothing to do here.
if (!mService.mInjector.isNetworkRestrictedForUid(uidRec.getUid())) {
continue;
}
if (!UserHandle.isApp(uidRec.getUid()) || !uidRec.hasInternetPermission) {
continue;
}
// If process state and capabilities are not changed, then there's nothing to do.
if (uidRec.getSetProcState() == uidRec.getCurProcState()
&& uidRec.getSetCapability() == uidRec.getCurCapability()) {
continue;
}
final int blockState = getBlockStateForUid(uidRec);
// No need to inform the app when the blockState is NETWORK_STATE_NO_CHANGE as
// there's nothing the app needs to do in this scenario.
if (blockState == NETWORK_STATE_NO_CHANGE) {
continue;
}
synchronized (uidRec.networkStateLock) {
if (blockState == NETWORK_STATE_BLOCK) {
if (blockingUids == null) {
blockingUids = new ArrayList<>();
}
blockingUids.add(uidRec.getUid());
} else {
if (DEBUG_NETWORK) {
Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
+ " threads for uid: " + uidRec);
}
if (uidRec.procStateSeqWaitingForNetwork != 0) {
uidRec.networkStateLock.notifyAll();
}
}
}
}
// There are no uids that need to block, so nothing more to do.
if (blockingUids == null) {
return;
}
for (int i = mLruProcesses.size() - 1; i >= 0; --i) {
final ProcessRecord app = mLruProcesses.get(i);
if (!blockingUids.contains(app.uid)) {
continue;
}
final IApplicationThread thread = app.getThread();
if (!app.isKilledByAm() && thread != null) {
final UidRecord uidRec = getUidRecordLOSP(app.uid);
try {
if (DEBUG_NETWORK) {
Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: "
+ uidRec);
}
if (uidRec != null) {
thread.setNetworkBlockSeq(uidRec.curProcStateSeq);
}
} catch (RemoteException ignored) {
}
}
}
}
long getNextProcStateSeq() {
return ++mProcStateSeqCounter;
}
/**
* Create a server socket in system_server, zygote will connect to it
* in order to send unsolicited messages to system_server.
*/
private LocalSocket createSystemServerSocketForZygote() {
// The file system entity for this socket is created with 0666 perms, owned
// by system:system. selinux restricts things so that only zygotes can
// access it.
final File socketFile = new File(UNSOL_ZYGOTE_MSG_SOCKET_PATH);
if (socketFile.exists()) {
socketFile.delete();
}
LocalSocket serverSocket = null;
try {
serverSocket = new LocalSocket(LocalSocket.SOCKET_DGRAM);
serverSocket.bind(new LocalSocketAddress(
UNSOL_ZYGOTE_MSG_SOCKET_PATH, LocalSocketAddress.Namespace.FILESYSTEM));
Os.chmod(UNSOL_ZYGOTE_MSG_SOCKET_PATH, 0666);
} catch (Exception e) {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException ex) {
}
serverSocket = null;
}
}
return serverSocket;
}
/**
* Handle the unsolicited message from zygote.
*/
private int handleZygoteMessages(FileDescriptor fd, int events) {
final int eventFd = fd.getInt$();
if ((events & EVENT_INPUT) != 0) {
// An incoming message from zygote
try {
final int len = Os.read(fd, mZygoteUnsolicitedMessage, 0,
mZygoteUnsolicitedMessage.length);
if (len > 0 && mZygoteSigChldMessage.length == Zygote.nativeParseSigChld(
mZygoteUnsolicitedMessage, len, mZygoteSigChldMessage)) {
mAppExitInfoTracker.handleZygoteSigChld(
mZygoteSigChldMessage[0] /* pid */,
mZygoteSigChldMessage[1] /* uid */,
mZygoteSigChldMessage[2] /* status */);
}
} catch (Exception e) {
Slog.w(TAG, "Exception in reading unsolicited zygote message: " + e);
}
}
return EVENT_INPUT;
}
/**
* Handle the death notification if it's a dying app.
*
* @return {@code true} if it's a dying app that we were tracking.
*/
@GuardedBy("mService")
boolean handleDyingAppDeathLocked(ProcessRecord app, int pid) {
if (mProcessNames.get(app.processName, app.uid) != app
&& mDyingProcesses.get(app.processName, app.uid) == app) {
// App has been removed already, meaning cleanup has done.
Slog.v(TAG, "Got obituary of " + pid + ":" + app.processName);
app.unlinkDeathRecipient();
// It's really gone now, let's remove from the dying process list.
mDyingProcesses.remove(app.processName, app.uid);
app.setDyingPid(0);
handlePrecedingAppDiedLocked(app);
return true;
}
return false;
}
/**
* Handle the case where the given app is a preceding instance of another process instance.
*
* @return {@code false} if this given app should not be allowed to restart.
*/
@GuardedBy("mService")
boolean handlePrecedingAppDiedLocked(ProcessRecord app) {
if (app.mSuccessor != null) {
// We don't allow restart with this ProcessRecord now,
// because we have created a new one already.
// If it's persistent, add the successor to mPersistentStartingProcesses
if (app.isPersistent() && !app.isRemoved()) {
if (mService.mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
mService.mPersistentStartingProcesses.add(app.mSuccessor);
}
}
// clean up the field so the successor's proc starter could proceed.
app.mSuccessor.mPredecessor = null;
app.mSuccessor = null;
// Remove any pending timeout msg.
mService.mProcStartHandler.removeMessages(
ProcStartHandler.MSG_PROCESS_KILL_TIMEOUT, app);
// Kick off the proc start for the succeeding instance
mService.mProcStartHandler.obtainMessage(
ProcStartHandler.MSG_PROCESS_DIED, app).sendToTarget();
return false;
}
return true;
}
@GuardedBy("mService")
void updateBackgroundRestrictedForUidPackageLocked(int uid, String packageName,
boolean restricted) {
final UidRecord uidRec = getUidRecordLOSP(uid);
if (uidRec != null) {
final long nowElapsed = SystemClock.elapsedRealtime();
uidRec.forEachProcess(app -> {
if (TextUtils.equals(app.info.packageName, packageName)) {
app.mState.setBackgroundRestricted(restricted);
if (restricted) {
mAppsInBackgroundRestricted.add(app);
final long future = killAppIfBgRestrictedAndCachedIdleLocked(
app, nowElapsed);
if (future > 0 && !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
future - nowElapsed);
}
} else {
mAppsInBackgroundRestricted.remove(app);
}
if (!app.isKilledByAm()) {
mService.enqueueOomAdjTargetLocked(app);
}
}
});
/* Will be a no-op if nothing pending */
mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
}
/**
* Kill the given app if it's in cached idle and background restricted mode.
*
* @return A future timestamp when the app should be killed at, or a 0 if it shouldn't
* be killed or it has been killed.
*/
@GuardedBy("mService")
long killAppIfBgRestrictedAndCachedIdleLocked(ProcessRecord app, long nowElapsed) {
final UidRecord uidRec = app.getUidRecord();
final long lastCanKillTime = app.mState.getLastCanKillOnBgRestrictedAndIdleTime();
if (!mService.mConstants.mKillBgRestrictedAndCachedIdle
|| app.isKilled() || app.getThread() == null || uidRec == null || !uidRec.isIdle()
|| !app.isCached() || app.mState.shouldNotKillOnBgRestrictedAndIdle()
|| !app.mState.isBackgroundRestricted() || lastCanKillTime == 0) {
return 0;
}
final long future = lastCanKillTime
+ mService.mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs;
if (future <= nowElapsed) {
app.killLocked("cached idle & background restricted",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY,
true);
return 0;
}
return future;
}
/**
* Called by {@link ActivityManagerService#enqueueUidChangeLocked} only, it doesn't schedule
* the standy killing checks because it should have been scheduled before enqueueing UID idle
* changed.
*/
@GuardedBy("mService")
void killAppIfBgRestrictedAndCachedIdleLocked(UidRecord uidRec) {
final long nowElapsed = SystemClock.elapsedRealtime();
uidRec.forEachProcess(app -> killAppIfBgRestrictedAndCachedIdleLocked(app, nowElapsed));
}
/**
* Called by ActivityManagerService when a process died.
*/
@GuardedBy("mService")
void noteProcessDiedLocked(final ProcessRecord app) {
if (DEBUG_PROCESSES) {
Slog.i(TAG, "note: " + app + " died, saving the exit info");
}
Watchdog.getInstance().processDied(app.processName, app.getPid());
if (app.getDeathRecipient() == null
&& mDyingProcesses.get(app.processName, app.uid) == app) {
// If we've done unlinkDeathRecipient before calling into this, remove from dying list.
mDyingProcesses.remove(app.processName, app.uid);
app.setDyingPid(0);
}
mAppExitInfoTracker.scheduleNoteProcessDied(app);
}
/**
* Called by ActivityManagerService when it decides to kill an application process.
*/
@GuardedBy("mService")
void noteAppKill(final ProcessRecord app, final @Reason int reason,
final @SubReason int subReason, final String msg) {
if (DEBUG_PROCESSES) {
Slog.i(TAG, "note: " + app + " is being killed, reason: " + reason
+ ", sub-reason: " + subReason + ", message: " + msg);
}
if (app.getPid() > 0 && !app.isolated && app.getDeathRecipient() != null) {
// We are killing it, put it into the dying process list.
mDyingProcesses.put(app.processName, app.uid, app);
app.setDyingPid(app.getPid());
}
mAppExitInfoTracker.scheduleNoteAppKill(app, reason, subReason, msg);
}
@GuardedBy("mService")
void noteAppKill(final int pid, final int uid, final @Reason int reason,
final @SubReason int subReason, final String msg) {
if (DEBUG_PROCESSES) {
Slog.i(TAG, "note: " + pid + " is being killed, reason: " + reason
+ ", sub-reason: " + subReason + ", message: " + msg);
}
final ProcessRecord app;
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(pid);
}
if (app != null && app.uid == uid && !app.isolated && app.getDeathRecipient() != null) {
// We are killing it, put it into the dying process list.
mDyingProcesses.put(app.processName, uid, app);
app.setDyingPid(app.getPid());
}
mAppExitInfoTracker.scheduleNoteAppKill(pid, uid, reason, subReason, msg);
}
/**
* Schedule to kill the given pids when the device is idle
*/
void killProcessesWhenImperceptible(int[] pids, String reason, int requester) {
if (ArrayUtils.isEmpty(pids)) {
return;
}
synchronized (mService) {
ProcessRecord app;
for (int i = 0; i < pids.length; i++) {
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(pids[i]);
}
if (app != null) {
mImperceptibleKillRunner.enqueueLocked(app, reason, requester);
}
}
}
}
/**
* Get the number of foreground services in all processes and number of processes that have
* foreground service within.
*/
Pair<Integer, Integer> getNumForegroundServices() {
int numForegroundServices = 0;
int procs = 0;
synchronized (mService) {
for (int i = 0, size = mLruProcesses.size(); i < size; i++) {
ProcessRecord pr = mLruProcesses.get(i);
int numFgs = pr.mServices.getNumForegroundServices();
if (numFgs > 0) {
numForegroundServices += numFgs;
procs++;
}
}
}
return new Pair<>(numForegroundServices, procs);
}
private final class ImperceptibleKillRunner extends IUidObserver.Stub {
private static final String EXTRA_PID = "pid";
private static final String EXTRA_UID = "uid";
private static final String EXTRA_TIMESTAMP = "timestamp";
private static final String EXTRA_REASON = "reason";
private static final String EXTRA_REQUESTER = "requester";
private static final String DROPBOX_TAG_IMPERCEPTIBLE_KILL = "imperceptible_app_kill";
// uid -> killing information mapping
private SparseArray<List<Bundle>> mWorkItems = new SparseArray<List<Bundle>>();
// The last time the various processes have been killed by us.
private ProcessMap<Long> mLastProcessKillTimes = new ProcessMap<>();
// Device idle or not.
private volatile boolean mIdle;
private boolean mUidObserverEnabled;
private Handler mHandler;
private IdlenessReceiver mReceiver;
private final class H extends Handler {
static final int MSG_DEVICE_IDLE = 0;
static final int MSG_UID_GONE = 1;
static final int MSG_UID_STATE_CHANGED = 2;
H(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DEVICE_IDLE:
handleDeviceIdle();
break;
case MSG_UID_GONE:
handleUidGone(msg.arg1 /* uid */);
break;
case MSG_UID_STATE_CHANGED:
handleUidStateChanged(msg.arg1 /* uid */, msg.arg2 /* procState */);
break;
}
}
}
private final class IdlenessReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final PowerManager pm = mService.mContext.getSystemService(PowerManager.class);
switch (intent.getAction()) {
case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
notifyDeviceIdleness(pm.isLightDeviceIdleMode());
break;
case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
notifyDeviceIdleness(pm.isDeviceIdleMode());
break;
}
}
}
ImperceptibleKillRunner(Looper looper) {
mHandler = new H(looper);
}
@GuardedBy("mService")
boolean enqueueLocked(ProcessRecord app, String reason, int requester) {
// Throttle the killing request for potential bad app to avoid cpu thrashing
Long last = app.isolated ? null : mLastProcessKillTimes.get(app.processName, app.uid);
if ((last != null) && (SystemClock.uptimeMillis()
< (last + ActivityManagerConstants.MIN_CRASH_INTERVAL))) {
return false;
}
final Bundle bundle = new Bundle();
bundle.putInt(EXTRA_PID, app.getPid());
bundle.putInt(EXTRA_UID, app.uid);
// Since the pid could be reused, let's get the actual start time of each process
bundle.putLong(EXTRA_TIMESTAMP, app.getStartTime());
bundle.putString(EXTRA_REASON, reason);
bundle.putInt(EXTRA_REQUESTER, requester);
List<Bundle> list = mWorkItems.get(app.uid);
if (list == null) {
list = new ArrayList<Bundle>();
mWorkItems.put(app.uid, list);
}
list.add(bundle);
if (mReceiver == null) {
mReceiver = new IdlenessReceiver();
IntentFilter filter = new IntentFilter(
PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mService.mContext.registerReceiver(mReceiver, filter);
}
return true;
}
void notifyDeviceIdleness(boolean idle) {
// No lock is held regarding mIdle, this function is the only updater and caller
// won't re-entry.
boolean diff = mIdle != idle;
mIdle = idle;
if (diff && idle) {
synchronized (mService) {
if (mWorkItems.size() > 0) {
mHandler.sendEmptyMessage(H.MSG_DEVICE_IDLE);
}
}
}
}
private void handleDeviceIdle() {
final DropBoxManager dbox = mService.mContext.getSystemService(DropBoxManager.class);
final boolean logToDropbox = dbox != null
&& dbox.isTagEnabled(DROPBOX_TAG_IMPERCEPTIBLE_KILL);
synchronized (mService) {
final int size = mWorkItems.size();
for (int i = size - 1; mIdle && i >= 0; i--) {
List<Bundle> list = mWorkItems.valueAt(i);
final int len = list.size();
for (int j = len - 1; mIdle && j >= 0; j--) {
Bundle bundle = list.get(j);
if (killProcessLocked(
bundle.getInt(EXTRA_PID),
bundle.getInt(EXTRA_UID),
bundle.getLong(EXTRA_TIMESTAMP),
bundle.getString(EXTRA_REASON),
bundle.getInt(EXTRA_REQUESTER),
dbox, logToDropbox)) {
list.remove(j);
}
}
if (list.size() == 0) {
mWorkItems.removeAt(i);
}
}
registerUidObserverIfNecessaryLocked();
}
}
@GuardedBy("mService")
private void registerUidObserverIfNecessaryLocked() {
// If there are still works remaining, register UID observer
if (!mUidObserverEnabled && mWorkItems.size() > 0) {
mUidObserverEnabled = true;
mService.registerUidObserver(this,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
ActivityManager.PROCESS_STATE_UNKNOWN, "android");
} else if (mUidObserverEnabled && mWorkItems.size() == 0) {
mUidObserverEnabled = false;
mService.unregisterUidObserver(this);
}
}
/**
* Kill the given processes, if they are not exempted.
*
* @return True if the process is killed, or it's gone already, or we are not allowed to
* kill it (one of the packages in this process is being exempted).
*/
@GuardedBy("mService")
private boolean killProcessLocked(final int pid, final int uid, final long timestamp,
final String reason, final int requester, final DropBoxManager dbox,
final boolean logToDropbox) {
ProcessRecord app = null;
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(pid);
}
if (app == null || app.getPid() != pid || app.uid != uid
|| app.getStartTime() != timestamp) {
// This process record has been reused for another process, meaning the old process
// has been gone.
return true;
}
if (app.getPkgList().searchEachPackage(pkgName -> {
if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.contains(pkgName)) {
// One of the packages in this process is exempted
return Boolean.TRUE;
}
return null;
}) != null) {
return true;
}
if (mService.mConstants.IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.contains(
app.mState.getReportedProcState())) {
// We need to reschedule it.
return false;
}
app.killLocked(reason, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_IMPERCEPTIBLE, true);
if (!app.isolated) {
mLastProcessKillTimes.put(app.processName, app.uid, SystemClock.uptimeMillis());
}
if (logToDropbox) {
final long now = SystemClock.elapsedRealtime();
final StringBuilder sb = new StringBuilder();
mService.appendDropBoxProcessHeaders(app, app.processName, sb);
sb.append("Reason: " + reason).append("\n");
sb.append("Requester UID: " + requester).append("\n");
dbox.addText(DROPBOX_TAG_IMPERCEPTIBLE_KILL, sb.toString());
}
return true;
}
private void handleUidStateChanged(int uid, int procState) {
final DropBoxManager dbox = mService.mContext.getSystemService(DropBoxManager.class);
final boolean logToDropbox = dbox != null
&& dbox.isTagEnabled(DROPBOX_TAG_IMPERCEPTIBLE_KILL);
synchronized (mService) {
if (mIdle && !mService.mConstants
.IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.contains(procState)) {
List<Bundle> list = mWorkItems.get(uid);
if (list != null) {
final int len = list.size();
for (int j = len - 1; mIdle && j >= 0; j--) {
Bundle bundle = list.get(j);
if (killProcessLocked(
bundle.getInt(EXTRA_PID),
bundle.getInt(EXTRA_UID),
bundle.getLong(EXTRA_TIMESTAMP),
bundle.getString(EXTRA_REASON),
bundle.getInt(EXTRA_REQUESTER),
dbox, logToDropbox)) {
list.remove(j);
}
}
if (list.size() == 0) {
mWorkItems.remove(uid);
}
registerUidObserverIfNecessaryLocked();
}
}
}
}
private void handleUidGone(int uid) {
synchronized (mService) {
mWorkItems.remove(uid);
registerUidObserverIfNecessaryLocked();
}
}
@Override
public void onUidGone(int uid, boolean disabled) {
mHandler.obtainMessage(H.MSG_UID_GONE, uid, 0).sendToTarget();
}
@Override
public void onUidActive(int uid) {
}
@Override
public void onUidIdle(int uid, boolean disabled) {
}
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
mHandler.obtainMessage(H.MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
}
@Override
public void onUidCachedChanged(int uid, boolean cached) {
}
@Override
public void onUidProcAdjChanged(int uid) {
}
};
}