From b61a02657b9e577179c934bbb5e199ce919c4642 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 14 May 2012 17:19:18 -0700 Subject: [PATCH] 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 --- core/java/android/app/ActivityThread.java | 2 +- .../java/com/android/server/am/ActivityStack.java | 23 ++++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index e2ebeba76c35..5085b1eb4fa6 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -275,7 +275,7 @@ public final class ActivityThread { } public String toString() { - ComponentName componentName = intent.getComponent(); + ComponentName componentName = intent != null ? intent.getComponent() : null; return "ActivityRecord{" + Integer.toHexString(System.identityHashCode(this)) + " token=" + token + " " + (componentName == null diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index c300411e965c..b9f63cf022bc 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -1036,6 +1036,11 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { if (mMainStack) { mService.reportResumedActivityLocked(r); } + r.state = ActivityState.RESUMED; + } else { + mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); + r.state = ActivityState.PAUSED; } return true; -- 2.11.0