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.content.pm.IPackageManager; 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.RemoteException; 30 import android.os.ServiceManager; 31 import android.os.SystemProperties; 32 import android.provider.Downloads; 33 import android.util.Slog; 34 35 import java.io.File; 36 import java.io.IOException; 37 38 /** 39 * Performs a number of miscellaneous, non-system-critical actions 40 * after the system has finished booting. 41 */ 42 public class BootReceiver extends BroadcastReceiver { 43 private static final String TAG = "BootReceiver"; 44 45 // Maximum size of a logged event (files get truncated if they're longer). 46 // Give userdebug builds a larger max to capture extra debug, esp. for last_kmsg. 47 private static final int LOG_SIZE = 48 SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536; 49 50 private static final File TOMBSTONE_DIR = new File("/data/tombstones"); 51 52 // The pre-froyo package and class of the system updater, which 53 // ran in the system process. We need to remove its packages here 54 // in order to clean up after a pre-froyo-to-froyo update. 55 private static final String OLD_UPDATER_PACKAGE = 56 "com.google.android.systemupdater"; 57 private static final String OLD_UPDATER_CLASS = 58 "com.google.android.systemupdater.SystemUpdateReceiver"; 59 60 // Keep a reference to the observer so the finalizer doesn't disable it. 61 private static FileObserver sTombstoneObserver = null; 62 63 @Override 64 public void onReceive(final Context context, Intent intent) { 65 // Log boot events in the background to avoid blocking the main thread with I/O 66 new Thread() { 67 @Override 68 public void run() { 69 try { 70 logBootEvents(context); 71 } catch (Exception e) { 72 Slog.e(TAG, "Can't log boot events", e); 73 } 74 try { 75 boolean onlyCore = false; 76 try { 77 onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService( 78 "package")).isOnlyCoreApps(); 79 } catch (RemoteException e) { 80 } 81 if (!onlyCore) { 82 removeOldUpdatePackages(context); 83 } 84 } catch (Exception e) { 85 Slog.e(TAG, "Can't remove old update packages", e); 86 } 87 88 } 89 }.start(); 90 } 91 92 private void removeOldUpdatePackages(Context context) { 93 Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS); 94 } 95 96 private void logBootEvents(Context ctx) throws IOException { 97 final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE); 98 final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE); 99 final String headers = new StringBuilder(512) 100 .append("Build: ").append(Build.FINGERPRINT).append("\n") 101 .append("Hardware: ").append(Build.BOARD).append("\n") 102 .append("Revision: ") 103 .append(SystemProperties.get("ro.revision", "")).append("\n") 104 .append("Bootloader: ").append(Build.BOOTLOADER).append("\n") 105 .append("Radio: ").append(Build.RADIO).append("\n") 106 .append("Kernel: ") 107 .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n")) 108 .append("\n").toString(); 109 final String bootReason = SystemProperties.get("ro.boot.bootreason", null); 110 111 String recovery = RecoverySystem.handleAftermath(); 112 if (recovery != null && db != null) { 113 db.addText("SYSTEM_RECOVERY_LOG", headers + recovery); 114 } 115 116 String lastKmsgFooter = ""; 117 if (bootReason != null) { 118 lastKmsgFooter = new StringBuilder(512) 119 .append("\n") 120 .append("Boot info:\n") 121 .append("Last boot reason: ").append(bootReason).append("\n") 122 .toString(); 123 } 124 125 if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) { 126 if ("encrypted".equals(SystemProperties.get("ro.crypto.state")) 127 && "trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))){ 128 // Encrypted, first boot to get PIN/pattern/password so data is tmpfs 129 // Don't set ro.runtime.firstboot so that we will do this again 130 // when data is properly mounted 131 } else { 132 String now = Long.toString(System.currentTimeMillis()); 133 SystemProperties.set("ro.runtime.firstboot", now); 134 } 135 if (db != null) db.addText("SYSTEM_BOOT", headers); 136 137 // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile()) 138 addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter, 139 "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG"); 140 addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter, 141 "/sys/fs/pstore/console-ramoops", -LOG_SIZE, 142 "SYSTEM_LAST_KMSG"); 143 addFileToDropBox(db, prefs, headers, "/cache/recovery/log", 144 -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); 145 addFileToDropBox(db, prefs, headers, "/cache/recovery/last_kmsg", 146 -LOG_SIZE, "SYSTEM_RECOVERY_KMSG"); 147 addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", 148 -LOG_SIZE, "APANIC_CONSOLE"); 149 addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", 150 -LOG_SIZE, "APANIC_THREADS"); 151 addAuditErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_AUDIT"); 152 addFsckErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_FSCK"); 153 } else { 154 if (db != null) db.addText("SYSTEM_RESTART", headers); 155 } 156 157 // Scan existing tombstones (in case any new ones appeared) 158 File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); 159 for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) { 160 if (tombstoneFiles[i].isFile()) { 161 addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(), 162 LOG_SIZE, "SYSTEM_TOMBSTONE"); 163 } 164 } 165 166 // Start watching for new tombstone files; will record them as they occur. 167 // This gets registered with the singleton file observer thread. 168 sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) { 169 @Override 170 public void onEvent(int event, String path) { 171 try { 172 File file = new File(TOMBSTONE_DIR, path); 173 if (file.isFile()) { 174 addFileToDropBox(db, prefs, headers, file.getPath(), LOG_SIZE, "SYSTEM_TOMBSTONE"); 175 } 176 } catch (IOException e) { 177 Slog.e(TAG, "Can't log tombstone", e); 178 } 179 } 180 }; 181 182 sTombstoneObserver.startWatching(); 183 } 184 185 private static void addFileToDropBox( 186 DropBoxManager db, SharedPreferences prefs, 187 String headers, String filename, int maxSize, String tag) throws IOException { 188 addFileWithFootersToDropBox(db, prefs, headers, "", filename, maxSize, 189 tag); 190 } 191 192 private static void addFileWithFootersToDropBox( 193 DropBoxManager db, SharedPreferences prefs, 194 String headers, String footers, String filename, int maxSize, 195 String tag) throws IOException { 196 if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled 197 198 File file = new File(filename); 199 long fileTime = file.lastModified(); 200 if (fileTime <= 0) return; // File does not exist 201 202 if (prefs != null) { 203 long lastTime = prefs.getLong(filename, 0); 204 if (lastTime == fileTime) return; // Already logged this particular file 205 // TODO: move all these SharedPreferences Editor commits 206 // outside this function to the end of logBootEvents 207 prefs.edit().putLong(filename, fileTime).apply(); 208 } 209 210 Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")"); 211 db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers); 212 } 213 214 private static void addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs, 215 String headers, int maxSize, String tag) throws IOException { 216 if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled 217 Slog.i(TAG, "Copying audit failures to DropBox"); 218 219 File file = new File("/proc/last_kmsg"); 220 long fileTime = file.lastModified(); 221 if (fileTime <= 0) { 222 file = new File("/sys/fs/pstore/console-ramoops"); 223 fileTime = file.lastModified(); 224 } 225 226 if (fileTime <= 0) return; // File does not exist 227 228 if (prefs != null) { 229 long lastTime = prefs.getLong(tag, 0); 230 if (lastTime == fileTime) return; // Already logged this particular file 231 // TODO: move all these SharedPreferences Editor commits 232 // outside this function to the end of logBootEvents 233 prefs.edit().putLong(tag, fileTime).apply(); 234 } 235 236 String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); 237 StringBuilder sb = new StringBuilder(); 238 for (String line : log.split("\n")) { 239 if (line.contains("audit")) { 240 sb.append(line + "\n"); 241 } 242 } 243 Slog.i(TAG, "Copied " + sb.toString().length() + " worth of audits to DropBox"); 244 db.addText(tag, headers + sb.toString()); 245 } 246 247 private static void addFsckErrorsToDropBox(DropBoxManager db, SharedPreferences prefs, 248 String headers, int maxSize, String tag) throws IOException { 249 boolean upload_needed = false; 250 if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled 251 Slog.i(TAG, "Checking for fsck errors"); 252 253 File file = new File("/dev/fscklogs/log"); 254 long fileTime = file.lastModified(); 255 if (fileTime <= 0) return; // File does not exist 256 257 String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"); 258 StringBuilder sb = new StringBuilder(); 259 for (String line : log.split("\n")) { 260 if (line.contains("FILE SYSTEM WAS MODIFIED")) { 261 upload_needed = true; 262 break; 263 } 264 } 265 266 if (upload_needed) { 267 addFileToDropBox(db, prefs, headers, "/dev/fscklogs/log", maxSize, tag); 268 } 269 270 // Remove the file so we don't re-upload if the runtime restarts. 271 file.delete(); 272 } 273 } 274