/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.app;

import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningAppProcessInfo.Importance;
import android.icu.text.SimpleDateFormat;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DebugUtils;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import android.util.proto.WireTypeMismatchException;

import com.android.internal.util.ArrayUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Date;
import java.util.Objects;
import java.util.zip.GZIPInputStream;

/**
 * Describes the information of an application process's death.
 *
 * <p>
 * Application process could die for many reasons, for example {@link #REASON_LOW_MEMORY}
 * when it was killed by the system because it was running low on memory. Reason
 * of the death can be retrieved via {@link #getReason}. Besides the reason, there are a few other
 * auxiliary APIs like {@link #getStatus} and {@link #getImportance} to help the caller with
 * additional diagnostic information.
 * </p>
 *
 */
public final class ApplicationExitInfo implements Parcelable {

    /**
     * Application process died due to unknown reason.
     */
    public static final int REASON_UNKNOWN = 0;

    /**
     * Application process exit normally by itself, for example,
     * via {@link java.lang.System#exit}; {@link #getStatus} will specify the exit code.
     *
     * <p>Applications should normally not do this, as the system has a better knowledge
     * in terms of process management.</p>
     */
    public static final int REASON_EXIT_SELF = 1;

    /**
     * Application process died due to the result of an OS signal; for example,
     * {@link android.system.OsConstants#SIGKILL}; {@link #getStatus} will specify the signal
     * number.
     */
    public static final int REASON_SIGNALED = 2;

    /**
     * Application process was killed by the system low memory killer, meaning the system was
     * under memory pressure at the time of kill.
     *
     * <p class="note">
     * Not all devices support reporting {@link #REASON_LOW_MEMORY}; on a device with no such
     * support, when a process is killed due to memory pressure, the {@link #getReason} will return
     * {@link #REASON_SIGNALED} and {@link #getStatus} will return
     * the value {@link android.system.OsConstants#SIGKILL}.
     *
     * Application should use {@link android.app.ActivityManager#isLowMemoryKillReportSupported()
     * ActivityManager.isLowMemoryKillReportSupported()} to check
     * if the device supports reporting {@link #REASON_LOW_MEMORY} or not.
     * </p>
     */
    public static final int REASON_LOW_MEMORY = 3;

    /**
     * Application process died because of an unhandled exception in Java code.
     */
    public static final int REASON_CRASH = 4;

    /**
     * Application process died because of a native code crash.
     */
    public static final int REASON_CRASH_NATIVE = 5;

    /**
     * Application process was killed due to being unresponsive (ANR).
     */
    public static final int REASON_ANR = 6;

    /**
     * Application process was killed because of initialization failure,
     * for example, it took too long to attach to the system during the start,
     * or there was an error during initialization.
     */
    public static final int REASON_INITIALIZATION_FAILURE = 7;

    /**
     * Application process was killed due to a runtime permission change.
     */
    public static final int REASON_PERMISSION_CHANGE = 8;

    /**
     * Application process was killed by the system due to excessive resource usage.
     */
    public static final int REASON_EXCESSIVE_RESOURCE_USAGE = 9;

    /**
     * Application process was killed because of the user request, for example,
     * user clicked the "Force stop" button of the application in the Settings,
     * or removed the application away from Recents.
     */
    public static final int REASON_USER_REQUESTED = 10;

    /**
     * Application process was killed, because the user it is running as on devices
     * with mutlple users, was stopped.
     */
    public static final int REASON_USER_STOPPED = 11;

    /**
     * Application process was killed because its dependency was going away, for example,
     * a stable content provider connection's client will be killed if the provider is killed.
     */
    public static final int REASON_DEPENDENCY_DIED = 12;

    /**
     * Application process was killed by the system for various other reasons which are
     * not by problems in apps and not actionable by apps, for example, the system just
     * finished updates; {@link #getDescription} will specify the cause given by the system.
     */
    public static final int REASON_OTHER = 13;

    /**
     * Application process was killed by App Freezer, for example, because it receives
     * sync binder transactions while being frozen.
     */
    public static final int REASON_FREEZER = 14;

