OSDN Git Service

Start processes asynchronously in AMS.
authorSudheer Shanka <sudheersai@google.com>
Mon, 16 Oct 2017 17:20:32 +0000 (10:20 -0700)
committerSudheer Shanka <sudheersai@google.com>
Fri, 15 Dec 2017 20:23:12 +0000 (12:23 -0800)
Currently, process start is initiated in ActivityManagerService
holding the main lock and this takes ~40ms to complete. As seen
in some of the issues in previous releases, this is one of the
contributors of the lock contention in system_server. This change
tries to address this issue by moving the process start outside
the locked section.

When a process start is required, instead of doing it synchronously,
this request will be posted on a handler thread. On the handler thread,
this process start request will be completed without holding a lock.
If for some reason, we decide the process is not needed anymore before
it is actually started or being started, then AMS does everything as
usual removing the references to the process from internal state except
actually killing the process which will be handled on the handler
thread.

Bug: 68775202
Test: Ran app startup perf tests using forrest, will update the bug with results.
Test: https://docs.google.com/spreadsheets/d/1cW81guRALZXKsN-WZsKyQiCSY-RgkJ2m_M9IfqIquz8
Test: cts-tradefed run singleCommand cts-dev -m CtsActivityManagerDeviceTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsWindowManagerDeviceTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsAppTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsMultiUserHostTestCases
Test: adb shell am instrument -e package com.android.server.am -w \
      com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
Test: adb shell am instrument -e class com.android.server.pm.UserManagerTest -w \
      com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
Test: adb shell am instrument -e package android.content -w \
      com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Test: cts-tradefed run singleCommand cts-dev -m CtsProviderTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsContentTestCases -t \
      android.content.cts.ContentResolverTest
Test: cts-tradefed run singleCommand cts-dev -m CtsContentTestCases -t \
      android.content.cts.ContentProviderTest
Test: cts-tradefed run singleCommand cts-dev -m CtsWebkitTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsHostsideWebViewTests
Test: cts-tradefed run singleCommand cts-dev -m CtsAssistTestCases
Test: make WebViewLoadingTests && make tradefed-all && tradefed.sh \
      run template/local_min --template:map test=WebViewLoadingTests
Test: adb shell setprop wrap.com.google.android.apps.maps \
      '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper"'
      && adb shell dumpsys meminfo --unreachable <PID_OF_APP>
      && check ppid of <APP> is logwrapper's pid.
Test: cts-tradefed run singleCommand cts-dev -m CtsWrapWrapNoDebugTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsWrapWrapDebugTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsWrapNoWrapTestCases
Test: cts-tradefed run singleCommand cts-dev -m CtsWrapWrapDebugMallocDebugTestCases
Test: manual

Change-Id: I1fe7ce48cd5a4aadccaf6b3d6fdb5cad3304f1d3

core/java/android/app/ActivityManagerInternal.java
core/java/android/app/ActivityThread.java
core/java/android/app/IActivityManager.aidl
core/java/android/provider/Settings.java
core/java/android/webkit/WebViewLibraryLoader.java
services/core/java/com/android/server/am/ActivityManagerConstants.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/AppErrors.java
services/core/java/com/android/server/am/BroadcastQueue.java
services/core/java/com/android/server/am/ProcessList.java
services/core/java/com/android/server/am/ProcessRecord.java

