Loading core/java/android/app/IActivityTaskManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -129,6 +129,7 @@ interface IActivityTaskManager { oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling); void activityResumed(in IBinder token); void activityTopResumedStateLost(); void activityPaused(in IBinder token); void activityStopped(in IBinder token, in Bundle state, in PersistableBundle persistentState, in CharSequence description); Loading core/java/android/app/servertransaction/TopResumedActivityChangeItem.java +22 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.app.ActivityTaskManager; import android.app.ClientTransactionHandler; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.Trace; /** Loading @@ -38,6 +40,26 @@ public class TopResumedActivityChangeItem extends ClientTransactionItem { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { if (mOnTop) { return; } // The loss of top resumed state can always be reported immediately in postExecute // because only three cases are possible: // 1. Activity is in RESUMED state now and it just handled the callback in #execute(). // 2. Activity wasn't RESUMED yet, which means that it didn't receive the top state yet. // 3. Activity is PAUSED or in other lifecycle state after PAUSED. In this case top resumed // state loss was already called right before pausing. try { ActivityTaskManager.getService().activityTopResumedStateLost(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } // ObjectPoolItem implementation Loading services/core/java/com/android/server/wm/ActivityRecord.java +8 −7 Original line number Diff line number Diff line Loading @@ -293,6 +293,7 @@ final class ActivityRecord extends ConfigurationContainer { long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity long pauseTime; // last time we started pausing the activity long launchTickTime; // base time for launch tick messages long topResumedStateLossTime; // last time we reported top resumed state loss to an activity // Last configuration reported to the activity in the client process. private MergedConfiguration mLastReportedConfiguration; private int mLastReportedDisplayId; Loading Loading @@ -694,14 +695,14 @@ final class ActivityRecord extends ConfigurationContainer { void scheduleTopResumedActivityChanged(boolean onTop) { if (!attachedToProcess()) { if (DEBUG_CONFIGURATION) { if (DEBUG_STATES) { Slog.w(TAG, "Can't report activity position update - client not running" + ", activityRecord=" + this); } return; } try { if (DEBUG_CONFIGURATION) { if (DEBUG_STATES) { Slog.v(TAG, "Sending position change to " + this + ", onTop: " + onTop); } Loading Loading @@ -3285,7 +3286,7 @@ final class ActivityRecord extends ConfigurationContainer { transaction.addCallback(callbackItem); transaction.setLifecycleStateRequest(lifecycleItem); mAtmService.getLifecycleManager().scheduleTransaction(transaction); mRootActivityContainer.updateTopResumedActivityIfNeeded(); mStackSupervisor.updateTopResumedActivityIfNeeded(); // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only // request resume if this activity is currently resumed, which implies we aren't // sleeping. Loading services/core/java/com/android/server/wm/ActivityStack.java +2 −1 Original line number Diff line number Diff line Loading @@ -1491,6 +1491,7 @@ class ActivityStack extends ConfigurationContainer { + " callers=" + Debug.getCallers(5)); r.setState(RESUMED, "minimalResumeActivityLocked"); r.completeResumeLocked(); mStackSupervisor.updateTopResumedActivityIfNeeded(); if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Launch completed; removing icicle of " + r.icicle); } Loading Loading @@ -2575,7 +2576,7 @@ class ActivityStack extends ConfigurationContainer { // Protect against recursion. mInResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); mRootActivityContainer.updateTopResumedActivityIfNeeded(); mStackSupervisor.updateTopResumedActivityIfNeeded(); // When resuming the top activity, it may be necessary to pause the top activity (for // example, returning to the lock screen. We suppress the normal pause logic in Loading services/core/java/com/android/server/wm/ActivityStackSupervisor.java +96 −3 Original line number Diff line number Diff line Loading @@ -170,6 +170,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // How long we can hold the launch wake lock before giving up. static final int LAUNCH_TIMEOUT = 10 * 1000; /** How long we wait until giving up on the activity telling us it released the top state. */ static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500; static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG; static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1; static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2; Loading @@ -179,6 +182,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14; static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15; static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16; static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17; // Used to indicate that windows of activities should be preserved during the resize. static final boolean PRESERVE_WINDOWS = true; Loading Loading @@ -300,6 +304,18 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { */ final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>(); /** * Cached value of the topmost resumed activity in the system. Updated when new activity is * resumed. */ private ActivityRecord mTopResumedActivity; /** * Flag indicating whether we're currently waiting for the previous top activity to handle the * loss of the state and report back before making new activity top resumed. */ private boolean mTopResumedActivityWaitingForPrev; /** The target stack bounds for the picture-in-picture mode changed that we need to report to * the application */ Rect mPipModeChangedTargetStackBounds; Loading Loading @@ -844,7 +860,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Schedule transaction. mService.getLifecycleManager().scheduleTransaction(clientTransaction); mRootActivityContainer.updateTopResumedActivityIfNeeded(); updateTopResumedActivityIfNeeded(); if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 && mService.mHasHeavyWeightFeature) { Loading Loading @@ -2296,6 +2312,73 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mHandler.sendEmptyMessage(IDLE_NOW_MSG); } /** * Updates the record of top resumed activity when it changes and handles reporting of the * state changes to previous and new top activities. It will immediately dispatch top resumed * state loss message to previous top activity (if haven't done it already). After the previous * activity releases the top state and reports back, message about acquiring top state will be * sent to the new top resumed activity. */ void updateTopResumedActivityIfNeeded() { final ActivityRecord prevTopActivity = mTopResumedActivity; final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack(); if (topStack == null || topStack.mResumedActivity == prevTopActivity) { return; } // Ask previous activity to release the top state. final boolean prevActivityReceivedTopState = prevTopActivity != null && !mTopResumedActivityWaitingForPrev; // mTopResumedActivityWaitingForPrev == true at this point would mean that an activity // before the prevTopActivity one hasn't reported back yet. So server never sent the top // resumed state change message to prevTopActivity. if (prevActivityReceivedTopState) { prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */); scheduleTopResumedStateLossTimeout(prevTopActivity); mTopResumedActivityWaitingForPrev = true; } // Update the current top activity. mTopResumedActivity = topStack.mResumedActivity; scheduleTopResumedActivityStateIfNeeded(); } /** Schedule top resumed state change if previous top activity already reported back. */ private void scheduleTopResumedActivityStateIfNeeded() { if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev) { mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */); } } /** * Limit the time given to the app to report handling of the state loss. */ private void scheduleTopResumedStateLossTimeout(ActivityRecord r) { final Message msg = mHandler.obtainMessage(TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG); msg.obj = r; r.topResumedStateLossTime = SystemClock.uptimeMillis(); mHandler.sendMessageDelayed(msg, TOP_RESUMED_STATE_LOSS_TIMEOUT); if (DEBUG_STATES) Slog.v(TAG_STATES, "Waiting for top state to be released by " + r); } /** * Handle a loss of top resumed state by an activity - update internal state and inform next top * activity if needed. */ void handleTopResumedStateReleased(boolean timeout) { if (DEBUG_STATES) { Slog.v(TAG_STATES, "Top resumed state released " + (timeout ? " (due to timeout)" : " (transition complete)")); } mHandler.removeMessages(TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG); if (!mTopResumedActivityWaitingForPrev) { // Top resumed activity state loss already handled. return; } mTopResumedActivityWaitingForPrev = false; scheduleTopResumedActivityStateIfNeeded(); } void removeTimeoutsForActivityLocked(ActivityRecord r) { if (DEBUG_IDLE) Slog.d(TAG_IDLE, "removeTimeoutsForActivity: Callers=" + Debug.getCallers(4)); Loading Loading @@ -2590,8 +2673,18 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Start home activities on displays with no activities. mRootActivityContainer.startHomeOnEmptyDisplays("homeChanged"); } } break; case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: { ActivityRecord r = (ActivityRecord) msg.obj; Slog.w(TAG, "Activity top resumed state loss timeout for " + r); synchronized (mService.mGlobalLock) { if (r.hasProcess()) { mService.logAppTooSlow(r.app, r.topResumedStateLossTime, "top state loss for " + r); } } break; handleTopResumedStateReleased(true /* timeout */); } break; } } } Loading Loading
core/java/android/app/IActivityTaskManager.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -129,6 +129,7 @@ interface IActivityTaskManager { oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling); void activityResumed(in IBinder token); void activityTopResumedStateLost(); void activityPaused(in IBinder token); void activityStopped(in IBinder token, in Bundle state, in PersistableBundle persistentState, in CharSequence description); Loading
core/java/android/app/servertransaction/TopResumedActivityChangeItem.java +22 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.app.ActivityTaskManager; import android.app.ClientTransactionHandler; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.Trace; /** Loading @@ -38,6 +40,26 @@ public class TopResumedActivityChangeItem extends ClientTransactionItem { Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } @Override public void postExecute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) { if (mOnTop) { return; } // The loss of top resumed state can always be reported immediately in postExecute // because only three cases are possible: // 1. Activity is in RESUMED state now and it just handled the callback in #execute(). // 2. Activity wasn't RESUMED yet, which means that it didn't receive the top state yet. // 3. Activity is PAUSED or in other lifecycle state after PAUSED. In this case top resumed // state loss was already called right before pausing. try { ActivityTaskManager.getService().activityTopResumedStateLost(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } // ObjectPoolItem implementation Loading
services/core/java/com/android/server/wm/ActivityRecord.java +8 −7 Original line number Diff line number Diff line Loading @@ -293,6 +293,7 @@ final class ActivityRecord extends ConfigurationContainer { long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity long pauseTime; // last time we started pausing the activity long launchTickTime; // base time for launch tick messages long topResumedStateLossTime; // last time we reported top resumed state loss to an activity // Last configuration reported to the activity in the client process. private MergedConfiguration mLastReportedConfiguration; private int mLastReportedDisplayId; Loading Loading @@ -694,14 +695,14 @@ final class ActivityRecord extends ConfigurationContainer { void scheduleTopResumedActivityChanged(boolean onTop) { if (!attachedToProcess()) { if (DEBUG_CONFIGURATION) { if (DEBUG_STATES) { Slog.w(TAG, "Can't report activity position update - client not running" + ", activityRecord=" + this); } return; } try { if (DEBUG_CONFIGURATION) { if (DEBUG_STATES) { Slog.v(TAG, "Sending position change to " + this + ", onTop: " + onTop); } Loading Loading @@ -3285,7 +3286,7 @@ final class ActivityRecord extends ConfigurationContainer { transaction.addCallback(callbackItem); transaction.setLifecycleStateRequest(lifecycleItem); mAtmService.getLifecycleManager().scheduleTransaction(transaction); mRootActivityContainer.updateTopResumedActivityIfNeeded(); mStackSupervisor.updateTopResumedActivityIfNeeded(); // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only // request resume if this activity is currently resumed, which implies we aren't // sleeping. Loading
services/core/java/com/android/server/wm/ActivityStack.java +2 −1 Original line number Diff line number Diff line Loading @@ -1491,6 +1491,7 @@ class ActivityStack extends ConfigurationContainer { + " callers=" + Debug.getCallers(5)); r.setState(RESUMED, "minimalResumeActivityLocked"); r.completeResumeLocked(); mStackSupervisor.updateTopResumedActivityIfNeeded(); if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Launch completed; removing icicle of " + r.icicle); } Loading Loading @@ -2575,7 +2576,7 @@ class ActivityStack extends ConfigurationContainer { // Protect against recursion. mInResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); mRootActivityContainer.updateTopResumedActivityIfNeeded(); mStackSupervisor.updateTopResumedActivityIfNeeded(); // When resuming the top activity, it may be necessary to pause the top activity (for // example, returning to the lock screen. We suppress the normal pause logic in Loading
services/core/java/com/android/server/wm/ActivityStackSupervisor.java +96 −3 Original line number Diff line number Diff line Loading @@ -170,6 +170,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // How long we can hold the launch wake lock before giving up. static final int LAUNCH_TIMEOUT = 10 * 1000; /** How long we wait until giving up on the activity telling us it released the top state. */ static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500; static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG; static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1; static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2; Loading @@ -179,6 +182,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14; static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15; static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16; static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17; // Used to indicate that windows of activities should be preserved during the resize. static final boolean PRESERVE_WINDOWS = true; Loading Loading @@ -300,6 +304,18 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { */ final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>(); /** * Cached value of the topmost resumed activity in the system. Updated when new activity is * resumed. */ private ActivityRecord mTopResumedActivity; /** * Flag indicating whether we're currently waiting for the previous top activity to handle the * loss of the state and report back before making new activity top resumed. */ private boolean mTopResumedActivityWaitingForPrev; /** The target stack bounds for the picture-in-picture mode changed that we need to report to * the application */ Rect mPipModeChangedTargetStackBounds; Loading Loading @@ -844,7 +860,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Schedule transaction. mService.getLifecycleManager().scheduleTransaction(clientTransaction); mRootActivityContainer.updateTopResumedActivityIfNeeded(); updateTopResumedActivityIfNeeded(); if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 && mService.mHasHeavyWeightFeature) { Loading Loading @@ -2296,6 +2312,73 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mHandler.sendEmptyMessage(IDLE_NOW_MSG); } /** * Updates the record of top resumed activity when it changes and handles reporting of the * state changes to previous and new top activities. It will immediately dispatch top resumed * state loss message to previous top activity (if haven't done it already). After the previous * activity releases the top state and reports back, message about acquiring top state will be * sent to the new top resumed activity. */ void updateTopResumedActivityIfNeeded() { final ActivityRecord prevTopActivity = mTopResumedActivity; final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack(); if (topStack == null || topStack.mResumedActivity == prevTopActivity) { return; } // Ask previous activity to release the top state. final boolean prevActivityReceivedTopState = prevTopActivity != null && !mTopResumedActivityWaitingForPrev; // mTopResumedActivityWaitingForPrev == true at this point would mean that an activity // before the prevTopActivity one hasn't reported back yet. So server never sent the top // resumed state change message to prevTopActivity. if (prevActivityReceivedTopState) { prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */); scheduleTopResumedStateLossTimeout(prevTopActivity); mTopResumedActivityWaitingForPrev = true; } // Update the current top activity. mTopResumedActivity = topStack.mResumedActivity; scheduleTopResumedActivityStateIfNeeded(); } /** Schedule top resumed state change if previous top activity already reported back. */ private void scheduleTopResumedActivityStateIfNeeded() { if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev) { mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */); } } /** * Limit the time given to the app to report handling of the state loss. */ private void scheduleTopResumedStateLossTimeout(ActivityRecord r) { final Message msg = mHandler.obtainMessage(TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG); msg.obj = r; r.topResumedStateLossTime = SystemClock.uptimeMillis(); mHandler.sendMessageDelayed(msg, TOP_RESUMED_STATE_LOSS_TIMEOUT); if (DEBUG_STATES) Slog.v(TAG_STATES, "Waiting for top state to be released by " + r); } /** * Handle a loss of top resumed state by an activity - update internal state and inform next top * activity if needed. */ void handleTopResumedStateReleased(boolean timeout) { if (DEBUG_STATES) { Slog.v(TAG_STATES, "Top resumed state released " + (timeout ? " (due to timeout)" : " (transition complete)")); } mHandler.removeMessages(TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG); if (!mTopResumedActivityWaitingForPrev) { // Top resumed activity state loss already handled. return; } mTopResumedActivityWaitingForPrev = false; scheduleTopResumedActivityStateIfNeeded(); } void removeTimeoutsForActivityLocked(ActivityRecord r) { if (DEBUG_IDLE) Slog.d(TAG_IDLE, "removeTimeoutsForActivity: Callers=" + Debug.getCallers(4)); Loading Loading @@ -2590,8 +2673,18 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Start home activities on displays with no activities. mRootActivityContainer.startHomeOnEmptyDisplays("homeChanged"); } } break; case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: { ActivityRecord r = (ActivityRecord) msg.obj; Slog.w(TAG, "Activity top resumed state loss timeout for " + r); synchronized (mService.mGlobalLock) { if (r.hasProcess()) { mService.logAppTooSlow(r.app, r.topResumedStateLossTime, "top state loss for " + r); } } break; handleTopResumedStateReleased(true /* timeout */); } break; } } } Loading