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
This commit is contained in:
Primiano Tucci
2014-07-25 18:03:16 +01:00
committed by Ben Murdoch
parent 6c778cebc7
commit 810c052d9b
7 changed files with 145 additions and 61 deletions

View File

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

View File

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

View File

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

View File

@@ -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<NP; ip++) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
+ procs.get(ip));
startProcessLocked(procs.get(ip), "on-hold", null, null /* ABI override */);
startProcessLocked(procs.get(ip), "on-hold", null, null /* ABI override */,
null /* entryPoint */, null /* entryPointArgs */);
}
}
@@ -8955,29 +8979,35 @@ public final class ActivityManagerService extends ActivityManagerNative
// =========================================================
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated) {
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
BatteryStatsImpl.Uid.Proc ps = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
int uid = info.uid;
if (isolated) {
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;
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;
}
}
}
/**

View File

@@ -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<ActivityRecord> activities = new ArrayList<ActivityRecord>();
// all ServiceRecord running in this process

View File

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

View File

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