Parse proto tombstones.
Bug: http://b/159164105
Test: manual
Change-Id: I8b85d8eeb343f390567b9d11bafcaa0d9aebddc7
diff --git a/Android.bp b/Android.bp
index 6a0bdc3..6a2bd95 100644
--- a/Android.bp
+++ b/Android.bp
@@ -678,6 +678,7 @@
srcs: [
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
+ ":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
],
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 110666f..a83edb7 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -16,17 +16,33 @@
package com.android.server.os;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import android.annotation.AppIdInt;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.os.FileObserver;
import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+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.Tombstone;
+
+import libcore.io.IoUtils;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
/**
* A class to manage native tombstones.
@@ -40,7 +56,13 @@
private final Handler mHandler;
private final TombstoneWatcher mWatcher;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<TombstoneFile> mTombstones;
+
NativeTombstoneManager(Context context) {
+ mTombstones = new SparseArray<TombstoneFile>();
mContext = context;
final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
@@ -70,7 +92,135 @@
return;
}
- BootReceiver.addTombstoneToDropBox(mContext, path);
+ if (filename.endsWith(".pb")) {
+ handleProtoTombstone(path);
+ } else {
+ BootReceiver.addTombstoneToDropBox(mContext, path);
+ }
+ }
+
+ private void handleProtoTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.endsWith(".pb")) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ final String suffix = filename.substring("tombstone_".length());
+ final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+ int number;
+ try {
+ number = Integer.parseInt(numberStr);
+ if (number < 0 || number > 99) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+ } catch (NumberFormatException ex) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+ } catch (FileNotFoundException ex) {
+ Slog.w(TAG, "failed to open " + path, ex);
+ return;
+ }
+
+ final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+ if (!parsedTombstone.isPresent()) {
+ IoUtils.closeQuietly(pfd);
+ return;
+ }
+
+ synchronized (mLock) {
+ TombstoneFile previous = mTombstones.get(number);
+ if (previous != null) {
+ previous.dispose();
+ }
+
+ mTombstones.put(number, parsedTombstone.get());
+ }
+ }
+
+ static class TombstoneFile {
+ final ParcelFileDescriptor mPfd;
+
+ final @UserIdInt int mUserId;
+ final @AppIdInt int mAppId;
+
+ boolean mPurged = false;
+
+ TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+ mPfd = pfd;
+ mUserId = userId;
+ mAppId = appId;
+ }
+
+ public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+ if (mPurged) {
+ return false;
+ }
+
+ if (userId.isPresent() && userId.get() != mUserId) {
+ return false;
+ }
+
+ if (appId.isPresent() && appId.get() != mAppId) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void dispose() {
+ IoUtils.closeQuietly(mPfd);
+ }
+
+ static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+ final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+ final ProtoInputStream stream = new ProtoInputStream(is);
+
+ int uid = 0;
+ String selinuxLabel = "";
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Tombstone.UID:
+ uid = stream.readInt(Tombstone.UID);
+ break;
+
+ case (int) Tombstone.SELINUX_LABEL:
+ selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ Slog.e(TAG, "Failed to parse tombstone", ex);
+ return Optional.empty();
+ }
+
+ if (!UserHandle.isApp(uid)) {
+ Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+ return Optional.empty();
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ final int appId = UserHandle.getAppId(uid);
+
+ if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+ Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+ return Optional.empty();
+ }
+
+ return Optional.of(new TombstoneFile(pfd, userId, appId));
+ }
}
class TombstoneWatcher extends FileObserver {