1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.SharedPreferences; 23 import android.os.Build; 24 import android.os.DropBoxManager; 25 import android.os.FileObserver; 26 import android.os.FileUtils; 27 import android.os.RecoverySystem; 28 import android.os.SystemProperties; 29 import android.provider.Downloads; 30 import android.util.Slog; 31 32 import java.io.File; 33 import java.io.IOException; 34 35 /** 36 * Performs a number of miscellaneous, non-system-critical actions 37 * after the system has finished booting. 38 */ 39 public class BootReceiver extends BroadcastReceiver { 40 private static final String TAG = "BootReceiver"; 41 42 // Maximum size of a logged event (files get truncated if they're longer). 43 // Give userdebug builds a larger max to capture extra debug, esp. for last_kmsg. 44 private static final int LOG_SIZE = 45 SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536; 46 47 private static final File TOMBSTONE_DIR = new File("/data/tombstones"); 48 49 // The pre-froyo package and class of the system updater, which 50 // ran in the system process. We need to remove its packages here 51 // in order to clean up after a pre-froyo-to-froyo update. 52 private static final String OLD_UPDATER_PACKAGE = 53 "com.google.android.systemupdater"; 54 private static final String OLD_UPDATER_CLASS = 55 "com.google.android.systemupdater.SystemUpdateReceiver"; 56 57 // Keep a reference to the observer so the finalizer doesn't disable it. 58 private static FileObserver sTombstoneObserver = null; 59 60 @Override onReceive(final Context context, Intent intent)61 public void onReceive(final Context context, Intent intent) { 62 // Log boot events in the background to avoid blocking the main thread with I/O 63 new Thread() { 64 @Override 65 public void run() { 66 try { 67 logBootEvents(context); 68 } catch (Exception e) { 69 Slog.e(TAG, "Can't log boot events", e); 70 } 71 try { 72 removeOldUpdatePackages(context); 73 } catch (Exception e) { 74 Slog.e(TAG, "Can't remove old update packages", e); 75 } 76 77 } 78 }.start(); 79 } 80 removeOldUpdatePackages(Context context)81 private void removeOldUpdatePackages(Context context) { 82 Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS); 83 } 84 logBootEvents(Context ctx)85 private void logBootEvents(Context ctx) throws IOException { 86 final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE); 87 final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE); 88 final String headers = new StringBuilder(512) 89 .append("Build: ").append(Build.FINGERPRINT).append("\n") 90 .append("Hardware: ").append(Build.BOARD).append("\n") 91 .append("Bootloader: ").append(Build.BOOTLOADER).append("\n") 92 .append("Radio: ").append(Build.RADIO).append("\n") 93 .append("Kernel: ") 94 .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")) 95 .append("\n").toString(); 96 97 String recovery = RecoverySystem.handleAftermath(); 98 if (recovery != null && db != null) { 99 db.addText("SYSTEM_RECOVERY_LOG", headers + recovery); 100 } 101 102 if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) { 103 String now = Long.toString(System.currentTimeMillis()); 104 SystemProperties.set("ro.runtime.firstboot", now); 105 if (db != null) db.addText("SYSTEM_BOOT", headers); 106 107 // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile()) 108 addFileToDropBox(db, prefs, headers, "/proc/last_kmsg", 109 -LOG_SIZE, "SYSTEM_LAST_KMSG"); 110 addFileToDropBox(db, prefs, headers, "/cache/recovery/log", 111 -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); 112 addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", 113 -LOG_SIZE, "APANIC_CONSOLE"); 114 addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", 115 -LOG_SIZE, "APANIC_THREADS"); 116 } else { 117 if (db != null) db.addText("SYSTEM_RESTART", headers); 118 } 119 120 // Scan existing tombstones (in case any new ones appeared) 121 File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); 122 for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { 123 addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(), 124 LOG_SIZE, "SYSTEM_TOMBSTONE"); 125 } 126 127 // Start watching for new tombstone files; will record them as they occur. 128 // This gets registered with the singleton file observer thread. 129 sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) { 130 @Override 131 public void onEvent(int event, String path) { 132 try { 133 String filename = new File(TOMBSTONE_DIR, path).getPath(); 134 addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE"); 135 } catch (IOException e) { 136 Slog.e(TAG, "Can't log tombstone", e); 137 } 138 } 139 }; 140 141 sTombstoneObserver.startWatching(); 142 } 143 addFileToDropBox( DropBoxManager db, SharedPreferences prefs, String headers, String filename, int maxSize, String tag)144 private static void addFileToDropBox( 145 DropBoxManager db, SharedPreferences prefs, 146 String headers, String filename, int maxSize, String tag) throws IOException { 147 if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled 148 149 File file = new File(filename); 150 long fileTime = file.lastModified(); 151 if (fileTime <= 0) return; // File does not exist 152 153 if (prefs != null) { 154 long lastTime = prefs.getLong(filename, 0); 155 if (lastTime == fileTime) return; // Already logged this particular file 156 // TODO: move all these SharedPreferences Editor commits 157 // outside this function to the end of logBootEvents 158 prefs.edit().putLong(filename, fileTime).apply(); 159 } 160 161 Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); 162 db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n")); 163 } 164 } 165