• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onReceive(final Context context, Intent intent)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 
removeOldUpdatePackages(Context context)92     private void removeOldUpdatePackages(Context context) {
93         Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
94     }
95 
logBootEvents(Context ctx)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 
110         String recovery = RecoverySystem.handleAftermath();
111         if (recovery != null && db != null) {
112             db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
113         }
114 
115         if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
116             String now = Long.toString(System.currentTimeMillis());
117             SystemProperties.set("ro.runtime.firstboot", now);
118             if (db != null) db.addText("SYSTEM_BOOT", headers);
119 
120             // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
121             addFileToDropBox(db, prefs, headers, "/proc/last_kmsg",
122                     -LOG_SIZE, "SYSTEM_LAST_KMSG");
123             addFileToDropBox(db, prefs, headers, "/cache/recovery/log",
124                     -LOG_SIZE, "SYSTEM_RECOVERY_LOG");
125             addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console",
126                     -LOG_SIZE, "APANIC_CONSOLE");
127             addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads",
128                     -LOG_SIZE, "APANIC_THREADS");
129             addAuditErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_AUDIT");
130         } else {
131             if (db != null) db.addText("SYSTEM_RESTART", headers);
132         }
133 
134         // Scan existing tombstones (in case any new ones appeared)
135         File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
136         for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
137             addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(),
138                     LOG_SIZE, "SYSTEM_TOMBSTONE");
139         }
140 
141         // Start watching for new tombstone files; will record them as they occur.
142         // This gets registered with the singleton file observer thread.
143         sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
144             @Override
145             public void onEvent(int event, String path) {
146                 try {
147                     String filename = new File(TOMBSTONE_DIR, path).getPath();
148                     addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE");
149                 } catch (IOException e) {
150                     Slog.e(TAG, "Can't log tombstone", e);
151                 }
152             }
153         };
154 
155         sTombstoneObserver.startWatching();
156     }
157 
addFileToDropBox( DropBoxManager db, SharedPreferences prefs, String headers, String filename, int maxSize, String tag)158     private static void addFileToDropBox(
159             DropBoxManager db, SharedPreferences prefs,
160             String headers, String filename, int maxSize, String tag) throws IOException {
161         if (db == null || !db.isTagEnabled(tag)) return;  // Logging disabled
162 
163         File file = new File(filename);
164         long fileTime = file.lastModified();
165         if (fileTime <= 0) return;  // File does not exist
166 
167         if (prefs != null) {
168             long lastTime = prefs.getLong(filename, 0);
169             if (lastTime == fileTime) return;  // Already logged this particular file
170             // TODO: move all these SharedPreferences Editor commits
171             // outside this function to the end of logBootEvents
172             prefs.edit().putLong(filename, fileTime).apply();
173         }
174 
175         Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
176         db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n"));
177     }
178 
addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs, String headers, int maxSize, String tag)179     private static void addAuditErrorsToDropBox(DropBoxManager db,  SharedPreferences prefs,
180             String headers, int maxSize, String tag) throws IOException {
181         if (db == null || !db.isTagEnabled(tag)) return;  // Logging disabled
182         Slog.i(TAG, "Copying audit failures to DropBox");
183 
184         File file = new File("/proc/last_kmsg");
185         long fileTime = file.lastModified();
186         if (fileTime <= 0) return;  // File does not exist
187 
188         if (prefs != null) {
189             long lastTime = prefs.getLong(tag, 0);
190             if (lastTime == fileTime) return;  // Already logged this particular file
191             // TODO: move all these SharedPreferences Editor commits
192             // outside this function to the end of logBootEvents
193             prefs.edit().putLong(tag, fileTime).apply();
194         }
195 
196         String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
197         StringBuilder sb = new StringBuilder();
198         for (String line : log.split("\n")) {
199             if (line.contains("audit")) {
200                 sb.append(line + "\n");
201             }
202         }
203         Slog.i(TAG, "Copied " + sb.toString().length() + " worth of audits to DropBox");
204         db.addText(tag, headers + sb.toString());
205     }
206 }
207