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.net.Downloads; 24 import android.os.Build; 25 import android.os.DropBoxManager; 26 import android.os.FileObserver; 27 import android.os.FileUtils; 28 import android.os.RecoverySystem; 29 import android.os.SystemProperties; 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 private static final int LOG_SIZE = 65536; 44 45 private static final File TOMBSTONE_DIR = new File("/data/tombstones"); 46 47 // The pre-froyo package and class of the system updater, which 48 // ran in the system process. We need to remove its packages here 49 // in order to clean up after a pre-froyo-to-froyo update. 50 private static final String OLD_UPDATER_PACKAGE = 51 "com.google.android.systemupdater"; 52 private static final String OLD_UPDATER_CLASS = 53 "com.google.android.systemupdater.SystemUpdateReceiver"; 54 55 // Keep a reference to the observer so the finalizer doesn't disable it. 56 private static FileObserver sTombstoneObserver = null; 57 58 @Override onReceive(final Context context, Intent intent)59 public void onReceive(final Context context, Intent intent) { 60 // Log boot events in the background to avoid blocking the main thread with I/O 61 new Thread() { 62 @Override 63 public void run() { 64 try { 65 logBootEvents(context); 66 } catch (Exception e) { 67 Slog.e(TAG, "Can't log boot events", e); 68 } 69 try { 70 removeOldUpdatePackages(context); 71 } catch (Exception e) { 72 Slog.e(TAG, "Can't remove old update packages", e); 73 } 74 75 } 76 }.start(); 77 } 78 removeOldUpdatePackages(Context ctx)79 private void removeOldUpdatePackages(Context ctx) { 80 Downloads.ByUri.removeAllDownloadsByPackage( 81 ctx, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS); 82 } 83 logBootEvents(Context ctx)84 private void logBootEvents(Context ctx) throws IOException { 85 final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE); 86 final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE); 87 final String headers = new StringBuilder(512) 88 .append("Build: ").append(Build.FINGERPRINT).append("\n") 89 .append("Hardware: ").append(Build.BOARD).append("\n") 90 .append("Bootloader: ").append(Build.BOOTLOADER).append("\n") 91 .append("Radio: ").append(Build.RADIO).append("\n") 92 .append("Kernel: ") 93 .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")) 94 .append("\n").toString(); 95 96 String recovery = RecoverySystem.handleAftermath(); 97 if (recovery != null && db != null) { 98 db.addText("SYSTEM_RECOVERY_LOG", headers + recovery); 99 } 100 101 if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) { 102 String now = Long.toString(System.currentTimeMillis()); 103 SystemProperties.set("ro.runtime.firstboot", now); 104 if (db != null) db.addText("SYSTEM_BOOT", headers); 105 106 // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile()) 107 addFileToDropBox(db, prefs, headers, "/proc/last_kmsg", 108 -LOG_SIZE, "SYSTEM_LAST_KMSG"); 109 addFileToDropBox(db, prefs, headers, "/cache/recovery/log", 110 -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); 111 addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", 112 -LOG_SIZE, "APANIC_CONSOLE"); 113 addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", 114 -LOG_SIZE, "APANIC_THREADS"); 115 } else { 116 if (db != null) db.addText("SYSTEM_RESTART", headers); 117 } 118 119 // Scan existing tombstones (in case any new ones appeared) 120 File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); 121 for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { 122 addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(), 123 LOG_SIZE, "SYSTEM_TOMBSTONE"); 124 } 125 126 // Start watching for new tombstone files; will record them as they occur. 127 // This gets registered with the singleton file observer thread. 128 sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) { 129 @Override 130 public void onEvent(int event, String path) { 131 try { 132 String filename = new File(TOMBSTONE_DIR, path).getPath(); 133 addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE"); 134 } catch (IOException e) { 135 Slog.e(TAG, "Can't log tombstone", e); 136 } 137 } 138 }; 139 140 sTombstoneObserver.startWatching(); 141 } 142 addFileToDropBox( DropBoxManager db, SharedPreferences prefs, String headers, String filename, int maxSize, String tag)143 private static void addFileToDropBox( 144 DropBoxManager db, SharedPreferences prefs, 145 String headers, String filename, int maxSize, String tag) throws IOException { 146 if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled 147 148 File file = new File(filename); 149 long fileTime = file.lastModified(); 150 if (fileTime <= 0) return; // File does not exist 151 152 if (prefs != null) { 153 long lastTime = prefs.getLong(filename, 0); 154 if (lastTime == fileTime) return; // Already logged this particular file 155 // TODO: move all these SharedPreferences Editor commits 156 // outside this function to the end of logBootEvents 157 prefs.edit().putLong(filename, fileTime).apply(); 158 } 159 160 Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); 161 db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n")); 162 } 163 } 164