Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c8dfd4c authored by Josh Gao's avatar Josh Gao
Browse files

Parse proto tombstones.

Bug: http://b/159164105
Test: manual
Change-Id: I8b85d8eeb343f390567b9d11bafcaa0d9aebddc7
parent 9a8b4358
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -678,6 +678,7 @@ gensrcs {
    srcs: [
        ":ipconnectivity-proto-src",
        ":libstats_atom_enum_protos",
        ":libtombstone_proto-src",
        "core/proto/**/*.proto",
        "libs/incident/**/*.proto",
    ],
+151 −1
Original line number Diff line number Diff line
@@ -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,8 +92,136 @@ public final class NativeTombstoneManager {
            return;
        }

        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 {
        TombstoneWatcher() {