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:
committed by
Ben Murdoch
parent
6c778cebc7
commit
810c052d9b
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user