diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 69d5c8d0d2ffb..854f5a4ab7240 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -428,6 +428,13 @@ public final class ApplicationExitInfo implements Parcelable {
*/
private IAppTraceRetriever mAppTraceRetriever;
+ /**
+ * ParcelFileDescriptor pointing to a native tombstone.
+ *
+ * @see #getTraceInputStream
+ */
+ private IParcelFileDescriptorRetriever mNativeTombstoneRetriever;
+
/** @hide */
@IntDef(prefix = { "REASON_" }, value = {
REASON_UNKNOWN,
@@ -603,22 +610,38 @@ public final class ApplicationExitInfo implements Parcelable {
* 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.
+ * 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 thatbecause 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) {
+ if (mAppTraceRetriever == null && mNativeTombstoneRetriever == null) {
return null;
}
+
try {
- final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
- mPackageName, mPackageUid, mPid);
- if (fd == null) {
- return null;
+ 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));
}
- return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
} catch (RemoteException e) {
return null;
}
@@ -849,6 +872,15 @@ public final class ApplicationExitInfo implements Parcelable {
mAppTraceRetriever = retriever;
}
+ /**
+ * @see mNativeTombstoneRetriever
+ *
+ * @hide
+ */
+ public void setNativeTombstoneRetriever(final IParcelFileDescriptorRetriever retriever) {
+ mNativeTombstoneRetriever = retriever;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -878,6 +910,12 @@ public final class ApplicationExitInfo implements Parcelable {
} else {
dest.writeInt(0);
}
+ if (mNativeTombstoneRetriever != null) {
+ dest.writeInt(1);
+ dest.writeStrongBinder(mNativeTombstoneRetriever.asBinder());
+ } else {
+ dest.writeInt(0);
+ }
}
/** @hide */
@@ -906,6 +944,7 @@ public final class ApplicationExitInfo implements Parcelable {
mState = other.mState;
mTraceFile = other.mTraceFile;
mAppTraceRetriever = other.mAppTraceRetriever;
+ mNativeTombstoneRetriever = other.mNativeTombstoneRetriever;
}
private ApplicationExitInfo(@NonNull Parcel in) {
@@ -928,6 +967,10 @@ public final class ApplicationExitInfo implements Parcelable {
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 =
@@ -986,6 +1029,7 @@ public final class ApplicationExitInfo implements Parcelable {
sb.append(" state=").append(ArrayUtils.isEmpty(mState)
? "empty" : Integer.toString(mState.length) + " bytes");
sb.append(" trace=").append(mTraceFile);
+
return sb.toString();
}
diff --git a/core/java/android/app/IParcelFileDescriptorRetriever.aidl b/core/java/android/app/IParcelFileDescriptorRetriever.aidl
new file mode 100644
index 0000000000000..7e808e74bd5d0
--- /dev/null
+++ b/core/java/android/app/IParcelFileDescriptorRetriever.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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 used to lazily provide a ParcelFileDescriptor to apps.
+ *
+ * @hide
+ */
+interface IParcelFileDescriptorRetriever {
+ /**
+ * Retrieve the ParcelFileDescriptor.
+ */
+ ParcelFileDescriptor getPfd();
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8f1e4e945bcb1..0d86c2467d4bc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -364,6 +364,7 @@ import com.android.server.compat.PlatformCompat;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
+import com.android.server.os.NativeTombstoneManager;
import com.android.server.pm.Installer;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.uri.GrantUri;
@@ -10410,6 +10411,9 @@ public class ActivityManagerService extends IActivityManager.Stub
mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL,
"getHistoricalProcessExitReasons", null);
+ NativeTombstoneManager tombstoneService = LocalServices.getService(
+ NativeTombstoneManager.class);
+
final ArrayList results = new ArrayList();
if (!TextUtils.isEmpty(packageName)) {
final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid,
@@ -10417,11 +10421,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (uid != Process.INVALID_UID) {
mProcessList.mAppExitInfoTracker.getExitInfo(
packageName, uid, pid, maxNum, results);
+ tombstoneService.collectTombstones(results, uid, pid, maxNum);
}
} else {
// If no package name is given, use the caller's uid as the filter uid.
mProcessList.mAppExitInfoTracker.getExitInfo(
packageName, callingUid, pid, maxNum, results);
+ tombstoneService.collectTombstones(results, callingUid, pid, maxNum);
}
return new ParceledListSlice(results);
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 374c215fc6d09..f2c1f554afdea 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -63,8 +63,10 @@ import com.android.internal.app.ProcessMap;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.IoThread;
+import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemServiceManager;
+import com.android.server.os.NativeTombstoneManager;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -78,6 +80,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -770,6 +773,10 @@ public final class AppExitInfoTracker {
* Helper function for shell command
*/
void clearHistoryProcessExitInfo(String packageName, int userId) {
+ NativeTombstoneManager tombstoneService = LocalServices.getService(
+ NativeTombstoneManager.class);
+ Optional appId = Optional.empty();
+
if (TextUtils.isEmpty(packageName)) {
synchronized (mLock) {
removeByUserIdLocked(userId);
@@ -777,10 +784,13 @@ public final class AppExitInfoTracker {
} else {
final int uid = mService.mPackageManagerInt.getPackageUid(packageName,
PackageManager.MATCH_ALL, userId);
+ appId = Optional.of(UserHandle.getAppId(uid));
synchronized (mLock) {
removePackageLocked(packageName, uid, true, userId);
}
}
+
+ tombstoneService.purge(Optional.of(userId), appId);
schedulePersistProcessExitInfo(true);
}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 3c5d45636e3e7..dc191a954a59b 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -16,12 +16,18 @@
package com.android.server.os;
+import static android.app.ApplicationExitInfo.REASON_CRASH_NATIVE;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import android.annotation.AppIdInt;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ApplicationExitInfo;
+import android.app.IParcelFileDescriptorRetriever;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -32,6 +38,7 @@ import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.StructStat;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoInputStream;
@@ -39,6 +46,7 @@ import android.util.proto.ProtoInputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.server.BootReceiver;
import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Cause;
import com.android.server.os.TombstoneProtos.Tombstone;
import libcore.io.IoUtils;
@@ -47,7 +55,11 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
/**
* A class to manage native tombstones.
@@ -153,7 +165,13 @@ public final class NativeTombstoneManager {
}
}
- private void purge(Optional userId, Optional appId) {
+ /**
+ * Remove native tombstones matching a user and/or app.
+ *
+ * @param userId user id to filter by, selects all users if empty
+ * @param appId app id to filter by, selects all users if empty
+ */
+ public void purge(Optional userId, Optional appId) {
mHandler.post(() -> {
synchronized (mLock) {
for (int i = mTombstones.size() - 1; i >= 0; --i) {
@@ -214,18 +232,97 @@ public final class NativeTombstoneManager {
}, filter, null, mHandler);
}
+ /**
+ * Collect native tombstones.
+ *
+ * @param output list to append to
+ * @param callingUid POSIX uid to filter by
+ * @param pid pid to filter by, ignored if zero
+ * @param maxNum maximum number of elements in output
+ */
+ public void collectTombstones(ArrayList output, int callingUid, int pid,
+ int maxNum) {
+ CompletableFuture