Java程序  |  3213行  |  141.14 KB

/*
 * 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_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.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
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_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
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_PROCESSES;
import static com.android.server.am.ActivityManagerService.TAG_PSS;
import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;

import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppProtoEnums;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
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.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
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.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.StatsLog;
import android.view.Display;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.MemInfoReader;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.Watchdog;
import com.android.server.pm.dex.DexManager;
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;

import dalvik.system.VMRuntime;

import libcore.io.IoUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
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.List;

/**
 * Activity manager code dealing with processes.
 *
 * Method naming convention:
 * <ul>
 * <li> Methods suffixed with "LS" should be called within the {@link #sLmkdSocketLock} lock.
 * </ul>
 */
public final class ProcessList {
    static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;

    // The minimum time we allow between crashes, for us to consider this
    // application to be bad and stop and its services and reject broadcasts.
    static final int MIN_CRASH_INTERVAL = 60 * 1000;

    // OOM adjustments for processes in various states:

    // Uninitialized value for any major or minor adj fields
    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.)
    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.
    static final int CACHED_APP_MAX_ADJ = 999;
    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.
    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.
    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.
    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.
    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.
    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.
    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.
    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.
    static final int PERCEPTIBLE_LOW_APP_ADJ = 250;

    // 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.
    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.
    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
    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!
    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.
    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.
    static final int PERSISTENT_PROC_ADJ = -800;

    // The system process runs at the default adjustment.
    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).
    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_BG_NONINTERACTIVE
    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;

    // We allow empty processes to stick around for at most 30 minutes.
    static final long MAX_EMPTY_TIME = 30 * 60 * 1000;

    // 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;

    // 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";

    // Low Memory Killer Daemon command codes.
    // These must be kept in sync with the definitions in lmkd.c
    //
    // LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
    // LMK_PROCPRIO <pid> <uid> <prio>
    // LMK_PROCREMOVE <pid>
    // LMK_PROCPURGE
    // LMK_GETKILLCNT
    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;

    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 Object sLmkdSocketLock = new Object();

    @GuardedBy("sLmkdSocketLock")
    private static LocalSocket sLmkdSocket;

    @GuardedBy("sLmkdSocketLock")
    private static OutputStream sLmkdOutputStream;

    @GuardedBy("sLmkdSocketLock")
    private static InputStream sLmkdInputStream;

    /**
     * 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.
     */
    @GuardedBy("mService")
    @VisibleForTesting
    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.
     */
    final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();

    /**
     * Where in mLruProcesses that the processes hosting activities start.
     */
    int mLruProcessActivityStart = 0;

    /**
     * Where in mLruProcesses that the processes hosting services start.
     * This is after (lower index) than mLruProcessesActivityStart.
     */
    int mLruProcessServiceStart = 0;

    /**
     * Current sequence id for process LRU updating.
     */
    int mLruSeq = 0;

    ActiveUids mActiveUids;

    /**
     * The currently running isolated processes.
     */
    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();

    /**
     * The currently running application zygotes.
     */
    final ProcessMap<AppZygote> mAppZygotes = new ProcessMap<AppZygote>();

    /**
     * The processes that are forked off an application zygote.
     */
    final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
            new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();

    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) {
            // Strip out userId
            final int appId = UserHandle.getAppId(uid);
            mUidUsed.delete(appId);
        }
    };

    /**
     * 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
    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
    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.
     */
    final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();

    /**
     * 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.
     */
    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;

        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;

                default:
                    super.handleMessage(msg);
            }
        }
    }

    ////////////////////  END FIELDS  ////////////////////

    ProcessList() {
        MemInfoReader minfo = new MemInfoReader();
        minfo.readMemInfo();
        mTotalMemMb = minfo.getTotalSize()/(1024*1024);
        updateOomLevels(0, 0, false);
    }

    void init(ActivityManagerService service, ActiveUids activeUids) {
        mService = service;
        mActiveUids = activeUids;

        if (sKillHandler == null) {
            sKillThread = new ServiceThread(TAG + ":kill",
                    THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
            sKillThread.start();
            sKillHandler = new KillHandler(sKillThread.getLooper());
        }
    }

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

    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));
        }
        // 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_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("fore  ", "fore", null, 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) {
        String procState;
        switch (curProcState) {
            case ActivityManager.PROCESS_STATE_PERSISTENT:
                procState = "PER ";
                break;
            case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
                procState = "PERU";
                break;
            case ActivityManager.PROCESS_STATE_TOP:
                procState = "TOP ";
                break;
            case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION:
                procState = "FGSL";
                break;
            case ActivityManager.PROCESS_STATE_BOUND_TOP:
                procState = "BTOP";
                break;
            case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE:
                procState = "FGS ";
                break;
            case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE:
                procState = "BFGS";
                break;
            case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
                procState = "IMPF";
                break;
            case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
                procState = "IMPB";
                break;
            case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND:
                procState = "TRNB";
                break;
            case ActivityManager.PROCESS_STATE_BACKUP:
                procState = "BKUP";
                break;
            case ActivityManager.PROCESS_STATE_SERVICE:
                procState = "SVC ";
                break;
            case ActivityManager.PROCESS_STATE_RECEIVER:
                procState = "RCVR";
                break;
            case ActivityManager.PROCESS_STATE_TOP_SLEEPING:
                procState = "TPSL";
                break;
            case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
                procState = "HVY ";
                break;
            case ActivityManager.PROCESS_STATE_HOME:
                procState = "HOME";
                break;
            case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
                procState = "LAST";
                break;
            case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
                procState = "CAC ";
                break;
            case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                procState = "CACC";
                break;
            case ActivityManager.PROCESS_STATE_CACHED_RECENT:
                procState = "CRE ";
                break;
            case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
                procState = "CEM ";
                break;
            case ActivityManager.PROCESS_STATE_NONEXISTENT:
                procState = "NONE";
                break;
            default:
                procState = "??";
                break;
        }
        return procState;
    }

    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_FOREGROUND_SERVICE_LOCATION:
                return AppProtoEnums.PROCESS_STATE_FOREGROUND_SERVICE;
            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_LOCATION
        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 void commitNextPssTime(ProcStateMemTracker tracker) {
        if (tracker.mPendingMemState >= 0) {
            tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
            tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
            tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
            tracker.mPendingMemState = -1;
        }
    }

    public static void abortNextPssTime(ProcStateMemTracker tracker) {
        tracker.mPendingMemState = -1;
    }

    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);
        if (writeLmkd(buf, repl)) {
            int i = repl.getInt();
            if (i != LMK_GETKILLCNT) {
                Slog.e("ActivityManager", "Failed to get kill count, code mismatch");
                return null;
            }
            return new Integer(repl.getInt());
        }
        return null;
    }

    @GuardedBy("sLmkdSocketLock")
    private static boolean openLmkdSocketLS() {
        try {
            sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
            sLmkdSocket.connect(
                new LocalSocketAddress("lmkd",
                        LocalSocketAddress.Namespace.RESERVED));
            sLmkdOutputStream = sLmkdSocket.getOutputStream();
            sLmkdInputStream = sLmkdSocket.getInputStream();
        } catch (IOException ex) {
            Slog.w(TAG, "lowmemorykiller daemon socket open failed");
            sLmkdSocket = null;
            return false;
        }

        return true;
    }

    // Never call directly, use writeLmkd() instead
    @GuardedBy("sLmkdSocketLock")
    private static boolean writeLmkdCommandLS(ByteBuffer buf) {
        try {
            sLmkdOutputStream.write(buf.array(), 0, buf.position());
        } catch (IOException ex) {
            Slog.w(TAG, "Error writing to lowmemorykiller socket");
            IoUtils.closeQuietly(sLmkdSocket);
            sLmkdSocket = null;
            return false;
        }
        return true;
    }

    // Never call directly, use writeLmkd() instead
    @GuardedBy("sLmkdSocketLock")
    private static boolean readLmkdReplyLS(ByteBuffer buf) {
        int len;
        try {
            len = sLmkdInputStream.read(buf.array(), 0, buf.array().length);
            if (len == buf.array().length) {
                return true;
            }
        } catch (IOException ex) {
            Slog.w(TAG, "Error reading from lowmemorykiller socket");
        }

        IoUtils.closeQuietly(sLmkdSocket);
        sLmkdSocket = null;
        return false;
    }

    private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
        synchronized (sLmkdSocketLock) {
            for (int i = 0; i < 3; i++) {
                if (sLmkdSocket == null) {
                    if (openLmkdSocketLS() == false) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ie) {
                        }
                        continue;
                    }

                    // Purge any previously registered pids
                    ByteBuffer purge_buf = ByteBuffer.allocate(4);
                    purge_buf.putInt(LMK_PROCPURGE);
                    if (writeLmkdCommandLS(purge_buf) == false) {
                        // Write failed, skip the rest and retry
                        continue;
                    }
                }
                if (writeLmkdCommandLS(buf) && (repl == null || readLmkdReplyLS(repl))) {
                    return true;
                }
            }
        }
        return false;
    }

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

    final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
            keepIfLarge) {
        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.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
                    // Don't use an app process or different user process for system component.
                    continue;
                }
                return procs.valueAt(i);
            }
        }
        ProcessRecord proc = mProcessNames.get(processName, uid);
        if (false && proc != null && !keepIfLarge
                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
                && proc.lastCachedPss >= 4000) {
            // Turn this condition on to cause killing to happen regularly, for testing.
            if (proc.baseProcessTracker != null) {
                proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
                for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
                    ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
                    StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
                            proc.info.uid,
                            holder.state.getName(),
                            holder.state.getPackage(),
                            proc.lastCachedPss, holder.appVersion);
                }
            }
            proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
        } else if (proc != null && !keepIfLarge
                && mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
            if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
                    .lastCachedPss);
            if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) {
                if (proc.baseProcessTracker != null) {
                    proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList,
                            proc.lastCachedPss);
                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
                        StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
                                proc.info.uid,
                                holder.state.getName(),
                                holder.state.getPackage(),
                                proc.lastCachedPss, holder.appVersion);
                    }
                }
                proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
            }
        }
        return proc;
    }

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

    ProcessRecord findAppProcessLocked(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);
                if (p.thread != null && p.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);
        }
    }

    /**
     * @return {@code true} if process start is successful, false otherwise.
     * @param app
     * @param hostingRecord
     * @param disableHiddenApiChecks
     * @param abiOverride
     */
    @GuardedBy("mService")
    boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
            boolean disableHiddenApiChecks, boolean mountExtStorageFull,
            String abiOverride) {
        if (app.pendingStart) {
            return true;
        }
        long startTime = SystemClock.elapsedRealtime();
        if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
            checkSlow(startTime, "startProcess: removing from pids map");
            mService.mPidsSelfLocked.remove(app);
            mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
            checkSlow(startTime, "startProcess: done removing from pids map");
            app.setPid(0);
            app.startSeq = 0;
        }

        if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
                TAG_PROCESSES,
                "startProcessLocked removing on hold: " + app);
        mService.mProcessesOnHold.remove(app);

        checkSlow(startTime, "startProcess: starting to update cpu stats");
        mService.updateCpuStats();
        checkSlow(startTime, "startProcess: done updating cpu stats");

        try {
            try {
                final int userId = UserHandle.getUserId(app.uid);
                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;
            if (!app.isolated) {
                int[] permGids = null;
                try {
                    checkSlow(startTime, "startProcess: getting gids from package manager");
                    final IPackageManager pm = AppGlobals.getPackageManager();
                    permGids = pm.getPackageGids(app.info.packageName,
                            MATCH_DIRECT_BOOT_AUTO, app.userId);
                    if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) {
                        mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
                    } else {
                        StorageManagerInternal storageManagerInternal = LocalServices.getService(
                                StorageManagerInternal.class);
                        mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
                                app.info.packageName);
                    }
                } catch (RemoteException e) {
                    throw e.rethrowAsRuntimeException();
                }

                /*
                 * Add shared application and profile GIDs so applications can share some
                 * resources like shared libraries and access user-wide resources
                 */
                if (ArrayUtils.isEmpty(permGids)) {
                    gids = new int[3];
                } else {
                    gids = new int[permGids.length + 3];
                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
                }
                gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));

                // Replace any invalid GIDs
                if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
                if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
            }
            app.mountMode = mountExternal;
            checkSlow(startTime, "startProcess: building args");
            if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
                uid = 0;
            }
            int runtimeFlags = 0;
            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                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 == true) {
                runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
            }
            if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0) {
                runtimeFlags |= Zygote.PROFILE_FROM_SHELL;
            }
            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 (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()
                    || (app.info.isPrivilegedApp()
                        && DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.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;
            }

            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 ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
                // 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) {
                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
            }

            app.gids = gids;
            app.setRequiredAbi(requiredAbi);
            app.instructionSet = instructionSet;

            // 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, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
                    startTime);
        } 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 mountExternal,
            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
            long startTime) {
        app.pendingStart = true;
        app.killedByAm = false;
        app.removed = false;
        app.killed = false;
        if (app.startSeq != 0) {
            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
                    + " with non-zero startSeq:" + app.startSeq);
        }
        if (app.pid != 0) {
            Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
                    + " with non-zero pid:" + app.pid);
        }
        final long startSeq = app.startSeq = ++mProcStartSeqCounter;
        app.setStartParams(uid, hostingRecord, seInfo, startTime);
        app.setUsingWrapper(invokeWith != null
                || SystemProperties.get("wrap." + 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(() -> {
                try {
                    final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
                            entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
                            app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
                    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.pendingStart = false;
                        mService.forceStopPackageLocked(app.info.packageName,
                                UserHandle.getAppId(app.uid),
                                false, false, true, false, false, app.userId, "start failure");
                    }
                }
            });
            return true;
        } else {
            try {
                final Process.ProcessStartResult startResult = startProcess(hostingRecord,
                        entryPoint, app,
                        uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
                        invokeWith, startTime);
                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
                        startSeq, false);
            } catch (RuntimeException e) {
                Slog.e(ActivityManagerService.TAG, "Failure starting process "
                        + app.processName, e);
                app.pendingStart = false;
                mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
                        false, false, true, false, false, app.userId, "start failure");
            }
            return app.pid > 0;
        }
    }

    @GuardedBy("mService")
    public void killAppZygoteIfNeededLocked(AppZygote appZygote) {
        final ApplicationInfo appInfo = appZygote.getAppInfo();
        ArrayList<ProcessRecord> zygoteProcesses = mAppZygoteProcesses.get(appZygote);
        if (zygoteProcesses != null && zygoteProcesses.size() == 0) {
            // Only remove if no longer in use now
            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.hostingRecord.getDefiningUid());
        if (appUidRange != null) {
            appUidRange.freeIsolatedUidLocked(app.uid);
        }

        final AppZygote appZygote = mAppZygotes.get(app.info.processName,
                app.hostingRecord.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.removed) {
                    // If we stopped this process because the package hosting it was removed,
                    // there's no point in delaying the app zygote kill.
                    killAppZygoteIfNeededLocked(appZygote);
                } 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.hostingRecord.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.hostingRecord.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.hostingRecord.getDefiningPackageName();
                appInfo.uid = uid;
                appZygote = new AppZygote(appInfo, 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 Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, 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 Process.ProcessStartResult startResult;
            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,
                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
            } else if (hostingRecord.usesAppZygote()) {
                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);

                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,
                        /*useUsapPool=*/ false,
                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
            } else {
                startResult = Process.start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, invokeWith, app.info.packageName,
                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
            }
            checkSlow(startTime, "startProcess: returned from zygote!");
            return startResult;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

    @GuardedBy("mService")
    final void startProcessLocked(ProcessRecord app, HostingRecord hostingRecord) {
        startProcessLocked(app, hostingRecord, null /* abiOverride */);
    }

    @GuardedBy("mService")
    final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
            String abiOverride) {
        return startProcessLocked(app, hostingRecord,
                false /* disableHiddenApiChecks */, false /* mountExtStorageFull */, abiOverride);
    }

    @GuardedBy("mService")
    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
        long startTime = SystemClock.elapsedRealtime();
        ProcessRecord app;
        if (!isolated) {
            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
            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.isBadProcessLocked(info)) {
                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
                            + "/" + info.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
                        + "/" + info.processName);
                mService.mAppErrors.resetProcessCrashTimeLocked(info);
                if (mService.mAppErrors.isBadProcessLocked(info)) {
                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
                            UserHandle.getUserId(info.uid), info.uid,
                            info.processName);
                    mService.mAppErrors.clearBadProcessLocked(info);
                    if (app != null) {
                        app.bad = 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.thread : null)
                + " pid=" + (app != null ? app.pid : -1));
        if (app != null && app.pid > 0) {
            if ((!knownToBeDead && !app.killed) || app.thread == 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.pid);
            mService.handleAppDiedLocked(app, true, true);
            checkSlow(startTime, "startProcess: done killing old proc");
        }

        if (app == null) {
            checkSlow(startTime, "startProcess: creating new process record");
            app = newProcessRecordLocked(info, processName, isolated, isolatedUid, hostingRecord);
            if (app == null) {
                Slog.w(TAG, "Failed making new process record for "
                        + processName + "/" + info.uid + " isolated=" + isolated);
                return null;
            }
            app.crashHandler = crashHandler;
            app.isolatedEntryPoint = entryPoint;
            app.isolatedEntryPointArgs = entryPointArgs;
            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, 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.killedByAm) {
            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.pendingStart) {
            if (sb == null) sb = new StringBuilder();
            sb.append("pendingStart=false;");
        }
        if (app.startSeq > expectedStartSeq) {
            if (sb == null) sb = new StringBuilder();
            sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
        }
        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.pid == 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.pendingStart = false;
            killProcessQuiet(pid);
            Process.killProcessGroup(app.uid, app.pid);
            return false;
        }
        mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
        checkSlow(app.startTime, "startProcess: done updating battery stats");

        EventLog.writeEvent(EventLogTags.AM_PROC_START,
                UserHandle.getUserId(app.startUid), pid, app.startUid,
                app.processName, app.hostingRecord.getType(),
                app.hostingRecord.getName() != null ? app.hostingRecord.getName() : "");

        try {
            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
                    app.seInfo, app.info.sourceDir, pid);
        } catch (RemoteException ex) {
            // Ignore
        }

        if (app.isPersistent()) {
            Watchdog.getInstance().processStarted(app.processName, pid);
        }

        checkSlow(app.startTime, "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.startUid);
        if (app.isolatedEntryPoint != null) {
            buf.append(" [");
            buf.append(app.isolatedEntryPoint);
            buf.append("]");
        }
        buf.append(" for ");
        buf.append(app.hostingRecord.getType());
        if (app.hostingRecord.getName() != null) {
            buf.append(" ");
            buf.append(app.hostingRecord.getName());
        }
        mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
        app.setPid(pid);
        app.setUsingWrapper(usingWrapper);
        app.pendingStart = false;
        checkSlow(app.startTime, "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.startSeq
                    + " pid:" + pid
                    + " belongs to another existing app:" + oldApp.processName
                    + " startSeq:" + oldApp.startSeq);
            mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
                    true /*replacingPid*/);
        }
        mService.mPidsSelfLocked.put(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.startTime, "startProcess: done updating pids map");
        return true;
    }

    final void removeLruProcessLocked(ProcessRecord app) {
        int lrui = mLruProcesses.lastIndexOf(app);
        if (lrui >= 0) {
            if (!app.killed) {
                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.pid > 0) {
                        killProcessQuiet(app.pid);
                        ProcessList.killProcessGroup(app.uid, app.pid);
                    } else {
                        app.pendingStart = false;
                    }
                }
            }
            if (lrui <= mLruProcessActivityStart) {
                mLruProcessActivityStart--;
            }
            if (lrui <= mLruProcessServiceStart) {
                mLruProcessServiceStart--;
            }
            mLruProcesses.remove(lrui);
        }
    }

    @GuardedBy("mService")
    boolean killPackageProcessesLocked(String packageName, int appId, int userId, int minOomAdj,
            String reason) {
        return killPackageProcessesLocked(packageName, appId, userId, minOomAdj,
                false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
                false /* evenPersistent */, false /* setRemoved */, reason);
    }

    @GuardedBy("mService")
    final boolean killPackageProcessesLocked(String packageName, int appId,
            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
            boolean doit, boolean evenPersistent, boolean setRemoved, String reason) {
        ArrayList<ProcessRecord> 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.removed) {
                    if (doit) {
                        procs.add(app);
                    }
                    continue;
                }

                // Skip process if it doesn't meet our oom adj requirement.
                if (app.setAdj < 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;
                }

                // 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.pkgDeps != null
                            && app.pkgDeps.contains(packageName);
                    if (!isDep && UserHandle.getAppId(app.uid) != appId) {
                        continue;
                    }
                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
                        continue;
                    }
                    if (!app.pkgList.containsKey(packageName) && !isDep) {
                        continue;
                    }
                }

                // Process has passed all conditions, kill it!
                if (!doit) {
                    return true;
                }
                if (setRemoved) {
                    app.removed = true;
                }
                procs.add(app);
            }
        }

        int N = procs.size();
        for (int i=0; i<N; i++) {
            removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
        }
        // 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);
        }
        mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
        return N > 0;
    }

    @GuardedBy("mService")
    boolean removeProcessLocked(ProcessRecord app,
            boolean callerWillRestart, boolean allowRestart, 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;
        if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app
                .pendingStart)) {
            int pid = app.pid;
            if (pid > 0) {
                mService.mPidsSelfLocked.remove(app);
                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.getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
                }
            }
            boolean willRestart = false;
            if (app.isPersistent() && !app.isolated) {
                if (!callerWillRestart) {
                    willRestart = true;
                } else {
                    needRestart = true;
                }
            }
            app.kill(reason, true);
            mService.handleAppDiedLocked(app, willRestart, allowRestart);
            if (willRestart) {
                removeLruProcessLocked(app);
                mService.addAppLocked(app.info, null, false, null /* ABI override */);
            }
        } else {
            mRemovedProcesses.add(app);
        }

        return needRestart;
    }

    @GuardedBy("mService")
    final 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.
        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) {
            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);
            // 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.mDeviceIdleTempWhitelist,
                    UserHandle.getAppId(proc.uid)) >= 0
                    || mService.mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
                uidRec.setWhitelist = uidRec.curWhitelist = true;
            }
            uidRec.updateHasInternetPermission();
            mActiveUids.put(proc.uid, uidRec);
            EventLogTags.writeAmUidRunning(uidRec.uid);
            mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
        }
        proc.uidRecord = uidRec;

        // Reset render thread tid if it was already set, so new process can set it again.
        proc.renderThreadTid = 0;
        uidRec.numProcs++;
        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());
        }
    }

    @GuardedBy("mService")
    final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
            boolean isolated, int isolatedUid, HostingRecord hostingRecord) {
        String proc = customProcess != null ? customProcess : info.processName;
        final int userId = UserHandle.getUserId(info.uid);
        int uid = info.uid;
        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;
            }
            mService.getPackageManagerInternalLocked().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);
            StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
                    StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
        }
        final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);

        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.
            r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
            r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
            r.setPersistent(true);
            r.maxAdj = 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.
            r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
        }
        addProcessNameLocked(r);
        return r;
    }

    @GuardedBy("mService")
    final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
        return removeProcessNameLocked(name, uid, null);
    }

    @GuardedBy("mService")
    final ProcessRecord removeProcessNameLocked(final String name, final int uid,
            final ProcessRecord expecting) {
        ProcessRecord old = mProcessNames.get(name, uid);
        // 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 (old != null && old.uidRecord != null) {
            old.uidRecord.numProcs--;
            if (old.uidRecord.numProcs == 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 " + old.uidRecord);
                mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
                EventLogTags.writeAmUidStopped(uid);
                mActiveUids.remove(uid);
                mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
            }
            old.uidRecord = null;
        }
        mIsolatedProcesses.remove(uid);
        mGlobalIsolatedUids.freeIsolatedUidLocked(uid);
        // Remove the (expected) ProcessRecord from the app zygote
        final ProcessRecord record = expecting != null ? expecting : old;
        if (record != null && record.appZygote) {
            removeProcessFromAppZygoteLocked(record);
        }

        return old;
    }

    /** Call setCoreSettings on all LRU processes, with the new settings. */
    @GuardedBy("mService")
    void updateCoreSettingsLocked(Bundle settings) {
        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            ProcessRecord processRecord = mLruProcesses.get(i);
            try {
                if (processRecord.thread != null) {
                    processRecord.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")
    void killAllBackgroundProcessesExceptLocked(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.removed || ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
                        && (maxProcState < 0 || app.setProcState > maxProcState))) {
                    procs.add(app);
                }
            }
        }

        final int N = procs.size();
        for (int i = 0; i < N; i++) {
            removeProcessLocked(procs.get(i), false, true, "kill all background except");
        }
    }

    /**
     * Call updateTimePrefs on all LRU processes
     * @param timePref The time pref to pass to each process
     */
    @GuardedBy("mService")
    void updateAllTimePrefsLocked(int timePref) {
        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            ProcessRecord r = mLruProcesses.get(i);
            if (r.thread != null) {
                try {
                    r.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 (mService) {
            for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
                ProcessRecord r = mLruProcesses.get(i);
                // 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.pid != ActivityManagerService.MY_PID && r.thread != null && !r.isolated) {
                    try {
                        r.thread.updateHttpProxy();
                    } catch (RemoteException ex) {
                        Slog.w(TAG, "Failed to update http proxy for: "
                                + r.info.processName);
                    }
                }
            }
        }
        ActivityThread.updateHttpProxy(mService.mContext);
    }

    @GuardedBy("mService")
    void clearAllDnsCacheLocked() {
        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            ProcessRecord r = mLruProcesses.get(i);
            if (r.thread != null) {
                try {
                    r.thread.clearDnsCache();
                } catch (RemoteException ex) {
                    Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
                }
            }
        }
    }

    @GuardedBy("mService")
    void handleAllTrustStorageUpdateLocked() {
        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            ProcessRecord r = mLruProcesses.get(i);
            if (r.thread != null) {
                try {
                    r.thread.handleTrustStorageUpdate();
                } catch (RemoteException ex) {
                    Slog.w(TAG, "Failed to handle trust storage update for: " +
                            r.info.processName);
                }
            }
        }
    }

    @GuardedBy("mService")
    int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
            int lruSeq, String what, Object obj, ProcessRecord srcApp) {
        app.lastActivityTime = 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.lruSeq = 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.
     */
    private void updateClientActivitiesOrdering(final ProcessRecord topApp, final int topI,
            final int bottomI, int endIndex) {
        if (topApp.hasActivitiesOrRecentTasks() || topApp.treatLikeActivity
                || !topApp.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;
        if (topApp.connectionGroup > 0) {
            int endImportance = topApp.connectionImportance;
            for (int i = endIndex; i >= bottomI; i--) {
                final ProcessRecord subProc = mLruProcesses.get(i);
                if (subProc.info.uid == uid
                        && subProc.connectionGroup == topApp.connectionGroup) {
                    if (i == endIndex && subProc.connectionImportance >= 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=" + subProc.connectionGroup
                                        + " importance=" + subProc.connectionImportance);
                        endIndex--;
                        endImportance = subProc.connectionImportance;
                    } 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="
                                        + subProc.connectionImportance);
                        boolean moved = false;
                        for (int pos = topI; pos > endIndex; pos--) {
                            final ProcessRecord posProc = mLruProcesses.get(pos);
                            if (subProc.connectionImportance
                                    <= posProc.connectionImportance) {
                                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 - 1, subProc);
                            if (DEBUG_LRU) Slog.d(TAG_LRU,
                                    "Moving " + subProc
                                            + " from position " + i + " to end of group @ "
                                            + endIndex);
                            endIndex--;
                            endImportance = subProc.connectionImportance;
                        }
                    }
                }
            }

        }
        // 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);
            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() || subProc.treatLikeActivity) {
                            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 (subProc.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 != subProc.connectionGroup) {
                                    // 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=" + subProc.connectionGroup);
                                    break;
                                }
                            } else {
                                if (DEBUG_LRU) Slog.d(TAG_LRU,
                                        "This is an activity client!  uid="
                                        + subProc.info.uid + " group=" + subProc.connectionGroup);
                                hasActivity = true;
                                connUid = subProc.info.uid;
                                connGroup = subProc.connectionGroup;
                            }
                        }
                        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);
                    for (endIndex--; endIndex >= bottomI; endIndex--) {
                        final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
                        if (nextEndProc.info.uid != uid
                                || nextEndProc.connectionGroup != endProc.connectionGroup) {
                            if (DEBUG_LRU) Slog.d(TAG_LRU,
                                    "Found next group or app: " + nextEndProc + " @ "
                                            + endIndex + " group=" + nextEndProc.connectionGroup);
                            break;
                        }
                    }
                }
                if (DEBUG_LRU) Slog.d(TAG_LRU,
                        "Bumping scan position to " + endIndex);
                i = endIndex;
            } else {
                i--;
            }
        }
    }

    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
            ProcessRecord client) {
        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
                || app.treatLikeActivity;
        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;
        }

        mLruSeq++;
        final long now = SystemClock.uptimeMillis();
        app.lastActivityTime = 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() && !app.treatLikeActivity
                    && 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=" + app.connectionGroup
                        + " importance=" + app.connectionImportance);
                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 (pos == mLruProcessActivityStart) {
                    mLruProcessActivityStart++;
                }
                if (pos == mLruProcessServiceStart) {
                    // Unless {@code #hasService} is implemented, currently the starting position
                    // for activity and service are the same, so the incoming position may equal to
                    // the starting position of service.
                    mLruProcessServiceStart++;
                }
                // 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;
                updateClientActivitiesOrdering(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) {
                updateClientActivitiesOrdering(app, mLruProcessServiceStart - 1, 0, index - 1);
            }
        }

        app.lruSeq = mLruSeq;

        // If the app is currently using a content provider or service,
        // bump those processes as well.
        for (int j = app.connections.size() - 1; j >= 0; j--) {
            ConnectionRecord cr = app.connections.valueAt(j);
            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                    && cr.binding.service.app != null
                    && cr.binding.service.app.lruSeq != mLruSeq
                    && (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
                    && !cr.binding.service.app.isPersistent()) {
                if (cr.binding.service.app.hasClientActivities()) {
                    if (nextActivityIndex >= 0) {
                        nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
                                now,
                                nextActivityIndex, mLruSeq,
                                "service connection", cr, app);
                    }
                } else {
                    nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
                            now,
                            nextIndex, mLruSeq,
                            "service connection", cr, app);
                }
            }
        }
        for (int j = app.conProviders.size() - 1; j >= 0; j--) {
            ContentProviderRecord cpr = app.conProviders.get(j).provider;
            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
                        "provider reference", cpr, app);
            }
        }
    }

    final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) {
        final IBinder threadBinder = thread.asBinder();
        // Find the application record.
        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            final ProcessRecord rec = mLruProcesses.get(i);
            if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
                return rec;
            }
        }
        return null;
    }

    boolean haveBackgroundProcessLocked() {
        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            final ProcessRecord rec = mLruProcesses.get(i);
            if (rec.thread != null
                    && rec.setProcState >= 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("mService")
    void fillInProcMemInfoLocked(ProcessRecord app,
            ActivityManager.RunningAppProcessInfo outInfo,
            int clientTargetSdk) {
        outInfo.pid = app.pid;
        outInfo.uid = app.info.uid;
        if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
            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.trimMemoryLevel;
        int adj = app.curAdj;
        int procState = app.getCurProcState();
        outInfo.importance = procStateToImportance(procState, adj, outInfo,
                clientTargetSdk);
        outInfo.importanceReasonCode = app.adjTypeCode;
        outInfo.processState = app.getCurProcState();
        outInfo.isFocused = (app == mService.getTopAppLocked());
        outInfo.lastActivityTime = app.lastActivityTime;
    }

    @GuardedBy("mService")
    List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(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);
            if ((!allUsers && app.userId != userId)
                    || (!allUids && app.uid != callingUid)) {
                continue;
            }
            if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
                // Generate process state info for running application
                ActivityManager.RunningAppProcessInfo currApp =
                        new ActivityManager.RunningAppProcessInfo(app.processName,
                                app.pid, app.getPackageList());
                fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
                if (app.adjSource instanceof ProcessRecord) {
                    currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
                    currApp.importanceReasonImportance =
                            ActivityManager.RunningAppProcessInfo.procStateToImportance(
                                    app.adjSourceProcState);
                } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
                    final ActivityServiceConnectionsHolder r =
                            (ActivityServiceConnectionsHolder) app.adjSource;
                    final int pid = r.getActivityPid();
                    if (pid != -1) {
                        currApp.importanceReasonPid = pid;
                    }
                }
                if (app.adjTarget instanceof ComponentName) {
                    currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
                }
                //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
                //        + " lru=" + currApp.lru);
                if (runList == null) {
                    runList = new ArrayList<>();
                }
                runList.add(currApp);
            }
        }
        return runList;
    }

    @GuardedBy("mService")
    int getLruSizeLocked() {
        return mLruProcesses.size();
    }

    @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")
    ArrayList<ProcessRecord> collectProcessesLocked(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.pid > 0 && proc.pid == pid) {
                    procs.add(proc);
                } else if (allPkgs && proc.pkgList != null
                        && proc.pkgList.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("mService")
    void updateApplicationInfoLocked(List<String> packagesToUpdate, int userId,
            boolean updateFrameworkRes) {
        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            final ProcessRecord app = mLruProcesses.get(i);
            if (app.thread == null) {
                continue;
            }

            if (userId != UserHandle.USER_ALL && app.userId != userId) {
                continue;
            }

            final int packageCount = app.pkgList.size();
            for (int j = 0; j < packageCount; j++) {
                final String packageName = app.pkgList.keyAt(j);
                if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
                    try {
                        final ApplicationInfo ai = AppGlobals.getPackageManager()
                                .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
                        if (ai != null) {
                            app.thread.scheduleApplicationInfoChanged(ai);
                        }
                    } catch (RemoteException e) {
                        Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
                                packageName, app));
                    }
                }
            }
        }
    }

    @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);
            if (r.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;
                        }
                    }
                    r.thread.dispatchPackageBroadcast(cmd, packages);
                } catch (RemoteException ex) {
                }
            }
        }

        if (!foundProcess) {
            try {
                AppGlobals.getPackageManager().notifyPackagesReplacedReceived(packages);
            } catch (RemoteException ignored) {
            }
        }
    }

    /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */
    @GuardedBy("mService")
    int getUidProcStateLocked(int uid) {
        UidRecord uidRec = mActiveUids.get(uid);
        return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
    }

    /** Returns the UidRecord for the given uid, if it exists. */
    @GuardedBy("mService")
    UidRecord getUidRecordLocked(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.idle) {
                continue;
            }
            mService.doStopUidLocked(uidRec.uid, uidRec);
        }
    }
}