diff options
| author | Jing Ji <jji@google.com> | 2020-01-19 18:37:04 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-01-19 18:37:04 +0000 |
| commit | 4a9cddd4076eb86ea42a3ceec3995eac7b03dfb3 (patch) | |
| tree | 566d34b5f338bad227eb20384661190947d0cbec /core/java/android | |
| parent | bf9c28900dbed9558a1cffac297c30c62ab72a4a (diff) | |
| parent | 8055a3a44617183f13d59499a7009637715b3852 (diff) | |
Merge changes from topic "app_kill_stats"
* changes:
Add support to get historical app process exit reason
Add support to send notification from Zygote to system_server
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/app/ActivityManager.java | 45 | ||||
| -rw-r--r-- | core/java/android/app/ApplicationExitInfo.aidl | 19 | ||||
| -rw-r--r-- | core/java/android/app/ApplicationExitInfo.java | 901 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.aidl | 27 |
4 files changed, 992 insertions, 0 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e1bd6aeb6f48..8753b986f2df 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -22,6 +22,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import android.Manifest; import android.annotation.DrawableRes; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -3480,6 +3481,50 @@ public class ActivityManager { } /** + * Return a list of {@link ApplicationExitInfo} records containing the reasons for the most + * recent app deaths. + * + * <p class="note"> Note: System stores this historical information in a ring buffer and only + * the most recent records will be returned. </p> + * + * <p class="note"> Note: In the case that this application was bound to an external service + * with flag {@link android.content.Context#BIND_EXTERNAL_SERVICE}, the process of that external + * service will be included in this package's exit info. </p> + * + * @param packageName Optional, a null value means match all packages belonging to the + * caller's UID. If this package belongs to another UID, you must hold + * {@link android.Manifest.permission#DUMP} in order to retrieve it. + * @param pid A process ID that used to belong to this package but died later; a value + * of 0 means to ignore this parameter and return all matching records. + * @param maxNum The maximum number of results to be returned; a value of 0 + * means to ignore this parameter and return all matching records + * + * @return a list of {@link ApplicationExitInfo} records matching the criteria, sorted in + * the order from most recent to least recent. + */ + @Nullable + public List<ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String packageName, + @IntRange(from = 0) int pid, @IntRange(from = 0) int maxNum) { + try { + ParceledListSlice<ApplicationExitInfo> r = getService().getHistoricalProcessExitReasons( + packageName, pid, maxNum, mContext.getUserId()); + return r == null ? null : r.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /* + * @return Whether or not the low memory kill will be reported in + * {@link #getHistoricalProcessExitReasons}. + * + * @see {@link ApplicationExitInfo#REASON_LOW_MEMORY} + */ + public static boolean isLowMemoryKillReportSupported() { + return SystemProperties.getBoolean("persist.sys.lmk.reportkills", false); + } + + /** * Return the importance of a given package name, based on the processes that are * currently running. The return value is one of the importance constants defined * in {@link RunningAppProcessInfo}, giving you the highest importance of all the diff --git a/core/java/android/app/ApplicationExitInfo.aidl b/core/java/android/app/ApplicationExitInfo.aidl new file mode 100644 index 000000000000..fb7d8f605c96 --- /dev/null +++ b/core/java/android/app/ApplicationExitInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable ApplicationExitInfo; diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java new file mode 100644 index 000000000000..4bf5f07e86be --- /dev/null +++ b/core/java/android/app/ApplicationExitInfo.java @@ -0,0 +1,901 @@ +/* + * 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.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.ActivityManager.RunningAppProcessInfo.Importance; +import android.icu.text.SimpleDateFormat; +import android.os.Parcel; +import android.os.Parcelable; +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 java.io.IOException; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Date; +import java.util.Objects; + +/** + * 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 ystem 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 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 by the system for various other reasons, + * for example, the application package got disabled by the user; + * {@link #getDescription} will specify the cause given by the system. + */ + public static final int REASON_OTHER = 10; + + /** + * 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; + + /** + * @see {@link #getPid} + */ + private int mPid; + + /** + * @see {@link #getRealUid} + */ + private int mRealUid; + + /** + * @see {@link #getPackageUid} + */ + private int mPackageUid; + + /** + * @see {@link #getDefiningUid} + */ + private int mDefiningUid; + + /** + * @see {@link #getProcessName} + */ + private String mProcessName; + + /** + * @see {@link #getReason} + */ + private @Reason int mReason; + + /** + * @see {@link #getStatus} + */ + private int mStatus; + + /** + * @see {@link #getImportance} + */ + private @Importance int mImportance; + + /** + * @see {@link #getPss} + */ + private int mPss; + + /** + * @see {@link #getRss} + */ + private int mRss; + + /** + * @see {@link #getTimestamp} + */ + private long mTimestamp; + + /** + * @see {@link #getDescription} + */ + private @Nullable String mDescription; + + /** + * @see {@link #getSubReason} + */ + private @SubReason int mSubReason; + + /** + * @see {@link #getConnectionGroup} + */ + private int mConnectionGroup; + + /** + * @see {@link #getPackageName} + */ + private String mPackageName; + + /** + * @see {@link #getPackageList} + */ + private String[] mPackageList; + + /** @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_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, + }) + @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 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 int 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 int getRss() { + return mRss; + } + + /** + * The timestamp of the process's death, in milliseconds since the epoch. + */ + public long getTimestamp() { + return mTimestamp; + } + + /** + * The human readable description of the process's death, given by the system; could be null. + */ + public @Nullable String getDescription() { + return mDescription; + } + + /** + * Return the user id of the record on a multi-user system. + * + * @hide + */ + @SystemApi + public @NonNull UserHandle getUserHandle() { + return UserHandle.of(UserHandle.getUserId(mRealUid)); + } + + /** + * 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 {@link android.content.Context#updateServiceGroup}. + * + * For internal use only. + * + * @hide + */ + public int getConnectionGroup() { + return mConnectionGroup; + } + + /** + * Name of first package running in this process; + * + * For system internal use only, will not retain across processes. + * + * @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 {@link #getPid} + * + * @hide + */ + public void setPid(final int pid) { + mPid = pid; + } + + /** + * @see {@link #getRealUid} + * + * @hide + */ + public void setRealUid(final int uid) { + mRealUid = uid; + } + + /** + * @see {@link #getPackageUid} + * + * @hide + */ + public void setPackageUid(final int uid) { + mPackageUid = uid; + } + + /** + * @see {@link #getDefiningUid} + * + * @hide + */ + public void setDefiningUid(final int uid) { + mDefiningUid = uid; + } + + /** + * @see {@link #getProcessName} + * + * @hide + */ + public void setProcessName(final String processName) { + mProcessName = processName; + } + + /** + * @see {@link #getReason} + * + * @hide + */ + public void setReason(final @Reason int reason) { + mReason = reason; + } + + /** + * @see {@link #getStatus} + * + * @hide + */ + public void setStatus(final int status) { + mStatus = status; + } + + /** + * @see {@link #getImportance} + * + * @hide + */ + public void setImportance(final @Importance int importance) { + mImportance = importance; + } + + /** + * @see {@link #getPss} + * + * @hide + */ + public void setPss(final int pss) { + mPss = pss; + } + + /** + * @see {@link #getRss} + * + * @hide + */ + public void setRss(final int rss) { + mRss = rss; + } + + /** + * @see {@link #getTimestamp} + * + * @hide + */ + public void setTimestamp(final long timestamp) { + mTimestamp = timestamp; + } + + /** + * @see {@link #getDescription} + * + * @hide + */ + public void setDescription(final String description) { + mDescription = description; + } + + /** + * @see {@link #getSubReason} + * + * @hide + */ + public void setSubReason(final @SubReason int subReason) { + mSubReason = subReason; + } + + /** + * @see {@link #getConnectionGroup} + * + * @hide + */ + public void setConnectionGroup(final int connectionGroup) { + mConnectionGroup = connectionGroup; + } + + /** + * @see {@link #getPackageName} + * + * @hide + */ + public void setPackageName(final String packageName) { + mPackageName = packageName; + } + + /** + * @see {@link #getPackageList} + * + * @hide + */ + public void setPackageList(final String[] packageList) { + mPackageList = packageList; + } + + @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.writeInt(mConnectionGroup); + dest.writeInt(mReason); + dest.writeInt(mSubReason); + dest.writeInt(mStatus); + dest.writeInt(mImportance); + dest.writeInt(mPss); + dest.writeInt(mRss); + dest.writeLong(mTimestamp); + dest.writeString(mDescription); + } + + /** @hide */ + public ApplicationExitInfo() { + } + + /** @hide */ + public ApplicationExitInfo(ApplicationExitInfo other) { + mPid = other.mPid; + mRealUid = other.mRealUid; + mPackageUid = other.mPackageUid; + mDefiningUid = other.mDefiningUid; + mProcessName = other.mProcessName; + 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; + } + + private ApplicationExitInfo(@NonNull Parcel in) { + mPid = in.readInt(); + mRealUid = in.readInt(); + mPackageUid = in.readInt(); + mDefiningUid = in.readInt(); + mProcessName = in.readString(); + mConnectionGroup = in.readInt(); + mReason = in.readInt(); + mSubReason = in.readInt(); + mStatus = in.readInt(); + mImportance = in.readInt(); + mPss = in.readInt(); + mRss = in.readInt(); + mTimestamp = in.readLong(); + mDescription = in.readString(); + } + + 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) { + 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 + " 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); + } + + @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(" 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); + 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_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"; + 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.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.readInt(ApplicationExitInfoProto.PSS); + break; + case (int) ApplicationExitInfoProto.RSS: + mRss = proto.readInt(ApplicationExitInfoProto.RSS); + break; + case (int) ApplicationExitInfoProto.TIMESTAMP: + mTimestamp = proto.readLong(ApplicationExitInfoProto.TIMESTAMP); + break; + case (int) ApplicationExitInfoProto.DESCRIPTION: + mDescription = proto.readString(ApplicationExitInfoProto.DESCRIPTION); + break; + } + } + proto.end(token); + } + + @Override + public boolean equals(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 + mPss; + result = 31 * result + mRss; + result = 31 * result + Long.hashCode(mTimestamp); + result = 31 * result + Objects.hashCode(mProcessName); + result = 31 * result + Objects.hashCode(mDescription); + return result; + } +} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 580382e0a4aa..126cc5a6f5c4 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -18,6 +18,7 @@ package android.app; import android.app.ActivityManager; import android.app.ApplicationErrorReport; +import android.app.ApplicationExitInfo; import android.app.ContentProviderHolder; import android.app.GrantedUriPermission; import android.app.IApplicationThread; @@ -604,4 +605,30 @@ interface IActivityManager { * Method for the app to tell system that it's wedged and would like to trigger an ANR. */ void appNotResponding(String reason); + + /** + * Return a list of {@link ApplicationExitInfo} records. + * + * <p class="note"> Note: System stores these historical information in a ring buffer, older + * records would be overwritten by newer records. </p> + * + * <p class="note"> Note: In the case that this application bound to an external service with + * flag {@link android.content.Context#BIND_EXTERNAL_SERVICE}, the process of that external + * service will be included in this package's exit info. </p> + * + * @param packageName Optional, an empty value means match all packages belonging to the + * caller's UID. If this package belongs to another UID, you must hold + * {@link android.Manifest.permission#DUMP} in order to retrieve it. + * @param pid Optional, it could be a process ID that used to belong to this package but + * died later; A value of 0 means to ignore this parameter and return all + * matching records. + * @param maxNum Optional, the maximum number of results should be returned; A value of 0 + * means to ignore this parameter and return all matching records + * @param userId The userId in the multi-user environment. + * + * @return a list of {@link ApplicationExitInfo} records with the matching criteria, sorted in + * the order from most recent to least recent. + */ + ParceledListSlice<ApplicationExitInfo> getHistoricalProcessExitReasons(String packageName, + int pid, int maxNum, int userId); } |
