diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 69d5c8d0d2ffbdc939e9effc879b442fdd43e300..854f5a4ab7240fa215f0ad81a67b9aa0138afd8d 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
+     * <a href="https://android.googlesource.com/platform/system/core/+/refs/heads/master/debuggerd/proto/tombstone.proto">this schema</a>.
+     * 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<ApplicationExitInfo> 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 0000000000000000000000000000000000000000..7e808e74bd5d0060bc2b1dffbb45f36d1391da42
--- /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 661d49690a00470deb33e7b6322f4d2dbc8a2e78..900871dfbbb4c922f0d5d5aa4c85ec94e1816989 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<ApplicationExitInfo> results = new ArrayList<ApplicationExitInfo>();
         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<ApplicationExitInfo>(results);
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 374c215fc6d09fa65437d52b519e211a2b9871a8..f2c1f554afdea6cc0b69ee594ef401a048583357 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<Integer> 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 3c5d45636e3e7af890970874ee32423c0f7c8096..dc191a954a59bbc71223d74551ed2216a4d4d14c 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<Integer> userId, Optional<Integer> 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<Integer> userId, Optional<Integer> 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<ApplicationExitInfo> output, int callingUid, int pid,
+            int maxNum) {
+        CompletableFuture<Object> future = new CompletableFuture<>();
+
+        if (!UserHandle.isApp(callingUid)) {
+            return;
+        }
+
+        final int userId = UserHandle.getUserId(callingUid);
+        final int appId = UserHandle.getAppId(callingUid);
+
+        mHandler.post(() -> {
+            boolean appendedTombstones = false;
+
+            synchronized (mLock) {
+                final int tombstonesSize = mTombstones.size();
+
+            tombstoneIter:
+                for (int i = 0; i < tombstonesSize; ++i) {
+                    TombstoneFile tombstone = mTombstones.valueAt(i);
+                    if (tombstone.matches(Optional.of(userId), Optional.of(appId))) {
+                        if (pid != 0 && tombstone.mPid != pid) {
+                            continue;
+                        }
+
+                        // Try to attach to an existing REASON_CRASH_NATIVE.
+                        final int outputSize = output.size();
+                        for (int j = 0; j < outputSize; ++j) {
+                            ApplicationExitInfo exitInfo = output.get(j);
+                            if (tombstone.matches(exitInfo)) {
+                                exitInfo.setNativeTombstoneRetriever(tombstone.getPfdRetriever());
+                                continue tombstoneIter;
+                            }
+                        }
+
+                        if (output.size() < maxNum) {
+                            appendedTombstones = true;
+                            output.add(tombstone.toAppExitInfo());
+                        }
+                    }
+                }
+            }
+
+            if (appendedTombstones) {
+                Collections.sort(output, (lhs, rhs) -> {
+                    // Reports should be ordered with newest reports first.
+                    long diff = rhs.getTimestamp() - lhs.getTimestamp();
+                    if (diff < 0) {
+                        return -1;
+                    } else if (diff == 0) {
+                        return 0;
+                    } else {
+                        return 1;
+                    }
+                });
+            }
+            future.complete(null);
+        });
+
+        try {
+            future.get();
+        } catch (ExecutionException | InterruptedException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
     static class TombstoneFile {
         final ParcelFileDescriptor mPfd;
 
-        final @UserIdInt int mUserId;
-        final @AppIdInt int mAppId;
+        @UserIdInt int mUserId;
+        @AppIdInt int mAppId;
+
+        int mPid;
+        int mUid;
+        String mProcessName;
+        @CurrentTimeMillisLong long mTimestampMs;
+        String mCrashReason;
 
         boolean mPurged = false;
+        final IParcelFileDescriptorRetriever mRetriever = new ParcelFileDescriptorRetriever();
 
-        TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+        TombstoneFile(ParcelFileDescriptor pfd) {
             mPfd = pfd;
-            mUserId = userId;
-            mAppId = appId;
         }
 
         public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
@@ -244,6 +341,26 @@ public final class NativeTombstoneManager {
             return true;
         }
 
+        public boolean matches(ApplicationExitInfo exitInfo) {
+            if (exitInfo.getReason() != REASON_CRASH_NATIVE) {
+                return false;
+            }
+
+            if (exitInfo.getPid() != mPid) {
+                return false;
+            }
+
+            if (exitInfo.getRealUid() != mUid) {
+                return false;
+            }
+
+            if (Math.abs(exitInfo.getTimestamp() - mTimestampMs) > 1000) {
+                return false;
+            }
+
+            return true;
+        }
+
         public void dispose() {
             IoUtils.closeQuietly(mPfd);
         }
@@ -271,16 +388,43 @@ public final class NativeTombstoneManager {
             final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
             final ProtoInputStream stream = new ProtoInputStream(is);
 
+            int pid = 0;
             int uid = 0;
+            String processName = "";
+            String crashReason = "";
             String selinuxLabel = "";
 
             try {
                 while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                     switch (stream.getFieldNumber()) {
+                        case (int) Tombstone.PID:
+                            pid = stream.readInt(Tombstone.PID);
+                            break;
+
                         case (int) Tombstone.UID:
                             uid = stream.readInt(Tombstone.UID);
                             break;
 
+                        case (int) Tombstone.PROCESS_NAME:
+                            processName = stream.readString(Tombstone.PROCESS_NAME);
+                            break;
+
+                        case (int) Tombstone.CAUSE:
+                            long token = stream.start(Tombstone.CAUSE);
+                        cause:
+                            while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                                switch (stream.getFieldNumber()) {
+                                    case (int) Cause.HUMAN_READABLE:
+                                        crashReason = stream.readString(Cause.HUMAN_READABLE);
+                                        break cause;
+
+                                    default:
+                                        break;
+                                }
+                            }
+                            stream.end(token);
+
+
                         case (int) Tombstone.SELINUX_LABEL:
                             selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
                             break;
@@ -299,6 +443,14 @@ public final class NativeTombstoneManager {
                 return Optional.empty();
             }
 
+            long timestampMs = 0;
+            try {
+                StructStat stat = Os.fstat(pfd.getFileDescriptor());
+                timestampMs = stat.st_atim.tv_sec * 1000 + stat.st_atim.tv_nsec / 1000000;
+            } catch (ErrnoException ex) {
+                Slog.e(TAG, "Failed to get timestamp of tombstone", ex);
+            }
+
             final int userId = UserHandle.getUserId(uid);
             final int appId = UserHandle.getAppId(uid);
 
@@ -307,7 +459,74 @@ public final class NativeTombstoneManager {
                 return Optional.empty();
             }
 
-            return Optional.of(new TombstoneFile(pfd, userId, appId));
+            TombstoneFile result = new TombstoneFile(pfd);
+
+            result.mUserId = userId;
+            result.mAppId = appId;
+            result.mPid = pid;
+            result.mUid = uid;
+            result.mProcessName = processName;
+            result.mTimestampMs = timestampMs;
+            result.mCrashReason = crashReason;
+
+            return Optional.of(result);
+        }
+
+        public IParcelFileDescriptorRetriever getPfdRetriever() {
+            return mRetriever;
+        }
+
+        public ApplicationExitInfo toAppExitInfo() {
+            ApplicationExitInfo info = new ApplicationExitInfo();
+            info.setPid(mPid);
+            info.setRealUid(mUid);
+            info.setPackageUid(mUid);
+            info.setDefiningUid(mUid);
+            info.setProcessName(mProcessName);
+            info.setReason(ApplicationExitInfo.REASON_CRASH_NATIVE);
+
+            // Signal numbers are architecture-specific!
+            // We choose to provide nothing here, to avoid leading users astray.
+            info.setStatus(0);
+
+            // No way for us to find out.
+            info.setImportance(RunningAppProcessInfo.IMPORTANCE_GONE);
+            info.setPackageName("");
+            info.setProcessStateSummary(null);
+
+            // We could find out, but they didn't get OOM-killed...
+            info.setPss(0);
+            info.setRss(0);
+
+            info.setTimestamp(mTimestampMs);
+            info.setDescription(mCrashReason);
+
+            info.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN);
+            info.setNativeTombstoneRetriever(mRetriever);
+
+            return info;
+        }
+
+
+        class ParcelFileDescriptorRetriever extends IParcelFileDescriptorRetriever.Stub {
+            ParcelFileDescriptorRetriever() {}
+
+            public @Nullable ParcelFileDescriptor getPfd() {
+                if (mPurged) {
+                    return null;
+                }
+
+                // Reopen the file descriptor as read-only.
+                try {
+                    final String path = "/proc/self/fd/" + mPfd.getFd();
+                    ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(path),
+                            MODE_READ_ONLY);
+                    return pfd;
+                } catch (FileNotFoundException ex) {
+                    Slog.e(TAG, "failed to reopen file descriptor as read-only", ex);
+                    return null;
+                }
+            }
         }
     }