    /**
     * Application process kills subreason is unknown.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_UNKNOWN = 0;

    /**
     * Application process was killed because user quit it on the "wait for debugger" dialog;
     * this would be set when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_WAIT_FOR_DEBUGGER = 1;

    /**
     * Application process was killed by the activity manager because there were too many cached
     * processes; this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_TOO_MANY_CACHED = 2;

    /**
     * Application process was killed by the activity manager because there were too many empty
     * processes; this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_TOO_MANY_EMPTY = 3;

    /**
     * Application process was killed by the activity manager because there were too many cached
     * processes and this process had been in empty state for a long time;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_TRIM_EMPTY = 4;

    /**
     * Application process was killed by the activity manager because system was on memory pressure
     * and this process took large amount of cached memory;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_LARGE_CACHED = 5;

    /**
     * Application process was killed by the activity manager because the system was on low memory
     * pressure for a significant amount of time since last idle;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_MEMORY_PRESSURE = 6;

    /**
     * Application process was killed by the activity manager due to excessive CPU usage;
     * this would be set only when the reason is {@link #REASON_EXCESSIVE_RESOURCE_USAGE}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_EXCESSIVE_CPU = 7;

    /**
     * System update has done (so the system update process should be killed);
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_SYSTEM_UPDATE_DONE = 8;

    /**
     * Kill all foreground services, for now it only occurs when enabling the quiet
     * mode for the managed profile;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_KILL_ALL_FG = 9;

    /**
     * All background processes except certain ones were killed, for now it only occurs
     * when the density of the default display is changed;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_KILL_ALL_BG_EXCEPT = 10;

    /**
     * The process associated with the UID was explicitly killed, for example,
     * it could be because of platform compatibility overrides;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_KILL_UID = 11;

    /**
     * The process was explicitly killed with its PID, typically because of
     * the low memory for surfaces;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_KILL_PID = 12;

    /**
     * The start of the process was invalid;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_INVALID_START = 13;

    /**
     * The process was killed because it's in an invalid state, typically
     * it's triggered from SHELL;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_INVALID_STATE = 14;

    /**
     * The process was killed when it's imperceptible to user, because it was
     * in a bad state;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_IMPERCEPTIBLE = 15;

    /**
     * The process was killed because it's being moved out from LRU list;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_REMOVE_LRU = 16;

    /**
     * The process was killed because it's isolated and was in a cached state;
     * this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_ISOLATED_NOT_NEEDED = 17;

    /**
     * The process was killed because it's in forced-app-standby state, and it's cached and
     * its uid state is idle; this would be set only when the reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY = 18;

    /**
     * The process was killed because it fails to freeze/unfreeze binder
     * or query binder frozen info while being frozen.
     * this would be set only when the reason is {@link #REASON_FREEZER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_FREEZER_BINDER_IOCTL = 19;

    /**
     * The process was killed because it receives sync binder transactions
     * while being frozen.
     * this would be set only when the reason is {@link #REASON_FREEZER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_FREEZER_BINDER_TRANSACTION = 20;

    /**
     * The process was killed because of force-stop, it could be due to that
     * the user clicked the "Force stop" button of the application in the Settings;
     * this would be set only when the reason is {@link #REASON_USER_REQUESTED}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_FORCE_STOP = 21;

    /**
     * The process was killed because the user removed the application away from Recents;
     * this would be set only when the reason is {@link #REASON_USER_REQUESTED}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_REMOVE_TASK = 22;

    /**
     * The process was killed because the user stopped the application from the task manager;
     * this would be set only when the reason is {@link #REASON_USER_REQUESTED}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_STOP_APP = 23;

    /**
     * The process was killed because the user stopped the application from developer options,
     * or via the adb shell commmand interface; this would be set only when the reason is
     * {@link #REASON_USER_REQUESTED}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_KILL_BACKGROUND = 24;

    /**
     * The process was killed because of package update; this would be set only when the reason is
     * {@link #REASON_USER_REQUESTED}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_PACKAGE_UPDATE = 25;

    /**
     * The process was killed because of undelivered broadcasts; this would be set only when the
     * reason is {@link #REASON_OTHER}.
     *
     * For internal use only.
     * @hide
     */
    public static final int SUBREASON_UNDELIVERED_BROADCAST = 26;

    // If there is any OEM code which involves additional app kill reasons, it should
    // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.

    /**
     * @see #getPid
     */
    private int mPid;

    /**
     * @see #getRealUid
     */
    private int mRealUid;

    /**
     * @see #getPackageUid
     */
    private int mPackageUid;

    /**
     * @see #getDefiningUid
     */
    private int mDefiningUid;

    /**
     * @see #getProcessName
     */
    private String mProcessName;

    /**
     * @see #getReason
     */
    private @Reason int mReason;

    /**
     * @see #getStatus
     */
    private int mStatus;

    /**
     * @see #getImportance
     */
    private @Importance int mImportance;

    /**
     * @see #getPss
     */
    private long mPss;

    /**
     * @see #getRss
     */
    private long mRss;

    /**
     * @see #getTimestamp
     */
    private @CurrentTimeMillisLong long mTimestamp;

    /**
     * @see #getDescription
     */
    private @Nullable String mDescription;

    /**
     * @see #getSubReason
     */
    private @SubReason int mSubReason;

    /**
     * @see #getConnectionGroup
     */
    private int mConnectionGroup;

    /**
     * @see #getPackageName
     */
    private String mPackageName;

    /**
     * @see #getPackageList
     */
    private String[] mPackageList;

    /**
     * @see #getProcessStateSummary
     */
    private byte[] mState;

    /**
     * The file to the trace file in the storage;
     *
     * for system internal use only, will not retain across processes.
     *
     * @see #getTraceInputStream
     */
    private File mTraceFile;

