Help out with issue #11497149: Clock app crashes when opened during...

...the repeated hour in DST transition

Record the last crash info that caused an app to be marked as a bad app.

Also for the battery work, add a system property tuning parameter to be
able to control the background service start delay, so we can easily
run experiments with it turned off if we want.

Change-Id: Ic33dc464d8011c918a39b912da09ea4f0fb28874
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index d6360a8..ea0b978a 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -26,6 +26,7 @@
 
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import com.android.internal.app.ProcessStats;
 import com.android.internal.os.BatteryStatsImpl;
@@ -239,7 +240,12 @@
 
     public ActiveServices(ActivityManagerService service) {
         mAm = service;
-        mMaxStartingBackground = ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
+        int maxBg = 0;
+        try {
+            maxBg = Integer.parseInt(SystemProperties.get("ro.config.max_starting_bg", "0"));
+        } catch(RuntimeException e) {
+        }
+        mMaxStartingBackground = maxBg > 0 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
     }
 
     ServiceRecord getServiceByName(ComponentName name, int callingUser) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 9be0d17..164e7b7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -457,6 +457,23 @@
     final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
 
     /**
+     * Information about a process that is currently marked as bad.
+     */
+    static final class BadProcessInfo {
+        BadProcessInfo(long time, String shortMsg, String longMsg, String stack) {
+            this.time = time;
+            this.shortMsg = shortMsg;
+            this.longMsg = longMsg;
+            this.stack = stack;
+        }
+
+        final long time;
+        final String shortMsg;
+        final String longMsg;
+        final String stack;
+    }
+
+    /**
      * Set of applications that we consider to be bad, and will reject
      * incoming broadcasts from (which the user has no control over).
      * Processes are added to this set when they have crashed twice within
@@ -464,7 +481,7 @@
      * later restarted (hopefully due to some user action).  The value is the
      * time it was added to the list.
      */
-    final ProcessMap<Long> mBadProcesses = new ProcessMap<Long>();
+    final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<BadProcessInfo>();
 
     /**
      * All of the processes we currently have running organized by pid.
@@ -9418,7 +9435,7 @@
                 ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
         startAppProblemLocked(app);
         app.stopFreezingAllLocked();
-        return handleAppCrashLocked(app);
+        return handleAppCrashLocked(app, shortMsg, longMsg, stackTrace);
     }
 
     private void makeAppNotRespondingLocked(ProcessRecord app,
@@ -9473,13 +9490,14 @@
                 app.waitDialog = null;
             }
             if (app.pid > 0 && app.pid != MY_PID) {
-                handleAppCrashLocked(app);
+                handleAppCrashLocked(app, null, null, null);
                 killUnneededProcessLocked(app, "user request after error");
             }
         }
     }
 
-    private boolean handleAppCrashLocked(ProcessRecord app) {
+    private boolean handleAppCrashLocked(ProcessRecord app, String shortMsg, String longMsg,
+            String stackTrace) {
         if (mHeadless) {
             Log.e(TAG, "handleAppCrashLocked: " + app.processName);
             return false;
@@ -9509,7 +9527,8 @@
                 if (!app.isolated) {
                     // XXX We don't have a way to mark isolated processes
                     // as bad, since they don't have a peristent identity.
-                    mBadProcesses.put(app.info.processName, app.uid, now);
+                    mBadProcesses.put(app.info.processName, app.uid,
+                            new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
                     mProcessCrashTimes.remove(app.info.processName, app.uid);
                 }
                 app.bad = true;
@@ -10771,11 +10790,11 @@
 
         if (mBadProcesses.getMap().size() > 0) {
             boolean printed = false;
-            final ArrayMap<String, SparseArray<Long>> pmap = mBadProcesses.getMap();
+            final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap();
             final int NP = pmap.size();
             for (int ip=0; ip<NP; ip++) {
                 String pname = pmap.keyAt(ip);
-                SparseArray<Long> uids = pmap.valueAt(ip);
+                SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
                 final int N = uids.size();
                 for (int i=0; i<N; i++) {
                     int puid = uids.keyAt(i);
@@ -10790,10 +10809,33 @@
                         pw.println("  Bad processes:");
                         printedAnything = true;
                     }
+                    BadProcessInfo info = uids.valueAt(i);
                     pw.print("    Bad process "); pw.print(pname);
                             pw.print(" uid "); pw.print(puid);
-                            pw.print(": crashed at time ");
-                            pw.println(uids.valueAt(i));
+                            pw.print(": crashed at time "); pw.println(info.time);
+                    if (info.shortMsg != null) {
+                        pw.print("      Short msg: "); pw.println(info.shortMsg);
+                    }
+                    if (info.longMsg != null) {
+                        pw.print("      Long msg: "); pw.println(info.longMsg);
+                    }
+                    if (info.stack != null) {
+                        pw.println("      Stack:");
+                        int lastPos = 0;
+                        for (int pos=0; pos<info.stack.length(); pos++) {
+                            if (info.stack.charAt(pos) == '\n') {
+                                pw.print("        ");
+                                pw.write(info.stack, lastPos, pos-lastPos);
+                                pw.println();
+                                lastPos = pos+1;
+                            }
+                        }
+                        if (lastPos < info.stack.length()) {
+                            pw.print("        ");
+                            pw.write(info.stack, lastPos, info.stack.length()-lastPos);
+                            pw.println();
+                        }
+                    }
                 }
             }
         }