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:
Narayan Kamath
2014-08-15 13:40:39 +00:00
committed by Android Git Automerger
21 changed files with 337 additions and 233 deletions

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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() {

View 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;
}
}

View File

@@ -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);
}
/**

View File

@@ -863,7 +863,7 @@ class ZygoteConnection {
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs);
parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
String className;

View File

@@ -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.
*/

View 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();
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}