index d7efa91..2440cd6 100644 (file)
@@ -99,7 +99,10 @@ public abstract class ActivityManagerInternal {
     // Called by the power manager.
     public abstract void onWakefulnessChanged(int wakefulness);
 
-    public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
+    /**
+     * @return {@code true} if process start is successful, {@code false} otherwise.
+     */
+    public abstract boolean startIsolatedProcess(String entryPoint, String[] mainArgs,
             String processName, String abiOverride, int uid, Runnable crashHandler);
 
     /**
index 5369adf..596eb77 100644 (file)
@@ -223,6 +223,12 @@ public final class ActivityThread extends ClientTransactionHandler {
      */
     public static final long INVALID_PROC_STATE_SEQ = -1;
 
+    /**
+     * Identifier for the sequence no. associated with this process start. It will be provided
+     * as one of the arguments when the process starts.
+     */
+    public static final String PROC_START_SEQ_IDENT = "seq=";
+
     private final Object mNetworkPolicyLock = new Object();
 
     /**
@@ -6274,7 +6280,7 @@ public final class ActivityThread extends ClientTransactionHandler {
         System.exit(0);
     }
 
-    private void attach(boolean system) {
+    private void attach(boolean system, long startSeq) {
         sCurrentActivityThread = this;
         mSystemThread = system;
         if (!system) {
@@ -6289,7 +6295,7 @@ public final class ActivityThread extends ClientTransactionHandler {
             RuntimeInit.setApplicationObject(mAppThread.asBinder());
             final IActivityManager mgr = ActivityManager.getService();
             try {
-                mgr.attachApplication(mAppThread);
+                mgr.attachApplication(mAppThread, startSeq);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -6367,7 +6373,7 @@ public final class ActivityThread extends ClientTransactionHandler {
             ThreadedRenderer.enableForegroundTrimming();
         }
         ActivityThread thread = new ActivityThread();
-        thread.attach(true);
+        thread.attach(true, 0);
         return thread;
     }
 
@@ -6439,8 +6445,19 @@ public final class ActivityThread extends ClientTransactionHandler {
 
         Looper.prepareMainLooper();
 
+        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
+        // It will be in the format "seq=114"
+        long startSeq = 0;
+        if (args != null) {
+            for (int i = args.length - 1; i >= 0; --i) {
+                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
+                    startSeq = Long.parseLong(
+                            args[i].substring(PROC_START_SEQ_IDENT.length()));
+                }
+            }
+        }
         ActivityThread thread = new ActivityThread();
-        thread.attach(false);
+        thread.attach(false, startSeq);
 
         if (sMainThreadHandler == null) {
             sMainThreadHandler = thread.getHandler();
index 84c07ec..f15199e 100644 (file)
@@ -108,7 +108,7 @@ interface IActivityManager {
     void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId);
     oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
             boolean abortBroadcast, int flags);
-    void attachApplication(in IApplicationThread app);
+    void attachApplication(in IApplicationThread app, long startSeq);
     oneway void activityIdle(in IBinder token, in Configuration config,
             in boolean stopProfiling);
     void activityPaused(in IBinder token);
index 6b1632a..9ace29c 100644 (file)
@@ -9463,6 +9463,7 @@ public final class Settings {
          * service_min_restart_time_between     (long)
          * service_max_inactivity               (long)
          * service_bg_start_timeout             (long)
+         * process_start_async                  (boolean)
          * </pre>
          *
          * <p>
index de0b97d..eb2b6bc 100644 (file)
@@ -123,10 +123,11 @@ public class WebViewLibraryLoader {
                 throw new IllegalArgumentException(
                         "Native library paths to the WebView RelRo process must not be null!");
             }
-            int pid = LocalServices.getService(ActivityManagerInternal.class).startIsolatedProcess(
-                    RelroFileCreator.class.getName(), new String[] { nativeLib.path },
-                    "WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
-            if (pid <= 0) throw new Exception("Failed to start the relro file creator process");
+            boolean success = LocalServices.getService(ActivityManagerInternal.class)
+                    .startIsolatedProcess(
+                            RelroFileCreator.class.getName(), new String[] { nativeLib.path },
+                            "WebViewLoader-" + abi, abi, Process.SHARED_RELRO_UID, crashHandler);
+            if (!success) throw new Exception("Failed to start the relro file creator process");
         } 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, t);
index b52db87..b3a596c 100644 (file)
@@ -66,6 +66,7 @@ final class ActivityManagerConstants extends ContentObserver {
     static final String KEY_BG_START_TIMEOUT = "service_bg_start_timeout";
     static final String KEY_BOUND_SERVICE_CRASH_RESTART_DURATION = "service_crash_restart_duration";
     static final String KEY_BOUND_SERVICE_CRASH_MAX_RETRY = "service_crash_max_retry";
+    static final String KEY_PROCESS_START_ASYNC = "process_start_async";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -93,6 +94,7 @@ final class ActivityManagerConstants extends ContentObserver {
     private static final long DEFAULT_BG_START_TIMEOUT = 15*1000;
     private static final long DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION = 30*60_000;
     private static final int DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY = 16;
+    private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
 
 
     // Maximum number of cached processes we will allow.
@@ -202,6 +204,9 @@ final class ActivityManagerConstants extends ContentObserver {
     // Maximum number of retries for bound foreground services that crash soon after start
     public long BOUND_SERVICE_MAX_CRASH_RETRY = DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY;
 
+    // Indicates if the processes need to be started asynchronously.
+    public boolean FLAG_PROCESS_START_ASYNC = DEFAULT_PROCESS_START_ASYNC;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -325,6 +330,8 @@ final class ActivityManagerConstants extends ContentObserver {
                 DEFAULT_BOUND_SERVICE_CRASH_RESTART_DURATION);
             BOUND_SERVICE_MAX_CRASH_RETRY = mParser.getInt(KEY_BOUND_SERVICE_CRASH_MAX_RETRY,
                 DEFAULT_BOUND_SERVICE_CRASH_MAX_RETRY);
+            FLAG_PROCESS_START_ASYNC = mParser.getBoolean(KEY_PROCESS_START_ASYNC,
+                    DEFAULT_PROCESS_START_ASYNC);
 
             updateMaxCachedProcesses();
         }
index 5d6cf74..b66ffae 100644 (file)
@@ -34,6 +34,7 @@ import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
 import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
 import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
 import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -353,6 +354,7 @@ import android.text.style.SuggestionSpan;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.LongSparseArray;
 import android.util.StatsLog;
 import android.util.TimingsTraceLog;
 import android.util.DebugUtils;
@@ -1588,6 +1590,20 @@ public class ActivityManagerService extends IActivityManager.Stub
     @VisibleForTesting
     long mProcStateSeqCounter = 0;
 
+    /**
+     * A global counter for generating sequence numbers to uniquely identify pending process starts.
+     */
+    @GuardedBy("this")
+    private long mProcStartSeqCounter = 0;
+
+    /**
+     * Contains {@link ProcessRecord} objects for pending process starts.
+     *
+     * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
+     */
+    @GuardedBy("this")
+    private final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
+
     private final Injector mInjector;
 
     static final class ProcessChangeItem {
@@ -1775,6 +1791,8 @@ public class ActivityManagerService extends IActivityManager.Stub
     final ServiceThread mHandlerThread;
     final MainHandler mHandler;
     final Handler mUiHandler;
+    final ServiceThread mProcStartHandlerThread;
+    final Handler mProcStartHandler;
 
     final ActivityManagerConstants mConstants;
 
@@ -2701,6 +2719,8 @@ public class ActivityManagerService extends IActivityManager.Stub
         mVrController = null;
         mLockTaskController = null;
         mLifecycleManager = null;
+        mProcStartHandlerThread = null;
+        mProcStartHandler = null;
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2725,6 +2745,11 @@ public class ActivityManagerService extends IActivityManager.Stub
         mHandler = new MainHandler(mHandlerThread.getLooper());
         mUiHandler = mInjector.getUiHandler(this);
 
+        mProcStartHandlerThread = new ServiceThread(TAG + ":procStart",
+                THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
+        mProcStartHandlerThread.start();
+        mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
+
         mConstants = new ActivityManagerConstants(this, mHandler);
 
         /* static; one-time init here */
@@ -3384,8 +3409,12 @@ public class ActivityManagerService extends IActivityManager.Stub
         if (lrui >= 0) {
             if (!app.killed) {
                 Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
-                killProcessQuiet(app.pid);
-                killProcessGroup(app.uid, app.pid);
+                if (app.pid > 0) {
+                    killProcessQuiet(app.pid);
+                    killProcessGroup(app.uid, app.pid);
+                } else {
+                    app.pendingStart = false;
+                }
             }
             if (lrui <= mLruProcessActivityStart) {
                 mLruProcessActivityStart--;
@@ -3646,7 +3675,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                 || transit == TRANSIT_TASK_TO_FRONT;
     }
 
-    int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+    boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
             String processName, String abiOverride, int uid, Runnable crashHandler) {
         synchronized(this) {
             ApplicationInfo info = new ApplicationInfo();
@@ -3668,7 +3697,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                     null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
                     uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
                     crashHandler);
-            return proc != null ? proc.pid : 0;
+            return proc != null;
         }
     }
 
@@ -3789,9 +3818,9 @@ public class ActivityManagerService extends IActivityManager.Stub
         }
 
         checkTime(startTime, "startProcess: stepping in to startProcess");
-        startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
+        final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
         checkTime(startTime, "startProcess: done starting proc!");
-        return (app.pid != 0) ? app : null;
+        return success ? app : null;
     }
 
     boolean isAllowedWhileBooting(ApplicationInfo ai) {
@@ -3803,8 +3832,14 @@ public class ActivityManagerService extends IActivityManager.Stub
         startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
     }
 
