From 810c052d9b117217152c2a609ccec056a2a61d1e Mon Sep 17 00:00:00 2001 From: Primiano Tucci Date: Fri, 25 Jul 2014 18:03:16 +0100 Subject: [PATCH] Cherry pick Introduce startIsolatedProcess private API in ActivityManager DO NOT MERGE The new API spawns a isolated process, using a custom uid, entrypoint and abi. Such API is used by the WebViewFactory to spawn its unpriviledged but trusted process (hence the fixed uid) which rewrites the rerlo file on boot / when an update occurs. Since both the ActivityManager service and the WebViewUpdate service live in the SystemServer their calls be dispatched locally and no binder interface needs to be exposed for the new startIsolatedProcess API. Original BUG:16403706 Original Change-Id: I327b59735c12698595e0dbcc4da5d759c9103b0a Bug: 16723226 Change-Id: Iecb49888e11eec9d302d9712953fd498db5821af --- .../android/app/ActivityManagerInternal.java | 2 + core/java/android/app/IActivityManager.java | 2 +- core/java/android/webkit/WebViewFactory.java | 46 ++++--- .../server/am/ActivityManagerService.java | 128 +++++++++++++----- .../com/android/server/am/ProcessRecord.java | 3 +- .../server/webkit/WebViewUpdateService.java | 18 ++- .../java/com/android/server/SystemServer.java | 7 +- 7 files changed, 145 insertions(+), 61 deletions(-) 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) {