am 07d033e9: am 5fb6ef85: am 09e5ce88: Merge "Remove system_server classes from the boot image." into lmp-dev
* commit '07d033e91b824417da9a605f8d0fabdb1d167b67': Remove system_server classes from the boot image.
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
207
core/java/com/android/internal/os/InstallerConnection.java
Normal file
207
core/java/com/android/internal/os/InstallerConnection.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -863,7 +863,7 @@ class ZygoteConnection {
|
||||
pipeFd, parsedArgs.remainingArgs);
|
||||
} else {
|
||||
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
|
||||
parsedArgs.remainingArgs);
|
||||
parsedArgs.remainingArgs, null /* classLoader */);
|
||||
}
|
||||
} else {
|
||||
String className;
|
||||
|
||||
@@ -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,20 +496,68 @@ 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.
|
||||
*/
|
||||
|
||||
260
core/java/com/android/server/BootReceiver.java
Normal file
260
core/java/com/android/server/BootReceiver.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user