-    private final void startProcessLocked(ProcessRecord app, String hostingType,
+    /**
+     * @return {@code true} if process start is successful, false otherwise.
+     */
+    private final boolean startProcessLocked(ProcessRecord app, String hostingType,
             String hostingNameStr, String abiOverride) {
+        if (app.pendingStart) {
+            return true;
+        }
         long startTime = SystemClock.elapsedRealtime();
         if (app.pid > 0 && app.pid != MY_PID) {
             checkTime(startTime, "startProcess: removing from pids map");
@@ -3960,102 +3995,235 @@ public class ActivityManagerService extends IActivityManager.Stub
             // Start the process.  It will either succeed and return a result containing
             // the PID of the new process, or else throw a RuntimeException.
             final String entryPoint = "android.app.ActivityThread";
+
+            return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
+                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
+                    startTime);
+        } catch (RuntimeException e) {
+            Slog.e(TAG, "Failure starting process " + app.processName, e);
+
+            // Something went very wrong while trying to start this process; one
+            // common case is when the package is frozen due to an active
+            // upgrade. To recover, clean up any active bookkeeping related to
+            // starting this process. (We already invoked this method once when
+            // the package was initially frozen through KILL_APPLICATION_MSG, so
+            // it doesn't hurt to use it again.)
+            forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
+                    false, true, false, false, UserHandle.getUserId(app.userId), "start failure");
+            return false;
+        }
+    }
+
+    @GuardedBy("this")
+    private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
+            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+            long startTime) {
+        app.pendingStart = true;
+        app.killedByAm = false;
+        app.removed = false;
+        app.killed = false;
+        final long startSeq = app.startSeq = ++mProcStartSeqCounter;
+        app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
+        if (mConstants.FLAG_PROCESS_START_ASYNC) {
+            if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
+                    "Posting procStart msg for " + app.toShortString());
+            mProcStartHandler.post(() -> {
+                try {
+                    synchronized (ActivityManagerService.this) {
+                        final String reason = isProcStartValidLocked(app, startSeq);
+                        if (reason != null) {
+                            Slog.w(TAG_PROCESSES, app + " not valid anymore,"
+                                    + " don't start process, " + reason);
+                            app.pendingStart = false;
+                            return;
+                        }
+                        app.usingWrapper = invokeWith != null
+                                || SystemProperties.get("wrap." + app.processName) != null;
+                        mPendingStarts.put(startSeq, app);
+                    }
+                    final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
+                            app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
+                            requiredAbi, instructionSet, invokeWith, app.startTime);
+                    synchronized (ActivityManagerService.this) {
+                        handleProcessStartedLocked(app, startResult, startSeq);
+                    }
+                } catch (RuntimeException e) {
+                    synchronized (ActivityManagerService.this) {
+                        Slog.e(TAG, "Failure starting process " + app.processName, e);
+                        mPendingStarts.remove(startSeq);
+                        app.pendingStart = false;
+                        forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+                                false, false, true, false, false,
+                                UserHandle.getUserId(app.userId), "start failure");
+                    }
+                }
+            });
+            return true;
+        } else {
+            try {
+                final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
+                        uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
+                        invokeWith, startTime);
+                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
+                        startSeq, false);
+            } catch (RuntimeException e) {
+                Slog.e(TAG, "Failure starting process " + app.processName, e);
+                app.pendingStart = false;
+                forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+                        false, false, true, false, false,
+                        UserHandle.getUserId(app.userId), "start failure");
+            }
+            return app.pid > 0;
+        }
+    }
+
+    private ProcessStartResult startProcess(String hostingType, String entryPoint,
+            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+            long startTime) {
+        try {
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                     app.processName);
             checkTime(startTime, "startProcess: asking zygote to start proc");
-            ProcessStartResult startResult;
+            final ProcessStartResult startResult;
             if (hostingType.equals("webview_service")) {
                 startResult = startWebView(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, null, null);
+                        app.info.dataDir, null,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, invokeWith, null);
+                        app.info.dataDir, invokeWith,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkTime(startTime, "startProcess: returned from zygote!");
+            return startResult;
+        } finally {
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
 
-            mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
-            checkTime(startTime, "startProcess: done updating battery stats");
-
-            EventLog.writeEvent(EventLogTags.AM_PROC_START,
-                    UserHandle.getUserId(uid), startResult.pid, uid,
-                    app.processName, hostingType,
-                    hostingNameStr != null ? hostingNameStr : "");
+    @GuardedBy("this")
+    private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
+        StringBuilder sb = null;
+        if (app.killedByAm) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("killedByAm=true;");
+        }
+        if (mProcessNames.get(app.processName, app.uid) != app) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("No entry in mProcessNames;");
+        }
+        if (!app.pendingStart) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("pendingStart=false;");
+        }
+        if (app.startSeq > expectedStartSeq) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
+        }
+        return sb == null ? null : sb.toString();
+    }
 
