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

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

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

Applications should normally not do this, as the system has a better knowledge * in terms of process management.

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

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

*/ 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 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; // 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; /** @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, }) @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, }) @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 true 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. * *

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.

*/ public long getPss() { return mPss; } /** * Last resident set size of the memory that the process had used in kB. * *

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.

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

Note: only intended to be human-readable and the system provides no * guarantees that the format is stable across devices or Android releases.

*/ 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 * this schema. * 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 = 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 = 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 = 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; } @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; } private ApplicationExitInfo(@NonNull Parcel in) { mPid = in.readInt(); mRealUid = in.readInt(); mPackageUid = in.readInt(); mDefiningUid = in.readInt(); mProcessName = in.readString(); mPackageName = 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 = 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()); } } public @NonNull static final Creator CREATOR = new Creator() { @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) { pw.println(prefix + "ApplicationExitInfo " + seqSuffix + ":"); pw.println(prefix + " timestamp=" + sdf.format(new Date(mTimestamp))); pw.println(prefix + " pid=" + mPid); pw.println(prefix + " realUid=" + mRealUid); pw.println(prefix + " packageUid=" + mPackageUid); pw.println(prefix + " definingUid=" + mDefiningUid); pw.println(prefix + " user=" + UserHandle.getUserId(mPackageUid)); pw.println(prefix + " process=" + mProcessName); pw.println(prefix + " reason=" + mReason + " (" + reasonCodeToString(mReason) + ")"); pw.println(prefix + " subreason=" + mSubReason + " (" + subreasonToString(mSubReason) + ")"); pw.println(prefix + " status=" + mStatus); pw.println(prefix + " importance=" + mImportance); pw.print(prefix + " pss="); DebugUtils.printSizeValue(pw, mPss << 10); pw.println(); pw.print(prefix + " rss="); DebugUtils.printSizeValue(pw, mRss << 10); pw.println(); pw.println(prefix + " description=" + mDescription); pw.println(prefix + " state=" + (ArrayUtils.isEmpty(mState) ? "empty" : Integer.toString(mState.length) + " bytes")); pw.println(prefix + " trace=" + mTraceFile); } @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(); } private 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"; 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"; 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 = 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 = 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; } }