Fix issue #6020164: Settings crashed on orientation change...

...while listening to TTS example

This was a nice one.  What was happening is that immediately upon
being created, the activity was starting another activity in a
different process.  The second activity would never show, just
immediately exit.  However the original activity had time to
pause and get into stopping itself before the second activity had
come back to the activity manager to say it was going away, resulting
in the activity manager asking the original activity to resume.

At this point the activity manager's state is that the second
activity is finishing and gone, and the original activity is
resumed.  However in the app process the original activity is
still working on stopping itself, and it eventually completes
this and tells the activity manager.  The activity manager now
changes its state to STOPPED, even though it is actually resumed
and that is the last thing it told it to be, and it is now
proceeding to set itself in that state.

This would result later in the activity manager sending an
unnecessary state change to the application.  In the case of
the screen here, we next do a rotation change, the activity
manager thinks the current state is STOPPED not RESUMED, so it
tells the application to relaunch the activity in a new config
but not in the resumed state.  Now it does the whole "start a
new temporary activity" thing again, at which point it tries
to pause the original activity again, and we have an unbalanced
onPause() call to the app and it falls over.

Change-Id: I38b680746f4c61ae30e7ce831e1de187adf60902
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c300411..b9f63cf 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1036,6 +1036,11 @@
 
     final void activityStoppedLocked(ActivityRecord r, Bundle icicle, Bitmap thumbnail,
             CharSequence description) {
+        if (r.state != ActivityState.STOPPING) {
+            Slog.i(TAG, "Activity reported stop, but no longer stopping: " + r);
+            mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
+            return;
+        }
         if (DEBUG_SAVED_STATE) Slog.i(TAG, "Saving icicle of " + r + ": " + icicle);
         if (icicle != null) {
             // If icicle is null, this is happening due to a timeout, so we
@@ -4394,14 +4399,14 @@
             r.forceNewConfig = false;
             if (r.app == null || r.app.thread == null) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                        "Switch is destroying non-running " + r);
+                        "Config is destroying non-running " + r);
                 destroyActivityLocked(r, true, false, "config");
             } else if (r.state == ActivityState.PAUSING) {
                 // A little annoying: we are waiting for this activity to
                 // finish pausing.  Let's not do anything now, but just
                 // flag that it needs to be restarted when done pausing.
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                        "Switch is skipping already pausing " + r);
+                        "Config is skipping already pausing " + r);
                 r.configDestroy = true;
                 return true;
             } else if (r.state == ActivityState.RESUMED) {
@@ -4410,12 +4415,12 @@
                 // Instead of doing the normal handshaking, just say
                 // "restart!".
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                        "Switch is restarting resumed " + r);
+                        "Config is relaunching resumed " + r);
                 relaunchActivityLocked(r, r.configChangeFlags, true);
                 r.configChangeFlags = 0;
             } else {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                        "Switch is restarting non-resumed " + r);
+                        "Config is relaunching non-resumed " + r);
                 relaunchActivityLocked(r, r.configChangeFlags, false);
                 r.configChangeFlags = 0;
             }
@@ -4461,7 +4466,9 @@
         r.startFreezingScreenLocked(r.app, 0);
         
         try {
-            if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r);
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
+                    (andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ")
+                    + r);
             r.forceNewConfig = false;
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents,
                     changes, !andResume, new Configuration(mService.mConfiguration));
@@ -4469,7 +4476,7 @@
             // the caller will only pass in 'andResume' if this activity is
             // currently resumed, which implies we aren't sleeping.
         } catch (RemoteException e) {
-            return false;
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG, "Relaunch failed", e);
         }
 
         if (andResume) {
@@ -4478,6 +4485,10 @@
             if (mMainStack) {
                 mService.reportResumedActivityLocked(r);
             }
+            r.state = ActivityState.RESUMED;
+        } else {
+            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+            r.state = ActivityState.PAUSED;
         }
 
         return true;