    /**
     * The Binder interface to retrieve the file descriptor to
     * the trace file from the system.
     */
    private IAppTraceRetriever mAppTraceRetriever;

    /**
     * ParcelFileDescriptor pointing to a native tombstone.
     *
     * @see #getTraceInputStream
     */
    private IParcelFileDescriptorRetriever mNativeTombstoneRetriever;

    /**
     * Whether or not we've logged this into the statsd.
     *
     * for system internal use only, will not retain across processes.
     */
    private boolean mLoggedInStatsd;

    /**
     * Whether or not this process hosts one or more foreground services.
     *
     * for system internal use only, will not retain across processes.
     */
    private boolean mHasForegroundServices;

    /** @hide */
    @IntDef(prefix = { "REASON_" }, value = {
        REASON_UNKNOWN,
        REASON_EXIT_SELF,
        REASON_SIGNALED,
        REASON_LOW_MEMORY,
        REASON_CRASH,
        REASON_CRASH_NATIVE,
        REASON_ANR,
        REASON_INITIALIZATION_FAILURE,
        REASON_PERMISSION_CHANGE,
        REASON_EXCESSIVE_RESOURCE_USAGE,
        REASON_USER_REQUESTED,
        REASON_USER_STOPPED,
        REASON_DEPENDENCY_DIED,
        REASON_OTHER,
        REASON_FREEZER,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Reason {}

    /** @hide */
    @IntDef(prefix = { "SUBREASON_" }, value = {
        SUBREASON_UNKNOWN,
        SUBREASON_WAIT_FOR_DEBUGGER,
        SUBREASON_TOO_MANY_CACHED,
        SUBREASON_TOO_MANY_EMPTY,
        SUBREASON_TRIM_EMPTY,
        SUBREASON_LARGE_CACHED,
        SUBREASON_MEMORY_PRESSURE,
        SUBREASON_EXCESSIVE_CPU,
        SUBREASON_SYSTEM_UPDATE_DONE,
        SUBREASON_KILL_ALL_FG,
        SUBREASON_KILL_ALL_BG_EXCEPT,
        SUBREASON_KILL_UID,
        SUBREASON_KILL_PID,
        SUBREASON_INVALID_START,
        SUBREASON_INVALID_STATE,
        SUBREASON_IMPERCEPTIBLE,
        SUBREASON_REMOVE_LRU,
        SUBREASON_ISOLATED_NOT_NEEDED,
        SUBREASON_FREEZER_BINDER_IOCTL,
        SUBREASON_FREEZER_BINDER_TRANSACTION,
        SUBREASON_FORCE_STOP,
        SUBREASON_REMOVE_TASK,
        SUBREASON_STOP_APP,
        SUBREASON_KILL_BACKGROUND,
        SUBREASON_PACKAGE_UPDATE,
        SUBREASON_UNDELIVERED_BROADCAST,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SubReason {}

    /**
     * The process id of the process that died.
     */
    public int getPid() {
        return mPid;
    }

    /**
     * The kernel user identifier of the process, most of the time the system uses this
     * to do access control checks. It's typically the uid of the package where the component is
     * running from, except the case of isolated process, where this field identifies the kernel
     * user identifier that this process is actually running with, while the {@link #getPackageUid}
     * identifies the kernel user identifier that is assigned at the package installation time.
     */
    public int getRealUid() {
        return mRealUid;
    }

    /**
     * Similar to {@link #getRealUid}, it's the kernel user identifier that is assigned at the
     * package installation time.
     */
    public int getPackageUid() {
        return mPackageUid;
    }

    /**
     * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and
     * {@link #getPackageUid}, if an external service has the
     * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set
     * to <code>true</code> and was bound with the flag
     * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will
     * be the kernel user identifier of the external service provider.
     */
    public int getDefiningUid() {
        return mDefiningUid;
    }

    /**
     * The actual process name it was running with.
     */
    public @NonNull String getProcessName() {
        return mProcessName;
    }

    /**
     * The reason code of the process's death.
     */
    public @Reason int getReason() {
        return mReason;
    }

    /**
     * The exit status argument of exit() if the application calls it, or the signal
     * number if the application is signaled.
     */
    public int getStatus() {
        return mStatus;
    }

    /**
     * The importance of the process that it used to have before the death.
     */
    public @Importance int getImportance() {
        return mImportance;
    }

    /**
     * Last proportional set size of the memory that the process had used in kB.
     *
     * <p class="note">Note: This is the value from last sampling on the process,
     * it's NOT the exact memory information prior to its death; and it'll be zero
     * if the process died before system had a chance to take the sample. </p>
     */
    public long getPss() {
        return mPss;
    }

    /**
     * Last resident set size of the memory that the process had used in kB.
     *
     * <p class="note">Note: This is the value from last sampling on the process,
     * it's NOT the exact memory information prior to its death; and it'll be zero
     * if the process died before system had a chance to take the sample. </p>
     */
    public long getRss() {
        return mRss;
    }

    /**
     * The timestamp of the process's death, in milliseconds since the epoch,
     * as returned by {@link java.lang.System#currentTimeMillis() System.currentTimeMillis()}.
     */
    public @CurrentTimeMillisLong long getTimestamp() {
        return mTimestamp;
    }

    /**
     * The human readable description of the process's death, given by the system; could be null.
     *
     * <p class="note">Note: only intended to be human-readable and the system provides no
     * guarantees that the format is stable across devices or Android releases.</p>
     */
    public @Nullable String getDescription() {
        return mDescription;
    }

    /**
     * Return the user id of the record on a multi-user system.
     */
    public @NonNull UserHandle getUserHandle() {
        return UserHandle.of(UserHandle.getUserId(mRealUid));
    }

    /**
     * Return the state data set by calling
     * {@link android.app.ActivityManager#setProcessStateSummary(byte[])
     * ActivityManager.setProcessStateSummary(byte[])} from the process before its death.
     *
     * @return The process-customized data
     * @see ActivityManager#setProcessStateSummary(byte[])
     */
    public @Nullable byte[] getProcessStateSummary() {
        return mState;
    }

    /**
     * Return the InputStream to the traces that was taken by the system
     * prior to the death of the process; typically it'll be available when
     * the reason is {@link #REASON_ANR}, though if the process gets an ANR
     * but recovers, and dies for another reason later, this trace will be included
     * in the record of {@link ApplicationExitInfo} still. Beginning with API 31,
     * tombstone traces will be returned for
     * {@link #REASON_CRASH_NATIVE}, with an InputStream containing a protobuf with
     * <a href="https://android.googlesource.com/platform/system/core/+/refs/heads/master/debuggerd/proto/tombstone.proto">this schema</a>.
     * Note that because these traces are kept in a separate global circular buffer, crashes may be
     * overwritten by newer crashes (including from other applications), so this may still return
     * null.
     *
     * @return The input stream to the traces that was taken by the system
     *         prior to the death of the process.
     */
    public @Nullable InputStream getTraceInputStream() throws IOException {
        if (mAppTraceRetriever == null && mNativeTombstoneRetriever == null) {
            return null;
        }

        try {
            if (mNativeTombstoneRetriever != null) {
                final ParcelFileDescriptor pfd = mNativeTombstoneRetriever.getPfd();
                if (pfd == null) {
                    return null;
                }

                return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
            } else {
                final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
                        mPackageName, mPackageUid, mPid);
                if (fd == null) {
                    return null;
                }
                return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
            }
        } catch (RemoteException e) {
            return null;
        }
    }

    /**
     * Similar to {@link #getTraceInputStream} but return the File object.
     *
     * For internal use only.
     *
     * @hide
     */
    public @Nullable File getTraceFile() {
        return mTraceFile;
    }

    /**
     * A subtype reason in conjunction with {@link #mReason}.
     *
     * For internal use only.
     *
     * @hide
     */
    public @SubReason int getSubReason() {
        return mSubReason;
    }

    /**
     * The connection group this process belongs to, if there is any.
     * @see android.content.Context#updateServiceGroup
     *
     * For internal use only.
     *
     * @hide
     */
    public int getConnectionGroup() {
        return mConnectionGroup;
    }

    /**
     * Name of first package running in this process;
     *
     * @hide
     */
    public String getPackageName() {
        return mPackageName;
    }

    /**
     * List of packages running in this process;
     *
     * For system internal use only, will not retain across processes.
     *
     * @hide
     */
    public String[] getPackageList() {
        return mPackageList;
    }

    /**
     * @see #getPid
     *
     * @hide
     */
    public void setPid(final int pid) {
        mPid = pid;
    }

    /**
     * @see #getRealUid
     *
     * @hide
     */
    public void setRealUid(final int uid) {
        mRealUid = uid;
    }

    /**
     * @see #getPackageUid
     *
     * @hide
     */
    public void setPackageUid(final int uid) {
        mPackageUid = uid;
    }

    /**
     * @see #getDefiningUid
     *
     * @hide
     */
    public void setDefiningUid(final int uid) {
        mDefiningUid = uid;
    }

    /**
     * @see #getProcessName
     *
     * @hide
     */
    public void setProcessName(final String processName) {
        mProcessName = intern(processName);
    }

    /**
     * @see #getReason
     *
     * @hide
     */
    public void setReason(final @Reason int reason) {
        mReason = reason;
    }

    /**
     * @see #getStatus
     *
     * @hide
     */
    public void setStatus(final int status) {
        mStatus = status;
    }

    /**
     * @see #getImportance
     *
     * @hide
     */
    public void setImportance(final @Importance int importance) {
        mImportance = importance;
    }

    /**
     * @see #getPss
     *
     * @hide
     */
    public void setPss(final long pss) {
        mPss = pss;
    }

    /**
     * @see #getRss
     *
     * @hide
     */
    public void setRss(final long rss) {
        mRss = rss;
    }

    /**
     * @see #getTimestamp
     *
     * @hide
     */
    public void setTimestamp(final @CurrentTimeMillisLong long timestamp) {
        mTimestamp = timestamp;
    }

    /**
     * @see #getDescription
     *
     * @hide
     */
    public void setDescription(final String description) {
        mDescription = intern(description);
    }

    /**
     * @see #getSubReason
     *
     * @hide
     */
    public void setSubReason(final @SubReason int subReason) {
        mSubReason = subReason;
    }

    /**
     * @see #getConnectionGroup
     *
     * @hide
     */
    public void setConnectionGroup(final int connectionGroup) {
        mConnectionGroup = connectionGroup;
    }

    /**
     * @see #getPackageName
     *
     * @hide
     */
    public void setPackageName(final String packageName) {
        mPackageName = intern(packageName);
    }

    /**
     * @see #getPackageList
     *
     * @hide
     */
    public void setPackageList(final String[] packageList) {
        mPackageList = packageList;
    }

    /**
     * @see #getProcessStateSummary
     *
     * @hide
     */
    public void setProcessStateSummary(final byte[] state) {
        mState = state;
    }

    /**
     * @see #getTraceFile
     *
     * @hide
     */
    public void setTraceFile(final File traceFile) {
        mTraceFile = traceFile;
    }

    /**
     * @see #mAppTraceRetriever
     *
     * @hide
     */
    public void setAppTraceRetriever(final IAppTraceRetriever retriever) {
        mAppTraceRetriever = retriever;
    }

    /**
     * @see mNativeTombstoneRetriever
     *
     * @hide
     */
    public void setNativeTombstoneRetriever(final IParcelFileDescriptorRetriever retriever) {
        mNativeTombstoneRetriever = retriever;
    }

    /**
     * @see #mLoggedInStatsd
     *
     * @hide
     */
    public boolean isLoggedInStatsd() {
        return mLoggedInStatsd;
    }

    /**
     * @see #mLoggedInStatsd
     *
     * @hide
     */
    public void setLoggedInStatsd(boolean loggedInStatsd) {
        mLoggedInStatsd = loggedInStatsd;
    }

    /**
     * @see #mHasForegroundServices
     *
     * @hide
     */
    public boolean hasForegroundServices() {
        return mHasForegroundServices;
    }

    /**
     * @see #mHasForegroundServices
     *
     * @hide
     */
    public void setHasForegroundServices(boolean hasForegroundServices) {
        mHasForegroundServices = hasForegroundServices;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mPid);
        dest.writeInt(mRealUid);
        dest.writeInt(mPackageUid);
        dest.writeInt(mDefiningUid);
        dest.writeString(mProcessName);
        dest.writeString(mPackageName);
        dest.writeInt(mConnectionGroup);
        dest.writeInt(mReason);
        dest.writeInt(mSubReason);
        dest.writeInt(mStatus);
        dest.writeInt(mImportance);
        dest.writeLong(mPss);
        dest.writeLong(mRss);
        dest.writeLong(mTimestamp);
        dest.writeString(mDescription);
        dest.writeByteArray(mState);
        if (mAppTraceRetriever != null) {
            dest.writeInt(1);
            dest.writeStrongBinder(mAppTraceRetriever.asBinder());
        } else {
            dest.writeInt(0);
        }
        if (mNativeTombstoneRetriever != null) {
            dest.writeInt(1);
            dest.writeStrongBinder(mNativeTombstoneRetriever.asBinder());
        } else {
            dest.writeInt(0);
        }
    }

    /** @hide */
    public ApplicationExitInfo() {
    }

    /** @hide */
    public ApplicationExitInfo(ApplicationExitInfo other) {
        mPid = other.mPid;
        mRealUid = other.mRealUid;
        mPackageUid = other.mPackageUid;
        mDefiningUid = other.mDefiningUid;
        mProcessName = other.mProcessName;
        mPackageName = other.mPackageName;
        mConnectionGroup = other.mConnectionGroup;
        mReason = other.mReason;
        mStatus = other.mStatus;
        mSubReason = other.mSubReason;
        mImportance = other.mImportance;
        mPss = other.mPss;
        mRss = other.mRss;
        mTimestamp = other.mTimestamp;
        mDescription = other.mDescription;
        mPackageName = other.mPackageName;
        mPackageList = other.mPackageList;
        mState = other.mState;
        mTraceFile = other.mTraceFile;
        mAppTraceRetriever = other.mAppTraceRetriever;
        mNativeTombstoneRetriever = other.mNativeTombstoneRetriever;
        mLoggedInStatsd = other.mLoggedInStatsd;
        mHasForegroundServices = other.mHasForegroundServices;
    }

    private ApplicationExitInfo(@NonNull Parcel in) {
        mPid = in.readInt();
        mRealUid = in.readInt();
        mPackageUid = in.readInt();
        mDefiningUid = in.readInt();
        mProcessName = intern(in.readString());
        mPackageName = intern(in.readString());
        mConnectionGroup = in.readInt();
        mReason = in.readInt();
        mSubReason = in.readInt();
        mStatus = in.readInt();
        mImportance = in.readInt();
        mPss = in.readLong();
        mRss = in.readLong();
        mTimestamp = in.readLong();
        mDescription = intern(in.readString());
        mState = in.createByteArray();
        if (in.readInt() == 1) {
            mAppTraceRetriever = IAppTraceRetriever.Stub.asInterface(in.readStrongBinder());
        }
        if (in.readInt() == 1) {
            mNativeTombstoneRetriever = IParcelFileDescriptorRetriever.Stub.asInterface(
                    in.readStrongBinder());
        }
    }

    private static String intern(@Nullable String source) {
        return source != null ? source.intern() : null;
    }

    public @NonNull static final Creator<ApplicationExitInfo> CREATOR =
            new Creator<ApplicationExitInfo>() {
        @Override
        public ApplicationExitInfo createFromParcel(Parcel in) {
            return new ApplicationExitInfo(in);
        }

        @Override
        public ApplicationExitInfo[] newArray(int size) {
            return new ApplicationExitInfo[size];
        }
    };

    /** @hide */
    public void dump(@NonNull PrintWriter pw, @Nullable String prefix, @Nullable String seqSuffix,
            @NonNull SimpleDateFormat sdf) {
        StringBuilder sb = new StringBuilder();
        sb.append(prefix)
                .append("ApplicationExitInfo ").append(seqSuffix).append(':')
                .append('\n');
        sb.append(prefix).append(' ')
                .append(" timestamp=").append(sdf.format(new Date(mTimestamp)))
                .append(" pid=").append(mPid)
                .append(" realUid=").append(mRealUid)
                .append(" packageUid=").append(mPackageUid)
                .append(" definingUid=").append(mDefiningUid)
                .append(" user=").append(UserHandle.getUserId(mPackageUid))
                .append('\n');
        sb.append(prefix).append(' ')
                .append(" process=").append(mProcessName)
                .append(" reason=").append(mReason)
                .append(" (").append(reasonCodeToString(mReason)).append(")")
                .append(" subreason=").append(mSubReason)
                .append(" (").append(subreasonToString(mSubReason)).append(")")
                .append(" status=").append(mStatus)
                .append('\n');
        sb.append(prefix).append(' ')
                .append(" importance=").append(mImportance)
                .append(" pss=");
        DebugUtils.sizeValueToString(mPss << 10, sb);
        sb.append(" rss=");
        DebugUtils.sizeValueToString(mRss << 10, sb);
        sb.append(" description=").append(mDescription)
                .append(" state=").append((ArrayUtils.isEmpty(mState)
                            ? "empty" : Integer.toString(mState.length) + " bytes"))
                .append(" trace=").append(mTraceFile)
                .append('\n');
        pw.print(sb.toString());
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("ApplicationExitInfo(timestamp=");
        sb.append(new SimpleDateFormat().format(new Date(mTimestamp)));
        sb.append(" pid=").append(mPid);
        sb.append(" realUid=").append(mRealUid);
        sb.append(" packageUid=").append(mPackageUid);
        sb.append(" definingUid=").append(mDefiningUid);
        sb.append(" user=").append(UserHandle.getUserId(mPackageUid));
        sb.append(" process=").append(mProcessName);
        sb.append(" reason=").append(mReason).append(" (")
                .append(reasonCodeToString(mReason)).append(")");
        sb.append(" subreason=").append(mSubReason).append(" (")
                .append(subreasonToString(mSubReason)).append(")");
        sb.append(" status=").append(mStatus);
        sb.append(" importance=").append(mImportance);
        sb.append(" pss="); DebugUtils.sizeValueToString(mPss << 10, sb);
        sb.append(" rss="); DebugUtils.sizeValueToString(mRss << 10, sb);
        sb.append(" description=").append(mDescription);
        sb.append(" state=").append(ArrayUtils.isEmpty(mState)
                ? "empty" : Integer.toString(mState.length) + " bytes");
        sb.append(" trace=").append(mTraceFile);

        return sb.toString();
    }

    /** @hide */
    public static String reasonCodeToString(@Reason int reason) {
        switch (reason) {
            case REASON_EXIT_SELF:
                return "EXIT_SELF";
            case REASON_SIGNALED:
                return "SIGNALED";
            case REASON_LOW_MEMORY:
                return "LOW_MEMORY";
            case REASON_CRASH:
                return "APP CRASH(EXCEPTION)";
            case REASON_CRASH_NATIVE:
                return "APP CRASH(NATIVE)";
            case REASON_ANR:
                return "ANR";
            case REASON_INITIALIZATION_FAILURE:
                return "INITIALIZATION FAILURE";
            case REASON_PERMISSION_CHANGE:
                return "PERMISSION CHANGE";
            case REASON_EXCESSIVE_RESOURCE_USAGE:
                return "EXCESSIVE RESOURCE USAGE";
            case REASON_USER_REQUESTED:
                return "USER REQUESTED";
            case REASON_USER_STOPPED:
                return "USER STOPPED";
            case REASON_DEPENDENCY_DIED:
                return "DEPENDENCY DIED";
            case REASON_OTHER:
                return "OTHER KILLS BY SYSTEM";
            case REASON_FREEZER:
                return "FREEZER";
            default:
                return "UNKNOWN";
        }
    }

    /** @hide */
    public static String subreasonToString(@SubReason int subreason) {
        switch (subreason) {
            case SUBREASON_WAIT_FOR_DEBUGGER:
                return "WAIT FOR DEBUGGER";
            case SUBREASON_TOO_MANY_CACHED:
                return "TOO MANY CACHED PROCS";
            case SUBREASON_TOO_MANY_EMPTY:
                return "TOO MANY EMPTY PROCS";
            case SUBREASON_TRIM_EMPTY:
                return "TRIM EMPTY";
            case SUBREASON_LARGE_CACHED:
                return "LARGE CACHED";
            case SUBREASON_MEMORY_PRESSURE:
                return "MEMORY PRESSURE";
            case SUBREASON_EXCESSIVE_CPU:
                return "EXCESSIVE CPU USAGE";
            case SUBREASON_SYSTEM_UPDATE_DONE:
                return "SYSTEM UPDATE_DONE";
            case SUBREASON_KILL_ALL_FG:
                return "KILL ALL FG";
            case SUBREASON_KILL_ALL_BG_EXCEPT:
                return "KILL ALL BG EXCEPT";
            case SUBREASON_KILL_UID:
                return "KILL UID";
            case SUBREASON_KILL_PID:
                return "KILL PID";
            case SUBREASON_INVALID_START:
                return "INVALID START";
            case SUBREASON_INVALID_STATE:
                return "INVALID STATE";
            case SUBREASON_IMPERCEPTIBLE:
                return "IMPERCEPTIBLE";
            case SUBREASON_REMOVE_LRU:
                return "REMOVE LRU";
            case SUBREASON_ISOLATED_NOT_NEEDED:
                return "ISOLATED NOT NEEDED";
            case SUBREASON_FREEZER_BINDER_IOCTL:
                return "FREEZER BINDER IOCTL";
            case SUBREASON_FREEZER_BINDER_TRANSACTION:
                return "FREEZER BINDER TRANSACTION";
            case SUBREASON_FORCE_STOP:
                return "FORCE STOP";
            case SUBREASON_REMOVE_TASK:
                return "REMOVE TASK";
            case SUBREASON_STOP_APP:
                return "STOP APP";
            case SUBREASON_KILL_BACKGROUND:
                return "KILL BACKGROUND";
            case SUBREASON_PACKAGE_UPDATE:
                return "PACKAGE UPDATE";
            case SUBREASON_UNDELIVERED_BROADCAST:
                return "UNDELIVERED BROADCAST";
            default:
                return "UNKNOWN";
        }
    }

    /**
     * Write to a protocol buffer output stream.
     * Protocol buffer message definition at {@link android.app.ApplicationExitInfoProto}
     *
     * @param proto    Stream to write the ApplicationExitInfo object to.
     * @param fieldId  Field Id of the ApplicationExitInfo as defined in the parent message
     * @hide
     */
    public void writeToProto(ProtoOutputStream proto, long fieldId) {
        final long token = proto.start(fieldId);
        proto.write(ApplicationExitInfoProto.PID, mPid);
        proto.write(ApplicationExitInfoProto.REAL_UID, mRealUid);
        proto.write(ApplicationExitInfoProto.PACKAGE_UID, mPackageUid);
        proto.write(ApplicationExitInfoProto.DEFINING_UID, mDefiningUid);
        proto.write(ApplicationExitInfoProto.PROCESS_NAME, mProcessName);
        proto.write(ApplicationExitInfoProto.CONNECTION_GROUP, mConnectionGroup);
        proto.write(ApplicationExitInfoProto.REASON, mReason);
        proto.write(ApplicationExitInfoProto.SUB_REASON, mSubReason);
        proto.write(ApplicationExitInfoProto.STATUS, mStatus);
        proto.write(ApplicationExitInfoProto.IMPORTANCE, mImportance);
        proto.write(ApplicationExitInfoProto.PSS, mPss);
        proto.write(ApplicationExitInfoProto.RSS, mRss);
        proto.write(ApplicationExitInfoProto.TIMESTAMP, mTimestamp);
        proto.write(ApplicationExitInfoProto.DESCRIPTION, mDescription);
        proto.write(ApplicationExitInfoProto.STATE, mState);
        proto.write(ApplicationExitInfoProto.TRACE_FILE,
                mTraceFile == null ? null : mTraceFile.getAbsolutePath());
        proto.end(token);
    }

    /**
     * Read from a protocol buffer input stream.
     * Protocol buffer message definition at {@link android.app.ApplicationExitInfoProto}
     *
     * @param proto   Stream to read the ApplicationExitInfo object from.
     * @param fieldId Field Id of the ApplicationExitInfo as defined in the parent message
     * @hide
     */
    public void readFromProto(ProtoInputStream proto, long fieldId)
            throws IOException, WireTypeMismatchException {
        final long token = proto.start(fieldId);
        while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
            switch (proto.getFieldNumber()) {
                case (int) ApplicationExitInfoProto.PID:
                    mPid = proto.readInt(ApplicationExitInfoProto.PID);
                    break;
                case (int) ApplicationExitInfoProto.REAL_UID:
                    mRealUid = proto.readInt(ApplicationExitInfoProto.REAL_UID);
                    break;
                case (int) ApplicationExitInfoProto.PACKAGE_UID:
                    mPackageUid = proto.readInt(ApplicationExitInfoProto.PACKAGE_UID);
                    break;
                case (int) ApplicationExitInfoProto.DEFINING_UID:
                    mDefiningUid = proto.readInt(ApplicationExitInfoProto.DEFINING_UID);
                    break;
                case (int) ApplicationExitInfoProto.PROCESS_NAME:
                    mProcessName = intern(proto.readString(ApplicationExitInfoProto.PROCESS_NAME));
                    break;
                case (int) ApplicationExitInfoProto.CONNECTION_GROUP:
                    mConnectionGroup = proto.readInt(ApplicationExitInfoProto.CONNECTION_GROUP);
                    break;
                case (int) ApplicationExitInfoProto.REASON:
                    mReason = proto.readInt(ApplicationExitInfoProto.REASON);
                    break;
                case (int) ApplicationExitInfoProto.SUB_REASON:
                    mSubReason = proto.readInt(ApplicationExitInfoProto.SUB_REASON);
                    break;
                case (int) ApplicationExitInfoProto.STATUS:
                    mStatus = proto.readInt(ApplicationExitInfoProto.STATUS);
                    break;
                case (int) ApplicationExitInfoProto.IMPORTANCE:
                    mImportance = proto.readInt(ApplicationExitInfoProto.IMPORTANCE);
                    break;
                case (int) ApplicationExitInfoProto.PSS:
                    mPss = proto.readLong(ApplicationExitInfoProto.PSS);
                    break;
                case (int) ApplicationExitInfoProto.RSS:
                    mRss = proto.readLong(ApplicationExitInfoProto.RSS);
                    break;
                case (int) ApplicationExitInfoProto.TIMESTAMP:
                    mTimestamp = proto.readLong(ApplicationExitInfoProto.TIMESTAMP);
                    break;
                case (int) ApplicationExitInfoProto.DESCRIPTION:
                    mDescription = intern(proto.readString(ApplicationExitInfoProto.DESCRIPTION));
                    break;
                case (int) ApplicationExitInfoProto.STATE:
                    mState = proto.readBytes(ApplicationExitInfoProto.STATE);
                    break;
                case (int) ApplicationExitInfoProto.TRACE_FILE:
                    final String path = proto.readString(ApplicationExitInfoProto.TRACE_FILE);
                    if (!TextUtils.isEmpty(path)) {
                        mTraceFile = new File(path);
                    }
                    break;
            }
        }
        proto.end(token);
    }

    @Override
    public boolean equals(@Nullable Object other) {
        if (other == null || !(other instanceof ApplicationExitInfo)) {
            return false;
        }
        ApplicationExitInfo o = (ApplicationExitInfo) other;
        return mPid == o.mPid && mRealUid == o.mRealUid && mPackageUid == o.mPackageUid
                && mDefiningUid == o.mDefiningUid
                && mConnectionGroup == o.mConnectionGroup && mReason == o.mReason
                && mSubReason == o.mSubReason && mImportance == o.mImportance
                && mStatus == o.mStatus && mTimestamp == o.mTimestamp
                && mPss == o.mPss && mRss == o.mRss
                && TextUtils.equals(mProcessName, o.mProcessName)
                && TextUtils.equals(mDescription, o.mDescription);
    }

    @Override
    public int hashCode() {
        int result = mPid;
        result = 31 * result + mRealUid;
        result = 31 * result + mPackageUid;
        result = 31 * result + mDefiningUid;
        result = 31 * result + mConnectionGroup;
        result = 31 * result + mReason;
        result = 31 * result + mSubReason;
        result = 31 * result + mImportance;
        result = 31 * result + mStatus;
        result = 31 * result + (int) mPss;
        result = 31 * result + (int) mRss;
        result = 31 * result + Long.hashCode(mTimestamp);
        result = 31 * result + Objects.hashCode(mProcessName);
        result = 31 * result + Objects.hashCode(mDescription);
        return result;
    }
}
