diff options
| author | Jing Ji <jji@google.com> | 2020-03-08 00:32:33 -0800 |
|---|---|---|
| committer | Jing Ji <jji@google.com> | 2020-03-18 10:30:43 -0700 |
| commit | 56006fd5bc6847a0a2b4715b3391edc2bb8ba2ce (patch) | |
| tree | fe738eb6d64164ca2cf810b892b2d97d9771d573 /core/java | |
| parent | cf5dc204f60096095ed6b0a58a529a8ca06d1371 (diff) | |
Add support to provide app its own ANR stack trace
...If it was killed due to ANR.
Also add support to set a cookie which could be
included into the app kill reason.
Bug: 148413462
Test: atest ApplicationExitInfoTest
Test: atest CtsAppExitTestCases:ActivityManagerAppExitInfoTest
Change-Id: I79d9955d8f5c5f42074f0e1567119b41fc486d50
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/app/ActivityManager.java | 31 | ||||
| -rw-r--r-- | core/java/android/app/ApplicationExitInfo.java | 205 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.aidl | 23 | ||||
| -rw-r--r-- | core/java/android/app/IAppTraceRetriever.aidl | 38 |
4 files changed, 261 insertions, 36 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f4ee8faaf9bf..b3a0be1bec1e 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3628,11 +3628,40 @@ public class ActivityManager { } } + /** + * Set custom state data for this process. It will be included in the record of + * {@link ApplicationExitInfo} on the death of the current calling process; the new process + * of the app can retrieve this state data by calling + * {@link ApplicationExitInfo#getProcessStateSummary} on the record returned by + * {@link #getHistoricalProcessExitReasons}. + * + * <p> This would be useful for the calling app to save its stateful data: if it's + * killed later for any reason, the new process of the app can know what the + * previous process of the app was doing. For instance, you could use this to encode + * the current level in a game, or a set of features/experiments that were enabled. Later you + * could analyze under what circumstances the app tends to crash or use too much memory. + * However, it's not suggested to rely on this to restore the applications previous UI state + * or so, it's only meant for analyzing application healthy status.</p> + * + * <p> System might decide to throttle the calls to this API; so call this API in a reasonable + * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}. + * </p> + * + * @param state The state data + */ + public void setProcessStateSummary(@Nullable byte[] state) { + try { + getService().setProcessStateSummary(state); + } 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} + * @see ApplicationExitInfo#REASON_LOW_MEMORY */ public static boolean isLowMemoryKillReportSupported() { return SystemProperties.getBoolean("persist.sys.lmk.reportkills", false); diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index 5df3257f2444..61be01f9f6c0 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -23,7 +23,9 @@ 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; @@ -31,12 +33,17 @@ 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. @@ -321,85 +328,105 @@ public final class ApplicationExitInfo implements Parcelable { // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000. /** - * @see {@link #getPid} + * @see #getPid */ private int mPid; /** - * @see {@link #getRealUid} + * @see #getRealUid */ private int mRealUid; /** - * @see {@link #getPackageUid} + * @see #getPackageUid */ private int mPackageUid; /** - * @see {@link #getDefiningUid} + * @see #getDefiningUid */ private int mDefiningUid; /** - * @see {@link #getProcessName} + * @see #getProcessName */ private String mProcessName; /** - * @see {@link #getReason} + * @see #getReason */ private @Reason int mReason; /** - * @see {@link #getStatus} + * @see #getStatus */ private int mStatus; /** - * @see {@link #getImportance} + * @see #getImportance */ private @Importance int mImportance; /** - * @see {@link #getPss} + * @see #getPss */ private long mPss; /** - * @see {@link #getRss} + * @see #getRss */ private long mRss; /** - * @see {@link #getTimestamp} + * @see #getTimestamp */ private @CurrentTimeMillisLong long mTimestamp; /** - * @see {@link #getDescription} + * @see #getDescription */ private @Nullable String mDescription; /** - * @see {@link #getSubReason} + * @see #getSubReason */ private @SubReason int mSubReason; /** - * @see {@link #getConnectionGroup} + * @see #getConnectionGroup */ private int mConnectionGroup; /** - * @see {@link #getPackageName} + * @see #getPackageName */ private String mPackageName; /** - * @see {@link #getPackageList} + * @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; + /** @hide */ @IntDef(prefix = { "REASON_" }, value = { REASON_UNKNOWN, @@ -557,6 +584,54 @@ public final class ApplicationExitInfo implements Parcelable { } /** + * Return the state data set by calling {@link ActivityManager#setProcessStateSummary} + * 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. + * + * @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) { + return null; + } + try { + 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. @@ -569,7 +644,7 @@ public final class ApplicationExitInfo implements Parcelable { /** * The connection group this process belongs to, if there is any. - * @see {@link android.content.Context#updateServiceGroup}. + * @see android.content.Context#updateServiceGroup * * For internal use only. * @@ -582,8 +657,6 @@ public final class ApplicationExitInfo implements Parcelable { /** * Name of first package running in this process; * - * For system internal use only, will not retain across processes. - * * @hide */ public String getPackageName() { @@ -602,7 +675,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getPid} + * @see #getPid * * @hide */ @@ -611,7 +684,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getRealUid} + * @see #getRealUid * * @hide */ @@ -620,7 +693,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getPackageUid} + * @see #getPackageUid * * @hide */ @@ -629,7 +702,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getDefiningUid} + * @see #getDefiningUid * * @hide */ @@ -638,7 +711,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getProcessName} + * @see #getProcessName * * @hide */ @@ -647,7 +720,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getReason} + * @see #getReason * * @hide */ @@ -656,7 +729,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getStatus} + * @see #getStatus * * @hide */ @@ -665,7 +738,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getImportance} + * @see #getImportance * * @hide */ @@ -674,7 +747,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getPss} + * @see #getPss * * @hide */ @@ -683,7 +756,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getRss} + * @see #getRss * * @hide */ @@ -692,7 +765,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getTimestamp} + * @see #getTimestamp * * @hide */ @@ -701,7 +774,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getDescription} + * @see #getDescription * * @hide */ @@ -710,7 +783,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getSubReason} + * @see #getSubReason * * @hide */ @@ -719,7 +792,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getConnectionGroup} + * @see #getConnectionGroup * * @hide */ @@ -728,7 +801,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getPackageName} + * @see #getPackageName * * @hide */ @@ -737,7 +810,7 @@ public final class ApplicationExitInfo implements Parcelable { } /** - * @see {@link #getPackageList} + * @see #getPackageList * * @hide */ @@ -745,6 +818,33 @@ public final class ApplicationExitInfo implements Parcelable { 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; + } + @Override public int describeContents() { return 0; @@ -757,6 +857,7 @@ public final class ApplicationExitInfo implements Parcelable { dest.writeInt(mPackageUid); dest.writeInt(mDefiningUid); dest.writeString(mProcessName); + dest.writeString(mPackageName); dest.writeInt(mConnectionGroup); dest.writeInt(mReason); dest.writeInt(mSubReason); @@ -766,6 +867,13 @@ public final class ApplicationExitInfo implements Parcelable { 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); + } } /** @hide */ @@ -779,6 +887,7 @@ public final class ApplicationExitInfo implements Parcelable { mPackageUid = other.mPackageUid; mDefiningUid = other.mDefiningUid; mProcessName = other.mProcessName; + mPackageName = other.mPackageName; mConnectionGroup = other.mConnectionGroup; mReason = other.mReason; mStatus = other.mStatus; @@ -790,6 +899,9 @@ public final class ApplicationExitInfo implements Parcelable { mDescription = other.mDescription; mPackageName = other.mPackageName; mPackageList = other.mPackageList; + mState = other.mState; + mTraceFile = other.mTraceFile; + mAppTraceRetriever = other.mAppTraceRetriever; } private ApplicationExitInfo(@NonNull Parcel in) { @@ -798,6 +910,7 @@ public final class ApplicationExitInfo implements Parcelable { mPackageUid = in.readInt(); mDefiningUid = in.readInt(); mProcessName = in.readString(); + mPackageName = in.readString(); mConnectionGroup = in.readInt(); mReason = in.readInt(); mSubReason = in.readInt(); @@ -807,6 +920,10 @@ public final class ApplicationExitInfo implements Parcelable { mRss = in.readLong(); mTimestamp = in.readLong(); mDescription = in.readString(); + mState = in.createByteArray(); + if (in.readInt() == 1) { + mAppTraceRetriever = IAppTraceRetriever.Stub.asInterface(in.readStrongBinder()); + } } public @NonNull static final Creator<ApplicationExitInfo> CREATOR = @@ -839,6 +956,9 @@ public final class ApplicationExitInfo implements Parcelable { 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 @@ -859,6 +979,9 @@ public final class ApplicationExitInfo implements Parcelable { 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(); } @@ -961,6 +1084,9 @@ public final class ApplicationExitInfo implements Parcelable { 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); } @@ -1019,6 +1145,15 @@ public final class ApplicationExitInfo implements Parcelable { 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); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 6f0611e68cda..b8221b4efa2f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -652,4 +652,27 @@ interface IActivityManager { */ void setActivityLocusContext(in ComponentName activity, in LocusId locusId, in IBinder appToken); + + /** + * Set custom state data for this process. It will be included in the record of + * {@link ApplicationExitInfo} on the death of the current calling process; the new process + * of the app can retrieve this state data by calling + * {@link ApplicationExitInfo#getProcessStateSummary} on the record returned by + * {@link #getHistoricalProcessExitReasons}. + * + * <p> This would be useful for the calling app to save its stateful data: if it's + * killed later for any reason, the new process of the app can know what the + * previous process of the app was doing. For instance, you could use this to encode + * the current level in a game, or a set of features/experiments that were enabled. Later you + * could analyze under what circumstances the app tends to crash or use too much memory. + * However, it's not suggested to rely on this to restore the applications previous UI state + * or so, it's only meant for analyzing application healthy status.</p> + * + * <p> System might decide to throttle the calls to this API; so call this API in a reasonable + * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}. + * </p> + * + * @param state The customized state data + */ + void setProcessStateSummary(in byte[] state); } diff --git a/core/java/android/app/IAppTraceRetriever.aidl b/core/java/android/app/IAppTraceRetriever.aidl new file mode 100644 index 000000000000..1463da7db5d1 --- /dev/null +++ b/core/java/android/app/IAppTraceRetriever.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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.os.ParcelFileDescriptor; + +/** + * An interface that's to be used by {@link ApplicationExitInfo#getTraceFile()} + * to retrieve the actual file descriptor to its trace file. + * + * @hide + */ +interface IAppTraceRetriever { + /** + * Retrieve the trace file with given packageName/uid/pid. + * + * @param packagename The target package name of the trace + * @param uid The target UID of the trace + * @param pid The target PID of the trace + * @return The file descriptor to the trace file, or null if it's not found. + */ + ParcelFileDescriptor getTraceFileDescriptor(in String packageName, + int uid, int pid); +} |
