Fix bug where we could get stuck repeatedly launching an activity.

A previous change to avoid losing activities if their process
happens to be gone at the point of launch (by counting that
activity as having its state saved) has resulted in a problem
where an activity that crashes during launch will be repeatedly
relaunched.

This is fixed here by explicitly keeping track of our attempts
to launch the activity since it was last able to save its state,
and not keeping it around if it looks like the launch is
repeatedly failing.

Change-Id: Icefd952443b7eb1222f233db95e0157fc3dd72d1
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c1b10cf..de9dda4 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -88,6 +88,7 @@
     static final boolean DEBUG_STATES = false;
     static final boolean DEBUG_ADD_REMOVE = false;
     static final boolean DEBUG_SAVED_STATE = false;
+    static final boolean DEBUG_APP = false;
 
     static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
     
@@ -653,6 +654,8 @@
 
         r.app = app;
         app.waitingToKill = null;
+        r.launchCount++;
+        r.lastLaunchTime = SystemClock.uptimeMillis();
 
         if (localLOGV) Slog.v(TAG, "Launching: " + r);
 
@@ -803,7 +806,7 @@
         // Is this activity's application already running?
         ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                 r.info.applicationInfo.uid);
-        
+
         if (r.launchTime == 0) {
             r.launchTime = SystemClock.uptimeMillis();
             if (mInitialStartTime == 0) {
@@ -1091,6 +1094,7 @@
             // haven't really saved the state.
             r.icicle = icicle;
             r.haveState = true;
+            r.launchCount = 0;
             r.updateThumbnail(thumbnail, description);
         }
         if (!r.stopped) {
@@ -3931,6 +3935,7 @@
         if (setState) {
             if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r + " (cleaning up)");
             r.state = ActivityState.DESTROYED;
+            if (DEBUG_APP) Slog.v(TAG, "Clearing app during cleanUp for activity " + r);
             r.app = null;
         }
 
@@ -3988,6 +3993,7 @@
         if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r
                 + " (removed from history)");
         r.state = ActivityState.DESTROYED;
+        if (DEBUG_APP) Slog.v(TAG, "Clearing app during remove for activity " + r);
         r.app = null;
         mService.mWindowManager.removeAppToken(r.appToken);
         if (VALIDATE_TOKENS) {
@@ -4130,6 +4136,7 @@
                 if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r
                         + " (destroy skipped)");
                 r.state = ActivityState.DESTROYED;
+                if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r);
                 r.app = null;
             }
         } else {
@@ -4141,6 +4148,7 @@
                 if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYED: " + r
                         + " (no app)");
                 r.state = ActivityState.DESTROYED;
+                if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity " + r);
                 r.app = null;
             }
         }
@@ -4215,7 +4223,21 @@
             if (DEBUG_CLEANUP) Slog.v(
                 TAG, "Record #" + i + " " + r + ": app=" + r.app);
             if (r.app == app) {
+                boolean remove;
                 if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
+                    // Don't currently have state for the activity, or
+                    // it is finishing -- always remove it.
+                    remove = true;
+                } else if (r.launchCount > 2 &&
+                        r.lastLaunchTime > (SystemClock.uptimeMillis()-60000)) {
+                    // We have launched this activity too many times since it was
+                    // able to run, so give up and remove it.
+                    remove = true;
+                } else {
+                    // The process may be gone, but the activity lives on!
+                    remove = false;
+                }
+                if (remove) {
                     if (ActivityStack.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) {
                         RuntimeException here = new RuntimeException("here");
                         here.fillInStackTrace();
@@ -4242,6 +4264,8 @@
                     if (r.visible) {
                         hasVisibleActivities = true;
                     }
+                    if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity "
+                            + r);
                     r.app = null;
                     r.nowVisible = false;
                     if (!r.haveState) {