-            try {
-                AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
-                        seInfo, app.info.sourceDir, startResult.pid);
-            } catch (RemoteException ex) {
-                // Ignore
+    @GuardedBy("this")
+    private boolean handleProcessStartedLocked(ProcessRecord pending,
+            ProcessStartResult startResult, long expectedStartSeq) {
+        // Indicates that this process start has been taken care of.
+        if (mPendingStarts.get(expectedStartSeq) == null) {
+            if (pending.pid == startResult.pid) {
+                pending.usingWrapper = startResult.usingWrapper;
+                // TODO: Update already existing clients of usingWrapper
             }
+            return false;
+        }
+        return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
+                expectedStartSeq, false);
+    }
 
-            if (app.persistent) {
-                Watchdog.getInstance().processStarted(app.processName, startResult.pid);
-            }
+    @GuardedBy("this")
+    private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
+            long expectedStartSeq, boolean procAttached) {
+        mPendingStarts.remove(expectedStartSeq);
+        final String reason = isProcStartValidLocked(app, expectedStartSeq);
+        if (reason != null) {
+            Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + pid
+                    + ", " + reason);
+            app.pendingStart = false;
+            Process.killProcessQuiet(pid);
+            Process.killProcessGroup(app.uid, app.pid);
+            return false;
+        }
+        mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
+        checkTime(app.startTime, "startProcess: done updating battery stats");
 
