summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/ActivityThread.java4
-rw-r--r--core/java/android/app/ContextImpl.java4
-rw-r--r--core/java/android/app/LoadedApk.java3
-rw-r--r--core/java/com/android/internal/os/InstallerConnection.java207
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java15
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java2
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java55
-rw-r--r--core/java/com/android/server/BootReceiver.java260
-rw-r--r--core/java/com/android/server/SystemService.java199
-rw-r--r--core/java/com/android/server/SystemServiceManager.java227
10 files changed, 535 insertions, 441 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1b82d8e..fed7ae3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1807,9 +1807,9 @@ public final class ActivityThread {
}
}
- public void installSystemApplicationInfo(ApplicationInfo info) {
+ public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
synchronized (this) {
- getSystemContext().installSystemApplicationInfo(info);
+ getSystemContext().installSystemApplicationInfo(info, classLoader);
// give ourselves a default profiler
mProfiler = new Profiler();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4cf8cb4..da343ac 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2301,8 +2301,8 @@ class ContextImpl extends Context {
}
}
- void installSystemApplicationInfo(ApplicationInfo info) {
- mPackageInfo.installSystemApplicationInfo(info);
+ void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
+ mPackageInfo.installSystemApplicationInfo(info, classLoader);
}
final void scheduleFinalCleanup(String who, String what) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index aa1f021..fcfc1c4 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -199,9 +199,10 @@ public final class LoadedApk {
/**
* Sets application info about the system package.
*/
- void installSystemApplicationInfo(ApplicationInfo info) {
+ void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
assert info.packageName.equals("android");
mApplicationInfo = info;
+ mClassLoader = classLoader;
}
public String getPackageName() {
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
new file mode 100644
index 0000000..e3f229f
--- /dev/null
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2008 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.internal.os;
+
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.util.Slog;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Represents a connection to {@code installd}. Allows multiple connect and
+ * disconnect cycles.
+ *
+ * @hide for internal use only
+ */
+public class InstallerConnection {
+ private static final String TAG = "InstallerConnection";
+ private static final boolean LOCAL_DEBUG = false;
+
+ private InputStream mIn;
+ private OutputStream mOut;
+ private LocalSocket mSocket;
+
+ private final byte buf[] = new byte[1024];
+
+ public InstallerConnection() {
+ }
+
+ public synchronized String transact(String cmd) {
+ if (!connect()) {
+ Slog.e(TAG, "connection failed");
+ return "-1";
+ }
+
+ if (!writeCommand(cmd)) {
+ /*
+ * If installd died and restarted in the background (unlikely but
+ * possible) we'll fail on the next write (this one). Try to
+ * reconnect and write the command one more time before giving up.
+ */
+ Slog.e(TAG, "write command failed? reconnect!");
+ if (!connect() || !writeCommand(cmd)) {
+ return "-1";
+ }
+ }
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "send: '" + cmd + "'");
+ }
+
+ final int replyLength = readReply();
+ if (replyLength > 0) {
+ String s = new String(buf, 0, replyLength);
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "recv: '" + s + "'");
+ }
+ return s;
+ } else {
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "fail");
+ }
+ return "-1";
+ }
+ }
+
+ public int execute(String cmd) {
+ String res = transact(cmd);
+ try {
+ return Integer.parseInt(res);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
+ StringBuilder builder = new StringBuilder("dexopt");
+ builder.append(' ');
+ builder.append(apkPath);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(isPublic ? " 1" : " 0");
+ builder.append(" *"); // No pkgName arg present
+ builder.append(' ');
+ builder.append(instructionSet);
+ return execute(builder.toString());
+ }
+
+ public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
+ StringBuilder builder = new StringBuilder("patchoat");
+ builder.append(' ');
+ builder.append(apkPath);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(isPublic ? " 1" : " 0");
+ builder.append(" *"); // No pkgName arg present
+ builder.append(' ');
+ builder.append(instructionSet);
+ return execute(builder.toString());
+ }
+
+ private boolean connect() {
+ if (mSocket != null) {
+ return true;
+ }
+ Slog.i(TAG, "connecting...");
+ try {
+ mSocket = new LocalSocket();
+
+ LocalSocketAddress address = new LocalSocketAddress("installd",
+ LocalSocketAddress.Namespace.RESERVED);
+
+ mSocket.connect(address);
+
+ mIn = mSocket.getInputStream();
+ mOut = mSocket.getOutputStream();
+ } catch (IOException ex) {
+ disconnect();
+ return false;
+ }
+ return true;
+ }
+
+ public void disconnect() {
+ Slog.i(TAG, "disconnecting...");
+ IoUtils.closeQuietly(mSocket);
+ IoUtils.closeQuietly(mIn);
+ IoUtils.closeQuietly(mOut);
+
+ mSocket = null;
+ mIn = null;
+ mOut = null;
+ }
+
+
+ private boolean readFully(byte[] buffer, int len) {
+ try {
+ Streams.readFully(mIn, buffer, 0, len);
+ } catch (IOException ioe) {
+ Slog.e(TAG, "read exception");
+ disconnect();
+ return false;
+ }
+
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "read " + len + " bytes");
+ }
+
+ return true;
+ }
+
+ private int readReply() {
+ if (!readFully(buf, 2)) {
+ return -1;
+ }
+
+ final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+ if ((len < 1) || (len > buf.length)) {
+ Slog.e(TAG, "invalid reply length (" + len + ")");
+ disconnect();
+ return -1;
+ }
+
+ if (!readFully(buf, len)) {
+ return -1;
+ }
+
+ return len;
+ }
+
+ private boolean writeCommand(String cmdString) {
+ final byte[] cmd = cmdString.getBytes();
+ final int len = cmd.length;
+ if ((len < 1) || (len > buf.length)) {
+ return false;
+ }
+
+ buf[0] = (byte) (len & 0xff);
+ buf[1] = (byte) ((len >> 8) & 0xff);
+ try {
+ mOut.write(buf, 0, 2);
+ mOut.write(cmd, 0, len);
+ } catch (IOException ex) {
+ Slog.e(TAG, "write error");
+ disconnect();
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 4a26b4b..d35fce4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -192,13 +192,14 @@ public class RuntimeInit {
*
* @param className Fully-qualified class name
* @param argv Argument vector for main()
+ * @param classLoader the classLoader to load {@className} with
*/
- private static void invokeStaticMain(String className, String[] argv)
+ private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
- cl = Class.forName(className);
+ cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
@@ -263,7 +264,7 @@ public class RuntimeInit {
* @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- public static final void zygoteInit(int targetSdkVersion, String[] argv)
+ public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
@@ -272,7 +273,7 @@ public class RuntimeInit {
commonInit();
nativeZygoteInit();
- applicationInit(targetSdkVersion, argv);
+ applicationInit(targetSdkVersion, argv, classLoader);
}
/**
@@ -290,10 +291,10 @@ public class RuntimeInit {
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
- applicationInit(targetSdkVersion, argv);
+ applicationInit(targetSdkVersion, argv, null);
}
- private static void applicationInit(int targetSdkVersion, String[] argv)
+ private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
@@ -317,7 +318,7 @@ public class RuntimeInit {
}
// Remaining arguments are passed to the start class's static main
- invokeStaticMain(args.startClass, args.startArgs);
+ invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
/**
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 0c48368..43ebb3d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -863,7 +863,7 @@ class ZygoteConnection {
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
- parsedArgs.remainingArgs);
+ parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
String className;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index eea4201..051de6e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -34,8 +34,11 @@ import android.system.Os;
import android.system.OsConstants;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.webkit.WebViewFactory;
+import dalvik.system.DexFile;
+import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -493,21 +496,69 @@ public class ZygoteInit {
Process.setArgV0(parsedArgs.niceName);
}
+ final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
+ if (systemServerClasspath != null) {
+ performSystemServerDexOpt(systemServerClasspath);
+ }
+
if (parsedArgs.invokeWith != null) {
+ String[] args = parsedArgs.remainingArgs;
+ // If we have a non-null system server class path, we'll have to duplicate the
+ // existing arguments and append the classpath to it. ART will handle the classpath
+ // correctly when we exec a new process.
+ if (systemServerClasspath != null) {
+ String[] amendedArgs = new String[args.length + 2];
+ amendedArgs[0] = "-cp";
+ amendedArgs[1] = systemServerClasspath;
+ System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
+ }
+
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
- null, parsedArgs.remainingArgs);
+ null, args);
} else {
+ ClassLoader cl = null;
+ if (systemServerClasspath != null) {
+ cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+
/*
* Pass the remaining arguments to SystemServer.
*/
- RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
+ RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
/* should never reach here */
}
/**
+ * Performs dex-opt on the elements of {@code classPath}, if needed. We
+ * choose the instruction set of the current runtime.
+ */
+ private static void performSystemServerDexOpt(String classPath) {
+ final String[] classPathElements = classPath.split(":");
+ final InstallerConnection installer = new InstallerConnection();
+ final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
+
+ try {
+ for (String classPathElement : classPathElements) {
+ final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
+ false /* defer */);
+ if (dexopt == DexFile.DEXOPT_NEEDED) {
+ installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
+ } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
+ installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
+ }
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException("Error starting system_server", ioe);
+ } finally {
+ installer.disconnect();
+ }
+ }
+
+ /**
* Prepare the arguments and fork for the system server process.
*/
private static boolean startSystemServer(String abiList, String socketName)
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
new file mode 100644
index 0000000..7249985
--- /dev/null
+++ b/core/java/com/android/server/BootReceiver.java
@@ -0,0 +1,260 @@
+/*
+ * 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.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.IPackageManager;
+import android.os.Build;
+import android.os.DropBoxManager;
+import android.os.FileObserver;
+import android.os.FileUtils;
+import android.os.RecoverySystem;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.Downloads;
+import android.util.Slog;
+
+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";
+
+ // Maximum size of a logged event (files get truncated if they're longer).
+ // Give userdebug builds a larger max to capture extra debug, esp. for last_kmsg.
+ private static final int LOG_SIZE =
+ SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536;
+
+ private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+ // The pre-froyo package and class of the system updater, which
+ // ran in the system process. We need to remove its packages here
+ // in order to clean up after a pre-froyo-to-froyo update.
+ private static final String OLD_UPDATER_PACKAGE =
+ "com.google.android.systemupdater";
+ private static final String OLD_UPDATER_CLASS =
+ "com.google.android.systemupdater.SystemUpdateReceiver";
+
+ // Keep a reference to the observer so the finalizer doesn't disable it.
+ private static FileObserver sTombstoneObserver = null;
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ // Log boot events in the background to avoid blocking the main thread with I/O
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ logBootEvents(context);
+ } catch (Exception e) {
+ Slog.e(TAG, "Can't log boot events", e);
+ }
+ try {
+ boolean onlyCore = false;
+ try {
+ onlyCore = IPackageManager.Stub.asInterface(ServiceManager.getService(
+ "package")).isOnlyCoreApps();
+ } catch (RemoteException e) {
+ }
+ if (!onlyCore) {
+ removeOldUpdatePackages(context);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Can't remove old update packages", e);
+ }
+
+ }
+ }.start();
+ }
+
+ private void removeOldUpdatePackages(Context context) {
+ Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
+ }
+
+ private void logBootEvents(Context ctx) throws IOException {
+ final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE);
+ final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE);
+ final String headers = new StringBuilder(512)
+ .append("Build: ").append(Build.FINGERPRINT).append("\n")
+ .append("Hardware: ").append(Build.BOARD).append("\n")
+ .append("Revision: ")
+ .append(SystemProperties.get("ro.revision", "")).append("\n")
+ .append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
+ .append("Radio: ").append(Build.RADIO).append("\n")
+ .append("Kernel: ")
+ .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
+ .append("\n").toString();
+ final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+
+ String recovery = RecoverySystem.handleAftermath();
+ if (recovery != null && db != null) {
+ db.addText("SYSTEM_RECOVERY_LOG", headers + recovery);
+ }
+
+ String lastKmsgFooter = "";
+ if (bootReason != null) {
+ lastKmsgFooter = new StringBuilder(512)
+ .append("\n")
+ .append("Boot info:\n")
+ .append("Last boot reason: ").append(bootReason).append("\n")
+ .toString();
+ }
+
+ 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", headers);
+
+ // Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
+ addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+ "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG");
+ addFileWithFootersToDropBox(db, prefs, headers, lastKmsgFooter,
+ "/sys/fs/pstore/console-ramoops", -LOG_SIZE,
+ "SYSTEM_LAST_KMSG");
+ addFileToDropBox(db, prefs, headers, "/cache/recovery/log",
+ -LOG_SIZE, "SYSTEM_RECOVERY_LOG");
+ addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console",
+ -LOG_SIZE, "APANIC_CONSOLE");
+ addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads",
+ -LOG_SIZE, "APANIC_THREADS");
+ addAuditErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_AUDIT");
+ addFsckErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_FSCK");
+ } else {
+ if (db != null) db.addText("SYSTEM_RESTART", headers);
+ }
+
+ // Scan existing tombstones (in case any new ones appeared)
+ File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+ for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+ addFileToDropBox(db, prefs, headers, tombstoneFiles[i].getPath(),
+ LOG_SIZE, "SYSTEM_TOMBSTONE");
+ }
+
+ // Start watching for new tombstone files; will record them as they occur.
+ // This gets registered with the singleton file observer thread.
+ sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CLOSE_WRITE) {
+ @Override
+ public void onEvent(int event, String path) {
+ try {
+ String filename = new File(TOMBSTONE_DIR, path).getPath();
+ addFileToDropBox(db, prefs, headers, filename, LOG_SIZE, "SYSTEM_TOMBSTONE");
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't log tombstone", e);
+ }
+ }
+ };
+
+ sTombstoneObserver.startWatching();
+ }
+
+ private static void addFileToDropBox(
+ DropBoxManager db, SharedPreferences prefs,
+ String headers, String filename, int maxSize, String tag) throws IOException {
+ addFileWithFootersToDropBox(db, prefs, headers, "", filename, maxSize,
+ tag);
+ }
+
+ private static void addFileWithFootersToDropBox(
+ DropBoxManager db, SharedPreferences prefs,
+ String headers, String footers, String filename, int maxSize,
+ String tag) throws IOException {
+ if (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
+
+ if (prefs != null) {
+ long lastTime = prefs.getLong(filename, 0);
+ if (lastTime == fileTime) return; // Already logged this particular file
+ // TODO: move all these SharedPreferences Editor commits
+ // outside this function to the end of logBootEvents
+ prefs.edit().putLong(filename, fileTime).apply();
+ }
+
+ Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
+ db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") + footers);
+ }
+
+ private static void addAuditErrorsToDropBox(DropBoxManager db, SharedPreferences prefs,
+ String headers, int maxSize, String tag) throws IOException {
+ if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
+ Slog.i(TAG, "Copying audit failures to DropBox");
+
+ File file = new File("/proc/last_kmsg");
+ long fileTime = file.lastModified();
+ if (fileTime <= 0) {
+ file = new File("/sys/fs/pstore/console-ramoops");
+ fileTime = file.lastModified();
+ }
+
+ if (fileTime <= 0) return; // File does not exist
+
+ if (prefs != null) {
+ long lastTime = prefs.getLong(tag, 0);
+ if (lastTime == fileTime) return; // Already logged this particular file
+ // TODO: move all these SharedPreferences Editor commits
+ // outside this function to the end of logBootEvents
+ prefs.edit().putLong(tag, fileTime).apply();
+ }
+
+ String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
+ StringBuilder sb = new StringBuilder();
+ for (String line : log.split("\n")) {
+ if (line.contains("audit")) {
+ sb.append(line + "\n");
+ }
+ }
+ Slog.i(TAG, "Copied " + sb.toString().length() + " worth of audits to DropBox");
+ db.addText(tag, headers + sb.toString());
+ }
+
+ private static void addFsckErrorsToDropBox(DropBoxManager db, SharedPreferences prefs,
+ String headers, int maxSize, String tag) throws IOException {
+ boolean upload_needed = false;
+ if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
+ Slog.i(TAG, "Checking for fsck errors");
+
+ File file = new File("/dev/fscklogs/log");
+ long fileTime = file.lastModified();
+ if (fileTime <= 0) return; // File does not exist
+
+ String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
+ StringBuilder sb = new StringBuilder();
+ for (String line : log.split("\n")) {
+ if (line.contains("FILE SYSTEM WAS MODIFIED")) {
+ upload_needed = true;
+ break;
+ }
+ }
+
+ if (upload_needed) {
+ addFileToDropBox(db, prefs, headers, "/dev/fscklogs/log", maxSize, tag);
+ }
+
+ // Remove the file so we don't re-upload if the runtime restarts.
+ file.delete();
+ }
+}
diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java
deleted file mode 100644
index 6e67970..0000000
--- a/core/java/com/android/server/SystemService.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2013 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.Context;
-import android.os.IBinder;
-import android.os.ServiceManager;
-
-/**
- * The base class for services running in the system process. Override and implement
- * the lifecycle event callback methods as needed.
- * <p>
- * The lifecycle of a SystemService:
- * </p><ul>
- * <li>The constructor is called and provided with the system {@link Context}
- * to initialize the system service.
- * <li>{@link #onStart()} is called to get the service running. The service should
- * publish its binder interface at this point using
- * {@link #publishBinderService(String, IBinder)}. It may also publish additional
- * local interfaces that other services within the system server may use to access
- * privileged internal functions.
- * <li>Then {@link #onBootPhase(int)} is called as many times as there are boot phases
- * until {@link #PHASE_BOOT_COMPLETE} is sent, which is the last boot phase. Each phase
- * is an opportunity to do special work, like acquiring optional service dependencies,
- * waiting to see if SafeMode is enabled, or registering with a service that gets
- * started after this one.
- * </ul><p>
- * NOTE: All lifecycle methods are called from the system server's main looper thread.
- * </p>
- *
- * {@hide}
- */
-public abstract class SystemService {
- /*
- * Boot Phases
- */
- public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // maybe should be a dependency?
-
- /**
- * After receiving this boot phase, services can obtain lock settings data.
- */
- public static final int PHASE_LOCK_SETTINGS_READY = 480;
-
- /**
- * After receiving this boot phase, services can safely call into core system services
- * such as the PowerManager or PackageManager.
- */
- public static final int PHASE_SYSTEM_SERVICES_READY = 500;
-
- /**
- * After receiving this boot phase, services can broadcast Intents.
- */
- public static final int PHASE_ACTIVITY_MANAGER_READY = 550;
-
- /**
- * After receiving this boot phase, services can start/bind to third party apps.
- * Apps will be able to make Binder calls into services at this point.
- */
- public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600;
-
- /**
- * After receiving this boot phase, services can allow user interaction with the device.
- * This phase occurs when boot has completed and the home application has started.
- * System services may prefer to listen to this phase rather than registering a
- * broadcast receiver for ACTION_BOOT_COMPLETED to reduce overall latency.
- */
- public static final int PHASE_BOOT_COMPLETED = 1000;
-
- private final Context mContext;
-
- /**
- * Initializes the system service.
- * <p>
- * Subclasses must define a single argument constructor that accepts the context
- * and passes it to super.
- * </p>
- *
- * @param context The system server context.
- */
- public SystemService(Context context) {
- mContext = context;
- }
-
- /**
- * Gets the system context.
- */
- public final Context getContext() {
- return mContext;
- }
-
- /**
- * Returns true if the system is running in safe mode.
- * TODO: we should define in which phase this becomes valid
- */
- public final boolean isSafeMode() {
- return getManager().isSafeMode();
- }
-
- /**
- * Called when the dependencies listed in the @Service class-annotation are available
- * and after the chosen start phase.
- * When this method returns, the service should be published.
- */
- public abstract void onStart();
-
- /**
- * Called on each phase of the boot process. Phases before the service's start phase
- * (as defined in the @Service annotation) are never received.
- *
- * @param phase The current boot phase.
- */
- public void onBootPhase(int phase) {}
-
- /**
- * Called when a new user is starting, for system services to initialize any per-user
- * state they maintain for running users.
- * @param userHandle The identifier of the user.
- */
- public void onStartUser(int userHandle) {}
-
- /**
- * Called when switching to a different foreground user, for system services that have
- * special behavior for whichever user is currently in the foreground. This is called
- * before any application processes are aware of the new user.
- * @param userHandle The identifier of the user.
- */
- public void onSwitchUser(int userHandle) {}
-
- /**
- * Called when an existing user is stopping, for system services to finalize any per-user
- * state they maintain for running users. This is called prior to sending the SHUTDOWN
- * broadcast to the user; it is a good place to stop making use of any resources of that
- * user (such as binding to a service running in the user).
- * @param userHandle The identifier of the user.
- */
- public void onStopUser(int userHandle) {}
-
- /**
- * Called when an existing user is stopping, for system services to finalize any per-user
- * state they maintain for running users. This is called after all application process
- * teardown of the user is complete.
- * @param userHandle The identifier of the user.
- */
- public void onCleanupUser(int userHandle) {}
-
- /**
- * Publish the service so it is accessible to other services and apps.
- */
- protected final void publishBinderService(String name, IBinder service) {
- publishBinderService(name, service, false);
- }
-
- /**
- * Publish the service so it is accessible to other services and apps.
- */
- protected final void publishBinderService(String name, IBinder service,
- boolean allowIsolated) {
- ServiceManager.addService(name, service, allowIsolated);
- }
-
- /**
- * Get a binder service by its name.
- */
- protected final IBinder getBinderService(String name) {
- return ServiceManager.getService(name);
- }
-
- /**
- * Publish the service so it is only accessible to the system process.
- */
- protected final <T> void publishLocalService(Class<T> type, T service) {
- LocalServices.addService(type, service);
- }
-
- /**
- * Get a local service by interface.
- */
- protected final <T> T getLocalService(Class<T> type) {
- return LocalServices.getService(type);
- }
-
- private SystemServiceManager getManager() {
- return LocalServices.getService(SystemServiceManager.class);
- }
-}
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
deleted file mode 100644
index fda6479..0000000
--- a/core/java/com/android/server/SystemServiceManager.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2013 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.Context;
-import android.util.Slog;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-
-/**
- * Manages creating, starting, and other lifecycle events of
- * {@link com.android.server.SystemService system services}.
- *
- * {@hide}
- */
-public class SystemServiceManager {
- private static final String TAG = "SystemServiceManager";
-
- private final Context mContext;
- private boolean mSafeMode;
-
- // Services that should receive lifecycle events.
- private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
-
- private int mCurrentPhase = -1;
-
- public SystemServiceManager(Context context) {
- mContext = context;
- }
-
- /**
- * Starts a service by class name.
- *
- * @return The service instance.
- */
- @SuppressWarnings("unchecked")
- public SystemService startService(String className) {
- final Class<SystemService> serviceClass;
- try {
- serviceClass = (Class<SystemService>)Class.forName(className);
- } catch (ClassNotFoundException ex) {
- Slog.i(TAG, "Starting " + className);
- throw new RuntimeException("Failed to create service " + className
- + ": service class not found, usually indicates that the caller should "
- + "have called PackageManager.hasSystemFeature() to check whether the "
- + "feature is available on this device before trying to start the "
- + "services that implement it", ex);
- }
- return startService(serviceClass);
- }
-
- /**
- * Creates and starts a system service. The class must be a subclass of
- * {@link com.android.server.SystemService}.
- *
- * @param serviceClass A Java class that implements the SystemService interface.
- * @return The service instance, never null.
- * @throws RuntimeException if the service fails to start.
- */
- @SuppressWarnings("unchecked")
- public <T extends SystemService> T startService(Class<T> serviceClass) {
- final String name = serviceClass.getName();
- Slog.i(TAG, "Starting " + name);
-
- // Create the service.
- if (!SystemService.class.isAssignableFrom(serviceClass)) {
- throw new RuntimeException("Failed to create " + name
- + ": service must extend " + SystemService.class.getName());
- }
- final T service;
- try {
- Constructor<T> constructor = serviceClass.getConstructor(Context.class);
- service = constructor.newInstance(mContext);
- } catch (InstantiationException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service could not be instantiated", ex);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service must have a public constructor with a Context argument", ex);
- } catch (NoSuchMethodException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service must have a public constructor with a Context argument", ex);
- } catch (InvocationTargetException ex) {
- throw new RuntimeException("Failed to create service " + name
- + ": service constructor threw an exception", ex);
- }
-
- // Register it.
- mServices.add(service);
-
- // Start it.
- try {
- service.onStart();
- } catch (RuntimeException ex) {
- throw new RuntimeException("Failed to start service " + name
- + ": onStart threw an exception", ex);
- }
- return service;
- }
-
- /**
- * Starts the specified boot phase for all system services that have been started up to
- * this point.
- *
- * @param phase The boot phase to start.
- */
- public void startBootPhase(final int phase) {
- if (phase <= mCurrentPhase) {
- throw new IllegalArgumentException("Next phase must be larger than previous");
- }
- mCurrentPhase = phase;
-
- Slog.i(TAG, "Starting phase " + mCurrentPhase);
-
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- try {
- service.onBootPhase(mCurrentPhase);
- } catch (Exception ex) {
- throw new RuntimeException("Failed to boot service "
- + service.getClass().getName()
- + ": onBootPhase threw an exception during phase "
- + mCurrentPhase, ex);
- }
- }
- }
-
- public void startUser(final int userHandle) {
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- try {
- service.onStartUser(userHandle);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting start of user " + userHandle
- + " to service " + service.getClass().getName(), ex);
- }
- }
- }
-
- public void switchUser(final int userHandle) {
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- try {
- service.onSwitchUser(userHandle);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
- + " to service " + service.getClass().getName(), ex);
- }
- }
- }
-
- public void stopUser(final int userHandle) {
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- try {
- service.onStopUser(userHandle);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
- + " to service " + service.getClass().getName(), ex);
- }
- }
- }
-
- public void cleanupUser(final int userHandle) {
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- try {
- service.onCleanupUser(userHandle);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
- + " to service " + service.getClass().getName(), ex);
- }
- }
- }
-
- /** Sets the safe mode flag for services to query. */
- public void setSafeMode(boolean safeMode) {
- mSafeMode = safeMode;
- }
-
- /**
- * Returns whether we are booting into safe mode.
- * @return safe mode flag
- */
- public boolean isSafeMode() {
- return mSafeMode;
- }
-
- /**
- * Outputs the state of this manager to the System log.
- */
- public void dump() {
- StringBuilder builder = new StringBuilder();
- builder.append("Current phase: ").append(mCurrentPhase).append("\n");
- builder.append("Services:\n");
- final int startedLen = mServices.size();
- for (int i = 0; i < startedLen; i++) {
- final SystemService service = mServices.get(i);
- builder.append("\t")
- .append(service.getClass().getSimpleName())
- .append("\n");
- }
-
- Slog.e(TAG, builder.toString());
- }
-}