diff options
Diffstat (limited to 'services/java/com/android')
4 files changed, 3766 insertions, 3605 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 252392b..2c6806b 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -26,6 +26,7 @@ import com.android.server.ProcessStats; import com.android.server.SystemServer; import com.android.server.Watchdog; import com.android.server.WindowManagerService; +import com.android.server.am.ActivityStack.ActivityState; import dalvik.system.Zygote; @@ -183,47 +184,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Maximum number of recent tasks that we can remember. static final int MAX_RECENT_TASKS = 20; - + // Amount of time after a call to stopAppSwitches() during which we will // prevent further untrusted switches from happening. static final long APP_SWITCH_DELAY_TIME = 5*1000; - - // How long until we reset a task when the user returns to it. Currently - // 30 minutes. - static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30; - - // Set to true to disable the icon that is shown while a new activity - // is being started. - static final boolean SHOW_APP_STARTING_ICON = true; - - // How long we wait until giving up on the last activity to pause. This - // is short because it directly impacts the responsiveness of starting the - // next activity. - static final int PAUSE_TIMEOUT = 500; - - /** - * How long we can hold the launch wake lock before giving up. - */ - static final int LAUNCH_TIMEOUT = 10*1000; // How long we wait for a launched process to attach to the activity manager // before we decide it's never going to come up for real. static final int PROC_START_TIMEOUT = 10*1000; - // How long we wait until giving up on the last activity telling us it - // is idle. - static final int IDLE_TIMEOUT = 10*1000; - // How long to wait after going idle before forcing apps to GC. static final int GC_TIMEOUT = 5*1000; // The minimum amount of time between successive GC requests for a process. static final int GC_MIN_INTERVAL = 60*1000; - // How long we wait until giving up on an activity telling us it has - // finished destroying itself. - static final int DESTROY_TIMEOUT = 10*1000; - // How long we allow a receiver to run before giving up on it. static final int BROADCAST_TIMEOUT = 10*1000; @@ -388,29 +363,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final String[] EMPTY_STRING_ARRAY = new String[0]; - enum ActivityState { - INITIALIZING, - RESUMED, - PAUSING, - PAUSED, - STOPPING, - STOPPED, - FINISHING, - DESTROYING, - DESTROYED - } - - /** - * The back history of all previous (and possibly still - * running) activities. It contains HistoryRecord objects. - */ - final ArrayList mHistory = new ArrayList(); - + public ActivityStack mMainStack; + /** * Description of a request to start a new activity, which has been held * due to app switches being disabled. */ - class PendingActivityLaunch { + static class PendingActivityLaunch { ActivityRecord r; ActivityRecord sourceRecord; Uri[] grantedUriPermissions; @@ -422,18 +381,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<PendingActivityLaunch>(); /** - * List of people waiting to find out about the next launched activity. - */ - final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched - = new ArrayList<IActivityManager.WaitResult>(); - - /** - * List of people waiting to find out about the next visible activity. - */ - final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible - = new ArrayList<IActivityManager.WaitResult>(); - - /** * List of all active broadcasts that are to be executed immediately * (without waiting for another broadcast to finish). Currently this only * contains broadcasts to registered receivers, to avoid spinning up @@ -463,57 +410,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean mBroadcastsScheduled = false; /** - * Set to indicate whether to issue an onUserLeaving callback when a - * newly launched activity is being brought in front of us. - */ - boolean mUserLeaving = false; - - /** - * When we are in the process of pausing an activity, before starting the - * next one, this variable holds the activity that is currently being paused. - */ - ActivityRecord mPausingActivity = null; - - /** - * Current activity that is resumed, or null if there is none. - */ - ActivityRecord mResumedActivity = null; - - /** * Activity we have told the window manager to have key focus. */ ActivityRecord mFocusedActivity = null; - - /** - * This is the last activity that we put into the paused state. This is - * used to determine if we need to do an activity transition while sleeping, - * when we normally hold the top activity paused. - */ - ActivityRecord mLastPausedActivity = null; - - /** - * List of activities that are waiting for a new activity - * to become visible before completing whatever operation they are - * supposed to do. - */ - final ArrayList<ActivityRecord> mWaitingVisibleActivities - = new ArrayList<ActivityRecord>(); - - /** - * List of activities that are ready to be stopped, but waiting - * for the next activity to settle down before doing so. It contains - * HistoryRecord objects. - */ - final ArrayList<ActivityRecord> mStoppingActivities - = new ArrayList<ActivityRecord>(); - - /** - * Animations that for the current transition have requested not to - * be considered for the transition animation. - */ - final ArrayList<ActivityRecord> mNoAnimActivities - = new ArrayList<ActivityRecord>(); - /** * List of intents that were used to start the most recent tasks. */ @@ -521,14 +420,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new ArrayList<TaskRecord>(); /** - * List of activities that are ready to be finished, but waiting - * for the previous activity to settle down before doing so. It contains - * HistoryRecord objects. - */ - final ArrayList<ActivityRecord> mFinishingActivities - = new ArrayList<ActivityRecord>(); - - /** * All of the applications we currently have running organized by name. * The keys are strings of the application package name (as * returned by the package manager), and the keys are ApplicationRecord @@ -628,16 +519,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * This is the process holding what we currently consider to be * the "home" activity. */ - private ProcessRecord mHomeProcess; + ProcessRecord mHomeProcess; /** - * List of running activities, sorted by recent usage. - * The first entry in the list is the least recently used. - * It contains HistoryRecord objects. - */ - private final ArrayList mLRUActivities = new ArrayList(); - - /** * Set of PendingResultRecord objects that are currently active. */ final HashSet mPendingResultRecords = new HashSet(); @@ -831,12 +715,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int mConfigurationSeq = 0; /** - * Set when we know we are going to be calling updateConfiguration() - * soon, so want to skip intermediate config checks. - */ - boolean mConfigWillChange; - - /** * Hardware-reported OpenGLES version. */ final int GL_ES_VERSION; @@ -892,21 +770,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * Set if we are shutting down the system, similar to sleeping. */ boolean mShuttingDown = false; - - /** - * Set when the system is going to sleep, until we have - * successfully paused the current activity and released our wake lock. - * At that point the system is allowed to actually sleep. - */ - PowerManager.WakeLock mGoingToSleep; - - /** - * We don't want to allow the device to go to sleep while in the process - * of launching an activity. This is primarily to allow alarm intent - * receivers to launch an activity and get that to run before the device - * goes back to sleep. - */ - PowerManager.WakeLock mLaunchingActivity; /** * Task identifier that activities are currently being started @@ -986,8 +849,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen long mLastWriteTime = 0; - long mInitialStartTime = 0; - /** * Set to true after the system has finished booting. */ @@ -1034,16 +895,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int WAIT_FOR_DEBUGGER_MSG = 6; static final int BROADCAST_INTENT_MSG = 7; static final int BROADCAST_TIMEOUT_MSG = 8; - static final int PAUSE_TIMEOUT_MSG = 9; - static final int IDLE_TIMEOUT_MSG = 10; - static final int IDLE_NOW_MSG = 11; static final int SERVICE_TIMEOUT_MSG = 12; static final int UPDATE_TIME_ZONE = 13; static final int SHOW_UID_ERROR_MSG = 14; static final int IM_FEELING_LUCKY_MSG = 15; - static final int LAUNCH_TIMEOUT_MSG = 16; - static final int DESTROY_TIMEOUT_MSG = 17; - static final int RESUME_TOP_ACTIVITY_MSG = 19; static final int PROC_START_TIMEOUT_MSG = 20; static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21; static final int KILL_APPLICATION_MSG = 22; @@ -1183,38 +1038,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen broadcastTimeout(); } } break; - case PAUSE_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; - // We don't at this point know if the activity is fullscreen, - // so we need to be conservative and assume it isn't. - Slog.w(TAG, "Activity pause timeout for " + token); - activityPaused(token, null, true); - } break; - case IDLE_TIMEOUT_MSG: { - if (mDidDexOpt) { - mDidDexOpt = false; - Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); - nmsg.obj = msg.obj; - mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT); - return; - } - // We don't at this point know if the activity is fullscreen, - // so we need to be conservative and assume it isn't. - IBinder token = (IBinder)msg.obj; - Slog.w(TAG, "Activity idle timeout for " + token); - activityIdleInternal(token, true, null); - } break; - case DESTROY_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; - // We don't at this point know if the activity is fullscreen, - // so we need to be conservative and assume it isn't. - Slog.w(TAG, "Activity destroy timeout for " + token); - activityDestroyed(token); - } break; - case IDLE_NOW_MSG: { - IBinder token = (IBinder)msg.obj; - activityIdle(token, null); - } break; case SERVICE_TIMEOUT_MSG: { if (mDidDexOpt) { mDidDexOpt = false; @@ -1257,25 +1080,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mUidAlert = null; } } break; - case LAUNCH_TIMEOUT_MSG: { - if (mDidDexOpt) { - mDidDexOpt = false; - Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); - mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT); - return; - } - synchronized (ActivityManagerService.this) { - if (mLaunchingActivity.isHeld()) { - Slog.w(TAG, "Launch timeout has expired, giving up wake lock!"); - mLaunchingActivity.release(); - } - } - } break; - case RESUME_TOP_ACTIVITY_MSG: { - synchronized (ActivityManagerService.this) { - resumeTopActivityLocked(null); - } - } break; case PROC_START_TIMEOUT_MSG: { if (mDidDexOpt) { mDidDexOpt = false; @@ -1424,11 +1228,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen Context context = at.getSystemContext(); m.mContext = context; m.mFactoryTest = factoryTest; - PowerManager pm = - (PowerManager)context.getSystemService(Context.POWER_SERVICE); - m.mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep"); - m.mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch"); - m.mLaunchingActivity.setReferenceCounted(false); + m.mMainStack = new ActivityStack(m, context, true); m.mBatteryStatsService.publish(context); m.mUsageStatsService.publish(context); @@ -1717,7 +1517,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return mAppBindArgs; } - private final void setFocusedActivityLocked(ActivityRecord r) { + final void setFocusedActivityLocked(ActivityRecord r) { if (mFocusedActivity != r) { mFocusedActivity = r; mWindowManager.setFocusedApp(r, true); @@ -1802,65 +1602,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void updateLruProcessLocked(ProcessRecord app, + final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj, boolean updateActivityTime) { mLruSeq++; updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0); } - private final boolean updateLRUListLocked(ActivityRecord r) { - final boolean hadit = mLRUActivities.remove(r); - mLRUActivities.add(r); - return hadit; - } - - private final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { - int i = mHistory.size()-1; - while (i >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (!r.finishing && r != notTop) { - return r; - } - i--; - } - return null; - } - - private final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) { - int i = mHistory.size()-1; - while (i >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (!r.finishing && !r.delayedResume && r != notTop) { - return r; - } - i--; - } - return null; - } - - /** - * This is a simplified version of topRunningActivityLocked that provides a number of - * optional skip-over modes. It is intended for use with the ActivityController hook only. - * - * @param token If non-null, any history records matching this token will be skipped. - * @param taskId If non-zero, we'll attempt to skip over records with the same task ID. - * - * @return Returns the HistoryRecord of the next activity on the stack. - */ - private final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) { - int i = mHistory.size()-1; - while (i >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); - // Note: the taskId check depends on real taskId fields being non-zero - if (!r.finishing && (token != r) && (taskId != r.task.taskId)) { - return r; - } - i--; - } - return null; - } - - private final ProcessRecord getProcessRecordLocked( + final ProcessRecord getProcessRecordLocked( String processName, int uid) { if (uid == Process.SYSTEM_UID) { // The system gets to run in any process. If there are multiple @@ -1874,7 +1622,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return proc; } - private void ensurePackageDexOpt(String packageName) { + void ensurePackageDexOpt(String packageName) { IPackageManager pm = AppGlobals.getPackageManager(); try { if (pm.performDexOpt(packageName)) { @@ -1884,175 +1632,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private boolean isNextTransitionForward() { + boolean isNextTransitionForward() { int transit = mWindowManager.getPendingAppTransition(); return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN || transit == WindowManagerPolicy.TRANSIT_TASK_OPEN || transit == WindowManagerPolicy.TRANSIT_TASK_TO_FRONT; } - private final boolean realStartActivityLocked(ActivityRecord r, - ProcessRecord app, boolean andResume, boolean checkConfig) - throws RemoteException { - - r.startFreezingScreenLocked(app, 0); - mWindowManager.setAppVisibility(r, true); - - // Have the window manager re-evaluate the orientation of - // the screen based on the new activity order. Note that - // as a result of this, it can call back into the activity - // manager with a new orientation. We don't care about that, - // because the activity is not currently running so we are - // just restarting it anyway. - if (checkConfig) { - Configuration config = mWindowManager.updateOrientationFromAppTokens( - mConfiguration, - r.mayFreezeScreenLocked(app) ? r : null); - updateConfigurationLocked(config, r); - } - - r.app = app; - - if (localLOGV) Slog.v(TAG, "Launching: " + r); - - int idx = app.activities.indexOf(r); - if (idx < 0) { - app.activities.add(r); - } - updateLruProcessLocked(app, true, true); - - try { - if (app.thread == null) { - throw new RemoteException(); - } - List<ResultInfo> results = null; - List<Intent> newIntents = null; - if (andResume) { - results = r.results; - newIntents = r.newIntents; - } - if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r - + " icicle=" + r.icicle - + " with results=" + results + " newIntents=" + newIntents - + " andResume=" + andResume); - if (andResume) { - EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, - System.identityHashCode(r), - r.task.taskId, r.shortComponentName); - } - if (r.isHomeActivity) { - mHomeProcess = app; - } - ensurePackageDexOpt(r.intent.getComponent().getPackageName()); - app.thread.scheduleLaunchActivity(new Intent(r.intent), r, - System.identityHashCode(r), - r.info, r.icicle, results, newIntents, !andResume, - isNextTransitionForward()); - - if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) { - // This may be a heavy-weight process! Note that the package - // manager will ensure that only activity can run in the main - // process of the .apk, which is the only thing that will be - // considered heavy-weight. - if (app.processName.equals(app.info.packageName)) { - if (mHeavyWeightProcess != null && mHeavyWeightProcess != app) { - Log.w(TAG, "Starting new heavy weight process " + app - + " when already running " + mHeavyWeightProcess); - } - mHeavyWeightProcess = app; - Message msg = mHandler.obtainMessage(POST_HEAVY_NOTIFICATION_MSG); - msg.obj = r; - mHandler.sendMessage(msg); - } - } - - } catch (RemoteException e) { - if (r.launchFailed) { - // This is the second time we failed -- finish activity - // and give up. - Slog.e(TAG, "Second failure launching " - + r.intent.getComponent().flattenToShortString() - + ", giving up", e); - appDiedLocked(app, app.pid, app.thread); - requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, - "2nd-crash"); - return false; - } - - // This is the first time we failed -- restart process and - // retry. - app.activities.remove(r); - throw e; - } - - r.launchFailed = false; - if (updateLRUListLocked(r)) { - Slog.w(TAG, "Activity " + r - + " being launched, but already in LRU list"); - } - - if (andResume) { - // As part of the process of launching, ActivityThread also performs - // a resume. - r.state = ActivityState.RESUMED; - r.icicle = null; - r.haveState = false; - r.stopped = false; - mResumedActivity = r; - r.task.touchActiveTime(); - completeResumeLocked(r); - pauseIfSleepingLocked(); - } else { - // This activity is not starting in the resumed state... which - // should look like we asked it to pause+stop (but remain visible), - // and it has done so and reported back the current icicle and - // other state. - r.state = ActivityState.STOPPED; - r.stopped = true; - } - - // Launch the new version setup screen if needed. We do this -after- - // launching the initial activity (that is, home), so that it can have - // a chance to initialize itself while in the background, making the - // switch back to it faster and look better. - startSetupActivityLocked(); - - return true; - } - - private final void startSpecificActivityLocked(ActivityRecord r, - boolean andResume, boolean checkConfig) { - // Is this activity's application already running? - ProcessRecord app = getProcessRecordLocked(r.processName, - r.info.applicationInfo.uid); - - if (r.startTime == 0) { - r.startTime = SystemClock.uptimeMillis(); - if (mInitialStartTime == 0) { - mInitialStartTime = r.startTime; - } - } else if (mInitialStartTime == 0) { - mInitialStartTime = SystemClock.uptimeMillis(); - } - - if (app != null && app.thread != null) { - try { - realStartActivityLocked(r, app, andResume, checkConfig); - return; - } catch (RemoteException e) { - Slog.w(TAG, "Exception when starting activity " - + r.intent.getComponent().flattenToShortString(), e); - } - - // If a dead object exception was thrown -- fall through to - // restart the application. - } - - startProcessLocked(r.processName, r.info.applicationInfo, true, 0, - "activity", r.intent.getComponent(), false); - } - - private final ProcessRecord startProcessLocked(String processName, + final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting) { ProcessRecord app = getProcessRecordLocked(processName, info.uid); @@ -2253,365 +1840,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) { - if (mPausingActivity != null) { - RuntimeException e = new RuntimeException(); - Slog.e(TAG, "Trying to pause when pause is already pending for " - + mPausingActivity, e); - } - ActivityRecord prev = mResumedActivity; - if (prev == null) { - RuntimeException e = new RuntimeException(); - Slog.e(TAG, "Trying to pause when nothing is resumed", e); - resumeTopActivityLocked(null); - return; - } - if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev); - mResumedActivity = null; - mPausingActivity = prev; - mLastPausedActivity = prev; - prev.state = ActivityState.PAUSING; - prev.task.touchActiveTime(); - - updateCpuStats(); - - if (prev.app != null && prev.app.thread != null) { - if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev); - try { - EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY, - System.identityHashCode(prev), - prev.shortComponentName); - prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving, - prev.configChangeFlags); - updateUsageStats(prev, false); - } catch (Exception e) { - // Ignore exception, if process died other code will cleanup. - Slog.w(TAG, "Exception thrown during pause", e); - mPausingActivity = null; - mLastPausedActivity = null; - } - } else { - mPausingActivity = null; - mLastPausedActivity = null; - } - - // If we are not going to sleep, we want to ensure the device is - // awake until the next activity is started. - if (!mSleeping && !mShuttingDown) { - mLaunchingActivity.acquire(); - if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) { - // To be safe, don't allow the wake lock to be held for too long. - Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); - mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT); - } - } - - - if (mPausingActivity != null) { - // Have the window manager pause its key dispatching until the new - // activity has started. If we're pausing the activity just because - // the screen is being turned off and the UI is sleeping, don't interrupt - // key dispatch; the same activity will pick it up again on wakeup. - if (!uiSleeping) { - prev.pauseKeyDispatchingLocked(); - } else { - if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off"); - } - - // Schedule a pause timeout in case the app doesn't respond. - // We don't give it much time because this directly impacts the - // responsiveness seen by the user. - Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG); - msg.obj = prev; - mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT); - if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete..."); - } else { - // This activity failed to schedule the - // pause, so just treat it as being paused now. - if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next."); - resumeTopActivityLocked(null); - } - } - - private final void completePauseLocked() { - ActivityRecord prev = mPausingActivity; - if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev); - - if (prev != null) { - if (prev.finishing) { - if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev); - prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE); - } else if (prev.app != null) { - if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev); - if (prev.waitingVisible) { - prev.waitingVisible = false; - mWaitingVisibleActivities.remove(prev); - if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v( - TAG, "Complete pause, no longer waiting: " + prev); - } - if (prev.configDestroy) { - // The previous is being paused because the configuration - // is changing, which means it is actually stopping... - // To juggle the fact that we are also starting a new - // instance right now, we need to first completely stop - // the current instance before starting the new one. - if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); - destroyActivityLocked(prev, true); - } else { - mStoppingActivities.add(prev); - if (mStoppingActivities.size() > 3) { - // If we already have a few activities waiting to stop, - // then give up on things going idle and start clearing - // them out. - if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle"); - Message msg = Message.obtain(); - msg.what = ActivityManagerService.IDLE_NOW_MSG; - mHandler.sendMessage(msg); - } - } - } else { - if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev); - prev = null; - } - mPausingActivity = null; - } - - if (!mSleeping && !mShuttingDown) { - resumeTopActivityLocked(prev); - } else { - if (mGoingToSleep.isHeld()) { - mGoingToSleep.release(); - } - if (mShuttingDown) { - notifyAll(); - } - } - - if (prev != null) { - prev.resumeKeyDispatchingLocked(); - } - - if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) { - long diff = 0; - synchronized (mProcessStatsThread) { - diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume; - } - if (diff > 0) { - BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics(); - synchronized (bsi) { - BatteryStatsImpl.Uid.Proc ps = - bsi.getProcessStatsLocked(prev.info.applicationInfo.uid, - prev.info.packageName); - if (ps != null) { - ps.addForegroundTimeLocked(diff); - } - } - } - } - prev.cpuTimeAtResume = 0; // reset it - } - - /** - * Once we know that we have asked an application to put an activity in - * the resumed state (either by launching it or explicitly telling it), - * this function updates the rest of our state to match that fact. - */ - private final void completeResumeLocked(ActivityRecord next) { - next.idle = false; - next.results = null; - next.newIntents = null; - - // schedule an idle timeout in case the app doesn't do it for us. - Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); - msg.obj = next; - mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT); - - if (false) { - // The activity was never told to pause, so just keep - // things going as-is. To maintain our own state, - // we need to emulate it coming back and saying it is - // idle. - msg = mHandler.obtainMessage(IDLE_NOW_MSG); - msg.obj = next; - mHandler.sendMessage(msg); - } - - reportResumedActivityLocked(next); - - next.thumbnail = null; - setFocusedActivityLocked(next); - next.resumeKeyDispatchingLocked(); - ensureActivitiesVisibleLocked(null, 0); - mWindowManager.executeAppTransition(); - mNoAnimActivities.clear(); - - // Mark the point when the activity is resuming - // TODO: To be more accurate, the mark should be before the onCreate, - // not after the onResume. But for subsequent starts, onResume is fine. - if (next.app != null) { - synchronized (mProcessStatsThread) { - next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid); - } - } else { - next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process - } - } - - /** - * Make sure that all activities that need to be visible (that is, they - * currently can be seen by the user) actually are. - */ - private final void ensureActivitiesVisibleLocked(ActivityRecord top, - ActivityRecord starting, String onlyThisProcess, int configChanges) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "ensureActivitiesVisible behind " + top - + " configChanges=0x" + Integer.toHexString(configChanges)); - - // If the top activity is not fullscreen, then we need to - // make sure any activities under it are now visible. - final int count = mHistory.size(); - int i = count-1; - while (mHistory.get(i) != top) { - i--; - } - ActivityRecord r; - boolean behindFullscreen = false; - for (; i>=0; i--) { - r = (ActivityRecord)mHistory.get(i); - if (DEBUG_VISBILITY) Slog.v( - TAG, "Make visible? " + r + " finishing=" + r.finishing - + " state=" + r.state); - if (r.finishing) { - continue; - } - - final boolean doThisProcess = onlyThisProcess == null - || onlyThisProcess.equals(r.processName); - - // First: if this is not the current activity being started, make - // sure it matches the current configuration. - if (r != starting && doThisProcess) { - ensureActivityConfigurationLocked(r, 0); - } - - if (r.app == null || r.app.thread == null) { - if (onlyThisProcess == null - || onlyThisProcess.equals(r.processName)) { - // This activity needs to be visible, but isn't even - // running... get it started, but don't resume it - // at this point. - if (DEBUG_VISBILITY) Slog.v( - TAG, "Start and freeze screen for " + r); - if (r != starting) { - r.startFreezingScreenLocked(r.app, configChanges); - } - if (!r.visible) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Starting and making visible: " + r); - mWindowManager.setAppVisibility(r, true); - } - if (r != starting) { - startSpecificActivityLocked(r, false, false); - } - } - - } else if (r.visible) { - // If this activity is already visible, then there is nothing - // else to do here. - if (DEBUG_VISBILITY) Slog.v( - TAG, "Skipping: already visible at " + r); - r.stopFreezingScreenLocked(false); - - } else if (onlyThisProcess == null) { - // This activity is not currently visible, but is running. - // Tell it to become visible. - r.visible = true; - if (r.state != ActivityState.RESUMED && r != starting) { - // If this activity is paused, tell it - // to now show its window. - if (DEBUG_VISBILITY) Slog.v( - TAG, "Making visible and scheduling visibility: " + r); - try { - mWindowManager.setAppVisibility(r, true); - r.app.thread.scheduleWindowVisibility(r, true); - r.stopFreezingScreenLocked(false); - } catch (Exception e) { - // Just skip on any failure; we'll make it - // visible when it next restarts. - Slog.w(TAG, "Exception thrown making visibile: " - + r.intent.getComponent(), e); - } - } - } - - // Aggregate current change flags. - configChanges |= r.configChangeFlags; - - if (r.fullscreen) { - // At this point, nothing else needs to be shown - if (DEBUG_VISBILITY) Slog.v( - TAG, "Stopping: fullscreen at " + r); - behindFullscreen = true; - i--; - break; - } - } - - // Now for any activities that aren't visible to the user, make - // sure they no longer are keeping the screen frozen. - while (i >= 0) { - r = (ActivityRecord)mHistory.get(i); - if (DEBUG_VISBILITY) Slog.v( - TAG, "Make invisible? " + r + " finishing=" + r.finishing - + " state=" + r.state - + " behindFullscreen=" + behindFullscreen); - if (!r.finishing) { - if (behindFullscreen) { - if (r.visible) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Making invisible: " + r); - r.visible = false; - try { - mWindowManager.setAppVisibility(r, false); - if ((r.state == ActivityState.STOPPING - || r.state == ActivityState.STOPPED) - && r.app != null && r.app.thread != null) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Scheduling invisibility: " + r); - r.app.thread.scheduleWindowVisibility(r, false); - } - } catch (Exception e) { - // Just skip on any failure; we'll make it - // visible when it next restarts. - Slog.w(TAG, "Exception thrown making hidden: " - + r.intent.getComponent(), e); - } - } else { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Already invisible: " + r); - } - } else if (r.fullscreen) { - if (DEBUG_VISBILITY) Slog.v( - TAG, "Now behindFullscreen: " + r); - behindFullscreen = true; - } - } - i--; - } - } - - /** - * Version of ensureActivitiesVisible that can easily be called anywhere. - */ - private final void ensureActivitiesVisibleLocked(ActivityRecord starting, - int configChanges) { - ActivityRecord r = topRunningActivityLocked(null); - if (r != null) { - ensureActivitiesVisibleLocked(r, starting, null, configChanges); - } - } - - private void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) { + void updateUsageStats(ActivityRecord resumedComponent, boolean resumed) { if (resumed) { mUsageStatsService.noteResumeComponent(resumedComponent.realActivity); } else { @@ -2619,7 +1848,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private boolean startHomeActivityLocked() { + boolean startHomeActivityLocked() { if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL && mTopAction == null) { // We are running in factory test mode, but unable to find @@ -2646,7 +1875,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen aInfo.applicationInfo.uid); if (app == null || app.instrumentationClass == null) { intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivityLocked(null, intent, null, null, 0, aInfo, + mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo, null, null, 0, 0, 0, false, false); } } @@ -2658,7 +1887,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen /** * Starts the "new version setup screen" if appropriate. */ - private void startSetupActivityLocked() { + void startSetupActivityLocked() { // Only do this once per boot. if (mCheckedForSetup) { return; @@ -2702,14 +1931,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setComponent(new ComponentName( ri.activityInfo.packageName, ri.activityInfo.name)); - startActivityLocked(null, intent, null, null, 0, ri.activityInfo, + mMainStack.startActivityLocked(null, intent, null, null, 0, ri.activityInfo, null, null, 0, 0, 0, false, false); } } } } - private void reportResumedActivityLocked(ActivityRecord r) { + void reportResumedActivityLocked(ActivityRecord r) { //Slog.i(TAG, "**** REPORT RESUME: " + r); final int identHash = System.identityHashCode(r); @@ -2728,1278 +1957,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } mWatchers.finishBroadcast(); } - - /** - * Ensure that the top activity in the stack is resumed. - * - * @param prev The previously resumed activity, for when in the process - * of pausing; can be null to call from elsewhere. - * - * @return Returns true if something is being resumed, or false if - * nothing happened. - */ - private final boolean resumeTopActivityLocked(ActivityRecord prev) { - // Find the first activity that is not finishing. - ActivityRecord next = topRunningActivityLocked(null); - - // Remember how we'll process this pause/resume situation, and ensure - // that the state is reset however we wind up proceeding. - final boolean userLeaving = mUserLeaving; - mUserLeaving = false; - - if (next == null) { - // There are no more activities! Let's just start up the - // Launcher... - return startHomeActivityLocked(); - } - - next.delayedResume = false; - - // If the top activity is the resumed one, nothing to do. - if (mResumedActivity == next && next.state == ActivityState.RESUMED) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - mWindowManager.executeAppTransition(); - mNoAnimActivities.clear(); - return false; - } - - // If we are sleeping, and there is no resumed activity, and the top - // activity is paused, well that is the state we want. - if ((mSleeping || mShuttingDown) - && mLastPausedActivity == next && next.state == ActivityState.PAUSED) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - mWindowManager.executeAppTransition(); - mNoAnimActivities.clear(); - return false; - } - - // The activity may be waiting for stop, but that is no longer - // appropriate for it. - mStoppingActivities.remove(next); - mWaitingVisibleActivities.remove(next); - - if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next); - - // If we are currently pausing an activity, then don't do anything - // until that is done. - if (mPausingActivity != null) { - if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity); - return false; - } - - // We need to start pausing the current activity so the top one - // can be resumed... - if (mResumedActivity != null) { - if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing"); - startPausingLocked(userLeaving, false); - return true; - } - - if (prev != null && prev != next) { - if (!prev.waitingVisible && next != null && !next.nowVisible) { - prev.waitingVisible = true; - mWaitingVisibleActivities.add(prev); - if (DEBUG_SWITCH) Slog.v( - TAG, "Resuming top, waiting visible to hide: " + prev); - } else { - // The next activity is already visible, so hide the previous - // activity's windows right now so we can show the new one ASAP. - // We only do this if the previous is finishing, which should mean - // it is on top of the one being resumed so hiding it quickly - // is good. Otherwise, we want to do the normal route of allowing - // the resumed activity to be shown so we can decide if the - // previous should actually be hidden depending on whether the - // new one is found to be full-screen or not. - if (prev.finishing) { - mWindowManager.setAppVisibility(prev, false); - if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: " - + prev + ", waitingVisible=" - + (prev != null ? prev.waitingVisible : null) - + ", nowVisible=" + next.nowVisible); - } else { - if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: " - + prev + ", waitingVisible=" - + (prev != null ? prev.waitingVisible : null) - + ", nowVisible=" + next.nowVisible); - } - } - } - - // We are starting up the next activity, so tell the window manager - // that the previous one will be hidden soon. This way it can know - // to ignore it when computing the desired screen orientation. - if (prev != null) { - if (prev.finishing) { - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare close transition: prev=" + prev); - if (mNoAnimActivities.contains(prev)) { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); - } else { - mWindowManager.prepareAppTransition(prev.task == next.task - ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE - : WindowManagerPolicy.TRANSIT_TASK_CLOSE); - } - mWindowManager.setAppWillBeHidden(prev); - mWindowManager.setAppVisibility(prev, false); - } else { - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare open transition: prev=" + prev); - if (mNoAnimActivities.contains(next)) { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); - } else { - mWindowManager.prepareAppTransition(prev.task == next.task - ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN - : WindowManagerPolicy.TRANSIT_TASK_OPEN); - } - } - if (false) { - mWindowManager.setAppWillBeHidden(prev); - mWindowManager.setAppVisibility(prev, false); - } - } else if (mHistory.size() > 1) { - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare open transition: no previous"); - if (mNoAnimActivities.contains(next)) { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); - } else { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); - } - } - - if (next.app != null && next.app.thread != null) { - if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next); - - // This activity is now becoming visible. - mWindowManager.setAppVisibility(next, true); - - ActivityRecord lastResumedActivity = mResumedActivity; - ActivityState lastState = next.state; - - updateCpuStats(); - - next.state = ActivityState.RESUMED; - mResumedActivity = next; - next.task.touchActiveTime(); - updateLruProcessLocked(next.app, true, true); - updateLRUListLocked(next); - - // Have the window manager re-evaluate the orientation of - // the screen based on the new activity order. - boolean updated; - synchronized (this) { - Configuration config = mWindowManager.updateOrientationFromAppTokens( - mConfiguration, - next.mayFreezeScreenLocked(next.app) ? next : null); - if (config != null) { - next.frozenBeforeDestroy = true; - } - updated = updateConfigurationLocked(config, next); - } - if (!updated) { - // The configuration update wasn't able to keep the existing - // instance of the activity, and instead started a new one. - // We should be all done, but let's just make sure our activity - // is still at the top and schedule another run if something - // weird happened. - ActivityRecord nextNext = topRunningActivityLocked(null); - if (DEBUG_SWITCH) Slog.i(TAG, - "Activity config changed during resume: " + next - + ", new next: " + nextNext); - if (nextNext != next) { - // Do over! - mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG); - } - setFocusedActivityLocked(next); - ensureActivitiesVisibleLocked(null, 0); - mWindowManager.executeAppTransition(); - mNoAnimActivities.clear(); - return true; - } - - try { - // Deliver all pending results. - ArrayList a = next.results; - if (a != null) { - final int N = a.size(); - if (!next.finishing && N > 0) { - if (DEBUG_RESULTS) Slog.v( - TAG, "Delivering results to " + next - + ": " + a); - next.app.thread.scheduleSendResult(next, a); - } - } - - if (next.newIntents != null) { - next.app.thread.scheduleNewIntent(next.newIntents, next); - } - - EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, - System.identityHashCode(next), - next.task.taskId, next.shortComponentName); - - next.app.thread.scheduleResumeActivity(next, - isNextTransitionForward()); - - pauseIfSleepingLocked(); - - } catch (Exception e) { - // Whoops, need to restart this activity! - next.state = lastState; - mResumedActivity = lastResumedActivity; - Slog.i(TAG, "Restarting because process died: " + next); - if (!next.hasBeenLaunched) { - next.hasBeenLaunched = true; - } else { - if (SHOW_APP_STARTING_ICON) { - mWindowManager.setAppStartingWindow( - next, next.packageName, next.theme, - next.nonLocalizedLabel, - next.labelRes, next.icon, null, true); - } - } - startSpecificActivityLocked(next, true, false); - return true; - } - - // From this point on, if something goes wrong there is no way - // to recover the activity. - try { - next.visible = true; - completeResumeLocked(next); - } catch (Exception e) { - // If any exception gets thrown, toss away this - // activity and try the next one. - Slog.w(TAG, "Exception thrown during resume of " + next, e); - requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null, - "resume-exception"); - return true; - } - - // Didn't need to use the icicle, and it is now out of date. - next.icicle = null; - next.haveState = false; - next.stopped = false; - - } else { - // Whoops, need to restart this activity! - if (!next.hasBeenLaunched) { - next.hasBeenLaunched = true; - } else { - if (SHOW_APP_STARTING_ICON) { - mWindowManager.setAppStartingWindow( - next, next.packageName, next.theme, - next.nonLocalizedLabel, - next.labelRes, next.icon, null, true); - } - if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next); - } - startSpecificActivityLocked(next, true, true); - } - - return true; - } - private final void startActivityLocked(ActivityRecord r, boolean newTask, - boolean doResume) { - final int NH = mHistory.size(); - - int addPos = -1; - - if (!newTask) { - // If starting in an existing task, find where that is... - ActivityRecord next = null; - boolean startIt = true; - for (int i = NH-1; i >= 0; i--) { - ActivityRecord p = (ActivityRecord)mHistory.get(i); - if (p.finishing) { - continue; - } - if (p.task == r.task) { - // Here it is! Now, if this is not yet visible to the - // user, then just add it without starting; it will - // get started when the user navigates back to it. - addPos = i+1; - if (!startIt) { - mHistory.add(addPos, r); - r.inHistory = true; - r.task.numActivities++; - mWindowManager.addAppToken(addPos, r, r.task.taskId, - r.info.screenOrientation, r.fullscreen); - if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); - } - return; - } - break; - } - if (p.fullscreen) { - startIt = false; - } - next = p; - } - } - - // Place a new activity at top of stack, so it is next to interact - // with the user. - if (addPos < 0) { - addPos = mHistory.size(); - } - - // If we are not placing the new activity frontmost, we do not want - // to deliver the onUserLeaving callback to the actual frontmost - // activity - if (addPos < NH) { - mUserLeaving = false; - if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false"); - } - - // Slot the activity into the history stack and proceed - mHistory.add(addPos, r); - r.inHistory = true; - r.frontOfTask = newTask; - r.task.numActivities++; - if (NH > 0) { - // We want to show the starting preview window if we are - // switching to a new task, or the next activity's process is - // not currently running. - boolean showStartingIcon = newTask; - ProcessRecord proc = r.app; - if (proc == null) { - proc = mProcessNames.get(r.processName, r.info.applicationInfo.uid); - } - if (proc == null || proc.thread == null) { - showStartingIcon = true; - } - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare open transition: starting " + r); - if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); - mNoAnimActivities.add(r); - } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_OPEN); - mNoAnimActivities.remove(r); - } else { - mWindowManager.prepareAppTransition(newTask - ? WindowManagerPolicy.TRANSIT_TASK_OPEN - : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); - mNoAnimActivities.remove(r); - } - mWindowManager.addAppToken( - addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); - boolean doShow = true; - if (newTask) { - // Even though this activity is starting fresh, we still need - // to reset it to make sure we apply affinities to move any - // existing activities from other tasks in to it. - // If the caller has requested that the target task be - // reset, then do so. - if ((r.intent.getFlags() - &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { - resetTaskIfNeededLocked(r, r); - doShow = topRunningNonDelayedActivityLocked(null) == r; - } - } - if (SHOW_APP_STARTING_ICON && doShow) { - // Figure out if we are transitioning from another activity that is - // "has the same starting icon" as the next one. This allows the - // window manager to keep the previous window it had previously - // created, if it still had one. - ActivityRecord prev = mResumedActivity; - if (prev != null) { - // We don't want to reuse the previous starting preview if: - // (1) The current activity is in a different task. - if (prev.task != r.task) prev = null; - // (2) The current activity is already displayed. - else if (prev.nowVisible) prev = null; - } - mWindowManager.setAppStartingWindow( - r, r.packageName, r.theme, r.nonLocalizedLabel, - r.labelRes, r.icon, prev, showStartingIcon); - } - } else { - // If this is the first activity, don't do any fancy animations, - // because there is nothing for it to animate on top of. - mWindowManager.addAppToken(addPos, r, r.task.taskId, - r.info.screenOrientation, r.fullscreen); - } - if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); - } - - if (doResume) { - resumeTopActivityLocked(null); - } - } - - /** - * Perform clear operation as requested by - * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the - * stack to the given task, then look for - * an instance of that activity in the stack and, if found, finish all - * activities on top of it and return the instance. - * - * @param newR Description of the new activity being started. - * @return Returns the old activity that should be continue to be used, - * or null if none was found. - */ - private final ActivityRecord performClearTaskLocked(int taskId, - ActivityRecord newR, int launchFlags, boolean doClear) { - int i = mHistory.size(); - - // First find the requested task. - while (i > 0) { - i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (r.task.taskId == taskId) { - i++; - break; - } - } - - // Now clear it. - while (i > 0) { - i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (r.finishing) { - continue; - } - if (r.task.taskId != taskId) { - return null; - } - if (r.realActivity.equals(newR.realActivity)) { - // Here it is! Now finish everything in front... - ActivityRecord ret = r; - if (doClear) { - while (i < (mHistory.size()-1)) { - i++; - r = (ActivityRecord)mHistory.get(i); - if (r.finishing) { - continue; - } - if (finishActivityLocked(r, i, Activity.RESULT_CANCELED, - null, "clear")) { - i--; - } - } - } - - // Finally, if this is a normal launch mode (that is, not - // expecting onNewIntent()), then we will finish the current - // instance of the activity so a new fresh one can be started. - if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE - && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { - if (!ret.finishing) { - int index = indexOfTokenLocked(ret); - if (index >= 0) { - finishActivityLocked(ret, index, Activity.RESULT_CANCELED, - null, "clear"); - } - return null; - } - } - - return ret; - } - } - - return null; - } - - /** - * Find the activity in the history stack within the given task. Returns - * the index within the history at which it's found, or < 0 if not found. - */ - private final int findActivityInHistoryLocked(ActivityRecord r, int task) { - int i = mHistory.size(); - while (i > 0) { - i--; - ActivityRecord candidate = (ActivityRecord)mHistory.get(i); - if (candidate.task.taskId != task) { - break; - } - if (candidate.realActivity.equals(r.realActivity)) { - return i; - } - } - - return -1; - } - - /** - * Reorder the history stack so that the activity at the given index is - * brought to the front. - */ - private final ActivityRecord moveActivityToFrontLocked(int where) { - ActivityRecord newTop = (ActivityRecord)mHistory.remove(where); - int top = mHistory.size(); - ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1); - mHistory.add(top, newTop); - oldTop.frontOfTask = false; - newTop.frontOfTask = true; - return newTop; - } - - /** - * Deliver a new Intent to an existing activity, so that its onNewIntent() - * method will be called at the proper time. - */ - private final void deliverNewIntentLocked(ActivityRecord r, Intent intent) { - boolean sent = false; - if (r.state == ActivityState.RESUMED - && r.app != null && r.app.thread != null) { - try { - ArrayList<Intent> ar = new ArrayList<Intent>(); - ar.add(new Intent(intent)); - r.app.thread.scheduleNewIntent(ar, r); - sent = true; - } catch (Exception e) { - Slog.w(TAG, "Exception thrown sending new intent to " + r, e); - } - } - if (!sent) { - r.addNewIntentLocked(new Intent(intent)); - } - } - - private final void logStartActivity(int tag, ActivityRecord r, - TaskRecord task) { - EventLog.writeEvent(tag, - System.identityHashCode(r), task.taskId, - r.shortComponentName, r.intent.getAction(), - r.intent.getType(), r.intent.getDataString(), - r.intent.getFlags()); - } - - private final int startActivityLocked(IApplicationThread caller, - Intent intent, String resolvedType, - Uri[] grantedUriPermissions, - int grantedMode, ActivityInfo aInfo, IBinder resultTo, - String resultWho, int requestCode, - int callingPid, int callingUid, boolean onlyIfNeeded, - boolean componentSpecified) { - Slog.i(TAG, "Starting activity: " + intent); - - ActivityRecord sourceRecord = null; - ActivityRecord resultRecord = null; - if (resultTo != null) { - int index = indexOfTokenLocked(resultTo); - if (DEBUG_RESULTS) Slog.v( - TAG, "Sending result to " + resultTo + " (index " + index + ")"); - if (index >= 0) { - sourceRecord = (ActivityRecord)mHistory.get(index); - if (requestCode >= 0 && !sourceRecord.finishing) { - resultRecord = sourceRecord; - } - } - } - - int launchFlags = intent.getFlags(); - - if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 - && sourceRecord != null) { - // Transfer the result target from the source activity to the new - // one being started, including any failures. - if (requestCode >= 0) { - return START_FORWARD_AND_REQUEST_CONFLICT; - } - resultRecord = sourceRecord.resultTo; - resultWho = sourceRecord.resultWho; - requestCode = sourceRecord.requestCode; - sourceRecord.resultTo = null; - if (resultRecord != null) { - resultRecord.removeResultsLocked( - sourceRecord, resultWho, requestCode); - } - } - - int err = START_SUCCESS; - - if (intent.getComponent() == null) { - // We couldn't find a class that can handle the given Intent. - // That's the end of that! - err = START_INTENT_NOT_RESOLVED; - } - - if (err == START_SUCCESS && aInfo == null) { - // We couldn't find the specific class specified in the Intent. - // Also the end of the line. - err = START_CLASS_NOT_FOUND; - } - - ProcessRecord callerApp = null; - if (err == START_SUCCESS && caller != null) { - callerApp = getRecordForAppLocked(caller); - if (callerApp != null) { - callingPid = callerApp.pid; - callingUid = callerApp.info.uid; - } else { - Slog.w(TAG, "Unable to find app for caller " + caller - + " (pid=" + callingPid + ") when starting: " - + intent.toString()); - err = START_PERMISSION_DENIED; - } - } - - if (err != START_SUCCESS) { - if (resultRecord != null) { - sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, - Activity.RESULT_CANCELED, null); - } - return err; - } - - final int perm = checkComponentPermission(aInfo.permission, callingPid, - callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid); - if (perm != PackageManager.PERMISSION_GRANTED) { - if (resultRecord != null) { - sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, - Activity.RESULT_CANCELED, null); - } - String msg = "Permission Denial: starting " + intent.toString() - + " from " + callerApp + " (pid=" + callingPid - + ", uid=" + callingUid + ")" - + " requires " + aInfo.permission; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - - if (mController != null) { - boolean abort = false; - try { - // The Intent we give to the watcher has the extra data - // stripped off, since it can contain private information. - Intent watchIntent = intent.cloneFilter(); - abort = !mController.activityStarting(watchIntent, - aInfo.applicationInfo.packageName); - } catch (RemoteException e) { - mController = null; - } - - if (abort) { - if (resultRecord != null) { - sendActivityResultLocked(-1, - resultRecord, resultWho, requestCode, - Activity.RESULT_CANCELED, null); - } - // We pretend to the caller that it was really started, but - // they will just get a cancel result. - return START_SUCCESS; - } - } - - ActivityRecord r = new ActivityRecord(this, callerApp, callingUid, - intent, resolvedType, aInfo, mConfiguration, - resultRecord, resultWho, requestCode, componentSpecified); - - if (mResumedActivity == null - || mResumedActivity.info.applicationInfo.uid != callingUid) { - if (!checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) { - PendingActivityLaunch pal = new PendingActivityLaunch(); - pal.r = r; - pal.sourceRecord = sourceRecord; - pal.grantedUriPermissions = grantedUriPermissions; - pal.grantedMode = grantedMode; - pal.onlyIfNeeded = onlyIfNeeded; - mPendingActivityLaunches.add(pal); - return START_SWITCHES_CANCELED; - } - } - - if (mDidAppSwitch) { - // This is the second allowed switch since we stopped switches, - // so now just generally allow switches. Use case: user presses - // home (switches disabled, switch to home, mDidAppSwitch now true); - // user taps a home icon (coming from home so allowed, we hit here - // and now allow anyone to switch again). - mAppSwitchesAllowedTime = 0; - } else { - mDidAppSwitch = true; - } - - doPendingActivityLaunchesLocked(false); - - return startActivityUncheckedLocked(r, sourceRecord, - grantedUriPermissions, grantedMode, onlyIfNeeded, true); - } - - private final void doPendingActivityLaunchesLocked(boolean doResume) { + final void doPendingActivityLaunchesLocked(boolean doResume) { final int N = mPendingActivityLaunches.size(); if (N <= 0) { return; } for (int i=0; i<N; i++) { PendingActivityLaunch pal = mPendingActivityLaunches.get(i); - startActivityUncheckedLocked(pal.r, pal.sourceRecord, + mMainStack.startActivityUncheckedLocked(pal.r, pal.sourceRecord, pal.grantedUriPermissions, pal.grantedMode, pal.onlyIfNeeded, doResume && i == (N-1)); } mPendingActivityLaunches.clear(); } - private final int startActivityUncheckedLocked(ActivityRecord r, - ActivityRecord sourceRecord, Uri[] grantedUriPermissions, - int grantedMode, boolean onlyIfNeeded, boolean doResume) { - final Intent intent = r.intent; - final int callingUid = r.launchedFromUid; - - int launchFlags = intent.getFlags(); - - // We'll invoke onUserLeaving before onPause only if the launching - // activity did not explicitly state that this is an automated launch. - mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; - if (DEBUG_USER_LEAVING) Slog.v(TAG, - "startActivity() => mUserLeaving=" + mUserLeaving); - - // If the caller has asked not to resume at this point, we make note - // of this in the record so that we can skip it when trying to find - // the top running activity. - if (!doResume) { - r.delayedResume = true; - } - - ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) - != 0 ? r : null; - - // If the onlyIfNeeded flag is set, then we can do this if the activity - // being launched is the same as the one making the call... or, as - // a special case, if we do not know the caller then we count the - // current top activity as the caller. - if (onlyIfNeeded) { - ActivityRecord checkedCaller = sourceRecord; - if (checkedCaller == null) { - checkedCaller = topRunningNonDelayedActivityLocked(notTop); - } - if (!checkedCaller.realActivity.equals(r.realActivity)) { - // Caller is not the same as launcher, so always needed. - onlyIfNeeded = false; - } - } - - if (grantedUriPermissions != null && callingUid > 0) { - for (int i=0; i<grantedUriPermissions.length; i++) { - grantUriPermissionLocked(callingUid, r.packageName, - grantedUriPermissions[i], grantedMode, r); - } - } - - grantUriPermissionFromIntentLocked(callingUid, r.packageName, - intent, r); - - if (sourceRecord == null) { - // This activity is not being started from another... in this - // case we -always- start a new task. - if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { - Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: " - + intent); - launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; - } - } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { - // The original activity who is starting us is running as a single - // instance... this new activity it is starting must go on its - // own task. - launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; - } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE - || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { - // The activity being started is a single instance... it always - // gets launched into its own task. - launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; - } - - if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { - // For whatever reason this activity is being launched into a new - // task... yet the caller has requested a result back. Well, that - // is pretty messed up, so instead immediately send back a cancel - // and let the new task continue launched as normal without a - // dependency on its originator. - Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result."); - sendActivityResultLocked(-1, - r.resultTo, r.resultWho, r.requestCode, - Activity.RESULT_CANCELED, null); - r.resultTo = null; - } - - boolean addingToTask = false; - if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && - (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0) - || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK - || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { - // If bring to front is requested, and no result is requested, and - // we can find a task that was started with this same - // component, then instead of launching bring that one to the front. - if (r.resultTo == null) { - // See if there is a task to bring to the front. If this is - // a SINGLE_INSTANCE activity, there can be one and only one - // instance of it in the history, and it is always in its own - // unique task, so we do a special search. - ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE - ? findTaskLocked(intent, r.info) - : findActivityLocked(intent, r.info); - if (taskTop != null) { - if (taskTop.task.intent == null) { - // This task was started because of movement of - // the activity based on affinity... now that we - // are actually launching it, we can assign the - // base intent. - taskTop.task.setIntent(intent, r.info); - } - // If the target task is not in the front, then we need - // to bring it to the front... except... well, with - // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like - // to have the same behavior as if a new instance was - // being started, which means not bringing it to the front - // if the caller is not itself in the front. - ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop); - if (curTop.task != taskTop.task) { - r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); - boolean callerAtFront = sourceRecord == null - || curTop.task == sourceRecord.task; - if (callerAtFront) { - // We really do want to push this one into the - // user's face, right now. - moveTaskToFrontLocked(taskTop.task, r); - } - } - // If the caller has requested that the target task be - // reset, then do so. - if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { - taskTop = resetTaskIfNeededLocked(taskTop, r); - } - if (onlyIfNeeded) { - // We don't need to start a new activity, and - // the client said not to do anything if that - // is the case, so this is it! And for paranoia, make - // sure we have correctly resumed the top activity. - if (doResume) { - resumeTopActivityLocked(null); - } - return START_RETURN_INTENT_TO_CALLER; - } - if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0 - || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK - || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { - // In this situation we want to remove all activities - // from the task up to the one being started. In most - // cases this means we are resetting the task to its - // initial state. - ActivityRecord top = performClearTaskLocked( - taskTop.task.taskId, r, launchFlags, true); - if (top != null) { - if (top.frontOfTask) { - // Activity aliases may mean we use different - // intents for the top activity, so make sure - // the task now has the identity of the new - // intent. - top.task.setIntent(r.intent, r.info); - } - logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); - deliverNewIntentLocked(top, r.intent); - } else { - // A special case: we need to - // start the activity because it is not currently - // running, and the caller has asked to clear the - // current task to have this activity at the top. - addingToTask = true; - // Now pretend like this activity is being started - // by the top of its task, so it is put in the - // right place. - sourceRecord = taskTop; - } - } else if (r.realActivity.equals(taskTop.task.realActivity)) { - // In this case the top activity on the task is the - // same as the one being launched, so we take that - // as a request to bring the task to the foreground. - // If the top activity in the task is the root - // activity, deliver this new intent to it if it - // desires. - if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 - && taskTop.realActivity.equals(r.realActivity)) { - logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task); - if (taskTop.frontOfTask) { - taskTop.task.setIntent(r.intent, r.info); - } - deliverNewIntentLocked(taskTop, r.intent); - } else if (!r.intent.filterEquals(taskTop.task.intent)) { - // In this case we are launching the root activity - // of the task, but with a different intent. We - // should start a new instance on top. - addingToTask = true; - sourceRecord = taskTop; - } - } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { - // In this case an activity is being launched in to an - // existing task, without resetting that task. This - // is typically the situation of launching an activity - // from a notification or shortcut. We want to place - // the new activity on top of the current task. - addingToTask = true; - sourceRecord = taskTop; - } else if (!taskTop.task.rootWasReset) { - // In this case we are launching in to an existing task - // that has not yet been started from its front door. - // The current task has been brought to the front. - // Ideally, we'd probably like to place this new task - // at the bottom of its stack, but that's a little hard - // to do with the current organization of the code so - // for now we'll just drop it. - taskTop.task.setIntent(r.intent, r.info); - } - if (!addingToTask) { - // We didn't do anything... but it was needed (a.k.a., client - // don't use that intent!) And for paranoia, make - // sure we have correctly resumed the top activity. - if (doResume) { - resumeTopActivityLocked(null); - } - return START_TASK_TO_FRONT; - } - } - } - } - - //String uri = r.intent.toURI(); - //Intent intent2 = new Intent(uri); - //Slog.i(TAG, "Given intent: " + r.intent); - //Slog.i(TAG, "URI is: " + uri); - //Slog.i(TAG, "To intent: " + intent2); - - if (r.packageName != null) { - // If the activity being launched is the same as the one currently - // at the top, then we need to check if it should only be launched - // once. - ActivityRecord top = topRunningNonDelayedActivityLocked(notTop); - if (top != null && r.resultTo == null) { - if (top.realActivity.equals(r.realActivity)) { - if (top.app != null && top.app.thread != null) { - if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 - || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP - || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { - logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); - // For paranoia, make sure we have correctly - // resumed the top activity. - if (doResume) { - resumeTopActivityLocked(null); - } - if (onlyIfNeeded) { - // We don't need to start a new activity, and - // the client said not to do anything if that - // is the case, so this is it! - return START_RETURN_INTENT_TO_CALLER; - } - deliverNewIntentLocked(top, r.intent); - return START_DELIVERED_TO_TOP; - } - } - } - } - - } else { - if (r.resultTo != null) { - sendActivityResultLocked(-1, - r.resultTo, r.resultWho, r.requestCode, - Activity.RESULT_CANCELED, null); - } - return START_CLASS_NOT_FOUND; - } - - boolean newTask = false; - - // Should this be considered a new task? - if (r.resultTo == null && !addingToTask - && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { - // todo: should do better management of integers. - mCurTask++; - if (mCurTask <= 0) { - mCurTask = 1; - } - r.task = new TaskRecord(mCurTask, r.info, intent, - (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); - if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r - + " in new task " + r.task); - newTask = true; - addRecentTaskLocked(r.task); - - } else if (sourceRecord != null) { - if (!addingToTask && - (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { - // In this case, we are adding the activity to an existing - // task, but the caller has asked to clear that task if the - // activity is already running. - ActivityRecord top = performClearTaskLocked( - sourceRecord.task.taskId, r, launchFlags, true); - if (top != null) { - logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); - deliverNewIntentLocked(top, r.intent); - // For paranoia, make sure we have correctly - // resumed the top activity. - if (doResume) { - resumeTopActivityLocked(null); - } - return START_DELIVERED_TO_TOP; - } - } else if (!addingToTask && - (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { - // In this case, we are launching an activity in our own task - // that may already be running somewhere in the history, and - // we want to shuffle it to the front of the stack if so. - int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId); - if (where >= 0) { - ActivityRecord top = moveActivityToFrontLocked(where); - logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); - deliverNewIntentLocked(top, r.intent); - if (doResume) { - resumeTopActivityLocked(null); - } - return START_DELIVERED_TO_TOP; - } - } - // An existing activity is starting this new activity, so we want - // to keep the new one in the same task as the one that is starting - // it. - r.task = sourceRecord.task; - if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r - + " in existing task " + r.task); - - } else { - // This not being started from an existing activity, and not part - // of a new task... just put it in the top task, though these days - // this case should never happen. - final int N = mHistory.size(); - ActivityRecord prev = - N > 0 ? (ActivityRecord)mHistory.get(N-1) : null; - r.task = prev != null - ? prev.task - : new TaskRecord(mCurTask, r.info, intent, - (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); - if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r - + " in new guessed " + r.task); - } - if (newTask) { - EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId); - } - logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task); - startActivityLocked(r, newTask, doResume); - return START_SUCCESS; - } - - void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, - long thisTime, long totalTime) { - for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) { - WaitResult w = mWaitingActivityLaunched.get(i); - w.timeout = timeout; - if (r != null) { - w.who = new ComponentName(r.info.packageName, r.info.name); - } - w.thisTime = thisTime; - w.totalTime = totalTime; - } - notify(); - } - - void reportActivityVisibleLocked(ActivityRecord r) { - for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) { - WaitResult w = mWaitingActivityVisible.get(i); - w.timeout = false; - if (r != null) { - w.who = new ComponentName(r.info.packageName, r.info.name); - } - w.totalTime = SystemClock.uptimeMillis() - w.thisTime; - w.thisTime = w.totalTime; - } - notify(); - } - - private final int startActivityMayWait(IApplicationThread caller, - Intent intent, String resolvedType, Uri[] grantedUriPermissions, - int grantedMode, IBinder resultTo, - String resultWho, int requestCode, boolean onlyIfNeeded, - boolean debug, WaitResult outResult, Configuration config) { - // Refuse possible leaked file descriptors - if (intent != null && intent.hasFileDescriptors()) { - throw new IllegalArgumentException("File descriptors passed in Intent"); - } - - boolean componentSpecified = intent.getComponent() != null; - - // Don't modify the client's object! - intent = new Intent(intent); - - // Collect information about the target of the Intent. - ActivityInfo aInfo; - try { - ResolveInfo rInfo = - AppGlobals.getPackageManager().resolveIntent( - intent, resolvedType, - PackageManager.MATCH_DEFAULT_ONLY - | STOCK_PM_FLAGS); - aInfo = rInfo != null ? rInfo.activityInfo : null; - } catch (RemoteException e) { - aInfo = null; - } - - if (aInfo != null) { - // Store the found target back into the intent, because now that - // we have it we never want to do this again. For example, if the - // user navigates back to this point in the history, we should - // always restart the exact same activity. - intent.setComponent(new ComponentName( - aInfo.applicationInfo.packageName, aInfo.name)); - - // Don't debug things in the system process - if (debug) { - if (!aInfo.processName.equals("system")) { - setDebugApp(aInfo.processName, true, false); - } - } - } - - synchronized (this) { - int callingPid; - int callingUid; - if (caller == null) { - callingPid = Binder.getCallingPid(); - callingUid = Binder.getCallingUid(); - } else { - callingPid = callingUid = -1; - } - - mConfigWillChange = config != null && mConfiguration.diff(config) != 0; - if (DEBUG_CONFIGURATION) Slog.v(TAG, - "Starting activity when config will change = " + mConfigWillChange); - - final long origId = Binder.clearCallingIdentity(); - - if (aInfo != null && - (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) { - // This may be a heavy-weight process! Check to see if we already - // have another, different heavy-weight process running. - if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) { - if (mHeavyWeightProcess != null && - (mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid || - !mHeavyWeightProcess.processName.equals(aInfo.processName))) { - int realCallingPid = callingPid; - int realCallingUid = callingUid; - if (caller != null) { - ProcessRecord callerApp = getRecordForAppLocked(caller); - if (callerApp != null) { - realCallingPid = callerApp.pid; - realCallingUid = callerApp.info.uid; - } else { - Slog.w(TAG, "Unable to find app for caller " + caller - + " (pid=" + realCallingPid + ") when starting: " - + intent.toString()); - return START_PERMISSION_DENIED; - } - } - - IIntentSender target = getIntentSenderLocked( - IActivityManager.INTENT_SENDER_ACTIVITY, "android", - realCallingUid, null, null, 0, intent, - resolvedType, PendingIntent.FLAG_CANCEL_CURRENT - | PendingIntent.FLAG_ONE_SHOT); - - Intent newIntent = new Intent(); - if (requestCode >= 0) { - // Caller is requesting a result. - newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true); - } - newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, - new IntentSender(target)); - if (mHeavyWeightProcess.activities.size() > 0) { - ActivityRecord hist = mHeavyWeightProcess.activities.get(0); - newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, - hist.packageName); - newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, - hist.task.taskId); - } - newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP, - aInfo.packageName); - newIntent.setFlags(intent.getFlags()); - newIntent.setClassName("android", - HeavyWeightSwitcherActivity.class.getName()); - intent = newIntent; - resolvedType = null; - caller = null; - callingUid = Binder.getCallingUid(); - callingPid = Binder.getCallingPid(); - componentSpecified = true; - try { - ResolveInfo rInfo = - AppGlobals.getPackageManager().resolveIntent( - intent, null, - PackageManager.MATCH_DEFAULT_ONLY - | STOCK_PM_FLAGS); - aInfo = rInfo != null ? rInfo.activityInfo : null; - } catch (RemoteException e) { - aInfo = null; - } - } - } - } - - int res = startActivityLocked(caller, intent, resolvedType, - grantedUriPermissions, grantedMode, aInfo, - resultTo, resultWho, requestCode, callingPid, callingUid, - onlyIfNeeded, componentSpecified); - - if (mConfigWillChange) { - // If the caller also wants to switch to a new configuration, - // do so now. This allows a clean switch, as we are waiting - // for the current activity to pause (so we will not destroy - // it), and have not yet started the next activity. - enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, - "updateConfiguration()"); - mConfigWillChange = false; - if (DEBUG_CONFIGURATION) Slog.v(TAG, - "Updating to new configuration after starting activity."); - updateConfigurationLocked(config, null); - } - - Binder.restoreCallingIdentity(origId); - - if (outResult != null) { - outResult.result = res; - if (res == IActivityManager.START_SUCCESS) { - mWaitingActivityLaunched.add(outResult); - do { - try { - wait(); - } catch (InterruptedException e) { - } - } while (!outResult.timeout && outResult.who == null); - } else if (res == IActivityManager.START_TASK_TO_FRONT) { - ActivityRecord r = this.topRunningActivityLocked(null); - if (r.nowVisible) { - outResult.timeout = false; - outResult.who = new ComponentName(r.info.packageName, r.info.name); - outResult.totalTime = 0; - outResult.thisTime = 0; - } else { - outResult.thisTime = SystemClock.uptimeMillis(); - mWaitingActivityVisible.add(outResult); - do { - try { - wait(); - } catch (InterruptedException e) { - } - } while (!outResult.timeout && outResult.who == null); - } - } - } - - return res; - } - } - public final int startActivity(IApplicationThread caller, Intent intent, String resolvedType, Uri[] grantedUriPermissions, int grantedMode, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug) { - return startActivityMayWait(caller, intent, resolvedType, + return mMainStack.startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, null, null); } @@ -4010,7 +1988,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug) { WaitResult res = new WaitResult(); - startActivityMayWait(caller, intent, resolvedType, + mMainStack.startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, res, null); return res; @@ -4021,7 +1999,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int grantedMode, IBinder resultTo, String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug, Configuration config) { - return startActivityMayWait(caller, intent, resolvedType, + return mMainStack.startActivityMayWait(caller, intent, resolvedType, grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, null, config); } @@ -4045,8 +2023,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. - if (mResumedActivity != null - && mResumedActivity.info.applicationInfo.uid == + if (mMainStack.mResumedActivity != null + && mMainStack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) { mAppSwitchesAllowedTime = 0; } @@ -4064,11 +2042,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized (this) { - int index = indexOfTokenLocked(callingActivity); + int index = mMainStack.indexOfTokenLocked(callingActivity); if (index < 0) { return false; } - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); if (r.app == null || r.app.thread == null) { // The caller is not running... d'oh! return false; @@ -4137,7 +2115,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); // XXX we are not dealing with propagating grantedUriPermissions... // those are not yet exposed to user code, so there is no need. - int res = startActivityLocked(r.app.thread, intent, + int res = mMainStack.startActivityLocked(r.app.thread, intent, r.resolvedType, null, 0, aInfo, resultTo, resultWho, requestCode, -1, r.launchedFromUid, false, false); Binder.restoreCallingIdentity(origId); @@ -4189,13 +2167,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - return startActivityLocked(null, intent, resolvedType, + return mMainStack.startActivityLocked(null, intent, resolvedType, null, 0, aInfo, resultTo, resultWho, requestCode, -1, uid, onlyIfNeeded, componentSpecified); } } - private final void addRecentTaskLocked(TaskRecord task) { + final void addRecentTaskLocked(TaskRecord task) { // Remove any existing entries that are the same kind of task. int N = mRecentTasks.size(); for (int i=0; i<N; i++) { @@ -4221,11 +2199,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index < 0) { return; } - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); final long origId = Binder.clearCallingIdentity(); mWindowManager.setAppOrientation(r, requestedOrientation); Configuration config = mWindowManager.updateOrientationFromAppTokens( @@ -4234,7 +2212,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (config != null) { r.frozenBeforeDestroy = true; if (!updateConfigurationLocked(config, r)) { - resumeTopActivityLocked(null); + mMainStack.resumeTopActivityLocked(null); } } Binder.restoreCallingIdentity(origId); @@ -4243,250 +2221,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public int getRequestedOrientation(IBinder token) { synchronized (this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index < 0) { return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); return mWindowManager.getAppOrientation(r); } } - private final void stopActivityLocked(ActivityRecord r) { - if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r); - if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 - || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) { - if (!r.finishing) { - requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, - "no-history"); - } - } else if (r.app != null && r.app.thread != null) { - if (mFocusedActivity == r) { - setFocusedActivityLocked(topRunningActivityLocked(null)); - } - r.resumeKeyDispatchingLocked(); - try { - r.stopped = false; - r.state = ActivityState.STOPPING; - if (DEBUG_VISBILITY) Slog.v( - TAG, "Stopping visible=" + r.visible + " for " + r); - if (!r.visible) { - mWindowManager.setAppVisibility(r, false); - } - r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags); - } catch (Exception e) { - // Maybe just ignore exceptions here... if the process - // has crashed, our death notification will clean things - // up. - Slog.w(TAG, "Exception thrown during pause", e); - // Just in case, assume it to be stopped. - r.stopped = true; - r.state = ActivityState.STOPPED; - if (r.configDestroy) { - destroyActivityLocked(r, true); - } - } - } - } - - /** - * @return Returns true if the activity is being finished, false if for - * some reason it is being left as-is. - */ - private final boolean requestFinishActivityLocked(IBinder token, int resultCode, - Intent resultData, String reason) { - if (DEBUG_RESULTS) Slog.v( - TAG, "Finishing activity: token=" + token - + ", result=" + resultCode + ", data=" + resultData); - - int index = indexOfTokenLocked(token); - if (index < 0) { - return false; - } - ActivityRecord r = (ActivityRecord)mHistory.get(index); - - // Is this the last activity left? - boolean lastActivity = true; - for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord p = (ActivityRecord)mHistory.get(i); - if (!p.finishing && p != r) { - lastActivity = false; - break; - } - } - - // If this is the last activity, but it is the home activity, then - // just don't finish it. - if (lastActivity) { - if (r.intent.hasCategory(Intent.CATEGORY_HOME)) { - return false; - } - } - - finishActivityLocked(r, index, resultCode, resultData, reason); - return true; - } - - /** - * @return Returns true if this activity has been removed from the history - * list, or false if it is still in the list and will be removed later. - */ - private final boolean finishActivityLocked(ActivityRecord r, int index, - int resultCode, Intent resultData, String reason) { - if (r.finishing) { - Slog.w(TAG, "Duplicate finish request for " + r); - return false; - } - - r.finishing = true; - EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, - System.identityHashCode(r), - r.task.taskId, r.shortComponentName, reason); - r.task.numActivities--; - if (index < (mHistory.size()-1)) { - ActivityRecord next = (ActivityRecord)mHistory.get(index+1); - if (next.task == r.task) { - if (r.frontOfTask) { - // The next activity is now the front of the task. - next.frontOfTask = true; - } - if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { - // If the caller asked that this activity (and all above it) - // be cleared when the task is reset, don't lose that information, - // but propagate it up to the next activity. - next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - } - } - } - - r.pauseKeyDispatchingLocked(); - if (mFocusedActivity == r) { - setFocusedActivityLocked(topRunningActivityLocked(null)); - } - - // send the result - ActivityRecord resultTo = r.resultTo; - if (resultTo != null) { - if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo - + " who=" + r.resultWho + " req=" + r.requestCode - + " res=" + resultCode + " data=" + resultData); - if (r.info.applicationInfo.uid > 0) { - grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid, - r.packageName, resultData, r); - } - resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, - resultData); - r.resultTo = null; - } - else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r); - - // Make sure this HistoryRecord is not holding on to other resources, - // because clients have remote IPC references to this object so we - // can't assume that will go away and want to avoid circular IPC refs. - r.results = null; - r.pendingResults = null; - r.newIntents = null; - r.icicle = null; - - if (mPendingThumbnails.size() > 0) { - // There are clients waiting to receive thumbnails so, in case - // this is an activity that someone is waiting for, add it - // to the pending list so we can correctly update the clients. - mCancelledThumbnails.add(r); - } - - if (mResumedActivity == r) { - boolean endTask = index <= 0 - || ((ActivityRecord)mHistory.get(index-1)).task != r.task; - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare close transition: finishing " + r); - mWindowManager.prepareAppTransition(endTask - ? WindowManagerPolicy.TRANSIT_TASK_CLOSE - : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE); - - // Tell window manager to prepare for this one to be removed. - mWindowManager.setAppVisibility(r, false); - - if (mPausingActivity == null) { - if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r); - if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false"); - startPausingLocked(false, false); - } - - } else if (r.state != ActivityState.PAUSING) { - // If the activity is PAUSING, we will complete the finish once - // it is done pausing; else we can just directly finish it here. - if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r); - return finishCurrentActivityLocked(r, index, - FINISH_AFTER_PAUSE) == null; - } else { - if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r); - } - - return false; - } - - private static final int FINISH_IMMEDIATELY = 0; - private static final int FINISH_AFTER_PAUSE = 1; - private static final int FINISH_AFTER_VISIBLE = 2; - - private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, - int mode) { - final int index = indexOfTokenLocked(r); - if (index < 0) { - return null; - } - - return finishCurrentActivityLocked(r, index, mode); - } - - private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, - int index, int mode) { - // First things first: if this activity is currently visible, - // and the resumed activity is not yet visible, then hold off on - // finishing until the resumed one becomes visible. - if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) { - if (!mStoppingActivities.contains(r)) { - mStoppingActivities.add(r); - if (mStoppingActivities.size() > 3) { - // If we already have a few activities waiting to stop, - // then give up on things going idle and start clearing - // them out. - Message msg = Message.obtain(); - msg.what = ActivityManagerService.IDLE_NOW_MSG; - mHandler.sendMessage(msg); - } - } - r.state = ActivityState.STOPPING; - updateOomAdjLocked(); - return r; - } - - // make sure the record is cleaned out of other places. - mStoppingActivities.remove(r); - mWaitingVisibleActivities.remove(r); - if (mResumedActivity == r) { - mResumedActivity = null; - } - final ActivityState prevState = r.state; - r.state = ActivityState.FINISHING; - - if (mode == FINISH_IMMEDIATELY - || prevState == ActivityState.STOPPED - || prevState == ActivityState.INITIALIZING) { - // If this activity is already stopped, we can just finish - // it right now. - return destroyActivityLocked(r, true) ? null : r; - } else { - // Need to go through the full pause cycle to get this - // activity into the stopped state and then finish it. - if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r); - mFinishingActivities.add(r); - resumeTopActivityLocked(null); - } - return r; - } - /** * This is the internal entry point for handling Activity.finish(). * @@ -4505,7 +2248,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { if (mController != null) { // Find the first activity that is not finishing. - ActivityRecord next = topRunningActivityLocked(token, 0); + ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0); if (next != null) { // ask watcher if this is allowed boolean resumeOK = true; @@ -4521,7 +2264,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } final long origId = Binder.clearCallingIdentity(); - boolean res = requestFinishActivityLocked(token, resultCode, + boolean res = mMainStack.requestFinishActivityLocked(token, resultCode, resultData, "app-request"); Binder.restoreCallingIdentity(origId); return res; @@ -4549,9 +2292,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (int i=0; i<activities.size(); i++) { ActivityRecord r = activities.get(i); if (!r.finishing) { - int index = indexOfTokenLocked(r); + int index = mMainStack.indexOfTokenLocked(r); if (index >= 0) { - finishActivityLocked(r, index, Activity.RESULT_CANCELED, + mMainStack.finishActivityLocked(r, index, Activity.RESULT_CANCELED, null, "finish-heavy"); } } @@ -4616,50 +2359,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - void sendActivityResultLocked(int callingUid, ActivityRecord r, - String resultWho, int requestCode, int resultCode, Intent data) { - - if (callingUid > 0) { - grantUriPermissionFromIntentLocked(callingUid, r.packageName, - data, r); - } - - if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r - + " : who=" + resultWho + " req=" + requestCode - + " res=" + resultCode + " data=" + data); - if (mResumedActivity == r && r.app != null && r.app.thread != null) { - try { - ArrayList<ResultInfo> list = new ArrayList<ResultInfo>(); - list.add(new ResultInfo(resultWho, requestCode, - resultCode, data)); - r.app.thread.scheduleSendResult(r, list); - return; - } catch (Exception e) { - Slog.w(TAG, "Exception thrown sending result to " + r, e); - } - } - - r.addResultLocked(null, resultWho, requestCode, resultCode, data); - } - public final void finishSubActivity(IBinder token, String resultWho, int requestCode) { synchronized(this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index < 0) { return; } - ActivityRecord self = (ActivityRecord)mHistory.get(index); + ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index); final long origId = Binder.clearCallingIdentity(); int i; - for (i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + for (i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r.resultTo == self && r.requestCode == requestCode) { if ((r.resultWho == null && resultWho == null) || (r.resultWho != null && r.resultWho.equals(resultWho))) { - finishActivityLocked(r, i, + mMainStack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "request-sub"); } } @@ -4672,8 +2389,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public boolean willActivityBeVisible(IBinder token) { synchronized(this) { int i; - for (i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + for (i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r == token) { return true; } @@ -4688,11 +2405,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim) { synchronized(this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index < 0) { return; } - ActivityRecord self = (ActivityRecord)mHistory.get(index); + ActivityRecord self = (ActivityRecord)mMainStack.mHistory.get(index); final long origId = Binder.clearCallingIdentity(); @@ -4707,192 +2424,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } /** - * Perform clean-up of service connections in an activity record. - */ - private final void cleanUpActivityServicesLocked(ActivityRecord r) { - // Throw away any services that have been bound by this activity. - if (r.connections != null) { - Iterator<ConnectionRecord> it = r.connections.iterator(); - while (it.hasNext()) { - ConnectionRecord c = it.next(); - removeConnectionLocked(c, null, r); - } - r.connections = null; - } - } - - /** - * Perform the common clean-up of an activity record. This is called both - * as part of destroyActivityLocked() (when destroying the client-side - * representation) and cleaning things up as a result of its hosting - * processing going away, in which case there is no remaining client-side - * state to destroy so only the cleanup here is needed. - */ - private final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) { - if (mResumedActivity == r) { - mResumedActivity = null; - } - if (mFocusedActivity == r) { - mFocusedActivity = null; - } - - r.configDestroy = false; - r.frozenBeforeDestroy = false; - - // Make sure this record is no longer in the pending finishes list. - // This could happen, for example, if we are trimming activities - // down to the max limit while they are still waiting to finish. - mFinishingActivities.remove(r); - mWaitingVisibleActivities.remove(r); - - // Remove any pending results. - if (r.finishing && r.pendingResults != null) { - for (WeakReference<PendingIntentRecord> apr : r.pendingResults) { - PendingIntentRecord rec = apr.get(); - if (rec != null) { - cancelIntentSenderLocked(rec, false); - } - } - r.pendingResults = null; - } - - if (cleanServices) { - cleanUpActivityServicesLocked(r); - } - - if (mPendingThumbnails.size() > 0) { - // There are clients waiting to receive thumbnails so, in case - // this is an activity that someone is waiting for, add it - // to the pending list so we can correctly update the clients. - mCancelledThumbnails.add(r); - } - - // Get rid of any pending idle timeouts. - mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); - mHandler.removeMessages(IDLE_TIMEOUT_MSG, r); - } - - private final void removeActivityFromHistoryLocked(ActivityRecord r) { - if (r.state != ActivityState.DESTROYED) { - mHistory.remove(r); - r.inHistory = false; - r.state = ActivityState.DESTROYED; - mWindowManager.removeAppToken(r); - if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); - } - cleanUpActivityServicesLocked(r); - removeActivityUriPermissionsLocked(r); - } - } - - /** - * Destroy the current CLIENT SIDE instance of an activity. This may be - * called both when actually finishing an activity, or when performing - * a configuration switch where we destroy the current client-side object - * but then create a new client-side object for this same HistoryRecord. - */ - private final boolean destroyActivityLocked(ActivityRecord r, - boolean removeFromApp) { - if (DEBUG_SWITCH) Slog.v( - TAG, "Removing activity: token=" + r - + ", app=" + (r.app != null ? r.app.processName : "(null)")); - EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, - System.identityHashCode(r), - r.task.taskId, r.shortComponentName); - - boolean removedFromHistory = false; - - cleanUpActivityLocked(r, false); - - final boolean hadApp = r.app != null; - - if (hadApp) { - if (removeFromApp) { - int idx = r.app.activities.indexOf(r); - if (idx >= 0) { - r.app.activities.remove(idx); - } - if (mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) { - mHeavyWeightProcess = null; - mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG); - } - if (r.persistent) { - decPersistentCountLocked(r.app); - } - if (r.app.activities.size() == 0) { - // No longer have activities, so update location in - // LRU list. - updateLruProcessLocked(r.app, true, false); - } - } - - boolean skipDestroy = false; - - try { - if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r); - r.app.thread.scheduleDestroyActivity(r, r.finishing, - r.configChangeFlags); - } catch (Exception e) { - // We can just ignore exceptions here... if the process - // has crashed, our death notification will clean things - // up. - //Slog.w(TAG, "Exception thrown during finish", e); - if (r.finishing) { - removeActivityFromHistoryLocked(r); - removedFromHistory = true; - skipDestroy = true; - } - } - - r.app = null; - r.nowVisible = false; - - if (r.finishing && !skipDestroy) { - r.state = ActivityState.DESTROYING; - Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG); - msg.obj = r; - mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT); - } else { - r.state = ActivityState.DESTROYED; - } - } else { - // remove this record from the history. - if (r.finishing) { - removeActivityFromHistoryLocked(r); - removedFromHistory = true; - } else { - r.state = ActivityState.DESTROYED; - } - } - - r.configChangeFlags = 0; - - if (!mLRUActivities.remove(r) && hadApp) { - Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list"); - } - - return removedFromHistory; - } - - private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) { - int i = list.size(); - if (localLOGV) Slog.v( - TAG, "Removing app " + app + " from list " + list - + " with " + i + " entries"); - while (i > 0) { - i--; - ActivityRecord r = (ActivityRecord)list.get(i); - if (localLOGV) Slog.v( - TAG, "Record #" + i + " " + r + ": app=" + r.app); - if (r.app == app) { - if (localLOGV) Slog.v(TAG, "Removing this entry!"); - list.remove(i); - } - } - } - - /** * Main function for removing an existing process from the activity manager * as a result of that process going away. Clears out all connections * to the process. @@ -4905,30 +2436,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // Just in case... - if (mPausingActivity != null && mPausingActivity.app == app) { - if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + mPausingActivity); - mPausingActivity = null; + if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) { + if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity); + mMainStack.mPausingActivity = null; } - if (mLastPausedActivity != null && mLastPausedActivity.app == app) { - mLastPausedActivity = null; + if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) { + mMainStack.mLastPausedActivity = null; } // Remove this application's activities from active lists. - removeHistoryRecordsForAppLocked(mLRUActivities, app); - removeHistoryRecordsForAppLocked(mStoppingActivities, app); - removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app); - removeHistoryRecordsForAppLocked(mFinishingActivities, app); + mMainStack.removeHistoryRecordsForAppLocked(app); boolean atTop = true; boolean hasVisibleActivities = false; // Clean out the history list. - int i = mHistory.size(); + int i = mMainStack.mHistory.size(); if (localLOGV) Slog.v( TAG, "Removing app " + app + " from history with " + i + " entries"); while (i > 0) { i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (localLOGV) Slog.v( TAG, "Record #" + i + " " + r + ": app=" + r.app); if (r.app == app) { @@ -4936,14 +2464,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (localLOGV) Slog.v( TAG, "Removing this entry! frozen=" + r.haveState + " finishing=" + r.finishing); - mHistory.remove(i); + mMainStack.mHistory.remove(i); r.inHistory = false; mWindowManager.removeAppToken(r); if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); + mWindowManager.validateAppTokens(mMainStack.mHistory); } - removeActivityUriPermissionsLocked(r); + r.removeUriPermissionsLocked(); } else { // We have the current state for this activity, so @@ -4960,7 +2488,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - cleanUpActivityLocked(r, true); + r.stack.cleanUpActivityLocked(r, true); r.state = ActivityState.STOPPED; } atTop = false; @@ -4977,14 +2505,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (!restarting) { - if (!resumeTopActivityLocked(null)) { + if (!mMainStack.resumeTopActivityLocked(null)) { // If there was nothing to resume, and we are not already // restarting this process, but there is a visible activity that // is hosted by the process... then make sure all visible // activities are running, taking care of restarting this // process. if (hasVisibleActivities) { - ensureActivitiesVisibleLocked(null, 0); + mMainStack.ensureActivitiesVisibleLocked(null, 0); } } } @@ -5003,7 +2531,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return -1; } - private final ProcessRecord getRecordForAppLocked( + final ProcessRecord getRecordForAppLocked( IApplicationThread thread) { if (thread == null) { return null; @@ -5013,7 +2541,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return appIndex >= 0 ? mLruProcesses.get(appIndex) : null; } - private final void appDiedLocked(ProcessRecord app, int pid, + final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) { mProcDeaths[0]++; @@ -5253,8 +2781,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void decPersistentCountLocked(ProcessRecord app) - { + final void decPersistentCountLocked(ProcessRecord app) { app.persistentActivities--; if (app.persistentActivities > 0) { // Still more of 'em... @@ -5282,11 +2809,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } synchronized(this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index < 0) { return; } - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); ProcessRecord app = r.app; if (localLOGV) Slog.v( @@ -5497,10 +3024,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mWindowManager.closeSystemDialogs(reason); - for (i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + for (i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) { - finishActivityLocked(r, i, + r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "close-sys"); } } @@ -5621,8 +3148,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean didSomething = killPackageProcessesLocked(name, uid, -100, callerWillRestart, doit); - for (i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + for (i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r.packageName.equals(name)) { if (!doit) { return true; @@ -5633,7 +3160,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.removed = true; } r.app = null; - finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall"); + r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "uninstall"); } } @@ -5665,7 +3192,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ac.removePackage(name); } } - resumeTopActivityLocked(null); + mMainStack.resumeTopActivityLocked(null); } return didSomething; @@ -5894,12 +3421,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean didSomething = false; // See if the top visible activity is waiting to run in this process... - ActivityRecord hr = topRunningActivityLocked(null); + ActivityRecord hr = mMainStack.topRunningActivityLocked(null); if (hr != null && normalMode) { if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid && processName.equals(hr.processName)) { try { - if (realStartActivityLocked(hr, app, true, true)) { + if (mMainStack.realStartActivityLocked(hr, app, true, true)) { didSomething = true; } } catch (Exception e) { @@ -5908,7 +3435,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen badApp = true; } } else { - ensureActivitiesVisibleLocked(hr, null, processName, 0); + mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0); } } @@ -5992,195 +3519,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public final void activityIdle(IBinder token, Configuration config) { final long origId = Binder.clearCallingIdentity(); - activityIdleInternal(token, false, config); + mMainStack.activityIdleInternal(token, false, config); Binder.restoreCallingIdentity(origId); } - final ArrayList<ActivityRecord> processStoppingActivitiesLocked( - boolean remove) { - int N = mStoppingActivities.size(); - if (N <= 0) return null; - - ArrayList<ActivityRecord> stops = null; - - final boolean nowVisible = mResumedActivity != null - && mResumedActivity.nowVisible - && !mResumedActivity.waitingVisible; - for (int i=0; i<N; i++) { - ActivityRecord s = mStoppingActivities.get(i); - if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible=" - + nowVisible + " waitingVisible=" + s.waitingVisible - + " finishing=" + s.finishing); - if (s.waitingVisible && nowVisible) { - mWaitingVisibleActivities.remove(s); - s.waitingVisible = false; - if (s.finishing) { - // If this activity is finishing, it is sitting on top of - // everyone else but we now know it is no longer needed... - // so get rid of it. Otherwise, we need to go through the - // normal flow and hide it once we determine that it is - // hidden by the activities in front of it. - if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s); - mWindowManager.setAppVisibility(s, false); - } - } - if (!s.waitingVisible && remove) { - if (localLOGV) Slog.v(TAG, "Ready to stop: " + s); - if (stops == null) { - stops = new ArrayList<ActivityRecord>(); - } - stops.add(s); - mStoppingActivities.remove(i); - N--; - i--; - } - } - - return stops; - } - void enableScreenAfterBoot() { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN, SystemClock.uptimeMillis()); mWindowManager.enableScreenAfterBoot(); } - final void activityIdleInternal(IBinder token, boolean fromTimeout, - Configuration config) { - if (localLOGV) Slog.v(TAG, "Activity idle: " + token); - - ArrayList<ActivityRecord> stops = null; - ArrayList<ActivityRecord> finishes = null; - ArrayList<ActivityRecord> thumbnails = null; - int NS = 0; - int NF = 0; - int NT = 0; - IApplicationThread sendThumbnail = null; - boolean booting = false; - boolean enableScreen = false; - - synchronized (this) { - if (token != null) { - mHandler.removeMessages(IDLE_TIMEOUT_MSG, token); - } - - // Get the activity record. - int index = indexOfTokenLocked(token); - if (index >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(index); - - if (fromTimeout) { - reportActivityLaunchedLocked(fromTimeout, r, -1, -1); - } - - // This is a hack to semi-deal with a race condition - // in the client where it can be constructed with a - // newer configuration from when we asked it to launch. - // We'll update with whatever configuration it now says - // it used to launch. - if (config != null) { - r.configuration = config; - } - - // No longer need to keep the device awake. - if (mResumedActivity == r && mLaunchingActivity.isHeld()) { - mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); - mLaunchingActivity.release(); - } - - // We are now idle. If someone is waiting for a thumbnail from - // us, we can now deliver. - r.idle = true; - scheduleAppGcsLocked(); - if (r.thumbnailNeeded && r.app != null && r.app.thread != null) { - sendThumbnail = r.app.thread; - r.thumbnailNeeded = false; - } - - // If this activity is fullscreen, set up to hide those under it. - - if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r); - ensureActivitiesVisibleLocked(null, 0); - - //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout); - if (!mBooted && !fromTimeout) { - mBooted = true; - enableScreen = true; - } - - } else if (fromTimeout) { - reportActivityLaunchedLocked(fromTimeout, null, -1, -1); - } - - // Atomically retrieve all of the other things to do. - stops = processStoppingActivitiesLocked(true); - NS = stops != null ? stops.size() : 0; - if ((NF=mFinishingActivities.size()) > 0) { - finishes = new ArrayList<ActivityRecord>(mFinishingActivities); - mFinishingActivities.clear(); - } - if ((NT=mCancelledThumbnails.size()) > 0) { - thumbnails = new ArrayList<ActivityRecord>(mCancelledThumbnails); - mCancelledThumbnails.clear(); - } - - booting = mBooting; - mBooting = false; - } - - int i; - - // Send thumbnail if requested. - if (sendThumbnail != null) { - try { - sendThumbnail.requestThumbnail(token); - } catch (Exception e) { - Slog.w(TAG, "Exception thrown when requesting thumbnail", e); - sendPendingThumbnail(null, token, null, null, true); - } - } - - // Stop any activities that are scheduled to do so but have been - // waiting for the next one to start. - for (i=0; i<NS; i++) { - ActivityRecord r = (ActivityRecord)stops.get(i); - synchronized (this) { - if (r.finishing) { - finishCurrentActivityLocked(r, FINISH_IMMEDIATELY); - } else { - stopActivityLocked(r); - } - } - } - - // Finish any activities that are scheduled to do so but have been - // waiting for the next one to start. - for (i=0; i<NF; i++) { - ActivityRecord r = (ActivityRecord)finishes.get(i); - synchronized (this) { - destroyActivityLocked(r, true); - } - } - - // Report back to any thumbnail receivers. - for (i=0; i<NT; i++) { - ActivityRecord r = (ActivityRecord)thumbnails.get(i); - sendPendingThumbnail(r, null, null, null, true); - } - - if (booting) { - finishBooting(); - } - - trimApplications(); - //dump(); - //mWindowManager.dump(); - - if (enableScreen) { - enableScreenAfterBoot(); - } - } - final void finishBooting() { IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); @@ -6249,39 +3597,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } final long origId = Binder.clearCallingIdentity(); - activityPaused(token, icicle, false); + mMainStack.activityPaused(token, icicle, false); Binder.restoreCallingIdentity(origId); } - final void activityPaused(IBinder token, Bundle icicle, boolean timeout) { - if (DEBUG_PAUSE) Slog.v( - TAG, "Activity paused: token=" + token + ", icicle=" + icicle - + ", timeout=" + timeout); - - ActivityRecord r = null; - - synchronized (this) { - int index = indexOfTokenLocked(token); - if (index >= 0) { - r = (ActivityRecord)mHistory.get(index); - if (!timeout) { - r.icicle = icicle; - r.haveState = true; - } - mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); - if (mPausingActivity == r) { - r.state = ActivityState.PAUSED; - completePauseLocked(); - } else { - EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, - System.identityHashCode(r), r.shortComponentName, - mPausingActivity != null - ? mPausingActivity.shortComponentName : "(none)"); - } - } - } - } - public final void activityStopped(IBinder token, Bitmap thumbnail, CharSequence description) { if (localLOGV) Slog.v( @@ -6292,17 +3611,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); synchronized (this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index >= 0) { - r = (ActivityRecord)mHistory.get(index); + r = (ActivityRecord)mMainStack.mHistory.get(index); r.thumbnail = thumbnail; r.description = description; r.stopped = true; r.state = ActivityState.STOPPED; if (!r.finishing) { if (r.configDestroy) { - destroyActivityLocked(r, true); - resumeTopActivityLocked(null); + r.stack.destroyActivityLocked(r, true); + r.stack.resumeTopActivityLocked(null); } } } @@ -6319,19 +3638,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public final void activityDestroyed(IBinder token) { if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token); - synchronized (this) { - mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token); - - int index = indexOfTokenLocked(token); - if (index >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(index); - if (r.state == ActivityState.DESTROYING) { - final long origId = Binder.clearCallingIdentity(); - removeActivityFromHistoryLocked(r); - Binder.restoreCallingIdentity(origId); - } - } - } + mMainStack.activityDestroyed(token); } public String getCallingPackage(IBinder token) { @@ -6349,9 +3656,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private ActivityRecord getCallingRecordLocked(IBinder token) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); if (r != null) { return r.resultTo; } @@ -6361,9 +3668,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public ComponentName getActivityClassForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); return r.intent.getComponent(); } return null; @@ -6372,9 +3679,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public String getPackageForToken(IBinder token) { synchronized(this) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); return r.packageName; } return null; @@ -6428,11 +3735,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int requestCode, Intent intent, String resolvedType, int flags) { ActivityRecord activity = null; if (type == INTENT_SENDER_ACTIVITY_RESULT) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index < 0) { return null; } - activity = (ActivityRecord)mHistory.get(index); + activity = (ActivityRecord)mMainStack.mHistory.get(index); if (activity.finishing) { return null; } @@ -6749,7 +4056,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private void grantUriPermissionLocked(int callingUid, + void grantUriPermissionLocked(int callingUid, String targetPkg, Uri uri, int modeFlags, ActivityRecord activity) { modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); @@ -6878,7 +4185,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private void grantUriPermissionFromIntentLocked(int callingUid, + void grantUriPermissionFromIntentLocked(int callingUid, String targetPkg, Intent intent, ActivityRecord activity) { if (intent == null) { return; @@ -6914,7 +4221,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private void removeUriPermissionIfNeededLocked(UriPermission perm) { + void removeUriPermissionIfNeededLocked(UriPermission perm) { if ((perm.modeFlags&(Intent.FLAG_GRANT_READ_URI_PERMISSION |Intent.FLAG_GRANT_WRITE_URI_PERMISSION)) == 0) { HashMap<Uri, UriPermission> perms @@ -6930,29 +4237,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private void removeActivityUriPermissionsLocked(ActivityRecord activity) { - if (activity.readUriPermissions != null) { - for (UriPermission perm : activity.readUriPermissions) { - perm.readActivities.remove(activity); - if (perm.readActivities.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - removeUriPermissionIfNeededLocked(perm); - } - } - } - if (activity.writeUriPermissions != null) { - for (UriPermission perm : activity.writeUriPermissions) { - perm.writeActivities.remove(activity); - if (perm.writeActivities.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - removeUriPermissionIfNeededLocked(perm); - } - } - } - } - private void revokeUriPermissionLocked(int callingUid, Uri uri, int modeFlags) { modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -7137,9 +4421,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen throw new SecurityException(msg); } - int pos = mHistory.size()-1; + int pos = mMainStack.mHistory.size()-1; ActivityRecord next = - pos >= 0 ? (ActivityRecord)mHistory.get(pos) : null; + pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null; ActivityRecord top = null; CharSequence topDescription = null; TaskRecord curTask = null; @@ -7148,7 +4432,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (pos >= 0 && maxNum > 0) { final ActivityRecord r = next; pos--; - next = pos >= 0 ? (ActivityRecord)mHistory.get(pos) : null; + next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null; // Initialize state for next task if needed. if (top == null || @@ -7297,12 +4581,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final int findAffinityTaskTopLocked(int startIndex, String affinity) { int j; - TaskRecord startTask = ((ActivityRecord)mHistory.get(startIndex)).task; + TaskRecord startTask = ((ActivityRecord)mMainStack.mHistory.get(startIndex)).task; TaskRecord jt = startTask; // First look backwards for (j=startIndex-1; j>=0; j--) { - ActivityRecord r = (ActivityRecord)mHistory.get(j); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j); if (r.task != jt) { jt = r.task; if (affinity.equals(jt.affinity)) { @@ -7312,10 +4596,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // Now look forwards - final int N = mHistory.size(); + final int N = mMainStack.mHistory.size(); jt = startTask; for (j=startIndex+1; j<N; j++) { - ActivityRecord r = (ActivityRecord)mHistory.get(j); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(j); if (r.task != jt) { if (affinity.equals(jt.affinity)) { return j; @@ -7325,7 +4609,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // Might it be at the top? - if (affinity.equals(((ActivityRecord)mHistory.get(N-1)).task.affinity)) { + if (affinity.equals(((ActivityRecord)mMainStack.mHistory.get(N-1)).task.affinity)) { return N-1; } @@ -7333,293 +4617,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } /** - * Perform a reset of the given task, if needed as part of launching it. - * Returns the new HistoryRecord at the top of the task. - */ - private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop, - ActivityRecord newActivity) { - boolean forceReset = (newActivity.info.flags - &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; - if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { - if ((newActivity.info.flags - &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { - forceReset = true; - } - } - - final TaskRecord task = taskTop.task; - - // We are going to move through the history list so that we can look - // at each activity 'target' with 'below' either the interesting - // activity immediately below it in the stack or null. - ActivityRecord target = null; - int targetI = 0; - int taskTopI = -1; - int replyChainEnd = -1; - int lastReparentPos = -1; - for (int i=mHistory.size()-1; i>=-1; i--) { - ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null; - - if (below != null && below.finishing) { - continue; - } - if (target == null) { - target = below; - targetI = i; - // If we were in the middle of a reply chain before this - // task, it doesn't appear like the root of the chain wants - // anything interesting, so drop it. - replyChainEnd = -1; - continue; - } - - final int flags = target.info.flags; - - final boolean finishOnTaskLaunch = - (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; - final boolean allowTaskReparenting = - (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; - - if (target.task == task) { - // We are inside of the task being reset... we'll either - // finish this activity, push it out for another task, - // or leave it as-is. We only do this - // for activities that are not the root of the task (since - // if we finish the root, we may no longer have the task!). - if (taskTopI < 0) { - taskTopI = targetI; - } - if (below != null && below.task == task) { - final boolean clearWhenTaskReset = - (target.intent.getFlags() - &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0; - if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) { - // If this activity is sending a reply to a previous - // activity, we can't do anything with it now until - // we reach the start of the reply chain. - // XXX note that we are assuming the result is always - // to the previous activity, which is almost always - // the case but we really shouldn't count on. - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting - && target.taskAffinity != null - && !target.taskAffinity.equals(task.affinity)) { - // If this activity has an affinity for another - // task, then we need to move it out of here. We will - // move it as far out of the way as possible, to the - // bottom of the activity stack. This also keeps it - // correctly ordered with any activities we previously - // moved. - ActivityRecord p = (ActivityRecord)mHistory.get(0); - if (target.taskAffinity != null - && target.taskAffinity.equals(p.task.affinity)) { - // If the activity currently at the bottom has the - // same task affinity as the one we are moving, - // then merge it into the same task. - target.task = p.task; - if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target - + " out to bottom task " + p.task); - } else { - mCurTask++; - if (mCurTask <= 0) { - mCurTask = 1; - } - target.task = new TaskRecord(mCurTask, target.info, null, - (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); - target.task.affinityIntent = target.intent; - if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target - + " out to new task " + target.task); - } - mWindowManager.setAppGroupId(target, task.taskId); - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - int dstPos = 0; - for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = (ActivityRecord)mHistory.get(srcPos); - if (p.finishing) { - continue; - } - if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p - + " out to target's task " + target.task); - task.numActivities--; - p.task = target.task; - target.task.numActivities++; - mHistory.remove(srcPos); - mHistory.add(dstPos, p); - mWindowManager.moveAppToken(dstPos, p); - mWindowManager.setAppGroupId(p, p.task.taskId); - dstPos++; - if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); - } - i++; - } - if (taskTop == p) { - taskTop = below; - } - if (taskTopI == replyChainEnd) { - taskTopI = -1; - } - replyChainEnd = -1; - addRecentTaskLocked(target.task); - } else if (forceReset || finishOnTaskLaunch - || clearWhenTaskReset) { - // If the activity should just be removed -- either - // because it asks for it, or the task should be - // cleared -- then finish it and anything that is - // part of its reply chain. - if (clearWhenTaskReset) { - // In this case, we want to finish this activity - // and everything above it, so be sneaky and pretend - // like these are all in the reply chain. - replyChainEnd = targetI+1; - while (replyChainEnd < mHistory.size() && - ((ActivityRecord)mHistory.get( - replyChainEnd)).task == task) { - replyChainEnd++; - } - replyChainEnd--; - } else if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - ActivityRecord p = null; - for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = (ActivityRecord)mHistory.get(srcPos); - if (p.finishing) { - continue; - } - if (finishActivityLocked(p, srcPos, - Activity.RESULT_CANCELED, null, "reset")) { - replyChainEnd--; - srcPos--; - } - } - if (taskTop == p) { - taskTop = below; - } - if (taskTopI == replyChainEnd) { - taskTopI = -1; - } - replyChainEnd = -1; - } else { - // If we were in the middle of a chain, well the - // activity that started it all doesn't want anything - // special, so leave it all as-is. - replyChainEnd = -1; - } - } else { - // Reached the bottom of the task -- any reply chain - // should be left as-is. - replyChainEnd = -1; - } - - } else if (target.resultTo != null) { - // If this activity is sending a reply to a previous - // activity, we can't do anything with it now until - // we reach the start of the reply chain. - // XXX note that we are assuming the result is always - // to the previous activity, which is almost always - // the case but we really shouldn't count on. - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - - } else if (taskTopI >= 0 && allowTaskReparenting - && task.affinity != null - && task.affinity.equals(target.taskAffinity)) { - // We are inside of another task... if this activity has - // an affinity for our task, then either remove it if we are - // clearing or move it over to our task. Note that - // we currently punt on the case where we are resetting a - // task that is not at the top but who has activities above - // with an affinity to it... this is really not a normal - // case, and we will need to later pull that task to the front - // and usually at that point we will do the reset and pick - // up those remaining activities. (This only happens if - // someone starts an activity in a new task from an activity - // in a task that is not currently on top.) - if (forceReset || finishOnTaskLaunch) { - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - ActivityRecord p = null; - for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = (ActivityRecord)mHistory.get(srcPos); - if (p.finishing) { - continue; - } - if (finishActivityLocked(p, srcPos, - Activity.RESULT_CANCELED, null, "reset")) { - taskTopI--; - lastReparentPos--; - replyChainEnd--; - srcPos--; - } - } - replyChainEnd = -1; - } else { - if (replyChainEnd < 0) { - replyChainEnd = targetI; - } - for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) { - ActivityRecord p = (ActivityRecord)mHistory.get(srcPos); - if (p.finishing) { - continue; - } - if (lastReparentPos < 0) { - lastReparentPos = taskTopI; - taskTop = p; - } else { - lastReparentPos--; - } - mHistory.remove(srcPos); - p.task.numActivities--; - p.task = task; - mHistory.add(lastReparentPos, p); - if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p - + " in to resetting task " + task); - task.numActivities++; - mWindowManager.moveAppToken(lastReparentPos, p); - mWindowManager.setAppGroupId(p, p.task.taskId); - if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); - } - } - replyChainEnd = -1; - - // Now we've moved it in to place... but what if this is - // a singleTop activity and we have put it on top of another - // instance of the same activity? Then we drop the instance - // below so it remains singleTop. - if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) { - for (int j=lastReparentPos-1; j>=0; j--) { - ActivityRecord p = (ActivityRecord)mHistory.get(j); - if (p.finishing) { - continue; - } - if (p.intent.getComponent().equals(target.intent.getComponent())) { - if (finishActivityLocked(p, j, - Activity.RESULT_CANCELED, null, "replace")) { - taskTopI--; - lastReparentPos--; - } - } - } - } - } - } - - target = below; - targetI = i; - } - - return taskTop; - } - - /** * TODO: Add mController hook */ public void moveTaskToFront(int task) { @@ -7637,14 +4634,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (int i=0; i<N; i++) { TaskRecord tr = mRecentTasks.get(i); if (tr.taskId == task) { - moveTaskToFrontLocked(tr, null); + mMainStack.moveTaskToFrontLocked(tr, null); return; } } - for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord hr = (ActivityRecord)mHistory.get(i); + for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i); if (hr.task.taskId == task) { - moveTaskToFrontLocked(hr.task, null); + mMainStack.moveTaskToFrontLocked(hr.task, null); return; } } @@ -7654,84 +4651,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) { - if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); - - final int task = tr.taskId; - int top = mHistory.size()-1; - - if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) { - // nothing to do! - return; - } - - ArrayList moved = new ArrayList(); - - // Applying the affinities may have removed entries from the history, - // so get the size again. - top = mHistory.size()-1; - int pos = top; - - // Shift all activities with this task up to the top - // of the stack, keeping them in the same internal order. - while (pos >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(pos); - if (localLOGV) Slog.v( - TAG, "At " + pos + " ckp " + r.task + ": " + r); - boolean first = true; - if (r.task.taskId == task) { - if (localLOGV) Slog.v(TAG, "Removing and adding at " + top); - mHistory.remove(pos); - mHistory.add(top, r); - moved.add(0, r); - top--; - if (first) { - addRecentTaskLocked(r.task); - first = false; - } - } - pos--; - } - - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare to front transition: task=" + tr); - if (reason != null && - (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); - ActivityRecord r = topRunningActivityLocked(null); - if (r != null) { - mNoAnimActivities.add(r); - } - } else { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT); - } - - mWindowManager.moveAppTokensToTop(moved); - if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); - } - - finishTaskMoveLocked(task); - EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task); - } - - private final void finishTaskMoveLocked(int task) { - resumeTopActivityLocked(null); - } - public void moveTaskToBack(int task) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToBack()"); synchronized(this) { - if (mResumedActivity != null && mResumedActivity.task.taskId == task) { + if (mMainStack.mResumedActivity != null + && mMainStack.mResumedActivity.task.taskId == task) { if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), Binder.getCallingUid(), "Task to back")) { return; } } final long origId = Binder.clearCallingIdentity(); - moveTaskToBackLocked(task, null); + mMainStack.moveTaskToBackLocked(task, null); Binder.restoreCallingIdentity(origId); } } @@ -7750,93 +4683,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); int taskId = getTaskForActivityLocked(token, !nonRoot); if (taskId >= 0) { - return moveTaskToBackLocked(taskId, null); + return mMainStack.moveTaskToBackLocked(taskId, null); } Binder.restoreCallingIdentity(origId); } return false; } - /** - * Worker method for rearranging history stack. Implements the function of moving all - * activities for a specific task (gathering them if disjoint) into a single group at the - * bottom of the stack. - * - * If a watcher is installed, the action is preflighted and the watcher has an opportunity - * to premeptively cancel the move. - * - * @param task The taskId to collect and move to the bottom. - * @return Returns true if the move completed, false if not. - */ - private final boolean moveTaskToBackLocked(int task, ActivityRecord reason) { - Slog.i(TAG, "moveTaskToBack: " + task); - - // If we have a watcher, preflight the move before committing to it. First check - // for *other* available tasks, but if none are available, then try again allowing the - // current task to be selected. - if (mController != null) { - ActivityRecord next = topRunningActivityLocked(null, task); - if (next == null) { - next = topRunningActivityLocked(null, 0); - } - if (next != null) { - // ask watcher if this is allowed - boolean moveOK = true; - try { - moveOK = mController.activityResuming(next.packageName); - } catch (RemoteException e) { - mController = null; - } - if (!moveOK) { - return false; - } - } - } - - ArrayList moved = new ArrayList(); - - if (DEBUG_TRANSITION) Slog.v(TAG, - "Prepare to back transition: task=" + task); - - final int N = mHistory.size(); - int bottom = 0; - int pos = 0; - - // Shift all activities with this task down to the bottom - // of the stack, keeping them in the same internal order. - while (pos < N) { - ActivityRecord r = (ActivityRecord)mHistory.get(pos); - if (localLOGV) Slog.v( - TAG, "At " + pos + " ckp " + r.task + ": " + r); - if (r.task.taskId == task) { - if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1)); - mHistory.remove(pos); - mHistory.add(bottom, r); - moved.add(r); - bottom++; - } - pos++; - } - - if (reason != null && - (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); - ActivityRecord r = topRunningActivityLocked(null); - if (r != null) { - mNoAnimActivities.add(r); - } - } else { - mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK); - } - mWindowManager.moveAppTokensToBottom(moved); - if (VALIDATE_TOKENS) { - mWindowManager.validateAppTokens(mHistory); - } - - finishTaskMoveLocked(task); - return true; - } - public void moveTaskBackwards(int task) { enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskBackwards()"); @@ -7863,10 +4716,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } int getTaskForActivityLocked(IBinder token, boolean onlyRoot) { - final int N = mHistory.size(); + final int N = mMainStack.mHistory.size(); TaskRecord lastTask = null; for (int i=0; i<N; i++) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r == token) { if (!onlyRoot || lastTask != r.task) { return r.task.taskId; @@ -7879,89 +4732,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return -1; } - /** - * Returns the top activity in any existing task matching the given - * Intent. Returns null if no such task is found. - */ - private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) { - ComponentName cls = intent.getComponent(); - if (info.targetActivity != null) { - cls = new ComponentName(info.packageName, info.targetActivity); - } - - TaskRecord cp = null; - - final int N = mHistory.size(); - for (int i=(N-1); i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (!r.finishing && r.task != cp - && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) { - cp = r.task; - //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString() - // + "/aff=" + r.task.affinity + " to new cls=" - // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity); - if (r.task.affinity != null) { - if (r.task.affinity.equals(info.taskAffinity)) { - //Slog.i(TAG, "Found matching affinity!"); - return r; - } - } else if (r.task.intent != null - && r.task.intent.getComponent().equals(cls)) { - //Slog.i(TAG, "Found matching class!"); - //dump(); - //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); - return r; - } else if (r.task.affinityIntent != null - && r.task.affinityIntent.getComponent().equals(cls)) { - //Slog.i(TAG, "Found matching class!"); - //dump(); - //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); - return r; - } - } - } - - return null; - } - - /** - * Returns the first activity (starting from the top of the stack) that - * is the same as the given activity. Returns null if no such activity - * is found. - */ - private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) { - ComponentName cls = intent.getComponent(); - if (info.targetActivity != null) { - cls = new ComponentName(info.packageName, info.targetActivity); - } - - final int N = mHistory.size(); - for (int i=(N-1); i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); - if (!r.finishing) { - if (r.intent.getComponent().equals(cls)) { - //Slog.i(TAG, "Found matching class!"); - //dump(); - //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); - return r; - } - } - } - - return null; - } - public void finishOtherInstances(IBinder token, ComponentName className) { synchronized(this) { final long origId = Binder.clearCallingIdentity(); - int N = mHistory.size(); + int N = mMainStack.mHistory.size(); TaskRecord lastTask = null; for (int i=0; i<N; i++) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r.realActivity.equals(className) && r != token && lastTask != r.task) { - if (finishActivityLocked(r, i, Activity.RESULT_CANCELED, + if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "others")) { i--; N--; @@ -7995,11 +4776,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized(this) { if (r == null) { - int index = indexOfTokenLocked(token); + int index = mMainStack.indexOfTokenLocked(token); if (index < 0) { return; } - r = (ActivityRecord)mHistory.get(index); + r = (ActivityRecord)mMainStack.mHistory.get(index); } if (thumbnail == null) { thumbnail = r.thumbnail; @@ -8531,12 +5312,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "unhandledBack()"); synchronized(this) { - int count = mHistory.size(); + int count = mMainStack.mHistory.size(); if (DEBUG_SWITCH) Slog.d( TAG, "Performing unhandledBack(): stack size = " + count); if (count > 1) { final long origId = Binder.clearCallingIdentity(); - finishActivityLocked((ActivityRecord)mHistory.get(count-1), + mMainStack.finishActivityLocked((ActivityRecord)mMainStack.mHistory.get(count-1), count-1, Activity.RESULT_CANCELED, null, "unhandled-back"); Binder.restoreCallingIdentity(origId); } @@ -8579,8 +5360,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mSleeping = true; mWindowManager.setEventDispatching(false); - if (mResumedActivity != null) { - pauseIfSleepingLocked(); + if (mMainStack.mResumedActivity != null) { + mMainStack.pauseIfSleepingLocked(); } else { Slog.w(TAG, "goingToSleep with no resumed activity!"); } @@ -8600,10 +5381,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mShuttingDown = true; mWindowManager.setEventDispatching(false); - if (mResumedActivity != null) { - pauseIfSleepingLocked(); + if (mMainStack.mResumedActivity != null) { + mMainStack.pauseIfSleepingLocked(); final long endTime = System.currentTimeMillis() + timeout; - while (mResumedActivity != null || mPausingActivity != null) { + while (mMainStack.mResumedActivity != null + || mMainStack.mPausingActivity != null) { long delay = endTime - System.currentTimeMillis(); if (delay <= 0) { Slog.w(TAG, "Activity manager shutdown timed out"); @@ -8624,35 +5406,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return timedout; } - void pauseIfSleepingLocked() { - if (mSleeping || mShuttingDown) { - if (!mGoingToSleep.isHeld()) { - mGoingToSleep.acquire(); - if (mLaunchingActivity.isHeld()) { - mLaunchingActivity.release(); - mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); - } - } - - // If we are not currently pausing an activity, get the current - // one to pause. If we are pausing one, we will just let that stuff - // run and release the wake lock when all done. - if (mPausingActivity == null) { - if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause..."); - if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false"); - startPausingLocked(false, true); - } - } - } - public void wakingUp() { synchronized(this) { - if (mGoingToSleep.isHeld()) { - mGoingToSleep.release(); + if (mMainStack.mGoingToSleep.isHeld()) { + mMainStack.mGoingToSleep.release(); } mWindowManager.setEventDispatching(true); mSleeping = false; - resumeTopActivityLocked(null); + mMainStack.resumeTopActivityLocked(null); } } @@ -8782,29 +5543,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void setImmersive(IBinder token, boolean immersive) { synchronized(this) { - int index = (token != null) ? indexOfTokenLocked(token) : -1; + int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1; if (index < 0) { throw new IllegalArgumentException(); } - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); r.immersive = immersive; } } public boolean isImmersive(IBinder token) { synchronized (this) { - int index = (token != null) ? indexOfTokenLocked(token) : -1; + int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1; if (index < 0) { throw new IllegalArgumentException(); } - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index); return r.immersive; } } public boolean isTopActivityImmersive() { synchronized (this) { - ActivityRecord r = topRunningActivityLocked(null); + ActivityRecord r = mMainStack.topRunningActivityLocked(null); return (r != null) ? r.immersive : false; } } @@ -9238,7 +5999,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } - resumeTopActivityLocked(null); + mMainStack.resumeTopActivityLocked(null); } } @@ -9326,12 +6087,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, app.info.processName, app.info.uid); killServicesLocked(app, false); - for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i); if (r.app == app) { Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed"); + r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED, null, "crashed"); } } if (!app.persistent) { @@ -9349,28 +6110,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } } else { - ActivityRecord r = topRunningActivityLocked(null); + ActivityRecord r = mMainStack.topRunningActivityLocked(null); if (r.app == app) { // If the top running activity is from this crashing // process, then terminate it to avoid getting in a loop. Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - int index = indexOfTokenLocked(r); - finishActivityLocked(r, index, + int index = mMainStack.indexOfTokenLocked(r); + r.stack.finishActivityLocked(r, index, Activity.RESULT_CANCELED, null, "crashed"); // Also terminate an activities below it that aren't yet // stopped, to avoid a situation where one will get // re-start our crashing activity once it gets resumed again. index--; if (index >= 0) { - r = (ActivityRecord)mHistory.get(index); + r = (ActivityRecord)mMainStack.mHistory.get(index); if (r.state == ActivityState.RESUMED || r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) { if (!r.isHomeActivity) { Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - finishActivityLocked(r, index, + r.stack.finishActivityLocked(r, index, Activity.RESULT_CANCELED, null, "crashed"); } } @@ -10075,31 +6836,31 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (needHeader) { pw.println(" Activity stack:"); } - dumpHistoryList(pw, mHistory, " ", "Hist", true); + dumpHistoryList(pw, mMainStack.mHistory, " ", "Hist", true); pw.println(" "); pw.println(" Running activities (most recent first):"); - dumpHistoryList(pw, mLRUActivities, " ", "Run", false); - if (mWaitingVisibleActivities.size() > 0) { + dumpHistoryList(pw, mMainStack.mLRUActivities, " ", "Run", false); + if (mMainStack.mWaitingVisibleActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting for another to become visible:"); - dumpHistoryList(pw, mWaitingVisibleActivities, " ", "Wait", false); + dumpHistoryList(pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false); } - if (mStoppingActivities.size() > 0) { + if (mMainStack.mStoppingActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to stop:"); - dumpHistoryList(pw, mStoppingActivities, " ", "Stop", false); + dumpHistoryList(pw, mMainStack.mStoppingActivities, " ", "Stop", false); } - if (mFinishingActivities.size() > 0) { + if (mMainStack.mFinishingActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to finish:"); - dumpHistoryList(pw, mFinishingActivities, " ", "Fin", false); + dumpHistoryList(pw, mMainStack.mFinishingActivities, " ", "Fin", false); } pw.println(" "); - pw.println(" mPausingActivity: " + mPausingActivity); - pw.println(" mResumedActivity: " + mResumedActivity); + pw.println(" mPausingActivity: " + mMainStack.mPausingActivity); + pw.println(" mResumedActivity: " + mMainStack.mResumedActivity); pw.println(" mFocusedActivity: " + mFocusedActivity); - pw.println(" mLastPausedActivity: " + mLastPausedActivity); + pw.println(" mLastPausedActivity: " + mMainStack.mLastPausedActivity); if (dumpAll && mRecentTasks.size() > 0) { pw.println(" "); @@ -10268,7 +7029,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess); } pw.println(" mConfiguration: " + mConfiguration); - pw.println(" mConfigWillChange: " + mConfigWillChange); + pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange); pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown); if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { @@ -10287,8 +7048,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " mBooting=" + mBooting + " mBooted=" + mBooted + " mFactoryTest=" + mFactoryTest); - pw.println(" mGoingToSleep=" + mGoingToSleep); - pw.println(" mLaunchingActivity=" + mLaunchingActivity); + pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep); + pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity); pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq); } @@ -10778,22 +7539,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return false; } - private final int indexOfTokenLocked(IBinder token) { - int count = mHistory.size(); - - // convert the token to an entry in the history. - int index = -1; - for (int i=count-1; i>=0; i--) { - Object o = mHistory.get(i); - if (o == token) { - index = i; - break; - } - } - - return index; - } - private final void killServicesLocked(ProcessRecord app, boolean allowRestart) { // Report disconnected services. @@ -12071,12 +8816,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ActivityRecord activity = null; if (token != null) { - int aindex = indexOfTokenLocked(token); + int aindex = mMainStack.indexOfTokenLocked(token); if (aindex < 0) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0; } - activity = (ActivityRecord)mHistory.get(aindex); + activity = (ActivityRecord)mMainStack.mHistory.get(aindex); } int clientLabel = 0; @@ -12180,7 +8925,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return 1; } - private void removeConnectionLocked( + void removeConnectionLocked( ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) { IBinder binder = c.conn.asBinder(); AppBindRecord b = c.binding; @@ -14012,18 +10757,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If the configuration changed, and the caller is not already // in the process of starting an activity, then find the top // activity to check if its configuration needs to change. - starting = topRunningActivityLocked(null); + starting = mMainStack.topRunningActivityLocked(null); } if (starting != null) { - kept = ensureActivityConfigurationLocked(starting, changes); + kept = mMainStack.ensureActivityConfigurationLocked(starting, changes); if (kept) { // If this didn't result in the starting activity being // destroyed, then we need to make sure at this point that all // other activities are made visible. if (DEBUG_SWITCH) Slog.i(TAG, "Config didn't destroy " + starting + ", ensuring others are correct."); - ensureActivitiesVisibleLocked(starting, changes); + mMainStack.ensureActivitiesVisibleLocked(starting, changes); } } @@ -14033,160 +10778,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return kept; } - - private final boolean relaunchActivityLocked(ActivityRecord r, - int changes, boolean andResume) { - List<ResultInfo> results = null; - List<Intent> newIntents = null; - if (andResume) { - results = r.results; - newIntents = r.newIntents; - } - if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r - + " with results=" + results + " newIntents=" + newIntents - + " andResume=" + andResume); - EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY - : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r), - r.task.taskId, r.shortComponentName); - - r.startFreezingScreenLocked(r.app, 0); - - try { - if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r); - r.app.thread.scheduleRelaunchActivity(r, results, newIntents, - changes, !andResume, mConfiguration); - // Note: don't need to call pauseIfSleepingLocked() here, because - // 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 (andResume) { - r.results = null; - r.newIntents = null; - reportResumedActivityLocked(r); - } - - return true; - } - - /** - * Make sure the given activity matches the current configuration. Returns - * false if the activity had to be destroyed. Returns true if the - * configuration is the same, or the activity will remain running as-is - * for whatever reason. Ensures the HistoryRecord is updated with the - * correct configuration and all other bookkeeping is handled. - */ - private final boolean ensureActivityConfigurationLocked(ActivityRecord r, - int globalChanges) { - if (mConfigWillChange) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Skipping config check (will change): " + r); - return true; - } - - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Ensuring correct configuration: " + r); - - // Short circuit: if the two configurations are the exact same - // object (the common case), then there is nothing to do. - Configuration newConfig = mConfiguration; - if (r.configuration == newConfig) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Configuration unchanged in " + r); - return true; - } - - // We don't worry about activities that are finishing. - if (r.finishing) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Configuration doesn't matter in finishing " + r); - r.stopFreezingScreenLocked(false); - return true; - } - - // Okay we now are going to make this activity have the new config. - // But then we need to figure out how it needs to deal with that. - Configuration oldConfig = r.configuration; - r.configuration = newConfig; - - // If the activity isn't currently running, just leave the new - // configuration and it will pick that up next time it starts. - if (r.app == null || r.app.thread == null) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Configuration doesn't matter not running " + r); - r.stopFreezingScreenLocked(false); - return true; - } - - // If the activity isn't persistent, there is a chance we will - // need to restart it. - if (!r.persistent) { - - // Figure out what has changed between the two configurations. - int changes = oldConfig.diff(newConfig); - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) { - Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x" - + Integer.toHexString(changes) + ", handles=0x" - + Integer.toHexString(r.info.configChanges) - + ", newConfig=" + newConfig); - } - if ((changes&(~r.info.configChanges)) != 0) { - // Aha, the activity isn't handling the change, so DIE DIE DIE. - r.configChangeFlags |= changes; - r.startFreezingScreenLocked(r.app, globalChanges); - if (r.app == null || r.app.thread == null) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Switch is destroying non-running " + r); - destroyActivityLocked(r, true); - } 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); - r.configDestroy = true; - return true; - } else if (r.state == ActivityState.RESUMED) { - // Try to optimize this case: the configuration is changing - // and we need to restart the top, resumed activity. - // Instead of doing the normal handshaking, just say - // "restart!". - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, - "Switch is restarting 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); - relaunchActivityLocked(r, r.configChangeFlags, false); - r.configChangeFlags = 0; - } - - // All done... tell the caller we weren't able to keep this - // activity around. - return false; - } - } - - // Default case: the activity can handle this new configuration, so - // hand it over. Note that we don't need to give it the new - // configuration, since we always send configuration changes to all - // process when they happen so it can just use whatever configuration - // it last got. - if (r.app != null && r.app.thread != null) { - try { - if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r); - r.app.thread.scheduleActivityConfigurationChanged(r); - } catch (RemoteException e) { - // If process died, whatever. - } - } - r.stopFreezingScreenLocked(false); - - return true; - } /** * Save the locale. You must be inside a synchronized (this) block. @@ -14542,8 +11133,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final boolean canGcNowLocked() { return mParallelBroadcasts.size() == 0 && mOrderedBroadcasts.size() == 0 - && (mSleeping || (mResumedActivity != null && - mResumedActivity.idle)); + && (mSleeping || (mMainStack.mResumedActivity != null && + mMainStack.mResumedActivity.idle)); } /** @@ -14714,11 +11305,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private final ActivityRecord resumedAppLocked() { - ActivityRecord resumedActivity = mResumedActivity; + ActivityRecord resumedActivity = mMainStack.mResumedActivity; if (resumedActivity == null || resumedActivity.app == null) { - resumedActivity = mPausingActivity; + resumedActivity = mMainStack.mPausingActivity; if (resumedActivity == null || resumedActivity.app == null) { - resumedActivity = topRunningActivityLocked(null); + resumedActivity = mMainStack.topRunningActivityLocked(null); } } return resumedActivity; @@ -14746,7 +11337,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return res; } - private final boolean updateOomAdjLocked() { + final boolean updateOomAdjLocked() { boolean didOomAdj = true; final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; @@ -14810,7 +11401,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return ENFORCE_PROCESS_LIMIT || mProcessLimit > 0 ? false : didOomAdj; } - private final void trimApplications() { + final void trimApplications() { synchronized (this) { int i; @@ -14942,7 +11533,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (j=0; j<NUMA; j++) { ActivityRecord r = app.activities.get(j); if (!r.finishing) { - destroyActivityLocked(r, false); + r.stack.destroyActivityLocked(r, false); } r.resultTo = null; } @@ -14979,25 +11570,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Finally, if there are too many activities now running, try to // finish as many as we can to get back down to the limit. for ( i=0; - i<mLRUActivities.size() - && mLRUActivities.size() > curMaxActivities; + i<mMainStack.mLRUActivities.size() + && mMainStack.mLRUActivities.size() > curMaxActivities; i++) { final ActivityRecord r - = (ActivityRecord)mLRUActivities.get(i); + = (ActivityRecord)mMainStack.mLRUActivities.get(i); // We can finish this one if we have its icicle saved and // it is not persistent. if ((r.haveState || !r.stateNotNeeded) && !r.visible && r.stopped && !r.persistent && !r.finishing) { - final int origSize = mLRUActivities.size(); - destroyActivityLocked(r, true); + final int origSize = mMainStack.mLRUActivities.size(); + r.stack.destroyActivityLocked(r, true); // This will remove it from the LRU list, so keep // our index at the same value. Note that this check to // see if the size changes is just paranoia -- if // something unexpected happens, we don't want to end up // in an infinite loop. - if (origSize > mLRUActivities.size()) { + if (origSize > mMainStack.mLRUActivities.size()) { i--; } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 22ac58d..79756a7 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -17,7 +17,7 @@ package com.android.server.am; import com.android.server.AttributeCache; -import com.android.server.am.ActivityManagerService.ActivityState; +import com.android.server.am.ActivityStack.ActivityState; import android.app.Activity; import android.content.ComponentName; @@ -32,6 +32,7 @@ import android.os.Process; import android.os.SystemClock; import android.util.EventLog; import android.util.Log; +import android.util.Slog; import android.view.IApplicationToken; import java.io.PrintWriter; @@ -44,6 +45,7 @@ import java.util.HashSet; */ class ActivityRecord extends IApplicationToken.Stub { final ActivityManagerService service; // owner + final ActivityStack stack; // owner final ActivityInfo info; // all about me final int launchedFromUid; // always the uid who started the activity. final Intent intent; // the original intent that generated us @@ -80,7 +82,7 @@ class ActivityRecord extends IApplicationToken.Stub { ProcessRecord app; // if non-null, hosting application Bitmap thumbnail; // icon representation of paused screen CharSequence description; // textual description of paused screen - ActivityManagerService.ActivityState state; // current state we are in + ActivityState state; // current state we are in Bundle icicle; // last saved activity state boolean frontOfTask; // is this the root activity of its task? boolean launchFailed; // set if a launched failed, to abort on 2nd try @@ -175,12 +177,13 @@ class ActivityRecord extends IApplicationToken.Stub { } } - ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, + ActivityRecord(ActivityManagerService _service, ActivityStack _stack, ProcessRecord _caller, int _launchedFromUid, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified) { service = _service; + stack = _stack; info = aInfo; launchedFromUid = _launchedFromUid; intent = _intent; @@ -191,7 +194,7 @@ class ActivityRecord extends IApplicationToken.Stub { resultTo = _resultTo; resultWho = _resultWho; requestCode = _reqCode; - state = ActivityManagerService.ActivityState.INITIALIZING; + state = ActivityState.INITIALIZING; frontOfTask = false; launchFailed = false; haveState = false; @@ -332,6 +335,52 @@ class ActivityRecord extends IApplicationToken.Stub { } newIntents.add(intent); } + + /** + * Deliver a new Intent to an existing activity, so that its onNewIntent() + * method will be called at the proper time. + */ + final void deliverNewIntentLocked(Intent intent) { + boolean sent = false; + if (state == ActivityState.RESUMED + && app != null && app.thread != null) { + try { + ArrayList<Intent> ar = new ArrayList<Intent>(); + ar.add(new Intent(intent)); + app.thread.scheduleNewIntent(ar, this); + sent = true; + } catch (Exception e) { + Slog.w(ActivityManagerService.TAG, + "Exception thrown sending new intent to " + this, e); + } + } + if (!sent) { + addNewIntentLocked(new Intent(intent)); + } + } + + void removeUriPermissionsLocked() { + if (readUriPermissions != null) { + for (UriPermission perm : readUriPermissions) { + perm.readActivities.remove(this); + if (perm.readActivities.size() == 0 && (perm.globalModeFlags + &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { + perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; + service.removeUriPermissionIfNeededLocked(perm); + } + } + } + if (writeUriPermissions != null) { + for (UriPermission perm : writeUriPermissions) { + perm.writeActivities.remove(this); + if (perm.writeActivities.size() == 0 && (perm.globalModeFlags + &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { + perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + service.removeUriPermissionIfNeededLocked(perm); + } + } + } + } void pauseKeyDispatchingLocked() { if (!keysPaused) { @@ -375,8 +424,8 @@ class ActivityRecord extends IApplicationToken.Stub { if (startTime != 0) { final long curTime = SystemClock.uptimeMillis(); final long thisTime = curTime - startTime; - final long totalTime = service.mInitialStartTime != 0 - ? (curTime - service.mInitialStartTime) : thisTime; + final long totalTime = stack.mInitialStartTime != 0 + ? (curTime - stack.mInitialStartTime) : thisTime; if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) { EventLog.writeEvent(EventLogTags.ACTIVITY_LAUNCH_TIME, System.identityHashCode(this), shortComponentName, @@ -392,14 +441,14 @@ class ActivityRecord extends IApplicationToken.Stub { sb.append(" ms)"); Log.i(ActivityManagerService.TAG, sb.toString()); } - service.reportActivityLaunchedLocked(false, this, thisTime, totalTime); + stack.reportActivityLaunchedLocked(false, this, thisTime, totalTime); if (totalTime > 0) { service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime); } startTime = 0; - service.mInitialStartTime = 0; + stack.mInitialStartTime = 0; } - service.reportActivityVisibleLocked(this); + stack.reportActivityVisibleLocked(this); if (ActivityManagerService.DEBUG_SWITCH) Log.v( ActivityManagerService.TAG, "windowsVisible(): " + this); if (!nowVisible) { @@ -408,27 +457,27 @@ class ActivityRecord extends IApplicationToken.Stub { // Instead of doing the full stop routine here, let's just // hide any activities we now can, and let them stop when // the normal idle happens. - service.processStoppingActivitiesLocked(false); + stack.processStoppingActivitiesLocked(false); } else { // If this activity was already idle, then we now need to // make sure we perform the full stop of any activities // that are waiting to do so. This is because we won't // do that while they are still waiting for this one to // become visible. - final int N = service.mWaitingVisibleActivities.size(); + final int N = stack.mWaitingVisibleActivities.size(); if (N > 0) { for (int i=0; i<N; i++) { ActivityRecord r = (ActivityRecord) - service.mWaitingVisibleActivities.get(i); + stack.mWaitingVisibleActivities.get(i); r.waitingVisible = false; if (ActivityManagerService.DEBUG_SWITCH) Log.v( ActivityManagerService.TAG, "Was waiting for visible: " + r); } - service.mWaitingVisibleActivities.clear(); + stack.mWaitingVisibleActivities.clear(); Message msg = Message.obtain(); - msg.what = ActivityManagerService.IDLE_NOW_MSG; - service.mHandler.sendMessage(msg); + msg.what = ActivityStack.IDLE_NOW_MSG; + stack.mHandler.sendMessage(msg); } } service.scheduleAppGcsLocked(); @@ -449,9 +498,9 @@ class ActivityRecord extends IApplicationToken.Stub { ActivityRecord r = this; if (r.waitingVisible) { // Hmmm, who might we be waiting for? - r = service.mResumedActivity; + r = stack.mResumedActivity; if (r == null) { - r = service.mPausingActivity; + r = stack.mPausingActivity; } // Both of those null? Fall back to 'this' again if (r == null) { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java new file mode 100644 index 0000000..de7b15c --- /dev/null +++ b/services/java/com/android/server/am/ActivityStack.java @@ -0,0 +1,3521 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import com.android.internal.app.HeavyWeightSwitcherActivity; +import com.android.internal.os.BatteryStatsImpl; +import com.android.server.am.ActivityManagerService.PendingActivityLaunch; + +import android.app.Activity; +import android.app.AppGlobals; +import android.app.IActivityManager; +import static android.app.IActivityManager.START_CLASS_NOT_FOUND; +import static android.app.IActivityManager.START_DELIVERED_TO_TOP; +import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; +import static android.app.IActivityManager.START_INTENT_NOT_RESOLVED; +import static android.app.IActivityManager.START_PERMISSION_DENIED; +import static android.app.IActivityManager.START_RETURN_INTENT_TO_CALLER; +import static android.app.IActivityManager.START_SUCCESS; +import static android.app.IActivityManager.START_SWITCHES_CANCELED; +import static android.app.IActivityManager.START_TASK_TO_FRONT; +import android.app.IApplicationThread; +import android.app.PendingIntent; +import android.app.ResultInfo; +import android.app.IActivityManager.WaitResult; +import android.content.ComponentName; +import android.content.Context; +import android.content.IIntentSender; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.view.WindowManagerPolicy; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * State and management of a single stack of activities. + */ +public class ActivityStack { + static final String TAG = ActivityManagerService.TAG; + static final boolean localLOGV = ActivityManagerService.localLOGV; + static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH; + static final boolean DEBUG_PAUSE = ActivityManagerService.DEBUG_PAUSE; + static final boolean DEBUG_VISBILITY = ActivityManagerService.DEBUG_VISBILITY; + static final boolean DEBUG_USER_LEAVING = ActivityManagerService.DEBUG_USER_LEAVING; + static final boolean DEBUG_TRANSITION = ActivityManagerService.DEBUG_TRANSITION; + static final boolean DEBUG_RESULTS = ActivityManagerService.DEBUG_RESULTS; + static final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION; + static final boolean DEBUG_TASKS = ActivityManagerService.DEBUG_TASKS; + + static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS; + + // How long we wait until giving up on the last activity telling us it + // is idle. + static final int IDLE_TIMEOUT = 10*1000; + + // How long we wait until giving up on the last activity to pause. This + // is short because it directly impacts the responsiveness of starting the + // next activity. + static final int PAUSE_TIMEOUT = 500; + + // 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 an activity telling us it has + // finished destroying itself. + static final int DESTROY_TIMEOUT = 10*1000; + + // How long until we reset a task when the user returns to it. Currently + // 30 minutes. + static final long ACTIVITY_INACTIVE_RESET_TIME = 1000*60*30; + + // Set to false to disable the preview that is shown while a new activity + // is being started. + static final boolean SHOW_APP_STARTING_PREVIEW = true; + + enum ActivityState { + INITIALIZING, + RESUMED, + PAUSING, + PAUSED, + STOPPING, + STOPPED, + FINISHING, + DESTROYING, + DESTROYED + } + + final ActivityManagerService mService; + final boolean mMainStack; + + final Context mContext; + + /** + * The back history of all previous (and possibly still + * running) activities. It contains HistoryRecord objects. + */ + final ArrayList mHistory = new ArrayList(); + + /** + * List of running activities, sorted by recent usage. + * The first entry in the list is the least recently used. + * It contains HistoryRecord objects. + */ + final ArrayList mLRUActivities = new ArrayList(); + + /** + * List of activities that are waiting for a new activity + * to become visible before completing whatever operation they are + * supposed to do. + */ + final ArrayList<ActivityRecord> mWaitingVisibleActivities + = new ArrayList<ActivityRecord>(); + + /** + * List of activities that are ready to be stopped, but waiting + * for the next activity to settle down before doing so. It contains + * HistoryRecord objects. + */ + final ArrayList<ActivityRecord> mStoppingActivities + = new ArrayList<ActivityRecord>(); + + /** + * Animations that for the current transition have requested not to + * be considered for the transition animation. + */ + final ArrayList<ActivityRecord> mNoAnimActivities + = new ArrayList<ActivityRecord>(); + + /** + * List of activities that are ready to be finished, but waiting + * for the previous activity to settle down before doing so. It contains + * HistoryRecord objects. + */ + final ArrayList<ActivityRecord> mFinishingActivities + = new ArrayList<ActivityRecord>(); + + /** + * List of people waiting to find out about the next launched activity. + */ + final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched + = new ArrayList<IActivityManager.WaitResult>(); + + /** + * List of people waiting to find out about the next visible activity. + */ + final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible + = new ArrayList<IActivityManager.WaitResult>(); + + /** + * Set when the system is going to sleep, until we have + * successfully paused the current activity and released our wake lock. + * At that point the system is allowed to actually sleep. + */ + final PowerManager.WakeLock mGoingToSleep; + + /** + * We don't want to allow the device to go to sleep while in the process + * of launching an activity. This is primarily to allow alarm intent + * receivers to launch an activity and get that to run before the device + * goes back to sleep. + */ + final PowerManager.WakeLock mLaunchingActivity; + + /** + * When we are in the process of pausing an activity, before starting the + * next one, this variable holds the activity that is currently being paused. + */ + ActivityRecord mPausingActivity = null; + + /** + * This is the last activity that we put into the paused state. This is + * used to determine if we need to do an activity transition while sleeping, + * when we normally hold the top activity paused. + */ + ActivityRecord mLastPausedActivity = null; + + /** + * Current activity that is resumed, or null if there is none. + */ + ActivityRecord mResumedActivity = null; + + /** + * Set when we know we are going to be calling updateConfiguration() + * soon, so want to skip intermediate config checks. + */ + boolean mConfigWillChange; + + /** + * Set to indicate whether to issue an onUserLeaving callback when a + * newly launched activity is being brought in front of us. + */ + boolean mUserLeaving = false; + + long mInitialStartTime = 0; + + static final int PAUSE_TIMEOUT_MSG = 9; + static final int IDLE_TIMEOUT_MSG = 10; + static final int IDLE_NOW_MSG = 11; + static final int LAUNCH_TIMEOUT_MSG = 16; + static final int DESTROY_TIMEOUT_MSG = 17; + static final int RESUME_TOP_ACTIVITY_MSG = 19; + + final Handler mHandler = new Handler() { + //public Handler() { + // if (localLOGV) Slog.v(TAG, "Handler started!"); + //} + + public void handleMessage(Message msg) { + switch (msg.what) { + case PAUSE_TIMEOUT_MSG: { + IBinder token = (IBinder)msg.obj; + // We don't at this point know if the activity is fullscreen, + // so we need to be conservative and assume it isn't. + Slog.w(TAG, "Activity pause timeout for " + token); + activityPaused(token, null, true); + } break; + case IDLE_TIMEOUT_MSG: { + if (mService.mDidDexOpt) { + mService.mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT); + return; + } + // We don't at this point know if the activity is fullscreen, + // so we need to be conservative and assume it isn't. + IBinder token = (IBinder)msg.obj; + Slog.w(TAG, "Activity idle timeout for " + token); + activityIdleInternal(token, true, null); + } break; + case DESTROY_TIMEOUT_MSG: { + IBinder token = (IBinder)msg.obj; + // We don't at this point know if the activity is fullscreen, + // so we need to be conservative and assume it isn't. + Slog.w(TAG, "Activity destroy timeout for " + token); + activityDestroyed(token); + } break; + case IDLE_NOW_MSG: { + IBinder token = (IBinder)msg.obj; + activityIdleInternal(token, false, null); + } break; + case LAUNCH_TIMEOUT_MSG: { + if (mService.mDidDexOpt) { + mService.mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT); + return; + } + synchronized (mService) { + if (mLaunchingActivity.isHeld()) { + Slog.w(TAG, "Launch timeout has expired, giving up wake lock!"); + mLaunchingActivity.release(); + } + } + } break; + case RESUME_TOP_ACTIVITY_MSG: { + synchronized (mService) { + resumeTopActivityLocked(null); + } + } break; + } + } + }; + + ActivityStack(ActivityManagerService service, Context context, boolean mainStack) { + mService = service; + mContext = context; + mMainStack = mainStack; + PowerManager pm = + (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep"); + mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch"); + mLaunchingActivity.setReferenceCounted(false); + } + + final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { + int i = mHistory.size()-1; + while (i >= 0) { + ActivityRecord r = (ActivityRecord)mHistory.get(i); + if (!r.finishing && r != notTop) { + return r; + } + i--; + } + return null; + } + + final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) { + int i = mHistory.size()-1; + while (i >= 0) { + ActivityRecord r = (ActivityRecord)mHistory.get(i); + if (!r.finishing && !r.delayedResume && r != notTop) { + return r; + } + i--; + } + return null; + } + + /** + * This is a simplified version of topRunningActivityLocked that provides a number of + * optional skip-over modes. It is intended for use with the ActivityController hook only. + * + * @param token If non-null, any history records matching this token will be skipped. + * @param taskId If non-zero, we'll attempt to skip over records with the same task ID. + * + * @return Returns the HistoryRecord of the next activity on the stack. + */ + final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) { + int i = mHistory.size()-1; + while (i >= 0) { + ActivityRecord r = (ActivityRecord)mHistory.get(i); + // Note: the taskId check depends on real taskId fields being non-zero + if (!r.finishing && (token != r) && (taskId != r.task.taskId)) { + return r; + } + i--; + } + return null; + } + + final int indexOfTokenLocked(IBinder token) { + int count = mHistory.size(); + + // convert the token to an entry in the history. + int index = -1; + for (int i=count-1; i>=0; i--) { + Object o = mHistory.get(i); + if (o == token) { + index = i; + break; + } + } + + return index; + } + + private final boolean updateLRUListLocked(ActivityRecord r) { + final boolean hadit = mLRUActivities.remove(r); + mLRUActivities.add(r); + return hadit; + } + + /** + * Returns the top activity in any existing task matching the given + * Intent. Returns null if no such task is found. + */ + private ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) { + ComponentName cls = intent.getComponent(); + if (info.targetActivity != null) { + cls = new ComponentName(info.packageName, info.targetActivity); + } + + TaskRecord cp = null; + + final int N = mHistory.size(); + for (int i=(N-1); i>=0; i--) { + ActivityRecord r = (ActivityRecord)mHistory.get(i); + if (!r.finishing && r.task != cp + && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) { + cp = r.task; + //Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString() + // + "/aff=" + r.task.affinity + " to new cls=" + // + intent.getComponent().flattenToShortString() + "/aff=" + taskAffinity); + if (r.task.affinity != null) { + if (r.task.affinity.equals(info.taskAffinity)) { + //Slog.i(TAG, "Found matching affinity!"); + return r; + } + } else if (r.task.intent != null + && r.task.intent.getComponent().equals(cls)) { + //Slog.i(TAG, "Found matching class!"); + //dump(); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + return r; + } else if (r.task.affinityIntent != null + && r.task.affinityIntent.getComponent().equals(cls)) { + //Slog.i(TAG, "Found matching class!"); + //dump(); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + return r; + } + } + } + + return null; + } + + /** + * Returns the first activity (starting from the top of the stack) that + * is the same as the given activity. Returns null if no such activity + * is found. + */ + private ActivityRecord findActivityLocked(Intent intent, ActivityInfo info) { + ComponentName cls = intent.getComponent(); + if (info.targetActivity != null) { + cls = new ComponentName(info.packageName, info.targetActivity); + } + + final int N = mHistory.size(); + for (int i=(N-1); i>=0; i--) { + ActivityRecord r = (ActivityRecord)mHistory.get(i); + if (!r.finishing) { + if (r.intent.getComponent().equals(cls)) { + //Slog.i(TAG, "Found matching class!"); + //dump(); + //Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent); + return r; + } + } + } + + return null; + } + + final boolean realStartActivityLocked(ActivityRecord r, + ProcessRecord app, boolean andResume, boolean checkConfig) + throws RemoteException { + + r.startFreezingScreenLocked(app, 0); + mService.mWindowManager.setAppVisibility(r, true); + + // Have the window manager re-evaluate the orientation of + // the screen based on the new activity order. Note that + // as a result of this, it can call back into the activity + // manager with a new orientation. We don't care about that, + // because the activity is not currently running so we are + // just restarting it anyway. + if (checkConfig) { + Configuration config = mService.mWindowManager.updateOrientationFromAppTokens( + mService.mConfiguration, + r.mayFreezeScreenLocked(app) ? r : null); + mService.updateConfigurationLocked(config, r); + } + + r.app = app; + + if (localLOGV) Slog.v(TAG, "Launching: " + r); + + int idx = app.activities.indexOf(r); + if (idx < 0) { + app.activities.add(r); + } + mService.updateLruProcessLocked(app, true, true); + + try { + if (app.thread == null) { + throw new RemoteException(); + } + List<ResultInfo> results = null; + List<Intent> newIntents = null; + if (andResume) { + results = r.results; + newIntents = r.newIntents; + } + if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r + + " icicle=" + r.icicle + + " with results=" + results + " newIntents=" + newIntents + + " andResume=" + andResume); + if (andResume) { + EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, + System.identityHashCode(r), + r.task.taskId, r.shortComponentName); + } + if (r.isHomeActivity) { + mService.mHomeProcess = app; + } + mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName()); + app.thread.scheduleLaunchActivity(new Intent(r.intent), r, + System.identityHashCode(r), + r.info, r.icicle, results, newIntents, !andResume, + mService.isNextTransitionForward()); + + if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) { + // This may be a heavy-weight process! Note that the package + // manager will ensure that only activity can run in the main + // process of the .apk, which is the only thing that will be + // considered heavy-weight. + if (app.processName.equals(app.info.packageName)) { + if (mService.mHeavyWeightProcess != null + && mService.mHeavyWeightProcess != app) { + Log.w(TAG, "Starting new heavy weight process " + app + + " when already running " + + mService.mHeavyWeightProcess); + } + mService.mHeavyWeightProcess = app; + Message msg = mService.mHandler.obtainMessage( + ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG); + msg.obj = r; + mService.mHandler.sendMessage(msg); + } + } + + } catch (RemoteException e) { + if (r.launchFailed) { + // This is the second time we failed -- finish activity + // and give up. + Slog.e(TAG, "Second failure launching " + + r.intent.getComponent().flattenToShortString() + + ", giving up", e); + mService.appDiedLocked(app, app.pid, app.thread); + requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, + "2nd-crash"); + return false; + } + + // This is the first time we failed -- restart process and + // retry. + app.activities.remove(r); + throw e; + } + + r.launchFailed = false; + if (updateLRUListLocked(r)) { + Slog.w(TAG, "Activity " + r + + " being launched, but already in LRU list"); + } + + if (andResume) { + // As part of the process of launching, ActivityThread also performs + // a resume. + r.state = ActivityState.RESUMED; + r.icicle = null; + r.haveState = false; + r.stopped = false; + mResumedActivity = r; + r.task.touchActiveTime(); + completeResumeLocked(r); + pauseIfSleepingLocked(); + } else { + // This activity is not starting in the resumed state... which + // should look like we asked it to pause+stop (but remain visible), + // and it has done so and reported back the current icicle and + // other state. + r.state = ActivityState.STOPPED; + r.stopped = true; + } + + // Launch the new version setup screen if needed. We do this -after- + // launching the initial activity (that is, home), so that it can have + // a chance to initialize itself while in the background, making the + // switch back to it faster and look better. + if (mMainStack) { + mService.startSetupActivityLocked(); + } + + return true; + } + + private final void startSpecificActivityLocked(ActivityRecord r, + boolean andResume, boolean checkConfig) { + // Is this activity's application already running? + ProcessRecord app = mService.getProcessRecordLocked(r.processName, + r.info.applicationInfo.uid); + + if (r.startTime == 0) { + r.startTime = SystemClock.uptimeMillis(); + if (mInitialStartTime == 0) { + mInitialStartTime = r.startTime; + } + } else if (mInitialStartTime == 0) { + mInitialStartTime = SystemClock.uptimeMillis(); + } + + if (app != null && app.thread != null) { + try { + realStartActivityLocked(r, app, andResume, checkConfig); + return; + } catch (RemoteException e) { + Slog.w(TAG, "Exception when starting activity " + + r.intent.getComponent().flattenToShortString(), e); + } + + // If a dead object exception was thrown -- fall through to + // restart the application. + } + + mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0, + "activity", r.intent.getComponent(), false); + } + + void pauseIfSleepingLocked() { + if (mService.mSleeping || mService.mShuttingDown) { + if (!mGoingToSleep.isHeld()) { + mGoingToSleep.acquire(); + if (mLaunchingActivity.isHeld()) { + mLaunchingActivity.release(); + mService.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); + } + } + + // If we are not currently pausing an activity, get the current + // one to pause. If we are pausing one, we will just let that stuff + // run and release the wake lock when all done. + if (mPausingActivity == null) { + if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause..."); + if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false"); + startPausingLocked(false, true); + } + } + } + + private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) { + if (mPausingActivity != null) { + RuntimeException e = new RuntimeException(); + Slog.e(TAG, "Trying to pause when pause is already pending for " + + mPausingActivity, e); + } + ActivityRecord prev = mResumedActivity; + if (prev == null) { + RuntimeException e = new RuntimeException(); + Slog.e(TAG, "Trying to pause when nothing is resumed", e); + resumeTopActivityLocked(null); + return; + } + if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev); + mResumedActivity = null; + mPausingActivity = prev; + mLastPausedActivity = prev; + prev.state = ActivityState.PAUSING; + prev.task.touchActiveTime(); + + mService.updateCpuStats(); + + if (prev.app != null && prev.app.thread != null) { + if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev); + try { + EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY, + System.identityHashCode(prev), + prev.shortComponentName); + prev.app.thread.schedulePauseActivity(prev, prev.finishing, userLeaving, + prev.configChangeFlags); + if (mMainStack) { + mService.updateUsageStats(prev, false); + } + } catch (Exception e) { + // Ignore exception, if process died other code will cleanup. + Slog.w(TAG, "Exception thrown during pause", e); + mPausingActivity = null; + mLastPausedActivity = null; + } + } else { + mPausingActivity = null; + mLastPausedActivity = null; + } + + // If we are not going to sleep, we want to ensure the device is + // awake until the next activity is started. + if (!mService.mSleeping && !mService.mShuttingDown) { + mLaunchingActivity.acquire(); + if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) { + // To be safe, don't allow the wake lock to be held for too long. + Message msg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); + mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT); + } + } + + + if (mPausingActivity != null) { + // Have the window manager pause its key dispatching until the new + // activity has started. If we're pausing the activity just because + // the screen is being turned off and the UI is sleeping, don't interrupt + // key dispatch; the same activity will pick it up again on wakeup. + if (!uiSleeping) { + prev.pauseKeyDispatchingLocked(); + } else { + if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off"); + } + + // Schedule a pause timeout in case the app doesn't respond. + // We don't give it much time because this directly impacts the + // responsiveness seen by the user. + Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG); + msg.obj = prev; + mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT); + if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete..."); + } else { + // This activity failed to schedule the + // pause, so just treat it as being paused now. + if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next."); + resumeTopActivityLocked(null); + } + } + + final void activityPaused(IBinder token, Bundle icicle, boolean timeout) { + if (DEBUG_PAUSE) Slog.v( + TAG, "Activity paused: token=" + token + ", icicle=" + icicle + + ", timeout=" + timeout); + + ActivityRecord r = null; + + synchronized (mService) { + int index = indexOfTokenLocked(token); + if (index >= 0) { + r = (ActivityRecord)mHistory.get(index); + if (!timeout) { + r.icicle = icicle; + r.haveState = true; + } + mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); + if (mPausingActivity == r) { + r.state = ActivityState.PAUSED; + completePauseLocked(); + } else { + EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, + System.identityHashCode(r), r.shortComponentName, + mPausingActivity != null + ? mPausingActivity.shortComponentName : "(none)"); + } + } + } + } + + private final void completePauseLocked() { + ActivityRecord prev = mPausingActivity; + if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev); + + if (prev != null) { + if (prev.finishing) { + if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev); + prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE); + } else if (prev.app != null) { + if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev); + if (prev.waitingVisible) { + prev.waitingVisible = false; + mWaitingVisibleActivities.remove(prev); + if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v( + TAG, "Complete pause, no longer waiting: " + prev); + } + if (prev.configDestroy) { + // The previous is being paused because the configuration + // is changing, which means it is actually stopping... + // To juggle the fact that we are also starting a new + // instance right now, we need to first completely stop + // the current instance before starting the new one. + if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); + destroyActivityLocked(prev, true); + } else { + mStoppingActivities.add(prev); + if (mStoppingActivities.size() > 3) { + // If we already have a few activities waiting to stop, + // then give up on things going idle and start clearing + // them out. + if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle"); + Message msg = Message.obtain(); + msg.what = IDLE_NOW_MSG; + mHandler.sendMessage(msg); + } + } + } else { + if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev); + prev = null; + } + mPausingActivity = null; + } + + if (!mService.mSleeping && !mService.mShuttingDown) { + resumeTopActivityLocked(prev); + } else { + if (mGoingToSleep.isHeld()) { + mGoingToSleep.release(); + } + if (mService.mShuttingDown) { + mService.notifyAll(); + } + } + + if (prev != null) { + prev.resumeKeyDispatchingLocked(); + } + + if (prev.app != null && prev.cpuTimeAtResume > 0 + && mService.mBatteryStatsService.isOnBattery()) { + long diff = 0; + synchronized (mService.mProcessStatsThread) { + diff = mService.mProcessStats.getCpuTimeForPid(prev.app.pid) + - prev.cpuTimeAtResume; + } + if (diff > 0) { + BatteryStatsImpl bsi = mService.mBatteryStatsService.getActiveStatistics(); + synchronized (bsi) { + BatteryStatsImpl.Uid.Proc ps = + bsi.getProcessStatsLocked(prev.info.applicationInfo.uid, + prev.info.packageName); + if (ps != null) { + ps.addForegroundTimeLocked(diff); + } + } + } + } + prev.cpuTimeAtResume = 0; // reset it + } + + /** + * Once we know that we have asked an application to put an activity in + * the resumed state (either by launching it or explicitly telling it), + * this function updates the rest of our state to match that fact. + */ + private final void completeResumeLocked(ActivityRecord next) { + next.idle = false; + next.results = null; + next.newIntents = null; + + // schedule an idle timeout in case the app doesn't do it for us. + Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); + msg.obj = next; + mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT); + + if (false) { + // The activity was never told to pause, so just keep + // things going as-is. To maintain our own state, + // we need to emulate it coming back and saying it is + // idle. + msg = mHandler.obtainMessage(IDLE_NOW_MSG); + msg.obj = next; + mHandler.sendMessage(msg); + } + + if (mMainStack) { + mService.reportResumedActivityLocked(next); + } + + next.thumbnail = null; + if (mMainStack) { + mService.setFocusedActivityLocked(next); + } + next.resumeKeyDispatchingLocked(); + ensureActivitiesVisibleLocked(null, 0); + mService.mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); + + // Mark the point when the activity is resuming + // TODO: To be more accurate, the mark should be before the onCreate, + // not after the onResume. But for subsequent starts, onResume is fine. + if (next.app != null) { + synchronized (mService.mProcessStatsThread) { + next.cpuTimeAtResume = mService.mProcessStats.getCpuTimeForPid(next.app.pid); + } + } else { + next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process + } + } + + /** + * Make sure that all activities that need to be visible (that is, they + * currently can be seen by the user) actually are. + */ + final void ensureActivitiesVisibleLocked(ActivityRecord top, + ActivityRecord starting, String onlyThisProcess, int configChanges) { + if (DEBUG_VISBILITY) Slog.v( + TAG, "ensureActivitiesVisible behind " + top + + " configChanges=0x" + Integer.toHexString(configChanges)); + + // If the top activity is not fullscreen, then we need to + // make sure any activities under it are now visible. + final int count = mHistory.size(); + int i = count-1; + while (mHistory.get(i) != top) { + i--; + } + ActivityRecord r; + boolean behindFullscreen = false; + for (; i>=0; i--) { + r = (ActivityRecord)mHistory.get(i); + if (DEBUG_VISBILITY) Slog.v( + TAG, "Make visible? " + r + " finishing=" + r.finishing + + " state=" + r.state); + if (r.finishing) { + continue; + } + + final boolean doThisProcess = onlyThisProcess == null + || onlyThisProcess.equals(r.processName); + + // First: if this is not the current activity being started, make + // sure it matches the current configuration. + if (r != starting && doThisProcess) { + ensureActivityConfigurationLocked(r, 0); + } + + if (r.app == null || r.app.thread == null) { + if (onlyThisProcess == null + || onlyThisProcess.equals(r.processName)) { + // This activity needs to be visible, but isn't even + // running... get it started, but don't resume it + // at this point. + if (DEBUG_VISBILITY) Slog.v( + TAG, "Start and freeze screen for " + r); + if (r != starting) { + r.startFreezingScreenLocked(r.app, configChanges); + } + if (!r.visible) { + if (DEBUG_VISBILITY) Slog.v( + TAG, "Starting and making visible: " + r); + mService.mWindowManager.setAppVisibility(r, true); + } + if (r != starting) { + startSpecificActivityLocked(r, false, false); + } + } + + } else if (r.visible) { + // If this activity is already visible, then there is nothing + // else to do here. + if (DEBUG_VISBILITY) Slog.v( + TAG, "Skipping: already visible at " + r); + r.stopFreezingScreenLocked(false); + + } else if (onlyThisProcess == null) { + // This activity is not currently visible, but is running. + // Tell it to become visible. + r.visible = true; + if (r.state != ActivityState.RESUMED && r != starting) { + // If this activity is paused, tell it + // to now show its window. + if (DEBUG_VISBILITY) Slog.v( + TAG, "Making visible and scheduling visibility: " + r); + try { + mService.mWindowManager.setAppVisibility(r, true); + r.app.thread.scheduleWindowVisibility(r, true); + r.stopFreezingScreenLocked(false); + } catch (Exception e) { + // Just skip on any failure; we'll make it + // visible when it next restarts. + Slog.w(TAG, "Exception thrown making visibile: " + + r.intent.getComponent(), e); + } + } + } + + // Aggregate current change flags. + configChanges |= r.configChangeFlags; + + if (r.fullscreen) { + // At this point, nothing else needs to be shown + if (DEBUG_VISBILITY) Slog.v( + TAG, "Stopping: fullscreen at " + r); + behindFullscreen = true; + i--; + break; + } + } + + // Now for any activities that aren't visible to the user, make + // sure they no longer are keeping the screen frozen. + while (i >= 0) { + r = (ActivityRecord)mHistory.get(i); + if (DEBUG_VISBILITY) Slog.v( + TAG, "Make invisible? " + r + " finishing=" + r.finishing + + " state=" + r.state + + " behindFullscreen=" + behindFullscreen); + if (!r.finishing) { + if (behindFullscreen) { + if (r.visible) { + if (DEBUG_VISBILITY) Slog.v( + TAG, "Making invisible: " + r); + r.visible = false; + try { + mService.mWindowManager.setAppVisibility(r, false); + if ((r.state == ActivityState.STOPPING + || r.state == ActivityState.STOPPED) + && r.app != null && r.app.thread != null) { + if (DEBUG_VISBILITY) Slog.v( + TAG, "Scheduling invisibility: " + r); + r.app.thread.scheduleWindowVisibility(r, false); + } + } catch (Exception e) { + // Just skip on any failure; we'll make it + // visible when it next restarts. + Slog.w(TAG, "Exception thrown making hidden: " + + r.intent.getComponent(), e); + } + } else { + if (DEBUG_VISBILITY) Slog.v( + TAG, "Already invisible: " + r); + } + } else if (r.fullscreen) { + if (DEBUG_VISBILITY) Slog.v( + TAG, "Now behindFullscreen: " + r); + behindFullscreen = true; + } + } + i--; + } + } + + /** + * Version of ensureActivitiesVisible that can easily be called anywhere. + */ + final void ensureActivitiesVisibleLocked(ActivityRecord starting, + int configChanges) { + ActivityRecord r = topRunningActivityLocked(null); + if (r != null) { + ensureActivitiesVisibleLocked(r, starting, null, configChanges); + } + } + + /** + * Ensure that the top activity in the stack is resumed. + * + * @param prev The previously resumed activity, for when in the process + * of pausing; can be null to call from elsewhere. + * + * @return Returns true if something is being resumed, or false if + * nothing happened. + */ + final boolean resumeTopActivityLocked(ActivityRecord prev) { + // Find the first activity that is not finishing. + ActivityRecord next = topRunningActivityLocked(null); + + // Remember how we'll process this pause/resume situation, and ensure + // that the state is reset however we wind up proceeding. + final boolean userLeaving = mUserLeaving; + mUserLeaving = false; + + if (next == null) { + // There are no more activities! Let's just start up the + // Launcher... + if (mMainStack) { + return mService.startHomeActivityLocked(); + } + } + + next.delayedResume = false; + + // If the top activity is the resumed one, nothing to do. + if (mResumedActivity == next && next.state == ActivityState.RESUMED) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + mService.mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); + return false; + } + + // If we are sleeping, and there is no resumed activity, and the top + // activity is paused, well that is the state we want. + if ((mService.mSleeping || mService.mShuttingDown) + && mLastPausedActivity == next && next.state == ActivityState.PAUSED) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + mService.mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); + return false; + } + + // The activity may be waiting for stop, but that is no longer + // appropriate for it. + mStoppingActivities.remove(next); + mWaitingVisibleActivities.remove(next); + + if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next); + + // If we are currently pausing an activity, then don't do anything + // until that is done. + if (mPausingActivity != null) { + if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity); + return false; + } + + // We need to start pausing the current activity so the top one + // can be resumed... + if (mResumedActivity != null) { + if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing"); + startPausingLocked(userLeaving, false); + return true; + } + + if (prev != null && prev != next) { + if (!prev.waitingVisible && next != null && !next.nowVisible) { + prev.waitingVisible = true; + mWaitingVisibleActivities.add(prev); + if (DEBUG_SWITCH) Slog.v( + TAG, "Resuming top, waiting visible to hide: " + prev); + } else { + // The next activity is already visible, so hide the previous + // activity's windows right now so we can show the new one ASAP. + // We only do this if the previous is finishing, which should mean + // it is on top of the one being resumed so hiding it quickly + // is good. Otherwise, we want to do the normal route of allowing + // the resumed activity to be shown so we can decide if the + // previous should actually be hidden depending on whether the + // new one is found to be full-screen or not. + if (prev.finishing) { + mService.mWindowManager.setAppVisibility(prev, false); + if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: " + + prev + ", waitingVisible=" + + (prev != null ? prev.waitingVisible : null) + + ", nowVisible=" + next.nowVisible); + } else { + if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: " + + prev + ", waitingVisible=" + + (prev != null ? prev.waitingVisible : null) + + ", nowVisible=" + next.nowVisible); + } + } + } + + // We are starting up the next activity, so tell the window manager + // that the previous one will be hidden soon. This way it can know + // to ignore it when computing the desired screen orientation. + if (prev != null) { + if (prev.finishing) { + if (DEBUG_TRANSITION) Slog.v(TAG, + "Prepare close transition: prev=" + prev); + if (mNoAnimActivities.contains(prev)) { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + } else { + mService.mWindowManager.prepareAppTransition(prev.task == next.task + ? WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE + : WindowManagerPolicy.TRANSIT_TASK_CLOSE); + } + mService.mWindowManager.setAppWillBeHidden(prev); + mService.mWindowManager.setAppVisibility(prev, false); + } else { + if (DEBUG_TRANSITION) Slog.v(TAG, + "Prepare open transition: prev=" + prev); + if (mNoAnimActivities.contains(next)) { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + } else { + mService.mWindowManager.prepareAppTransition(prev.task == next.task + ? WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN + : WindowManagerPolicy.TRANSIT_TASK_OPEN); + } + } + if (false) { + mService.mWindowManager.setAppWillBeHidden(prev); + mService.mWindowManager.setAppVisibility(prev, false); + } + } else if (mHistory.size() > 1) { + if (DEBUG_TRANSITION) Slog.v(TAG, + "Prepare open transition: no previous"); + if (mNoAnimActivities.contains(next)) { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + } else { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); + } + } + + if (next.app != null && next.app.thread != null) { + if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next); + + // This activity is now becoming visible. + mService.mWindowManager.setAppVisibility(next, true); + + ActivityRecord lastResumedActivity = mResumedActivity; + ActivityState lastState = next.state; + + mService.updateCpuStats(); + + next.state = ActivityState.RESUMED; + mResumedActivity = next; + next.task.touchActiveTime(); + mService.updateLruProcessLocked(next.app, true, true); + updateLRUListLocked(next); + + // Have the window manager re-evaluate the orientation of + // the screen based on the new activity order. + boolean updated = false; + if (mMainStack) { + synchronized (mService) { + Configuration config = mService.mWindowManager.updateOrientationFromAppTokens( + mService.mConfiguration, + next.mayFreezeScreenLocked(next.app) ? next : null); + if (config != null) { + next.frozenBeforeDestroy = true; + } + updated = mService.updateConfigurationLocked(config, next); + } + } + if (!updated) { + // The configuration update wasn't able to keep the existing + // instance of the activity, and instead started a new one. + // We should be all done, but let's just make sure our activity + // is still at the top and schedule another run if something + // weird happened. + ActivityRecord nextNext = topRunningActivityLocked(null); + if (DEBUG_SWITCH) Slog.i(TAG, + "Activity config changed during resume: " + next + + ", new next: " + nextNext); + if (nextNext != next) { + // Do over! + mHandler.sendEmptyMessage(RESUME_TOP_ACTIVITY_MSG); + } + if (mMainStack) { + mService.setFocusedActivityLocked(next); + } + ensureActivitiesVisibleLocked(null, 0); + mService.mWindowManager.executeAppTransition(); + mNoAnimActivities.clear(); + return true; + } + + try { + // Deliver all pending results. + ArrayList a = next.results; + if (a != null) { + final int N = a.size(); + if (!next.finishing && N > 0) { + if (DEBUG_RESULTS) Slog.v( + TAG, "Delivering results to " + next + + ": " + a); + next.app.thread.scheduleSendResult(next, a); + } + } + + if (next.newIntents != null) { + next.app.thread.scheduleNewIntent(next.newIntents, next); + } + + EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, + System.identityHashCode(next), + next.task.taskId, next.shortComponentName); + + next.app.thread.scheduleResumeActivity(next, + mService.isNextTransitionForward()); + + pauseIfSleepingLocked(); + + } catch (Exception e) { + // Whoops, need to restart this activity! + next.state = lastState; + mResumedActivity = lastResumedActivity; + Slog.i(TAG, "Restarting because process died: " + next); + if (!next.hasBeenLaunched) { + next.hasBeenLaunched = true; + } else { + if (SHOW_APP_STARTING_PREVIEW && mMainStack) { + mService.mWindowManager.setAppStartingWindow( + next, next.packageName, next.theme, + next.nonLocalizedLabel, + next.labelRes, next.icon, null, true); + } + } + startSpecificActivityLocked(next, true, false); + return true; + } + + // From this point on, if something goes wrong there is no way + // to recover the activity. + try { + next.visible = true; + completeResumeLocked(next); + } catch (Exception e) { + // If any exception gets thrown, toss away this + // activity and try the next one. + Slog.w(TAG, "Exception thrown during resume of " + next, e); + requestFinishActivityLocked(next, Activity.RESULT_CANCELED, null, + "resume-exception"); + return true; + } + + // Didn't need to use the icicle, and it is now out of date. + next.icicle = null; + next.haveState = false; + next.stopped = false; + + } else { + // Whoops, need to restart this activity! + if (!next.hasBeenLaunched) { + next.hasBeenLaunched = true; + } else { + if (SHOW_APP_STARTING_PREVIEW) { + mService.mWindowManager.setAppStartingWindow( + next, next.packageName, next.theme, + next.nonLocalizedLabel, + next.labelRes, next.icon, null, true); + } + if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next); + } + startSpecificActivityLocked(next, true, true); + } + + return true; + } + + private final void startActivityLocked(ActivityRecord r, boolean newTask, + boolean doResume) { + final int NH = mHistory.size(); + + int addPos = -1; + + if (!newTask) { + // If starting in an existing task, find where that is... + ActivityRecord next = null; + boolean startIt = true; + for (int i = NH-1; i >= 0; i--) { + ActivityRecord p = (ActivityRecord)mHistory.get(i); + if (p.finishing) { + continue; + } + if (p.task == r.task) { + // Here it is! Now, if this is not yet visible to the + // user, then just add it without starting; it will + // get started when the user navigates back to it. + addPos = i+1; + if (!startIt) { + mHistory.add(addPos, r); + r.inHistory = true; + r.task.numActivities++; + mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, + r.info.screenOrientation, r.fullscreen); + if (VALIDATE_TOKENS) { + mService.mWindowManager.validateAppTokens(mHistory); + } + return; + } + break; + } + if (p.fullscreen) { + startIt = false; + } + next = p; + } + } + + // Place a new activity at top of stack, so it is next to interact + // with the user. + if (addPos < 0) { + addPos = mHistory.size(); + } + + // If we are not placing the new activity frontmost, we do not want + // to deliver the onUserLeaving callback to the actual frontmost + // activity + if (addPos < NH) { + mUserLeaving = false; + if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() behind front, mUserLeaving=false"); + } + + // Slot the activity into the history stack and proceed + mHistory.add(addPos, r); + r.inHistory = true; + r.frontOfTask = newTask; + r.task.numActivities++; + if (NH > 0) { + // We want to show the starting preview window if we are + // switching to a new task, or the next activity's process is + // not currently running. + boolean showStartingIcon = newTask; + ProcessRecord proc = r.app; + if (proc == null) { + proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid); + } + if (proc == null || proc.thread == null) { + showStartingIcon = true; + } + if (DEBUG_TRANSITION) Slog.v(TAG, + "Prepare open transition: starting " + r); + if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + mNoAnimActivities.add(r); + } else if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { + mService.mWindowManager.prepareAppTransition( + WindowManagerPolicy.TRANSIT_TASK_OPEN); + mNoAnimActivities.remove(r); + } else { + mService.mWindowManager.prepareAppTransition(newTask + ? WindowManagerPolicy.TRANSIT_TASK_OPEN + : WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN); + mNoAnimActivities.remove(r); + } + mService.mWindowManager.addAppToken( + addPos, r, r.task.taskId, r.info.screenOrientation, r.fullscreen); + boolean doShow = true; + if (newTask) { + // Even though this activity is starting fresh, we still need + // to reset it to make sure we apply affinities to move any + // existing activities from other tasks in to it. + // If the caller has requested that the target task be + // reset, then do so. + if ((r.intent.getFlags() + &Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { + resetTaskIfNeededLocked(r, r); + doShow = topRunningNonDelayedActivityLocked(null) == r; + } + } + if (SHOW_APP_STARTING_PREVIEW && doShow) { + // Figure out if we are transitioning from another activity that is + // "has the same starting icon" as the next one. This allows the + // window manager to keep the previous window it had previously + // created, if it still had one. + ActivityRecord prev = mResumedActivity; + if (prev != null) { + // We don't want to reuse the previous starting preview if: + // (1) The current activity is in a different task. + if (prev.task != r.task) prev = null; + // (2) The current activity is already displayed. + else if (prev.nowVisible) prev = null; + } + mService.mWindowManager.setAppStartingWindow( + r, r.packageName, r.theme, r.nonLocalizedLabel, + r.labelRes, r.icon, prev, showStartingIcon); + } + } else { + // If this is the first activity, don't do any fancy animations, + // because there is nothing for it to animate on top of. + mService.mWindowManager.addAppToken(addPos, r, r.task.taskId, + r.info.screenOrientation, r.fullscreen); + } + if (VALIDATE_TOKENS) { + mService.mWindowManager.validateAppTokens(mHistory); + } + + if (doResume) { + resumeTopActivityLocked(null); + } + } + + /** + * Perform a reset of the given task, if needed as part of launching it. + * Returns the new HistoryRecord at the top of the task. + */ + private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop, + ActivityRecord newActivity) { + boolean forceReset = (newActivity.info.flags + &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; + if (taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { + if ((newActivity.info.flags + &ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { + forceReset = true; + } + } + + final TaskRecord task = taskTop.task; + + // We are going to move through the history list so that we can look + // at each activity 'target' with 'below' either the interesting + // activity immediately below it in the stack or null. + ActivityRecord target = null; + int targetI = 0; + int taskTopI = -1; + int replyChainEnd = -1; + int lastReparentPos = -1; + for (int i=mHistory.size()-1; i>=-1; i--) { + ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null; + + if (below != null && below.finishing) { + continue; + } + if (target == null) { + target = below; + targetI = i; + // If we were in the middle of a reply chain before this + // task, it doesn't appear like the root of the chain wants + // anything interesting, so drop it. + replyChainEnd = -1; + continue; + } + + final int flags = target.info.flags; + + final boolean finishOnTaskLaunch = + (flags&ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; + final boolean allowTaskReparenting = + (flags&ActivityInfo.FLAG_ALLOW_TASK_REPARENTING) != 0; + + if (target.task == task) { + // We are inside of the task being reset... we'll either + // finish this activity, push it out for another task, + // or leave it as-is. We only do this + // for activities that are not the root of the task (since + // if we finish the root, we may no longer have the task!). + if (taskTopI < 0) { + taskTopI = targetI; + } + if (below != null && below.task == task) { + final boolean clearWhenTaskReset = + (target.intent.getFlags() + &Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0; + if (!finishOnTaskLaunch && !clearWhenTaskReset && target.resultTo != null) { + // If this activity is sending a reply to a previous + // activity, we can't do anything with it now until + // we reach the start of the reply chain. + // XXX note that we are assuming the result is always + // to the previous activity, which is almost always + // the case but we really shouldn't count on. + if (replyChainEnd < 0) { + replyChainEnd = targetI; + } + } else if (!finishOnTaskLaunch && !clearWhenTaskReset && allowTaskReparenting + && target.taskAffinity != null + && !target.taskAffinity.equals(task.affinity)) { + // If this activity has an affinity for another + // task, then we need to move it out of here. We will + // move it as far out of the way as possible, to the + // bottom of the activity stack. This also keeps it + // correctly ordered with any activities we previously + // moved. + ActivityRecord p = (ActivityRecord)mHistory.get(0); + if (target.taskAffinity != null + && target.taskAffinity.equals(p.task.affinity)) { + // If the activity currently at the bottom has the + // same task affinity as the one we are moving, + // then merge it into the same task. + target.task = p.task; + if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + + " out to bottom task " + p.task); + } else { + mService.mCurTask++; + if (mService.mCurTask <= 0) { + mService.mCurTask = 1; + } + target.task = new TaskRecord(mService.mCurTask, target.info, null, + (target.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); + target.task.affinityIntent = target.intent; + if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target + + " out to new task " + target.task); + } + mService.mWindowManager.setAppGroupId(target, task.taskId); + if (replyChainEnd < 0) { + replyChainEnd = targetI; + } + int dstPos = 0; + for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { + p = (ActivityRecord)mHistory.get(srcPos); + if (p.finishing) { + continue; + } + if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p + + " out to target's task " + target.task); + task.numActivities--; + p.task = target.task; + target.task.numActivities++; + mHistory.remove(srcPos); + mHistory.add(dstPos, p); + mService.mWindowManager.moveAppToken(dstPos, p); + mService.mWindowManager.setAppGroupId(p, p.task.taskId); + dstPos++; + if (VALIDATE_TOKENS) { + mService.mWindowManager.validateAppTokens(mHistory); + } + i++; + } + if (taskTop == p) { + taskTop = below; + } + if (taskTopI == replyChainEnd) { + taskTopI = -1; + } + replyChainEnd = -1; + if (mMainStack) { + mService.addRecentTaskLocked(target.task); + } + } else if (forceReset || finishOnTaskLaunch + || clearWhenTaskReset) { + // If the activity should just be removed -- either + // because it asks for it, or the task should be + // cleared -- then finish it and anything that is + // part of its reply chain. + if (clearWhenTaskReset) { + // In this case, we want to finish this activity + // and everything above it, so be sneaky and pretend + // like these are all in the reply chain. + replyChainEnd = targetI+1; + while (replyChainEnd < mHistory.size() && + ((ActivityRecord)mHistory.get( + replyChainEnd)).task == task) { + replyChainEnd++; + } + replyChainEnd--; + } else if (replyChainEnd < 0) { + replyChainEnd = targetI; + } + ActivityRecord p = null; + for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { + p = (ActivityRecord)mHistory.get(srcPos); + if (p.finishing) { + continue; + } + if (finishActivityLocked(p, srcPos, + Activity.RESULT_CANCELED, null, "reset")) { + replyChainEnd--; + srcPos--; + } + } + if (taskTop == p) { + taskTop = below; + } + if (taskTopI == replyChainEnd) { + taskTopI = -1; + } + replyChainEnd = -1; + } else { + // If we were in the middle of a chain, well the + // activity that started it all doesn't want anything + // special, so leave it all as-is. + replyChainEnd = -1; + } + } else { + // Reached the bottom of the task -- any reply chain + // should be left as-is. + replyChainEnd = -1; + } + + } else if (target.resultTo != null) { + // If this activity is sending a reply to a previous + // activity, we can't do anything with it now until + // we reach the start of the reply chain. + // XXX note that we are assuming the result is always + // to the previous activity, which is almost always + // the case but we really shouldn't count on. + if (replyChainEnd < 0) { + replyChainEnd = targetI; + } + + } else if (taskTopI >= 0 && allowTaskReparenting + && task.affinity != null + && task.affinity.equals(target.taskAffinity)) { + // We are inside of another task... if this activity has + // an affinity for our task, then either remove it if we are + // clearing or move it over to our task. Note that + // we currently punt on the case where we are resetting a + // task that is not at the top but who has activities above + // with an affinity to it... this is really not a normal + // case, and we will need to later pull that task to the front + // and usually at that point we will do the reset and pick + // up those remaining activities. (This only happens if + // someone starts an activity in a new task from an activity + // in a task that is not currently on top.) + if (forceReset || finishOnTaskLaunch) { + if (replyChainEnd < 0) { + replyChainEnd = targetI; + } + ActivityRecord p = null; + for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { + p = (ActivityRecord)mHistory.get(srcPos); + if (p.finishing) { + continue; + } + if (finishActivityLocked(p, srcPos, + Activity.RESULT_CANCELED, null, "reset")) { + taskTopI--; + lastReparentPos--; + replyChainEnd--; + srcPos--; + } + } + replyChainEnd = -1; + } else { + if (replyChainEnd < 0) { + replyChainEnd = targetI; + } + for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) { + ActivityRecord p = (ActivityRecord)mHistory.get(srcPos); + if (p.finishing) { + continue; + } + if (lastReparentPos < 0) { + lastReparentPos = taskTopI; + taskTop = p; + } else { + lastReparentPos--; + } + mHistory.remove(srcPos); + p.task.numActivities--; + p.task = task; + mHistory.add(lastReparentPos, p); + if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + + " in to resetting task " + task); + task.numActivities++; + mService.mWindowManager.moveAppToken(lastReparentPos, p); + mService.mWindowManager.setAppGroupId(p, p.task.taskId); + if (VALIDATE_TOKENS) { + mService.mWindowManager.validateAppTokens(mHistory); + } + } + replyChainEnd = -1; + + // Now we've moved it in to place... but what if this is + // a singleTop activity and we have put it on top of another + // instance of the same activity? Then we drop the instance + // below so it remains singleTop. + if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) { + for (int j=lastReparentPos-1; j>=0; j--) { + ActivityRecord p = (ActivityRecord)mHistory.get(j); + if (p.finishing) { + continue; + } + if (p.intent.getComponent().equals(target.intent.getComponent())) { + if (finishActivityLocked(p, j, + Activity.RESULT_CANCELED, null, "replace")) { + taskTopI--; + lastReparentPos--; + } + } + } + } + } + } + + target = below; + targetI = i; + } + + return taskTop; + } + + /** + * Perform clear operation as requested by + * {@link Intent#FLAG_ACTIVITY_CLEAR_TOP}: search from the top of the + * stack to the given task, then look for + * an instance of that activity in the stack and, if found, finish all + * activities on top of it and return the instance. + * + * @param newR Description of the new activity being started. + * @return Returns the old activity that should be continue to be used, + * or null if none was found. + */ + private final ActivityRecord performClearTaskLocked(int taskId, + ActivityRecord newR, int launchFlags, boolean doClear) { + int i = mHistory.size(); + + // First find the requested task. + while (i > 0) { + i--; + ActivityRecord r = (ActivityRecord)mHistory.get(i); + if (r.task.taskId == taskId) { + i++; + break; + } + } + + // Now clear it. + while (i > 0) { + i--; + ActivityRecord r = (ActivityRecord)mHistory.get(i); + if (r.finishing) { + continue; + } + if (r.task.taskId != taskId) { + return null; + } + if (r.realActivity.equals(newR.realActivity)) { + // Here it is! Now finish everything in front... + ActivityRecord ret = r; + if (doClear) { + while (i < (mHistory.size()-1)) { + i++; + r = (ActivityRecord)mHistory.get(i); + if (r.finishing) { + continue; + } + if (finishActivityLocked(r, i, Activity.RESULT_CANCELED, + null, "clear")) { + i--; + } + } + } + + // Finally, if this is a normal launch mode (that is, not + // expecting onNewIntent()), then we will finish the current + // instance of the activity so a new fresh one can be started. + if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE + && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) { + if (!ret.finishing) { + int index = indexOfTokenLocked(ret); + if (index >= 0) { + finishActivityLocked(ret, index, Activity.RESULT_CANCELED, + null, "clear"); + } + return null; + } + } + + return ret; + } + } + + return null; + } + + /** + * Find the activity in the history stack within the given task. Returns + * the index within the history at which it's found, or < 0 if not found. + */ + private final int findActivityInHistoryLocked(ActivityRecord r, int task) { + int i = mHistory.size(); + while (i > 0) { + i--; + ActivityRecord candidate = (ActivityRecord)mHistory.get(i); + if (candidate.task.taskId != task) { + break; + } + if (candidate.realActivity.equals(r.realActivity)) { + return i; + } + } + + return -1; + } + + /** + * Reorder the history stack so that the activity at the given index is + * brought to the front. + */ + private final ActivityRecord moveActivityToFrontLocked(int where) { + ActivityRecord newTop = (ActivityRecord)mHistory.remove(where); + int top = mHistory.size(); + ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1); + mHistory.add(top, newTop); + oldTop.frontOfTask = false; + newTop.frontOfTask = true; + return newTop; + } + + final int startActivityLocked(IApplicationThread caller, + Intent intent, String resolvedType, + Uri[] grantedUriPermissions, + int grantedMode, ActivityInfo aInfo, IBinder resultTo, + String resultWho, int requestCode, + int callingPid, int callingUid, boolean onlyIfNeeded, + boolean componentSpecified) { + Slog.i(TAG, "Starting activity: " + intent); + + ActivityRecord sourceRecord = null; + ActivityRecord resultRecord = null; + if (resultTo != null) { + int index = indexOfTokenLocked(resultTo); + if (DEBUG_RESULTS) Slog.v( + TAG, "Sending result to " + resultTo + " (index " + index + ")"); + if (index >= 0) { + sourceRecord = (ActivityRecord)mHistory.get(index); + if (requestCode >= 0 && !sourceRecord.finishing) { + resultRecord = sourceRecord; + } + } + } + + int launchFlags = intent.getFlags(); + + if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 + && sourceRecord != null) { + // Transfer the result target from the source activity to the new + // one being started, including any failures. + if (requestCode >= 0) { + return START_FORWARD_AND_REQUEST_CONFLICT; + } + resultRecord = sourceRecord.resultTo; + resultWho = sourceRecord.resultWho; + requestCode = sourceRecord.requestCode; + sourceRecord.resultTo = null; + if (resultRecord != null) { + resultRecord.removeResultsLocked( + sourceRecord, resultWho, requestCode); + } + } + + int err = START_SUCCESS; + + if (intent.getComponent() == null) { + // We couldn't find a class that can handle the given Intent. + // That's the end of that! + err = START_INTENT_NOT_RESOLVED; + } + + if (err == START_SUCCESS && aInfo == null) { + // We couldn't find the specific class specified in the Intent. + // Also the end of the line. + err = START_CLASS_NOT_FOUND; + } + + ProcessRecord callerApp = null; + if (err == START_SUCCESS && caller != null) { + callerApp = mService.getRecordForAppLocked(caller); + if (callerApp != null) { + callingPid = callerApp.pid; + callingUid = callerApp.info.uid; + } else { + Slog.w(TAG, "Unable to find app for caller " + caller + + " (pid=" + callingPid + ") when starting: " + + intent.toString()); + err = START_PERMISSION_DENIED; + } + } + + if (err != START_SUCCESS) { + if (resultRecord != null) { + sendActivityResultLocked(-1, + resultRecord, resultWho, requestCode, + Activity.RESULT_CANCELED, null); + } + return err; + } + + final int perm = mService.checkComponentPermission(aInfo.permission, callingPid, + callingUid, aInfo.exported ? -1 : aInfo.applicationInfo.uid); + if (perm != PackageManager.PERMISSION_GRANTED) { + if (resultRecord != null) { + sendActivityResultLocked(-1, + resultRecord, resultWho, requestCode, + Activity.RESULT_CANCELED, null); + } + String msg = "Permission Denial: starting " + intent.toString() + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ")" + + " requires " + aInfo.permission; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + + if (mMainStack) { + if (mService.mController != null) { + boolean abort = false; + try { + // The Intent we give to the watcher has the extra data + // stripped off, since it can contain private information. + Intent watchIntent = intent.cloneFilter(); + abort = !mService.mController.activityStarting(watchIntent, + aInfo.applicationInfo.packageName); + } catch (RemoteException e) { + mService.mController = null; + } + + if (abort) { + if (resultRecord != null) { + sendActivityResultLocked(-1, + resultRecord, resultWho, requestCode, + Activity.RESULT_CANCELED, null); + } + // We pretend to the caller that it was really started, but + // they will just get a cancel result. + return START_SUCCESS; + } + } + } + + ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, + intent, resolvedType, aInfo, mService.mConfiguration, + resultRecord, resultWho, requestCode, componentSpecified); + + if (mMainStack) { + if (mResumedActivity == null + || mResumedActivity.info.applicationInfo.uid != callingUid) { + if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) { + PendingActivityLaunch pal = new PendingActivityLaunch(); + pal.r = r; + pal.sourceRecord = sourceRecord; + pal.grantedUriPermissions = grantedUriPermissions; + pal.grantedMode = grantedMode; + pal.onlyIfNeeded = onlyIfNeeded; + mService.mPendingActivityLaunches.add(pal); + return START_SWITCHES_CANCELED; + } + } + + if (mService.mDidAppSwitch) { + // This is the second allowed switch since we stopped switches, + // so now just generally allow switches. Use case: user presses + // home (switches disabled, switch to home, mDidAppSwitch now true); + // user taps a home icon (coming from home so allowed, we hit here + // and now allow anyone to switch again). + mService.mAppSwitchesAllowedTime = 0; + } else { + mService.mDidAppSwitch = true; + } + + mService.doPendingActivityLaunchesLocked(false); + } + + return startActivityUncheckedLocked(r, sourceRecord, + grantedUriPermissions, grantedMode, onlyIfNeeded, true); + } + + final int startActivityUncheckedLocked(ActivityRecord r, + ActivityRecord sourceRecord, Uri[] grantedUriPermissions, + int grantedMode, boolean onlyIfNeeded, boolean doResume) { + final Intent intent = r.intent; + final int callingUid = r.launchedFromUid; + + int launchFlags = intent.getFlags(); + + // We'll invoke onUserLeaving before onPause only if the launching + // activity did not explicitly state that this is an automated launch. + mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0; + if (DEBUG_USER_LEAVING) Slog.v(TAG, + "startActivity() => mUserLeaving=" + mUserLeaving); + + // If the caller has asked not to resume at this point, we make note + // of this in the record so that we can skip it when trying to find + // the top running activity. + if (!doResume) { + r.delayedResume = true; + } + + ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) + != 0 ? r : null; + + // If the onlyIfNeeded flag is set, then we can do this if the activity + // being launched is the same as the one making the call... or, as + // a special case, if we do not know the caller then we count the + // current top activity as the caller. + if (onlyIfNeeded) { + ActivityRecord checkedCaller = sourceRecord; + if (checkedCaller == null) { + checkedCaller = topRunningNonDelayedActivityLocked(notTop); + } + if (!checkedCaller.realActivity.equals(r.realActivity)) { + // Caller is not the same as launcher, so always needed. + onlyIfNeeded = false; + } + } + + if (grantedUriPermissions != null && callingUid > 0) { + for (int i=0; i<grantedUriPermissions.length; i++) { + mService.grantUriPermissionLocked(callingUid, r.packageName, + grantedUriPermissions[i], grantedMode, r); + } + } + + mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, + intent, r); + + if (sourceRecord == null) { + // This activity is not being started from another... in this + // case we -always- start a new task. + if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { + Slog.w(TAG, "startActivity called from non-Activity context; forcing Intent.FLAG_ACTIVITY_NEW_TASK for: " + + intent); + launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; + } + } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { + // The original activity who is starting us is running as a single + // instance... this new activity it is starting must go on its + // own task. + launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; + } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE + || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { + // The activity being started is a single instance... it always + // gets launched into its own task. + launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK; + } + + if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + // For whatever reason this activity is being launched into a new + // task... yet the caller has requested a result back. Well, that + // is pretty messed up, so instead immediately send back a cancel + // and let the new task continue launched as normal without a + // dependency on its originator. + Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result."); + sendActivityResultLocked(-1, + r.resultTo, r.resultWho, r.requestCode, + Activity.RESULT_CANCELED, null); + r.resultTo = null; + } + + boolean addingToTask = false; + if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 && + (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0) + || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK + || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { + // If bring to front is requested, and no result is requested, and + // we can find a task that was started with this same + // component, then instead of launching bring that one to the front. + if (r.resultTo == null) { + // See if there is a task to bring to the front. If this is + // a SINGLE_INSTANCE activity, there can be one and only one + // instance of it in the history, and it is always in its own + // unique task, so we do a special search. + ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE + ? findTaskLocked(intent, r.info) + : findActivityLocked(intent, r.info); + if (taskTop != null) { + if (taskTop.task.intent == null) { + // This task was started because of movement of + // the activity based on affinity... now that we + // are actually launching it, we can assign the + // base intent. + taskTop.task.setIntent(intent, r.info); + } + // If the target task is not in the front, then we need + // to bring it to the front... except... well, with + // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like + // to have the same behavior as if a new instance was + // being started, which means not bringing it to the front + // if the caller is not itself in the front. + ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop); + if (curTop.task != taskTop.task) { + r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); + boolean callerAtFront = sourceRecord == null + || curTop.task == sourceRecord.task; + if (callerAtFront) { + // We really do want to push this one into the + // user's face, right now. + moveTaskToFrontLocked(taskTop.task, r); + } + } + // If the caller has requested that the target task be + // reset, then do so. + if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { + taskTop = resetTaskIfNeededLocked(taskTop, r); + } + if (onlyIfNeeded) { + // We don't need to start a new activity, and + // the client said not to do anything if that + // is the case, so this is it! And for paranoia, make + // sure we have correctly resumed the top activity. + if (doResume) { + resumeTopActivityLocked(null); + } + return START_RETURN_INTENT_TO_CALLER; + } + if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0 + || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK + || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) { + // In this situation we want to remove all activities + // from the task up to the one being started. In most + // cases this means we are resetting the task to its + // initial state. + ActivityRecord top = performClearTaskLocked( + taskTop.task.taskId, r, launchFlags, true); + if (top != null) { + if (top.frontOfTask) { + // Activity aliases may mean we use different + // intents for the top activity, so make sure + // the task now has the identity of the new + // intent. + top.task.setIntent(r.intent, r.info); + } + logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); + top.deliverNewIntentLocked(r.intent); + } else { + // A special case: we need to + // start the activity because it is not currently + // running, and the caller has asked to clear the + // current task to have this activity at the top. + addingToTask = true; + // Now pretend like this activity is being started + // by the top of its task, so it is put in the + // right place. + sourceRecord = taskTop; + } + } else if (r.realActivity.equals(taskTop.task.realActivity)) { + // In this case the top activity on the task is the + // same as the one being launched, so we take that + // as a request to bring the task to the foreground. + // If the top activity in the task is the root + // activity, deliver this new intent to it if it + // desires. + if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 + && taskTop.realActivity.equals(r.realActivity)) { + logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task); + if (taskTop.frontOfTask) { + taskTop.task.setIntent(r.intent, r.info); + } + taskTop.deliverNewIntentLocked(r.intent); + } else if (!r.intent.filterEquals(taskTop.task.intent)) { + // In this case we are launching the root activity + // of the task, but with a different intent. We + // should start a new instance on top. + addingToTask = true; + sourceRecord = taskTop; + } + } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) { + // In this case an activity is being launched in to an + // existing task, without resetting that task. This + // is typically the situation of launching an activity + // from a notification or shortcut. We want to place + // the new activity on top of the current task. + addingToTask = true; + sourceRecord = taskTop; + } else if (!taskTop.task.rootWasReset) { + // In this case we are launching in to an existing task + // that has not yet been started from its front door. + // The current task has been brought to the front. + // Ideally, we'd probably like to place this new task + // at the bottom of its stack, but that's a little hard + // to do with the current organization of the code so + // for now we'll just drop it. + taskTop.task.setIntent(r.intent, r.info); + } + if (!addingToTask) { + // We didn't do anything... but it was needed (a.k.a., client + // don't use that intent!) And for paranoia, make + // sure we have correctly resumed the top activity. + if (doResume) { + resumeTopActivityLocked(null); + } + return START_TASK_TO_FRONT; + } + } + } + } + + //String uri = r.intent.toURI(); + //Intent intent2 = new Intent(uri); + //Slog.i(TAG, "Given intent: " + r.intent); + //Slog.i(TAG, "URI is: " + uri); + //Slog.i(TAG, "To intent: " + intent2); + + if (r.packageName != null) { + // If the activity being launched is the same as the one currently + // at the top, then we need to check if it should only be launched + // once. + ActivityRecord top = topRunningNonDelayedActivityLocked(notTop); + if (top != null && r.resultTo == null) { + if (top.realActivity.equals(r.realActivity)) { + if (top.app != null && top.app.thread != null) { + if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 + || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP + || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) { + logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task); + // For paranoia, make sure we have correctly + // resumed the top activity. + if (doResume) { + resumeTopActivityLocked(null); + } + if (onlyIfNeeded) { + // We don't need to start a new activity, and + // the client said not to do anything if that + // is the case, so this is it! + return START_RETURN_INTENT_TO_CALLER; + } + top.deliverNewIntentLocked(r.intent); + return START_DELIVERED_TO_TOP; + } + } + } + } + + } else { + if (r.resultTo != null) { + sendActivityResultLocked(-1, + r.resultTo, r.resultWho, r.requestCode, + Activity.RESULT_CANCELED, null); + } + return START_CLASS_NOT_FOUND; + } + + boolean newTask = false; + + // Should this be considered a new task? + if (r.resultTo == null && !addingToTask + && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + // todo: should do better management of integers. + mService.mCurTask++; + if (mService.mCurTask <= 0) { + mService.mCurTask = 1; + } + r.task = new TaskRecord(mService.mCurTask, r.info, intent, + (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + + " in new task " + r.task); + newTask = true; + if (mMainStack) { + mService.addRecentTaskLocked(r.task); + } + + } else if (sourceRecord != null) { + if (!addingToTask && + (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) { + // In this case, we are adding the activity to an existing + // task, but the caller has asked to clear that task if the + // activity is already running. + ActivityRecord top = performClearTaskLocked( + sourceRecord.task.taskId, r, launchFlags, true); + if (top != null) { + logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); + top.deliverNewIntentLocked(r.intent); + // For paranoia, make sure we have correctly + // resumed the top activity. + if (doResume) { + resumeTopActivityLocked(null); + } + return START_DELIVERED_TO_TOP; + } + } else if (!addingToTask && + (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) { + // In this case, we are launching an activity in our own task + // that may already be running somewhere in the history, and + // we want to shuffle it to the front of the stack if so. + int where = findActivityInHistoryLocked(r, sourceRecord.task.taskId); + if (where >= 0) { + ActivityRecord top = moveActivityToFrontLocked(where); + logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task); + top.deliverNewIntentLocked(r.intent); + if (doResume) { + resumeTopActivityLocked(null); + } + return START_DELIVERED_TO_TOP; + } + } + // An existing activity is starting this new activity, so we want + // to keep the new one in the same task as the one that is starting + // it. + r.task = sourceRecord.task; + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + + " in existing task " + r.task); + + } else { + // This not being started from an existing activity, and not part + // of a new task... just put it in the top task, though these days + // this case should never happen. + final int N = mHistory.size(); + ActivityRecord prev = + N > 0 ? (ActivityRecord)mHistory.get(N-1) : null; + r.task = prev != null + ? prev.task + : new TaskRecord(mService.mCurTask, r.info, intent, + (r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0); + if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + + " in new guessed " + r.task); + } + if (newTask) { + EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId); + } + logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task); + startActivityLocked(r, newTask, doResume); + return START_SUCCESS; + } + + final int startActivityMayWait(IApplicationThread caller, + Intent intent, String resolvedType, Uri[] grantedUriPermissions, + int grantedMode, IBinder resultTo, + String resultWho, int requestCode, boolean onlyIfNeeded, + boolean debug, WaitResult outResult, Configuration config) { + // Refuse possible leaked file descriptors + if (intent != null && intent.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + + boolean componentSpecified = intent.getComponent() != null; + + // Don't modify the client's object! + intent = new Intent(intent); + + // Collect information about the target of the Intent. + ActivityInfo aInfo; + try { + ResolveInfo rInfo = + AppGlobals.getPackageManager().resolveIntent( + intent, resolvedType, + PackageManager.MATCH_DEFAULT_ONLY + | ActivityManagerService.STOCK_PM_FLAGS); + aInfo = rInfo != null ? rInfo.activityInfo : null; + } catch (RemoteException e) { + aInfo = null; + } + + if (aInfo != null) { + // Store the found target back into the intent, because now that + // we have it we never want to do this again. For example, if the + // user navigates back to this point in the history, we should + // always restart the exact same activity. + intent.setComponent(new ComponentName( + aInfo.applicationInfo.packageName, aInfo.name)); + + // Don't debug things in the system process + if (debug) { + if (!aInfo.processName.equals("system")) { + mService.setDebugApp(aInfo.processName, true, false); + } + } + } + + synchronized (mService) { + int callingPid; + int callingUid; + if (caller == null) { + callingPid = Binder.getCallingPid(); + callingUid = Binder.getCallingUid(); + } else { + callingPid = callingUid = -1; + } + + mConfigWillChange = config != null + && mService.mConfiguration.diff(config) != 0; + if (DEBUG_CONFIGURATION) Slog.v(TAG, + "Starting activity when config will change = " + mConfigWillChange); + + final long origId = Binder.clearCallingIdentity(); + + if (mMainStack && aInfo != null && + (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) { + // This may be a heavy-weight process! Check to see if we already + // have another, different heavy-weight process running. + if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) { + if (mService.mHeavyWeightProcess != null && + (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid || + !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) { + int realCallingPid = callingPid; + int realCallingUid = callingUid; + if (caller != null) { + ProcessRecord callerApp = mService.getRecordForAppLocked(caller); + if (callerApp != null) { + realCallingPid = callerApp.pid; + realCallingUid = callerApp.info.uid; + } else { + Slog.w(TAG, "Unable to find app for caller " + caller + + " (pid=" + realCallingPid + ") when starting: " + + intent.toString()); + return START_PERMISSION_DENIED; + } + } + + IIntentSender target = mService.getIntentSenderLocked( + IActivityManager.INTENT_SENDER_ACTIVITY, "android", + realCallingUid, null, null, 0, intent, + resolvedType, PendingIntent.FLAG_CANCEL_CURRENT + | PendingIntent.FLAG_ONE_SHOT); + + Intent newIntent = new Intent(); + if (requestCode >= 0) { + // Caller is requesting a result. + newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true); + } + newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, + new IntentSender(target)); + if (mService.mHeavyWeightProcess.activities.size() > 0) { + ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0); + newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, + hist.packageName); + newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, + hist.task.taskId); + } + newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP, + aInfo.packageName); + newIntent.setFlags(intent.getFlags()); + newIntent.setClassName("android", + HeavyWeightSwitcherActivity.class.getName()); + intent = newIntent; + resolvedType = null; + caller = null; + callingUid = Binder.getCallingUid(); + callingPid = Binder.getCallingPid(); + componentSpecified = true; + try { + ResolveInfo rInfo = + AppGlobals.getPackageManager().resolveIntent( + intent, null, + PackageManager.MATCH_DEFAULT_ONLY + | ActivityManagerService.STOCK_PM_FLAGS); + aInfo = rInfo != null ? rInfo.activityInfo : null; + } catch (RemoteException e) { + aInfo = null; + } + } + } + } + + int res = startActivityLocked(caller, intent, resolvedType, + grantedUriPermissions, grantedMode, aInfo, + resultTo, resultWho, requestCode, callingPid, callingUid, + onlyIfNeeded, componentSpecified); + + if (mConfigWillChange && mMainStack) { + // If the caller also wants to switch to a new configuration, + // do so now. This allows a clean switch, as we are waiting + // for the current activity to pause (so we will not destroy + // it), and have not yet started the next activity. + mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, + "updateConfiguration()"); + mConfigWillChange = false; + if (DEBUG_CONFIGURATION) Slog.v(TAG, + "Updating to new configuration after starting activity."); + mService.updateConfigurationLocked(config, null); + } + + Binder.restoreCallingIdentity(origId); + + if (outResult != null) { + outResult.result = res; + if (res == IActivityManager.START_SUCCESS) { + mWaitingActivityLaunched.add(outResult); + do { + try { + wait(); + } catch (InterruptedException e) { + } + } while (!outResult.timeout && outResult.who == null); + } else if (res == IActivityManager.START_TASK_TO_FRONT) { + ActivityRecord r = this.topRunningActivityLocked(null); + if (r.nowVisible) { + outResult.timeout = false; + outResult.who = new ComponentName(r.info.packageName, r.info.name); + outResult.totalTime = 0; + outResult.thisTime = 0; + } else { + outResult.thisTime = SystemClock.uptimeMillis(); + mWaitingActivityVisible.add(outResult); + do { + try { + wait(); + } catch (InterruptedException e) { + } + } while (!outResult.timeout && outResult.who == null); + } + } + } + + return res; + } + } + + void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, + long thisTime, long totalTime) { + for (int i=mWaitingActivityLaunched.size()-1; i>=0; i--) { + WaitResult w = mWaitingActivityLaunched.get(i); + w.timeout = timeout; + if (r != null) { + w.who = new ComponentName(r.info.packageName, r.info.name); + } + w.thisTime = thisTime; + w.totalTime = totalTime; + } + mService.notifyAll(); + } + + void reportActivityVisibleLocked(ActivityRecord r) { + for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) { + WaitResult w = mWaitingActivityVisible.get(i); + w.timeout = false; + if (r != null) { + w.who = new ComponentName(r.info.packageName, r.info.name); + } + w.totalTime = SystemClock.uptimeMillis() - w.thisTime; + w.thisTime = w.totalTime; + } + mService.notifyAll(); + } + + void sendActivityResultLocked(int callingUid, ActivityRecord r, + String resultWho, int requestCode, int resultCode, Intent data) { + + if (callingUid > 0) { + mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, + data, r); + } + + if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r + + " : who=" + resultWho + " req=" + requestCode + + " res=" + resultCode + " data=" + data); + if (mResumedActivity == r && r.app != null && r.app.thread != null) { + try { + ArrayList<ResultInfo> list = new ArrayList<ResultInfo>(); + list.add(new ResultInfo(resultWho, requestCode, + resultCode, data)); + r.app.thread.scheduleSendResult(r, list); + return; + } catch (Exception e) { + Slog.w(TAG, "Exception thrown sending result to " + r, e); + } + } + + r.addResultLocked(null, resultWho, requestCode, resultCode, data); + } + + private final void stopActivityLocked(ActivityRecord r) { + if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r); + if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 + || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) { + if (!r.finishing) { + requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null, + "no-history"); + } + } else if (r.app != null && r.app.thread != null) { + if (mMainStack) { + if (mService.mFocusedActivity == r) { + mService.setFocusedActivityLocked(topRunningActivityLocked(null)); + } + } + r.resumeKeyDispatchingLocked(); + try { + r.stopped = false; + r.state = ActivityState.STOPPING; + if (DEBUG_VISBILITY) Slog.v( + TAG, "Stopping visible=" + r.visible + " for " + r); + if (!r.visible) { + mService.mWindowManager.setAppVisibility(r, false); + } + r.app.thread.scheduleStopActivity(r, r.visible, r.configChangeFlags); + } catch (Exception e) { + // Maybe just ignore exceptions here... if the process + // has crashed, our death notification will clean things + // up. + Slog.w(TAG, "Exception thrown during pause", e); + // Just in case, assume it to be stopped. + r.stopped = true; + r.state = ActivityState.STOPPED; + if (r.configDestroy) { + destroyActivityLocked(r, true); + } + } + } + } + + final ArrayList<ActivityRecord> processStoppingActivitiesLocked( + boolean remove) { + int N = mStoppingActivities.size(); + if (N <= 0) return null; + + ArrayList<ActivityRecord> stops = null; + + final boolean nowVisible = mResumedActivity != null + && mResumedActivity.nowVisible + && !mResumedActivity.waitingVisible; + for (int i=0; i<N; i++) { + ActivityRecord s = mStoppingActivities.get(i); + if (localLOGV) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + + nowVisible + " waitingVisible=" + s.waitingVisible + + " finishing=" + s.finishing); + if (s.waitingVisible && nowVisible) { + mWaitingVisibleActivities.remove(s); + s.waitingVisible = false; + if (s.finishing) { + // If this activity is finishing, it is sitting on top of + // everyone else but we now know it is no longer needed... + // so get rid of it. Otherwise, we need to go through the + // normal flow and hide it once we determine that it is + // hidden by the activities in front of it. + if (localLOGV) Slog.v(TAG, "Before stopping, can hide: " + s); + mService.mWindowManager.setAppVisibility(s, false); + } + } + if (!s.waitingVisible && remove) { + if (localLOGV) Slog.v(TAG, "Ready to stop: " + s); + if (stops == null) { + stops = new ArrayList<ActivityRecord>(); + } + stops.add(s); + mStoppingActivities.remove(i); + N--; + i--; + } + } + + return stops; + } + + final void activityIdleInternal(IBinder token, boolean fromTimeout, + Configuration config) { + if (localLOGV) Slog.v(TAG, "Activity idle: " + token); + + ArrayList<ActivityRecord> stops = null; + ArrayList<ActivityRecord> finishes = null; + ArrayList<ActivityRecord> thumbnails = null; + int NS = 0; + int NF = 0; + int NT = 0; + IApplicationThread sendThumbnail = null; + boolean booting = false; + boolean enableScreen = false; + + synchronized (mService) { + if (token != null) { + mHandler.removeMessages(IDLE_TIMEOUT_MSG, token); + } + + // Get the activity record. + int index = indexOfTokenLocked(token); + if (index >= 0) { + ActivityRecord r = (ActivityRecord)mHistory.get(index); + + if (fromTimeout) { + reportActivityLaunchedLocked(fromTimeout, r, -1, -1); + } + + // This is a hack to semi-deal with a race condition + // in the client where it can be constructed with a + // newer configuration from when we asked it to launch. + // We'll update with whatever configuration it now says + // it used to launch. + if (config != null) { + r.configuration = config; + } + + // No longer need to keep the device awake. + if (mResumedActivity == r && mLaunchingActivity.isHeld()) { + mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); + mLaunchingActivity.release(); + } + + // We are now idle. If someone is waiting for a thumbnail from + // us, we can now deliver. + r.idle = true; + mService.scheduleAppGcsLocked(); + if (r.thumbnailNeeded && r.app != null && r.app.thread != null) { + sendThumbnail = r.app.thread; + r.thumbnailNeeded = false; + } + + // If this activity is fullscreen, set up to hide those under it. + + if (DEBUG_VISBILITY) Slog.v(TAG, "Idle activity for " + r); + ensureActivitiesVisibleLocked(null, 0); + + //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout); + if (mMainStack) { + if (!mService.mBooted && !fromTimeout) { + mService.mBooted = true; + enableScreen = true; + } + } + + } else if (fromTimeout) { + reportActivityLaunchedLocked(fromTimeout, null, -1, -1); + } + + // Atomically retrieve all of the other things to do. + stops = processStoppingActivitiesLocked(true); + NS = stops != null ? stops.size() : 0; + if ((NF=mFinishingActivities.size()) > 0) { + finishes = new ArrayList<ActivityRecord>(mFinishingActivities); + mFinishingActivities.clear(); + } + if ((NT=mService.mCancelledThumbnails.size()) > 0) { + thumbnails = new ArrayList<ActivityRecord>(mService.mCancelledThumbnails); + mService.mCancelledThumbnails.clear(); + } + + if (mMainStack) { + booting = mService.mBooting; + mService.mBooting = false; + } + } + + int i; + + // Send thumbnail if requested. + if (sendThumbnail != null) { + try { + sendThumbnail.requestThumbnail(token); + } catch (Exception e) { + Slog.w(TAG, "Exception thrown when requesting thumbnail", e); + mService.sendPendingThumbnail(null, token, null, null, true); + } + } + + // Stop any activities that are scheduled to do so but have been + // waiting for the next one to start. + for (i=0; i<NS; i++) { + ActivityRecord r = (ActivityRecord)stops.get(i); + synchronized (mService) { + if (r.finishing) { + finishCurrentActivityLocked(r, FINISH_IMMEDIATELY); + } else { + stopActivityLocked(r); + } + } + } + + // Finish any activities that are scheduled to do so but have been + // waiting for the next one to start. + for (i=0; i<NF; i++) { + ActivityRecord r = (ActivityRecord)finishes.get(i); + synchronized (mService) { + destroyActivityLocked(r, true); + } + } + + // Report back to any thumbnail receivers. + for (i=0; i<NT; i++) { + ActivityRecord r = (ActivityRecord)thumbnails.get(i); + mService.sendPendingThumbnail(r, null, null, null, true); + } + + if (booting) { + mService.finishBooting(); + } + + mService.trimApplications(); + //dump(); + //mWindowManager.dump(); + + if (enableScreen) { + mService.enableScreenAfterBoot(); + } + } + + /** + * @return Returns true if the activity is being finished, false if for + * some reason it is being left as-is. + */ + final boolean requestFinishActivityLocked(IBinder token, int resultCode, + Intent resultData, String reason) { + if (DEBUG_RESULTS) Slog.v( + TAG, "Finishing activity: token=" + token + + ", result=" + resultCode + ", data=" + resultData); + + int index = indexOfTokenLocked(token); + if (index < 0) { + return false; + } + ActivityRecord r = (ActivityRecord)mHistory.get(index); + + // Is this the last activity left? + boolean lastActivity = true; + for (int i=mHistory.size()-1; i>=0; i--) { + ActivityRecord p = (ActivityRecord)mHistory.get(i); + if (!p.finishing && p != r) { + lastActivity = false; + break; + } + } + + // If this is the last activity, but it is the home activity, then + // just don't finish it. + if (lastActivity) { + if (r.intent.hasCategory(Intent.CATEGORY_HOME)) { + return false; + } + } + + finishActivityLocked(r, index, resultCode, resultData, reason); + return true; + } + + /** + * @return Returns true if this activity has been removed from the history + * list, or false if it is still in the list and will be removed later. + */ + final boolean finishActivityLocked(ActivityRecord r, int index, + int resultCode, Intent resultData, String reason) { + if (r.finishing) { + Slog.w(TAG, "Duplicate finish request for " + r); + return false; + } + + r.finishing = true; + EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, + System.identityHashCode(r), + r.task.taskId, r.shortComponentName, reason); + r.task.numActivities--; + if (index < (mHistory.size()-1)) { + ActivityRecord next = (ActivityRecord)mHistory.get(index+1); + if (next.task == r.task) { + if (r.frontOfTask) { + // The next activity is now the front of the task. + next.frontOfTask = true; + } + if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) { + // If the caller asked that this activity (and all above it) + // be cleared when the task is reset, don't lose that information, + // but propagate it up to the next activity. + next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + } + } + } + + r.pauseKeyDispatchingLocked(); + if (mMainStack) { + if (mService.mFocusedActivity == r) { + mService.setFocusedActivityLocked(topRunningActivityLocked(null)); + } + } + + // send the result + ActivityRecord resultTo = r.resultTo; + if (resultTo != null) { + if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo + + " who=" + r.resultWho + " req=" + r.requestCode + + " res=" + resultCode + " data=" + resultData); + if (r.info.applicationInfo.uid > 0) { + mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid, + r.packageName, resultData, r); + } + resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, + resultData); + r.resultTo = null; + } + else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r); + + // Make sure this HistoryRecord is not holding on to other resources, + // because clients have remote IPC references to this object so we + // can't assume that will go away and want to avoid circular IPC refs. + r.results = null; + r.pendingResults = null; + r.newIntents = null; + r.icicle = null; + + if (mService.mPendingThumbnails.size() > 0) { + // There are clients waiting to receive thumbnails so, in case + // this is an activity that someone is waiting for, add it + // to the pending list so we can correctly update the clients. + mService.mCancelledThumbnails.add(r); + } + + if (mResumedActivity == r) { + boolean endTask = index <= 0 + || ((ActivityRecord)mHistory.get(index-1)).task != r.task; + if (DEBUG_TRANSITION) Slog.v(TAG, + "Prepare close transition: finishing " + r); + mService.mWindowManager.prepareAppTransition(endTask + ? WindowManagerPolicy.TRANSIT_TASK_CLOSE + : WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE); + + // Tell window manager to prepare for this one to be removed. + mService.mWindowManager.setAppVisibility(r, false); + + if (mPausingActivity == null) { + if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r); + if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false"); + startPausingLocked(false, false); + } + + } else if (r.state != ActivityState.PAUSING) { + // If the activity is PAUSING, we will complete the finish once + // it is done pausing; else we can just directly finish it here. + if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r); + return finishCurrentActivityLocked(r, index, + FINISH_AFTER_PAUSE) == null; + } else { + if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r); + } + + return false; + } + + private static final int FINISH_IMMEDIATELY = 0; + private static final int FINISH_AFTER_PAUSE = 1; + private static final int FINISH_AFTER_VISIBLE = 2; + + private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, + int mode) { + final int index = indexOfTokenLocked(r); + if (index < 0) { + return null; + } + + return finishCurrentActivityLocked(r, index, mode); + } + + private final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, + int index, int mode) { + // First things first: if this activity is currently visible, + // and the resumed activity is not yet visible, then hold off on + // finishing until the resumed one becomes visible. + if (mode == FINISH_AFTER_VISIBLE && r.nowVisible) { + if (!mStoppingActivities.contains(r)) { + mStoppingActivities.add(r); + if (mStoppingActivities.size() > 3) { + // If we already have a few activities waiting to stop, + // then give up on things going idle and start clearing + // them out. + Message msg = Message.obtain(); + msg.what = IDLE_NOW_MSG; + mHandler.sendMessage(msg); + } + } + r.state = ActivityState.STOPPING; + mService.updateOomAdjLocked(); + return r; + } + + // make sure the record is cleaned out of other places. + mStoppingActivities.remove(r); + mWaitingVisibleActivities.remove(r); + if (mResumedActivity == r) { + mResumedActivity = null; + } + final ActivityState prevState = r.state; + r.state = ActivityState.FINISHING; + + if (mode == FINISH_IMMEDIATELY + || prevState == ActivityState.STOPPED + || prevState == ActivityState.INITIALIZING) { + // If this activity is already stopped, we can just finish + // it right now. + return destroyActivityLocked(r, true) ? null : r; + } else { + // Need to go through the full pause cycle to get this + // activity into the stopped state and then finish it. + if (localLOGV) Slog.v(TAG, "Enqueueing pending finish: " + r); + mFinishingActivities.add(r); + resumeTopActivityLocked(null); + } + return r; + } + + /** + * Perform the common clean-up of an activity record. This is called both + * as part of destroyActivityLocked() (when destroying the client-side + * representation) and cleaning things up as a result of its hosting + * processing going away, in which case there is no remaining client-side + * state to destroy so only the cleanup here is needed. + */ + final void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices) { + if (mResumedActivity == r) { + mResumedActivity = null; + } + if (mService.mFocusedActivity == r) { + mService.mFocusedActivity = null; + } + + r.configDestroy = false; + r.frozenBeforeDestroy = false; + + // Make sure this record is no longer in the pending finishes list. + // This could happen, for example, if we are trimming activities + // down to the max limit while they are still waiting to finish. + mFinishingActivities.remove(r); + mWaitingVisibleActivities.remove(r); + + // Remove any pending results. + if (r.finishing && r.pendingResults != null) { + for (WeakReference<PendingIntentRecord> apr : r.pendingResults) { + PendingIntentRecord rec = apr.get(); + if (rec != null) { + mService.cancelIntentSenderLocked(rec, false); + } + } + r.pendingResults = null; + } + + if (cleanServices) { + cleanUpActivityServicesLocked(r); + } + + if (mService.mPendingThumbnails.size() > 0) { + // There are clients waiting to receive thumbnails so, in case + // this is an activity that someone is waiting for, add it + // to the pending list so we can correctly update the clients. + mService.mCancelledThumbnails.add(r); + } + + // Get rid of any pending idle timeouts. + mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); + mHandler.removeMessages(IDLE_TIMEOUT_MSG, r); + } + + private final void removeActivityFromHistoryLocked(ActivityRecord r) { + if (r.state != ActivityState.DESTROYED) { + mHistory.remove(r); + r.inHistory = false; + r.state = ActivityState.DESTROYED; + mService.mWindowManager.removeAppToken(r); + if (VALIDATE_TOKENS) { + mService.mWindowManager.validateAppTokens(mHistory); + } + cleanUpActivityServicesLocked(r); + r.removeUriPermissionsLocked(); + } + } + + /** + * Perform clean-up of service connections in an activity record. + */ + final void cleanUpActivityServicesLocked(ActivityRecord r) { + // Throw away any services that have been bound by this activity. + if (r.connections != null) { + Iterator<ConnectionRecord> it = r.connections.iterator(); + while (it.hasNext()) { + ConnectionRecord c = it.next(); + mService.removeConnectionLocked(c, null, r); + } + r.connections = null; + } + } + + /** + * Destroy the current CLIENT SIDE instance of an activity. This may be + * called both when actually finishing an activity, or when performing + * a configuration switch where we destroy the current client-side object + * but then create a new client-side object for this same HistoryRecord. + */ + final boolean destroyActivityLocked(ActivityRecord r, + boolean removeFromApp) { + if (DEBUG_SWITCH) Slog.v( + TAG, "Removing activity: token=" + r + + ", app=" + (r.app != null ? r.app.processName : "(null)")); + EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, + System.identityHashCode(r), + r.task.taskId, r.shortComponentName); + + boolean removedFromHistory = false; + + cleanUpActivityLocked(r, false); + + final boolean hadApp = r.app != null; + + if (hadApp) { + if (removeFromApp) { + int idx = r.app.activities.indexOf(r); + if (idx >= 0) { + r.app.activities.remove(idx); + } + if (mService.mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) { + mService.mHeavyWeightProcess = null; + mService.mHandler.sendEmptyMessage( + ActivityManagerService.CANCEL_HEAVY_NOTIFICATION_MSG); + } + if (r.persistent) { + mService.decPersistentCountLocked(r.app); + } + if (r.app.activities.size() == 0) { + // No longer have activities, so update location in + // LRU list. + mService.updateLruProcessLocked(r.app, true, false); + } + } + + boolean skipDestroy = false; + + try { + if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r); + r.app.thread.scheduleDestroyActivity(r, r.finishing, + r.configChangeFlags); + } catch (Exception e) { + // We can just ignore exceptions here... if the process + // has crashed, our death notification will clean things + // up. + //Slog.w(TAG, "Exception thrown during finish", e); + if (r.finishing) { + removeActivityFromHistoryLocked(r); + removedFromHistory = true; + skipDestroy = true; + } + } + + r.app = null; + r.nowVisible = false; + + if (r.finishing && !skipDestroy) { + r.state = ActivityState.DESTROYING; + Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG); + msg.obj = r; + mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT); + } else { + r.state = ActivityState.DESTROYED; + } + } else { + // remove this record from the history. + if (r.finishing) { + removeActivityFromHistoryLocked(r); + removedFromHistory = true; + } else { + r.state = ActivityState.DESTROYED; + } + } + + r.configChangeFlags = 0; + + if (!mLRUActivities.remove(r) && hadApp) { + Slog.w(TAG, "Activity " + r + " being finished, but not in LRU list"); + } + + return removedFromHistory; + } + + final void activityDestroyed(IBinder token) { + synchronized (mService) { + mHandler.removeMessages(DESTROY_TIMEOUT_MSG, token); + + int index = indexOfTokenLocked(token); + if (index >= 0) { + ActivityRecord r = (ActivityRecord)mHistory.get(index); + if (r.state == ActivityState.DESTROYING) { + final long origId = Binder.clearCallingIdentity(); + removeActivityFromHistoryLocked(r); + Binder.restoreCallingIdentity(origId); + } + } + } + } + + private static void removeHistoryRecordsForAppLocked(ArrayList list, ProcessRecord app) { + int i = list.size(); + if (localLOGV) Slog.v( + TAG, "Removing app " + app + " from list " + list + + " with " + i + " entries"); + while (i > 0) { + i--; + ActivityRecord r = (ActivityRecord)list.get(i); + if (localLOGV) Slog.v( + TAG, "Record #" + i + " " + r + ": app=" + r.app); + if (r.app == app) { + if (localLOGV) Slog.v(TAG, "Removing this entry!"); + list.remove(i); + } + } + } + + void removeHistoryRecordsForAppLocked(ProcessRecord app) { + removeHistoryRecordsForAppLocked(mLRUActivities, app); + removeHistoryRecordsForAppLocked(mStoppingActivities, app); + removeHistoryRecordsForAppLocked(mWaitingVisibleActivities, app); + removeHistoryRecordsForAppLocked(mFinishingActivities, app); + } + + final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) { + if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr); + + final int task = tr.taskId; + int top = mHistory.size()-1; + + if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) { + // nothing to do! + return; + } + + ArrayList moved = new ArrayList(); + + // Applying the affinities may have removed entries from the history, + // so get the size again. + top = mHistory.size()-1; + int pos = top; + + // Shift all activities with this task up to the top + // of the stack, keeping them in the same internal order. + while (pos >= 0) { + ActivityRecord r = (ActivityRecord)mHistory.get(pos); + if (localLOGV) Slog.v( + TAG, "At " + pos + " ckp " + r.task + ": " + r); + boolean first = true; + if (r.task.taskId == task) { + if (localLOGV) Slog.v(TAG, "Removing and adding at " + top); + mHistory.remove(pos); + mHistory.add(top, r); + moved.add(0, r); + top--; + if (first && mMainStack) { + mService.addRecentTaskLocked(r.task); + first = false; + } + } + pos--; + } + + if (DEBUG_TRANSITION) Slog.v(TAG, + "Prepare to front transition: task=" + tr); + if (reason != null && + (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + ActivityRecord r = topRunningActivityLocked(null); + if (r != null) { + mNoAnimActivities.add(r); + } + } else { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_FRONT); + } + + mService.mWindowManager.moveAppTokensToTop(moved); + if (VALIDATE_TOKENS) { + mService.mWindowManager.validateAppTokens(mHistory); + } + + finishTaskMoveLocked(task); + EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, task); + } + + private final void finishTaskMoveLocked(int task) { + resumeTopActivityLocked(null); + } + + /** + * Worker method for rearranging history stack. Implements the function of moving all + * activities for a specific task (gathering them if disjoint) into a single group at the + * bottom of the stack. + * + * If a watcher is installed, the action is preflighted and the watcher has an opportunity + * to premeptively cancel the move. + * + * @param task The taskId to collect and move to the bottom. + * @return Returns true if the move completed, false if not. + */ + final boolean moveTaskToBackLocked(int task, ActivityRecord reason) { + Slog.i(TAG, "moveTaskToBack: " + task); + + // If we have a watcher, preflight the move before committing to it. First check + // for *other* available tasks, but if none are available, then try again allowing the + // current task to be selected. + if (mMainStack && mService.mController != null) { + ActivityRecord next = topRunningActivityLocked(null, task); + if (next == null) { + next = topRunningActivityLocked(null, 0); + } + if (next != null) { + // ask watcher if this is allowed + boolean moveOK = true; + try { + moveOK = mService.mController.activityResuming(next.packageName); + } catch (RemoteException e) { + mService.mController = null; + } + if (!moveOK) { + return false; + } + } + } + + ArrayList moved = new ArrayList(); + + if (DEBUG_TRANSITION) Slog.v(TAG, + "Prepare to back transition: task=" + task); + + final int N = mHistory.size(); + int bottom = 0; + int pos = 0; + + // Shift all activities with this task down to the bottom + // of the stack, keeping them in the same internal order. + while (pos < N) { + ActivityRecord r = (ActivityRecord)mHistory.get(pos); + if (localLOGV) Slog.v( + TAG, "At " + pos + " ckp " + r.task + ": " + r); + if (r.task.taskId == task) { + if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1)); + mHistory.remove(pos); + mHistory.add(bottom, r); + moved.add(r); + bottom++; + } + pos++; + } + + if (reason != null && + (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_NONE); + ActivityRecord r = topRunningActivityLocked(null); + if (r != null) { + mNoAnimActivities.add(r); + } + } else { + mService.mWindowManager.prepareAppTransition(WindowManagerPolicy.TRANSIT_TASK_TO_BACK); + } + mService.mWindowManager.moveAppTokensToBottom(moved); + if (VALIDATE_TOKENS) { + mService.mWindowManager.validateAppTokens(mHistory); + } + + finishTaskMoveLocked(task); + return true; + } + + private final void logStartActivity(int tag, ActivityRecord r, + TaskRecord task) { + EventLog.writeEvent(tag, + System.identityHashCode(r), task.taskId, + r.shortComponentName, r.intent.getAction(), + r.intent.getType(), r.intent.getDataString(), + r.intent.getFlags()); + } + + /** + * Make sure the given activity matches the current configuration. Returns + * false if the activity had to be destroyed. Returns true if the + * configuration is the same, or the activity will remain running as-is + * for whatever reason. Ensures the HistoryRecord is updated with the + * correct configuration and all other bookkeeping is handled. + */ + final boolean ensureActivityConfigurationLocked(ActivityRecord r, + int globalChanges) { + if (mConfigWillChange) { + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Skipping config check (will change): " + r); + return true; + } + + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Ensuring correct configuration: " + r); + + // Short circuit: if the two configurations are the exact same + // object (the common case), then there is nothing to do. + Configuration newConfig = mService.mConfiguration; + if (r.configuration == newConfig) { + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Configuration unchanged in " + r); + return true; + } + + // We don't worry about activities that are finishing. + if (r.finishing) { + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Configuration doesn't matter in finishing " + r); + r.stopFreezingScreenLocked(false); + return true; + } + + // Okay we now are going to make this activity have the new config. + // But then we need to figure out how it needs to deal with that. + Configuration oldConfig = r.configuration; + r.configuration = newConfig; + + // If the activity isn't currently running, just leave the new + // configuration and it will pick that up next time it starts. + if (r.app == null || r.app.thread == null) { + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Configuration doesn't matter not running " + r); + r.stopFreezingScreenLocked(false); + return true; + } + + // If the activity isn't persistent, there is a chance we will + // need to restart it. + if (!r.persistent) { + + // Figure out what has changed between the two configurations. + int changes = oldConfig.diff(newConfig); + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) { + Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x" + + Integer.toHexString(changes) + ", handles=0x" + + Integer.toHexString(r.info.configChanges) + + ", newConfig=" + newConfig); + } + if ((changes&(~r.info.configChanges)) != 0) { + // Aha, the activity isn't handling the change, so DIE DIE DIE. + r.configChangeFlags |= changes; + r.startFreezingScreenLocked(r.app, globalChanges); + if (r.app == null || r.app.thread == null) { + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Switch is destroying non-running " + r); + destroyActivityLocked(r, true); + } 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); + r.configDestroy = true; + return true; + } else if (r.state == ActivityState.RESUMED) { + // Try to optimize this case: the configuration is changing + // and we need to restart the top, resumed activity. + // Instead of doing the normal handshaking, just say + // "restart!". + if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, + "Switch is restarting 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); + relaunchActivityLocked(r, r.configChangeFlags, false); + r.configChangeFlags = 0; + } + + // All done... tell the caller we weren't able to keep this + // activity around. + return false; + } + } + + // Default case: the activity can handle this new configuration, so + // hand it over. Note that we don't need to give it the new + // configuration, since we always send configuration changes to all + // process when they happen so it can just use whatever configuration + // it last got. + if (r.app != null && r.app.thread != null) { + try { + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r); + r.app.thread.scheduleActivityConfigurationChanged(r); + } catch (RemoteException e) { + // If process died, whatever. + } + } + r.stopFreezingScreenLocked(false); + + return true; + } + + private final boolean relaunchActivityLocked(ActivityRecord r, + int changes, boolean andResume) { + List<ResultInfo> results = null; + List<Intent> newIntents = null; + if (andResume) { + results = r.results; + newIntents = r.newIntents; + } + if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r + + " with results=" + results + " newIntents=" + newIntents + + " andResume=" + andResume); + EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY + : EventLogTags.AM_RELAUNCH_ACTIVITY, System.identityHashCode(r), + r.task.taskId, r.shortComponentName); + + r.startFreezingScreenLocked(r.app, 0); + + try { + if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r); + r.app.thread.scheduleRelaunchActivity(r, results, newIntents, + changes, !andResume, mService.mConfiguration); + // Note: don't need to call pauseIfSleepingLocked() here, because + // 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 (andResume) { + r.results = null; + r.newIntents = null; + if (mMainStack) { + mService.reportResumedActivityLocked(r); + } + } + + return true; + } +} diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index e7e9130..7a85eb8 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -218,7 +218,7 @@ class PendingIntentRecord extends IIntentSender.Stub { } break; case IActivityManager.INTENT_SENDER_ACTIVITY_RESULT: - owner.sendActivityResultLocked(-1, key.activity, + key.activity.stack.sendActivityResultLocked(-1, key.activity, key.who, key.requestCode, code, finalIntent); break; case IActivityManager.INTENT_SENDER_BROADCAST: |