-            checkTime(startTime, "startProcess: building log message");
-            StringBuilder buf = mStringBuilder;
-            buf.setLength(0);
-            buf.append("Start proc ");
-            buf.append(startResult.pid);
-            buf.append(':');
-            buf.append(app.processName);
-            buf.append('/');
-            UserHandle.formatUid(buf, uid);
-            if (app.isolatedEntryPoint != null) {
-                buf.append(" [");
-                buf.append(app.isolatedEntryPoint);
-                buf.append("]");
-            }
-            buf.append(" for ");
-            buf.append(hostingType);
-            if (hostingNameStr != null) {
-                buf.append(" ");
-                buf.append(hostingNameStr);
-            }
-            Slog.i(TAG, buf.toString());
-            app.setPid(startResult.pid);
-            app.usingWrapper = startResult.usingWrapper;
-            app.removed = false;
-            app.killed = false;
-            app.killedByAm = false;
-            checkTime(startTime, "startProcess: starting to update pids map");
-            ProcessRecord oldApp;
-            synchronized (mPidsSelfLocked) {
-                oldApp = mPidsSelfLocked.get(startResult.pid);
-            }
-            // If there is already an app occupying that pid that hasn't been cleaned up
-            if (oldApp != null && !app.isolated) {
-                // Clean up anything relating to this pid first
-                Slog.w(TAG, "Reusing pid " + startResult.pid
-                        + " while app is still mapped to it");
-                cleanUpApplicationRecordLocked(oldApp, false, false, -1,
-                        true /*replacingPid*/);
-            }
-            synchronized (mPidsSelfLocked) {
-                this.mPidsSelfLocked.put(startResult.pid, app);
+        EventLog.writeEvent(EventLogTags.AM_PROC_START,
+                UserHandle.getUserId(app.startUid), pid, app.startUid,
+                app.processName, app.hostingType,
+                app.hostingNameStr != null ? app.hostingNameStr : "");
+
+        try {
+            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
+                    app.seInfo, app.info.sourceDir, pid);
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+
+        if (app.persistent) {
+            Watchdog.getInstance().processStarted(app.processName, pid);
+        }
+
+        checkTime(app.startTime, "startProcess: building log message");
+        StringBuilder buf = mStringBuilder;
+        buf.setLength(0);
+        buf.append("Start proc ");
+        buf.append(pid);
+        buf.append(':');
+        buf.append(app.processName);
+        buf.append('/');
+        UserHandle.formatUid(buf, app.startUid);
+        if (app.isolatedEntryPoint != null) {
+            buf.append(" [");
+            buf.append(app.isolatedEntryPoint);
+            buf.append("]");
+        }
+        buf.append(" for ");
+        buf.append(app.hostingType);
+        if (app.hostingNameStr != null) {
+            buf.append(" ");
+            buf.append(app.hostingNameStr);
+        }
+        Slog.i(TAG, buf.toString());
+        app.setPid(pid);
+        app.usingWrapper = usingWrapper;
+        app.pendingStart = false;
+        checkTime(app.startTime, "startProcess: starting to update pids map");
+        ProcessRecord oldApp;
+        synchronized (mPidsSelfLocked) {
+            oldApp = mPidsSelfLocked.get(pid);
+        }
+        // If there is already an app occupying that pid that hasn't been cleaned up
+        if (oldApp != null && !app.isolated) {
+            // Clean up anything relating to this pid first
+            Slog.w(TAG, "Reusing pid " + pid
+                    + " while app is still mapped to it");
+            cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+                    true /*replacingPid*/);
+        }
+        synchronized (mPidsSelfLocked) {
+            this.mPidsSelfLocked.put(pid, app);
+            if (!procAttached) {
                 Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                 msg.obj = app;
-                mHandler.sendMessageDelayed(msg, startResult.usingWrapper
+                mHandler.sendMessageDelayed(msg, usingWrapper
                         ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
             }
-            checkTime(startTime, "startProcess: done updating pids map");
-        } catch (RuntimeException e) {
-            Slog.e(TAG, "Failure starting process " + app.processName, e);
-
-            // Something went very wrong while trying to start this process; one
-            // common case is when the package is frozen due to an active
-            // upgrade. To recover, clean up any active bookkeeping related to
-            // starting this process. (We already invoked this method once when
-            // the package was initially frozen through KILL_APPLICATION_MSG, so
-            // it doesn't hurt to use it again.)
-            forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
-                    false, true, false, false, UserHandle.getUserId(app.userId), "start failure");
         }
+        checkTime(app.startTime, "startProcess: done updating pids map");
+        return true;
     }
 
     void updateUsageStats(ActivityRecord component, boolean resumed) {
@@ -5894,7 +6062,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                 return;
             }
 
