Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 810c052d authored by Primiano Tucci's avatar Primiano Tucci Committed by Ben Murdoch
Browse files

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
parent 6c778ceb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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);
}
+28 −18
Original line number Diff line number Diff line
@@ -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();
        }
    }

+93 −35
Original line number Diff line number Diff line
@@ -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,11 +3105,13 @@ public final class ActivityManagerService extends ActivityManagerNative
            app.killedByAm = false;
            synchronized (mPidsSelfLocked) {
                this.mPidsSelfLocked.put(startResult.pid, app);
                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.
            app.setPid(0);
@@ -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,12 +8979,13 @@ 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) {
            if (isolatedUid == 0) {
                int userId = UserHandle.getUserId(uid);
                int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
                while (true) {
@@ -8979,6 +9004,11 @@ public final class ActivityManagerService extends ActivityManagerNative
                        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;
            }
        }
    }
    /**
+2 −1
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@ 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>();
+15 −3
Original line number Diff line number Diff line
@@ -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) {
Loading