summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorJing Ji <jji@google.com>2020-03-08 00:32:33 -0800
committerJing Ji <jji@google.com>2020-03-18 10:30:43 -0700
commit56006fd5bc6847a0a2b4715b3391edc2bb8ba2ce (patch)
treefe738eb6d64164ca2cf810b892b2d97d9771d573 /core/java
parentcf5dc204f60096095ed6b0a58a529a8ca06d1371 (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.java31
-rw-r--r--core/java/android/app/ApplicationExitInfo.java205
-rw-r--r--core/java/android/app/IActivityManager.aidl23
-rw-r--r--core/java/android/app/IAppTraceRetriever.aidl38
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);
+}