-            if (app != null) {
+            if (app != null && app.pid > 0) {
                 ArrayList<Integer> firstPids = new ArrayList<Integer>();
                 firstPids.add(app.pid);
                 dumpStackTraces(tracesPath, firstPids, null, null, true /* useTombstoned */);
@@ -6856,13 +7024,19 @@ public class ActivityManagerService extends IActivityManager.Stub
             mHeavyWeightProcess = null;
         }
         boolean needRestart = false;
-        if (app.pid > 0 && app.pid != MY_PID) {
+        if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
             int pid = app.pid;
-            synchronized (mPidsSelfLocked) {
-                mPidsSelfLocked.remove(pid);
-                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+            if (pid > 0) {
+                synchronized (mPidsSelfLocked) {
+                    mPidsSelfLocked.remove(pid);
+                    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+                }
+                mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+                if (app.isolated) {
+                    mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+                    getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
+                }
             }
-            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             boolean willRestart = false;
             if (app.persistent && !app.isolated) {
                 if (!callerWillRestart) {
@@ -6872,10 +7046,6 @@ public class ActivityManagerService extends IActivityManager.Stub
                 }
             }
             app.kill(reason, true);
-            if (app.isolated) {
-                mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
-                getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
-            }
             handleAppDiedLocked(app, willRestart, allowRestart);
             if (willRestart) {
                 removeLruProcessLocked(app);
@@ -6949,7 +7119,7 @@ public class ActivityManagerService extends IActivityManager.Stub
     }
 
     private final boolean attachApplicationLocked(IApplicationThread thread,
-            int pid) {
+            int pid, int callingUid, long startSeq) {
 
         // Find the application record that is being attached...  either via
         // the pid if we are running in multiple processes, or just pull the
@@ -6964,6 +7134,17 @@ public class ActivityManagerService extends IActivityManager.Stub
             app = null;
         }
 
+        // It's possible that process called attachApplication before we got a chance to
+        // update the internal state.
+        if (app == null && startSeq > 0) {
+            final ProcessRecord pending = mPendingStarts.get(startSeq);
+            if (pending != null && pending.startUid == callingUid
+                    && handleProcessStartedLocked(pending, pid, pending.usingWrapper,
+                            startSeq, true)) {
+                app = pending;
+            }
+        }
+
         if (app == null) {
             Slog.w(TAG, "No pending application record for pid " + pid
                     + " (IApplicationThread " + thread + "); dropping process");
@@ -7258,11 +7439,12 @@ public class ActivityManagerService extends IActivityManager.Stub
     }
 
     @Override
-    public final void attachApplication(IApplicationThread thread) {
+    public final void attachApplication(IApplicationThread thread, long startSeq) {
         synchronized (this) {
             int callingPid = Binder.getCallingPid();
+            final int callingUid = Binder.getCallingUid();
             final long origId = Binder.clearCallingIdentity();
-            attachApplicationLocked(thread, callingPid);
+            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
             Binder.restoreCallingIdentity(origId);
         }
     }
@@ -11326,7 +11508,11 @@ public class ActivityManagerService extends IActivityManager.Stub
 
     private final long[] mProcessStateStatsLongs = new long[1];
 
-    boolean isProcessAliveLocked(ProcessRecord proc) {
+    private boolean isProcessAliveLocked(ProcessRecord proc) {
+        if (proc.pid <= 0) {
+            if (DEBUG_OOM_ADJ) Slog.d(TAG, "Process hasn't started yet: " + proc);
+            return false;
+        }
         if (proc.procStatFile == null) {
             proc.procStatFile = "/proc/" + proc.pid + "/stat";
         }
@@ -15884,6 +16070,14 @@ public class ActivityManagerService extends IActivityManager.Stub
             }
             pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
         }
+        if (dumpAll && mPendingStarts.size() > 0) {
+            if (needSep) pw.println();
+            needSep = true;
+            pw.println("  mPendingStarts: ");
+            for (int i = 0, len = mPendingStarts.size(); i < len; ++i ) {
+                pw.println("    " + mPendingStarts.keyAt(i) + ": " + mPendingStarts.valueAt(i));
+            }
+        }
         if (dumpPackage == null) {
             pw.println("  mGlobalConfiguration: " + getGlobalConfiguration());
             mStackSupervisor.dumpDisplayConfigs(pw, "  ");
@@ -16918,7 +17112,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                 }
                 for (int i=mLruProcesses.size()-1; i>=0; i--) {
                     ProcessRecord proc = mLruProcesses.get(i);
-                    if (proc.pid == pid) {
+                    if (proc.pid > 0 && proc.pid == pid) {
                         procs.add(proc);
                     } else if (allPkgs && proc.pkgList != null
                             && proc.pkgList.containsKey(args[start])) {
@@ -18344,7 +18538,7 @@ public class ActivityManagerService extends IActivityManager.Stub
 
         for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
             ProcessChangeItem item = mPendingProcessChanges.get(i);
-            if (item.pid == app.pid) {
+            if (app.pid > 0 && item.pid == app.pid) {
                 mPendingProcessChanges.remove(i);
                 mAvailProcessChanges.add(item);
             }
@@ -18396,6 +18590,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                 ProcessList.remove(app.pid);
             }
             addProcessNameLocked(app);
+            app.pendingStart = false;
             startProcessLocked(app, "restart", app.processName);
             return true;
         } else if (app.pid > 0 && app.pid != MY_PID) {
@@ -23588,7 +23783,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                         + ")\n");
                     if (app.pid > 0 && app.pid != MY_PID) {
                         app.kill("empty", false);
-                    } else {
+                    } else if (app.thread != null) {
                         try {
                             app.thread.scheduleExit();
                         } catch (Exception e) {
@@ -24104,7 +24299,7 @@ public class ActivityManagerService extends IActivityManager.Stub
         }
 
         @Override
-        public int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
+        public boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
                 String processName, String abiOverride, int uid, Runnable crashHandler) {
             return ActivityManagerService.this.startIsolatedProcess(entryPoint, entryPointArgs,
                     processName, abiOverride, uid, crashHandler);
index d0bc33a..35465a7 100644 (file)
@@ -521,13 +521,13 @@ class AppErrors {
      *
      * @param app The ProcessRecord in which the error occurred.
      * @param condition Crashing, Application Not Responding, etc.  Values are defined in
-     *                      ActivityManager.AppErrorStateInfo
+     *                      ActivityManager.ProcessErrorStateInfo
      * @param activity The activity associated with the crash, if known.
      * @param shortMsg Short message describing the crash.
      * @param longMsg Long message describing the crash.
      * @param stackTrace Full crash stack trace, may be null.
      *
-     * @return Returns a fully-formed AppErrorStateInfo record.
+     * @return Returns a fully-formed ProcessErrorStateInfo record.
      */
     private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
             int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
index aa82d00..ea90db3 100644 (file)
@@ -322,7 +322,7 @@ public final class BroadcastQueue {
     public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
         boolean didSomething = false;
         final BroadcastRecord br = mPendingBroadcast;
-        if (br != null && br.curApp.pid == app.pid) {
+        if (br != null && br.curApp.pid > 0 && br.curApp.pid == app.pid) {
             if (br.curApp != app) {
                 Slog.e(TAG, "App mismatch when sending pending broadcast to "
                         + app.processName + ", intended target is " + br.curApp.processName);
@@ -876,9 +876,16 @@ public final class BroadcastQueue {
                         + mPendingBroadcast.curApp);
 
                 boolean isDead;
-                synchronized (mService.mPidsSelfLocked) {
-                    ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
-                    isDead = proc == null || proc.crashing;
+                if (mPendingBroadcast.curApp.pid > 0) {
+                    synchronized (mService.mPidsSelfLocked) {
+                        ProcessRecord proc = mService.mPidsSelfLocked.get(
+                                mPendingBroadcast.curApp.pid);
+                        isDead = proc == null || proc.crashing;
+                    }
+                } else {
+                    final ProcessRecord proc = mService.mProcessNames.get(
+                            mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
+                    isDead = proc == null || !proc.pendingStart;
                 }
                 if (!isDead) {
                     // It's still alive, so keep waiting
index 6fb3dbb..eb1636d 100644 (file)
@@ -628,6 +628,7 @@ public final class ProcessList {
 
     /**
      * Set the out-of-memory badness adjustment for a process.
+     * If {@code pid <= 0}, this method will be a no-op.
      *
      * @param pid The process identifier to set.
      * @param uid The uid of the app
@@ -636,6 +637,10 @@ public final class ProcessList {
      * {@hide}
      */
     public static final void setOomAdj(int pid, int uid, int amt) {
+        // This indicates that the process is not started yet and so no need to proceed further.
+        if (pid <= 0) {
+            return;
+        }
         if (amt == UNKNOWN_ADJ)
             return;
 
@@ -657,6 +662,10 @@ public final class ProcessList {
      * {@hide}
      */
     public static final void remove(int pid) {
+        // This indicates that the process is not started yet and so no need to proceed further.
+        if (pid <= 0) {
+            return;
+        }
         ByteBuffer buf = ByteBuffer.allocate(4 * 2);
         buf.putInt(LMK_PROCREMOVE);
         buf.putInt(pid);
index 71d6604..a1e5947 100644 (file)
@@ -196,6 +196,9 @@ final class ProcessRecord {
 
     String shortStringName;     // caching of toShortString() result.
     String stringName;          // caching of toString() result.
+    boolean pendingStart;       // Process start is pending.
+    long startSeq;              // Seq no. indicating the latest process start associated with
+                                // this process record.
 
     // These reports are generated & stored when an app gets into an error condition.
     // They will be "null" when all is OK.
@@ -211,6 +214,23 @@ final class ProcessRecord {
     // App is allowed to manage whitelists such as temporary Power Save mode whitelist.
     boolean whitelistManager;
 
+    // Params used in starting this process.
+    String hostingType;
+    String hostingNameStr;
+    String seInfo;
+    long startTime;
+    // This will be same as {@link #uid} usually except for some apps used during factory testing.
+    int startUid;
+
+    void setStartParams(int startUid, String hostingType, String hostingNameStr, String seInfo,
+            long startTime) {
+        this.startUid = startUid;
+        this.hostingType = hostingType;
+        this.hostingNameStr = hostingNameStr;
+        this.seInfo = seInfo;
+        this.startTime = startTime;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         final long nowUptime = SystemClock.uptimeMillis();
 
@@ -348,6 +368,10 @@ final class ProcessRecord {
         if (hasStartedServices) {
             pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
         }
+        if (pendingStart) {
+            pw.print(prefix); pw.print("pendingStart="); pw.println(pendingStart);
+        }
+        pw.print(prefix); pw.print("startSeq="); pw.println(startSeq);
         if (setProcState > ActivityManager.PROCESS_STATE_SERVICE) {
             pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
                     if (lastCpuTime > 0) {
@@ -627,9 +651,13 @@ final class ProcessRecord {
             if (noisy) {
                 Slog.i(TAG, "Killing " + toShortString() + " (adj " + setAdj + "): " + reason);
             }
-            EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
-            Process.killProcessQuiet(pid);
-            ActivityManagerService.killProcessGroup(uid, pid);
+            if (pid > 0) {
+                EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
+                Process.killProcessQuiet(pid);
+                ActivityManagerService.killProcessGroup(uid, pid);
+            } else {
+                pendingStart = false;
+            }
             if (!persistent) {
                 killed = true;
                 killedByAm = true;