diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 5262a5f2b2b2d..2a17fa6a8a26b 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -25,4 +25,6 @@ public abstract class ActivityManagerInternal { // Called by the power manager. public abstract void goingToSleep(); public abstract void wakingUp(); + public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs, + String processName, String abiOverride, int uid, Runnable crashHandler); } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5347f03de8853..772e13235fd74 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -454,7 +454,7 @@ public interface IActivityManager extends IInterface { * Private non-Binder interfaces */ /* package */ boolean testIsSystemReady(); - + /** Information you can retrieve about a particular application. */ public static class ContentProviderHolder implements Parcelable { public final ProviderInfo info; diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 0401fb7729f5c..ef3ab4a672d86 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -16,6 +16,7 @@ package android.webkit; +import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.content.Context; import android.content.pm.PackageManager; @@ -26,6 +27,7 @@ import android.os.ServiceManager; import android.os.StrictMode; import android.util.AndroidRuntimeException; import android.util.Log; +import com.android.server.LocalServices; import dalvik.system.VMRuntime; import java.io.File; @@ -129,9 +131,9 @@ public final class WebViewFactory { } else { Log.e(LOGTAG, "reserving address space failed"); } - } catch (Throwable e) { + } catch (Throwable t) { // Log and discard errors at this stage as we must not crash the zygote. - Log.e(LOGTAG, "error preparing native loader", e); + Log.e(LOGTAG, "error preparing native loader", t); } } @@ -144,29 +146,37 @@ public final class WebViewFactory { public static void prepareWebViewInSystemServer() { if (DEBUG) Log.v(LOGTAG, "creating relro files"); if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_64).exists()) { - createRelroFile(Build.SUPPORTED_64_BIT_ABIS[0]); + createRelroFile(true /* is64Bit */); } if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) { - createRelroFile(Build.SUPPORTED_32_BIT_ABIS[0]); + createRelroFile(false /* is64Bit */); } } - private static void createRelroFile(String abi) { + private static void createRelroFile(final boolean is64Bit) { + String abi = is64Bit ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]; + + // crashHandler is invoked by the ActivityManagerService when the isolated process crashes. + Runnable crashHandler = new Runnable() { + @Override + public void run() { + try { + getUpdateService().notifyRelroCreationCompleted(is64Bit, false); + } catch (RemoteException e) { + Log.e(LOGTAG, "Cannot reach WebViewUpdateService. " + e.getMessage()); + } + } + }; + try { - Process.start("android.webkit.WebViewFactory$RelroFileCreator", - "WebViewLoader-" + abi, - Process.SHARED_RELRO_UID, - Process.SHARED_RELRO_UID, - null, - 0, // TODO(torne): do we need to set debug flags? - Zygote.MOUNT_EXTERNAL_NONE, - Build.VERSION.SDK_INT, - null, - abi, - null); - } catch (Throwable e) { + String[] args = null; // TODO: plumb native library paths via args. + LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess( + RelroFileCreator.class.getName(), args, "WebViewLoader-" + abi, abi, + Process.SHARED_RELRO_UID, crashHandler); + } catch (Throwable t) { // Log and discard errors as we must not crash the system server. - Log.e(LOGTAG, "error starting relro file creator for abi " + abi, e); + Log.e(LOGTAG, "error starting relro file creator for abi " + abi, t); + crashHandler.run(); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f3ac0f59b0c34..15058554a907d 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2034,7 +2034,7 @@ public final class ActivityManagerService extends ActivityManagerNative mSystemThread.installSystemApplicationInfo(info); synchronized (this) { - ProcessRecord app = newProcessRecordLocked(info, info.processName, false); + ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0); app.persistent = true; app.pid = MY_PID; app.maxAdj = ProcessList.SYSTEM_ADJ; @@ -2838,6 +2838,16 @@ public final class ActivityManagerService extends ActivityManagerNative ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting, boolean isolated, boolean keepIfLarge) { + return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType, + hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge, + null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */, + null /* crashHandler */); + } + + final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, + boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, + boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge, + String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) { ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(processName, info.uid, keepIfLarge); @@ -2905,7 +2915,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app == null) { - app = newProcessRecordLocked(info, processName, isolated); + app = newProcessRecordLocked(info, processName, isolated, isolatedUid); + app.crashHandler = crashHandler; if (app == null) { Slog.w(TAG, "Failed making new process record for " + processName + "/" + info.uid + " isolated=" + isolated); @@ -2932,7 +2943,8 @@ public final class ActivityManagerService extends ActivityManagerNative return app; } - startProcessLocked(app, hostingType, hostingNameStr, null /* ABI override */); + startProcessLocked( + app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs); return (app.pid != 0) ? app : null; } @@ -2940,8 +2952,10 @@ public final class ActivityManagerService extends ActivityManagerNative return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0; } - private final void startProcessLocked(ProcessRecord app, - String hostingType, String hostingNameStr, String abiOverride) { + private final void startProcessLocked(ProcessRecord app, String hostingType, + String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) { + boolean isActivityProcess = (entryPoint == null); + if (entryPoint == null) entryPoint = "android.app.ActivityThread"; if (app.pid > 0 && app.pid != MY_PID) { synchronized (mPidsSelfLocked) { mPidsSelfLocked.remove(app.pid); @@ -3034,9 +3048,9 @@ public final class ActivityManagerService extends ActivityManagerNative // Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. - Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", + Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, - app.info.targetSdkVersion, app.info.seinfo, requiredAbi, null); + app.info.targetSdkVersion, app.info.seinfo, requiredAbi, entryPointArgs); if (app.isolated) { mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid); @@ -3056,6 +3070,11 @@ public final class ActivityManagerService extends ActivityManagerNative buf.setLength(0); buf.append("Start proc "); buf.append(app.processName); + if (!isActivityProcess) { + buf.append(" ["); + buf.append(entryPoint); + buf.append("]"); + } buf.append(" for "); buf.append(hostingType); if (hostingNameStr != null) { @@ -3086,10 +3105,12 @@ public final class ActivityManagerService extends ActivityManagerNative app.killedByAm = false; synchronized (mPidsSelfLocked) { this.mPidsSelfLocked.put(startResult.pid, app); - Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); - msg.obj = app; - mHandler.sendMessageDelayed(msg, startResult.usingWrapper - ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); + if (isActivityProcess) { + Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + msg.obj = app; + mHandler.sendMessageDelayed(msg, startResult.usingWrapper + ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); + } } } catch (RuntimeException e) { // XXX do better error recovery. @@ -5359,7 +5380,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.deathRecipient = adr; } catch (RemoteException e) { app.resetPackageList(mProcessStats); - startProcessLocked(app, "link fail", processName, null /* ABI override */); + startProcessLocked(app, "link fail", processName, null /* ABI override */, + null /* entryPoint */, null /* entryPointArgs */); return false; } @@ -5452,7 +5474,8 @@ public final class ActivityManagerService extends ActivityManagerNative app.resetPackageList(mProcessStats); app.unlinkDeathRecipient(); - startProcessLocked(app, "bind fail", processName, null /* ABI override */); + startProcessLocked(app, "bind fail", processName, null /* ABI override */, + null /* entryPoint */, null /* entryPointArgs */); return false; } @@ -5607,7 +5630,8 @@ public final class ActivityManagerService extends ActivityManagerNative for (int ip=0; ip Process.LAST_ISOLATED_UID) { - mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID; - } - uid = UserHandle.getUid(userId, mNextIsolatedProcessUid); - mNextIsolatedProcessUid++; - if (mIsolatedProcesses.indexOfKey(uid) < 0) { - // No process for this uid, use it. - break; - } - stepsLeft--; - if (stepsLeft <= 0) { - return null; + if (isolatedUid == 0) { + int userId = UserHandle.getUserId(uid); + int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1; + while (true) { + if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID + || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) { + mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID; + } + uid = UserHandle.getUid(userId, mNextIsolatedProcessUid); + mNextIsolatedProcessUid++; + if (mIsolatedProcesses.indexOfKey(uid) < 0) { + // No process for this uid, use it. + break; + } + stepsLeft--; + if (stepsLeft <= 0) { + return null; + } } + } else { + // Special case for startIsolatedProcess (internal only), where + // the uid of the isolated process is specified by the caller. + uid = isolatedUid; } } return new ProcessRecord(stats, info, proc, uid); @@ -8993,7 +9023,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app == null) { - app = newProcessRecordLocked(info, null, isolated); + app = newProcessRecordLocked(info, null, isolated, 0); mProcessNames.put(info.processName, app.uid, app); if (isolated) { mIsolatedProcesses.put(app.uid, app); @@ -9019,8 +9049,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) { mPersistentStartingProcesses.add(app); - startProcessLocked(app, "added application", app.processName, - abiOverride); + startProcessLocked(app, "added application", app.processName, abiOverride, + null /* entryPoint */, null /* entryPointArgs */); } return app; @@ -10519,6 +10549,7 @@ public final class ActivityManagerService extends ActivityManagerNative mProcessCrashTimes.put(app.info.processName, app.uid, now); } + if (app.crashHandler != null) mHandler.post(app.crashHandler); return true; } @@ -13517,7 +13548,8 @@ public final class ActivityManagerService extends ActivityManagerNative // We have components that still need to be running in the // process, so re-launch it. mProcessNames.put(app.processName, app.uid, app); - startProcessLocked(app, "restart", app.processName, null /* ABI override */); + startProcessLocked(app, "restart", app.processName, null /* ABI override */, + null /* entryPoint */, null /* entryPointArgs */); } else if (app.pid > 0 && app.pid != MY_PID) { // Goodbye! boolean removed; @@ -17849,6 +17881,32 @@ public final class ActivityManagerService extends ActivityManagerNative public void wakingUp() { ActivityManagerService.this.wakingUp(); } + + @Override + public int startIsolatedProcess(String entryPoint, String[] entryPointArgs, + String processName, String abiOverride, int uid, Runnable crashHandler) { + synchronized(ActivityManagerService.this) { + ApplicationInfo info = new ApplicationInfo(); + // In general the ApplicationInfo.uid isn't neccesarily equal to ProcessRecord.uid. + // For isolated processes, the former contains the parent's uid and the latter the + // actual uid of the isolated process. + // In the special case introduced by this method (which is, starting an isolated + // process directly from the SystemServer without an actual parent app process) the + // closest thing to a parent's uid is SYSTEM_UID. + // The only important thing here is to keep AI.uid != PR.uid, in order to trigger + // the |isolated| logic in the ProcessRecord constructor. + info.uid = Process.SYSTEM_UID; + info.processName = processName; + info.className = entryPoint; + info.packageName = "android"; + startProcessLocked(processName, info /* info */, false /* knownToBeDead */, + 0 /* intentFlags */, "" /* hostingType */, null /* hostingName */, + true /* allowWhileBooting */, true /* isolated */, uid, + true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs, + crashHandler); + return 0; + } + } } /** diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index b33f7b70f9ac8..f1bcb6051e4a4 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -127,7 +127,8 @@ final class ProcessRecord { Object adjSource; // Debugging: option dependent object. int adjSourceProcState; // Debugging: proc state of adjSource's process. Object adjTarget; // Debugging: target component impacting oom_adj. - + Runnable crashHandler; // Optional local handler to be invoked in the process crash. + // contains HistoryRecord objects final ArrayList activities = new ArrayList(); // all ServiceRecord running in this process diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index 60724e73ed624..06dc362b545f7 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -23,6 +23,7 @@ import android.content.IntentFilter; import android.os.Binder; import android.os.Process; import android.util.Log; +import android.util.Slog; import android.webkit.IWebViewUpdateService; import android.webkit.WebViewFactory; @@ -30,6 +31,8 @@ import android.webkit.WebViewFactory; * Private service to wait for the updatable WebView to be ready for use. * @hide */ +// TODO This should be implemented using the new pattern for system services. +// See comments in CL 509840. public class WebViewUpdateService extends IWebViewUpdateService.Stub { private static final String TAG = "WebViewUpdateService"; @@ -57,11 +60,14 @@ public class WebViewUpdateService extends IWebViewUpdateService.Stub { /** * The shared relro process calls this to notify us that it's done trying to create a relro - * file. + * file. This method gets called even if the relro creation has failed or the process crashed. */ + @Override // Binder call public void notifyRelroCreationCompleted(boolean is64Bit, boolean success) { - // Verify that the caller is the shared relro process. - if (Binder.getCallingUid() != Process.SHARED_RELRO_UID) { + // Verify that the caller is either the shared relro process (nominal case) or the system + // server (only in the case the relro process crashes and we get here via the crashHandler). + if (Binder.getCallingUid() != Process.SHARED_RELRO_UID && + Binder.getCallingUid() != Process.SYSTEM_UID) { return; } @@ -78,7 +84,13 @@ public class WebViewUpdateService extends IWebViewUpdateService.Stub { /** * WebViewFactory calls this to block WebView loading until the relro file is created. */ + @Override // Binder call public void waitForRelroCreationCompleted(boolean is64Bit) { + if (Binder.getCallingUid() == Process.SYSTEM_UID) { + Slog.wtf(TAG, "Trying to load WebView from the SystemServer", + new IllegalStateException()); + } + synchronized (this) { if (is64Bit) { while (!mRelroReady64Bit) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d9553547ed8c4..6f185d54d5a60 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -425,9 +425,6 @@ public final class SystemServer { Slog.i(TAG, "WebView Update Service"); ServiceManager.addService("webviewupdate", new WebViewUpdateService(context)); - Slog.i(TAG, "WebViewFactory preparation"); - WebViewFactory.prepareWebViewInSystemServer(); - Slog.i(TAG, "Scheduling Policy"); ServiceManager.addService("scheduling_policy", new SchedulingPolicyService()); @@ -1080,6 +1077,10 @@ public final class SystemServer { } catch (Throwable e) { reportWtf("observing native crashes", e); } + + Slog.i(TAG, "WebViewFactory preparation"); + WebViewFactory.prepareWebViewInSystemServer(); + try { startSystemUi(context); } catch (Throwable e) {