From 3d40df335e4c0df972720271a84277077f168f65 Mon Sep 17 00:00:00 2001 From: Dan Egnor Date: Tue, 17 Nov 2009 13:36:31 -0800 Subject: [PATCH] Add boot events (SYSTEM_LAST_KMSG and friends) to the dropbox. Optimize DropBoxManagerService.dump() a bit. --- .../java/com/android/server/BootReceiver.java | 122 ++++++++++++++---- .../android/server/DropBoxManagerService.java | 82 ++++++------ 2 files changed, 137 insertions(+), 67 deletions(-) diff --git a/services/java/com/android/server/BootReceiver.java b/services/java/com/android/server/BootReceiver.java index 590b1e4d5e164..1d31d09f14665 100644 --- a/services/java/com/android/server/BootReceiver.java +++ b/services/java/com/android/server/BootReceiver.java @@ -1,40 +1,108 @@ /* -** -** Copyright 2007, 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. -*/ + * Copyright (C) 2009 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 com.android.server; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.BroadcastReceiver; +import android.os.Build; +import android.os.DropBoxManager; +import android.os.FileUtils; +import android.os.ParcelFileDescriptor; +import android.os.SystemProperties; import android.provider.Settings; +import android.util.Log; + +import com.android.internal.os.RecoverySystem; + +import java.io.File; +import java.io.IOException; + +/** + * Performs a number of miscellaneous, non-system-critical actions + * after the system has finished booting. + */ +public class BootReceiver extends BroadcastReceiver { + private static final String TAG = "BootReceiver"; -public class BootReceiver extends BroadcastReceiver -{ @Override - public void onReceive(Context context, Intent intent) - { - Intent service = new Intent(context, com.android.server.LoadAverageService.class); - ContentResolver res = context.getContentResolver(); - boolean shown = Settings.System.getInt( - res, Settings.System.SHOW_PROCESSES, 0) != 0; - if (shown) { - context.startService(service); + public void onReceive(Context context, Intent intent) { + try { + logBootEvents(context); + } catch (Exception e) { + Log.e(TAG, "Can't log boot events", e); + } + + try { + RecoverySystem.handleAftermath(); + } catch (Exception e) { + Log.e(TAG, "Can't handle recovery aftermath", e); + } + + try { + // Start the load average overlay, if activated + ContentResolver res = context.getContentResolver(); + if (Settings.System.getInt(res, Settings.System.SHOW_PROCESSES, 0) != 0) { + Intent loadavg = new Intent(context, com.android.server.LoadAverageService.class); + context.startService(loadavg); + } + } catch (Exception e) { + Log.e(TAG, "Can't start load average service", e); } } -} + private void logBootEvents(Context context) throws IOException { + DropBoxManager db = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE); + + String build = + "Build: " + Build.FINGERPRINT + "\nKernel: " + + FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"); + + 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", build); + } else { + if (db != null) db.addText("SYSTEM_RESTART", build); + return; // Subsequent boot, don't log kernel boot log + } + + ContentResolver cr = context.getContentResolver(); + logBootFile(cr, db, "/cache/recovery/log", "SYSTEM_RECOVERY_LOG"); + logBootFile(cr, db, "/data/dontpanic/last_kmsg", "SYSTEM_LAST_KMSG"); + logBootFile(cr, db, "/data/dontpanic/apanic_console", "APANIC_CONSOLE"); + logBootFile(cr, db, "/data/dontpanic/apanic_threads", "APANIC_THREADS"); + } + + private void logBootFile(ContentResolver cr, DropBoxManager db, String filename, String tag) + throws IOException { + if (cr == null || db == null || !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); + if (lastTime == fileTime) return; // Already logged this particular file + + db.addFile(tag, + ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY), + DropBoxManager.IS_TEXT); + } +} diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java index 48d455d3ca19a..5103cf4089847 100644 --- a/services/java/com/android/server/DropBoxManagerService.java +++ b/services/java/com/android/server/DropBoxManagerService.java @@ -23,12 +23,13 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Debug; import android.os.DropBoxManager; import android.os.ParcelFileDescriptor; import android.os.StatFs; import android.os.SystemClock; import android.provider.Settings; -import android.text.format.DateFormat; +import android.text.format.Time; import android.util.Log; import com.android.internal.os.IDropBoxManagerService; @@ -56,8 +57,6 @@ import java.util.zip.GZIPOutputStream; /** * Implementation of {@link IDropBoxManagerService} using the filesystem. * Clients use {@link DropBoxManager} to access this service. - * - * {@hide} */ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { private static final String TAG = "DropBoxManagerService"; @@ -67,6 +66,8 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { private static final int DEFAULT_AGE_SECONDS = 3 * 86400; private static final int QUOTA_RESCAN_MILLIS = 5000; + private static final boolean PROFILE_DUMP = false; + // TODO: This implementation currently uses one file per entry, which is // inefficient for smallish entries -- consider using a single queue file // per tag (or even globally) instead. @@ -257,6 +258,9 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { return; } + if (PROFILE_DUMP) Debug.startMethodTracing("/data/trace/dropbox.dump"); + + Formatter out = new Formatter(); boolean doPrint = false, doFile = false; ArrayList searchArgs = new ArrayList(); for (int i = 0; args != null && i < args.length; i++) { @@ -265,53 +269,51 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { } else if (args[i].equals("-f") || args[i].equals("--file")) { doFile = true; } else if (args[i].startsWith("-")) { - pw.print("Unknown argument: "); - pw.println(args[i]); + out.format("Unknown argument: %s\n", args[i]); } else { searchArgs.add(args[i]); } } - pw.format("Drop box contents: %d entries", mAllFiles.contents.size()); - pw.println(); + out.format("Drop box contents: %d entries\n", mAllFiles.contents.size()); if (!searchArgs.isEmpty()) { - pw.print("Searching for:"); - for (String a : searchArgs) pw.format(" %s", a); - pw.println(); + out.format("Searching for:"); + for (String a : searchArgs) out.format(" %s", a); + out.format("\n"); } - int numFound = 0; - pw.println(); + int numFound = 0, numArgs = searchArgs.size(); + Time time = new Time(); + out.format("\n"); for (EntryFile entry : mAllFiles.contents) { - String date = new Formatter().format("%s.%03d", - DateFormat.format("yyyy-MM-dd kk:mm:ss", entry.timestampMillis), - entry.timestampMillis % 1000).toString(); - + time.set(entry.timestampMillis); + String date = time.format("%Y-%m-%d %H:%M:%S"); boolean match = true; - for (String a: searchArgs) match = match && (date.contains(a) || a.equals(entry.tag)); + for (int i = 0; i < numArgs && match; i++) { + String arg = searchArgs.get(i); + match = (date.contains(arg) || arg.equals(entry.tag)); + } if (!match) continue; numFound++; - pw.print(date); - pw.print(" "); - pw.print(entry.tag == null ? "(no tag)" : entry.tag); + out.format("%s.%03d %s", date, entry.timestampMillis % 1000, + entry.tag == null ? "(no tag)" : entry.tag); if (entry.file == null) { - pw.println(" (no file)"); + out.format(" (no file)\n"); continue; } else if ((entry.flags & DropBoxManager.IS_EMPTY) != 0) { - pw.println(" (contents lost)"); + out.format(" (contents lost)\n"); continue; } else { - pw.print((entry.flags & DropBoxManager.IS_GZIPPED) != 0 ? " (comopressed " : " ("); - pw.print((entry.flags & DropBoxManager.IS_TEXT) != 0 ? "text" : "data"); - pw.format(", %d bytes)", entry.file.length()); - pw.println(); + out.format(" (%s%s, %d bytes)\n", + (entry.flags & DropBoxManager.IS_GZIPPED) != 0 ? "compressed " : "", + (entry.flags & DropBoxManager.IS_TEXT) != 0 ? "text" : "data", + entry.file.length()); } if (doFile || (doPrint && (entry.flags & DropBoxManager.IS_TEXT) == 0)) { - if (!doPrint) pw.print(" "); - pw.println(entry.file.getPath()); + out.format("%s%s\n", (doPrint ? "" : " "), entry.file.getPath()); } if ((entry.flags & DropBoxManager.IS_TEXT) != 0 && (doPrint || !doFile)) { @@ -327,36 +329,36 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { for (;;) { int n = r.read(buf); if (n <= 0) break; - pw.write(buf, 0, n); + out.format("%s", new String(buf, 0, n)); newline = (buf[n - 1] == '\n'); } - if (!newline) pw.println(); + if (!newline) out.format("\n"); } else { String text = dbe.getText(70); boolean truncated = (text.length() == 70); - pw.print(" "); - pw.print(text.trim().replace('\n', '/')); - if (truncated) pw.print(" ..."); - pw.println(); + out.format(" %s%s\n", text.trim().replace('\n', '/'), + truncated ? " ..." : ""); } } catch (IOException e) { - pw.print("*** "); - pw.println(e.toString()); + out.format("*** %s\n", e.toString()); Log.e(TAG, "Can't read: " + entry.file, e); } finally { if (dbe != null) dbe.close(); } } - if (doPrint) pw.println(); + if (doPrint) out.format("\n"); } - if (numFound == 0) pw.println("(No entries found.)"); + if (numFound == 0) out.format("(No entries found.)\n"); if (args == null || args.length == 0) { - if (!doPrint) pw.println(); - pw.println("Usage: dumpsys dropbox [--print|--file] [YYYY-mm-dd] [HH:MM:SS.SSS] [tag]"); + if (!doPrint) out.format("\n"); + out.format("Usage: dumpsys dropbox [--print|--file] [YYYY-mm-dd] [HH:MM:SS] [tag]\n"); } + + pw.write(out.toString()); + if (PROFILE_DUMP) Debug.stopMethodTracing(); } ///////////////////////////////////////////////////////////////////////////