Parse proto tombstones.

Bug: http://b/159164105
Test: manual
Change-Id: I8b85d8eeb343f390567b9d11bafcaa0d9aebddc7
This commit is contained in:
Josh Gao
2021-01-29 13:40:30 -08:00
parent 9a8b4358d3
commit 1c8dfd4c1e
2 changed files with 152 additions and 1 deletions

View File

@@ -678,6 +678,7 @@ gensrcs {
srcs: [
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
],

View File

@@ -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 @@ public final class NativeTombstoneManager {
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 @@ public final class NativeTombstoneManager {
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 {