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

Commit 3ae6162d authored by Dan Egnor's avatar Dan Egnor Committed by Android (Google) Code Review
Browse files

Merge "Report tombstone (native crash) data to the dropbox."

parents 2060b637 492c6ed5
Loading
Loading
Loading
Loading
+56 −28
Original line number Diff line number Diff line
@@ -20,8 +20,10 @@ import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.DropBoxManager;
import android.os.FileObserver;
import android.os.FileUtils;
import android.os.RecoverySystem;
import android.os.SystemProperties;
@@ -38,10 +40,14 @@ import java.io.IOException;
public class BootReceiver extends BroadcastReceiver {
    private static final String TAG = "BootReceiver";

    // Negative meaning capture the *last* 64K of the file
    // (passed to FileUtils.readTextFile)
    // Negative to read the *last* 64K of the file (per FileUtils.readTextFile)
    private static final int LOG_SIZE = -65536;

    private static final File TOMBSTONE_DIR = new File("/data/tombstones");

    // Keep a reference to the observer so the finalizer doesn't disable it.
    private static FileObserver sTombstoneObserver = null;

    @Override
    public void onReceive(Context context, Intent intent) {
        try {
@@ -68,49 +74,71 @@ public class BootReceiver extends BroadcastReceiver {
        }
    }

    private void logBootEvents(Context context) throws IOException {
        DropBoxManager db = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
    private void logBootEvents(Context ctx) throws IOException {
        final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE);
        final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE);
        final String props = new StringBuilder()
            .append("Build: ").append(Build.FINGERPRINT).append("\n")
            .append("Hardware: ").append(Build.BOARD).append("\n")
            .append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
            .append("Radio: ").append(Build.RADIO).append("\n")
            .append("Kernel: ")
            .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
            .toString();

        StringBuilder props = new StringBuilder();
        props.append("Build: ").append(Build.FINGERPRINT).append("\n");
        props.append("Hardware: ").append(Build.BOARD).append("\n");
        props.append("Bootloader: ").append(Build.BOOTLOADER).append("\n");
        props.append("Radio: ").append(Build.RADIO).append("\n");
        props.append("Kernel: ");
        props.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"));
        if (db == null || prefs == null) return;

        if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
            String now = Long.toString(System.currentTimeMillis());
            SystemProperties.set("ro.runtime.firstboot", now);
            if (db != null) db.addText("SYSTEM_BOOT", props.toString());
            db.addText("SYSTEM_BOOT", props);
            addFileToDropBox(db, prefs, props, "/proc/last_kmsg", "SYSTEM_LAST_KMSG");
            addFileToDropBox(db, prefs, props, "/cache/recovery/log", "SYSTEM_RECOVERY_LOG");
            addFileToDropBox(db, prefs, props, "/data/dontpanic/apanic_console", "APANIC_CONSOLE");
            addFileToDropBox(db, prefs, props, "/data/dontpanic/apanic_threads", "APANIC_THREADS");
        } else {
            if (db != null) db.addText("SYSTEM_RESTART", props.toString());
            return;  // Subsequent boot, don't log kernel boot log
            db.addText("SYSTEM_RESTART", props);
        }

        // Scan existing tombstones (in case any new ones appeared)
        File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
        for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
            addFileToDropBox(db, prefs, props, tombstoneFiles[i].getPath(), "SYSTEM_TOMBSTONE");
        }

        // Start watching for new tombstone files; will record them as they occur.
        // This gets registered with the singleton file observer thread.
        sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
            @Override
            public void onEvent(int event, String path) {
                try {
                    String filename = new File(TOMBSTONE_DIR, path).getPath();
                    addFileToDropBox(db, prefs, props, filename, "SYSTEM_TOMBSTONE");
                } catch (IOException e) {
                    Log.e(TAG, "Can't log tombstone", e);
                }
            }
        };

        ContentResolver cr = context.getContentResolver();
        logBootFile(cr, db, props, "/cache/recovery/log", "SYSTEM_RECOVERY_LOG");
        logBootFile(cr, db, props, "/proc/last_kmsg", "SYSTEM_LAST_KMSG");
        logBootFile(cr, db, props, "/data/dontpanic/apanic_console", "APANIC_CONSOLE");
        logBootFile(cr, db, props, "/data/dontpanic/apanic_threads", "APANIC_THREADS");
        sTombstoneObserver.startWatching();
    }

    private void logBootFile(ContentResolver cr, DropBoxManager db,
            CharSequence headers, String filename, String tag) throws IOException {
        if (cr == null || db == null || !db.isTagEnabled(tag)) return;  // Logging disabled
    private static void addFileToDropBox(
            DropBoxManager db, SharedPreferences prefs,
            String headers, String filename, String tag) throws IOException {
        if (!db.isTagEnabled(tag)) return;  // Logging disabled

        File file = new File(filename);
        long fileTime = file.lastModified();
        if (fileTime <= 0) return;  // File does not exist

        String setting = "logfile:" + filename;
        long lastTime = Settings.Secure.getLong(cr, setting, 0);
        long lastTime = prefs.getLong(filename, 0);
        if (lastTime == fileTime) return;  // Already logged this particular file
        Settings.Secure.putLong(cr, setting, fileTime);
        prefs.edit().putLong(filename, fileTime).commit();

        StringBuilder report = new StringBuilder(headers);
        report.append("\n");
        report.append(FileUtils.readTextFile(new File(filename), LOG_SIZE, "[[TRUNCATED]]\n"));
        StringBuilder report = new StringBuilder(headers).append("\n");
        report.append(FileUtils.readTextFile(file, LOG_SIZE, "[[TRUNCATED]]\n"));
        db.addText(tag, report.toString());
        Log.i(TAG, "Logging " + filename + " to DropBox (" + tag + ")");
    }
}