diff options
21 files changed, 1028 insertions, 540 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b4ea49b9430d..81ba7a733225 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2849,7 +2849,7 @@ public class ActivityManagerService extends IActivityManager.Stub mTempConfig.setToDefaults(); mTempConfig.setLocales(LocaleList.getDefault()); mConfigurationSeq = mTempConfig.seq = 1; - mStackSupervisor = new ActivityStackSupervisor(this); + mStackSupervisor = createStackSupervisor(); mStackSupervisor.onConfigurationChanged(mTempConfig); mKeyguardController = mStackSupervisor.mKeyguardController; mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); @@ -2897,6 +2897,10 @@ public class ActivityManagerService extends IActivityManager.Stub Watchdog.getInstance().addThread(mHandler); } + protected ActivityStackSupervisor createStackSupervisor() { + return new ActivityStackSupervisor(this, mHandler.getLooper()); + } + public void setSystemServiceManager(SystemServiceManager mgr) { mSystemServiceManager = mgr; } @@ -3151,7 +3155,8 @@ public class ActivityManagerService extends IActivityManager.Stub * {@link ActivityStack#setResumedActivityLocked} when an activity is resumed. */ void setResumedActivityUncheckLocked(ActivityRecord r, String reason) { - if (r.task.isApplicationTask()) { + final TaskRecord task = r.getTask(); + if (task.isApplicationTask()) { if (mCurAppTimeTracker != r.appTimeTracker) { // We are switching app tracking. Complete the current one. if (mCurAppTimeTracker != null) { @@ -3174,17 +3179,18 @@ public class ActivityManagerService extends IActivityManager.Stub // TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null // TODO: Probably not, because we don't want to resume voice on switching // back to this activity - if (r.task.voiceInteractor != null) { - startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid); + if (task.voiceInteractor != null) { + startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid); } else { finishRunningVoiceLocked(); if (mLastResumedActivity != null) { final IVoiceInteractionSession session; - if (mLastResumedActivity.task != null - && mLastResumedActivity.task.voiceSession != null) { - session = mLastResumedActivity.task.voiceSession; + final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask(); + if (lastResumedActivityTask != null + && lastResumedActivityTask.voiceSession != null) { + session = lastResumedActivityTask.voiceSession; } else { session = mLastResumedActivity.voiceSession; } @@ -3317,7 +3323,7 @@ public class ActivityManagerService extends IActivityManager.Stub final void showAskCompatModeDialogLocked(ActivityRecord r) { Message msg = Message.obtain(); msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG; - msg.obj = r.task.askedCompatMode ? null : r; + msg.obj = r.getTask().askedCompatMode ? null : r; mUiHandler.sendMessage(msg); } @@ -4723,7 +4729,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (ActivityRecord.forTokenLocked(callingActivity) != activity) { throw new SecurityException("Only focused activity can call startVoiceInteraction"); } - if (mRunningVoice != null || activity.task.voiceSession != null + if (mRunningVoice != null || activity.getTask().voiceSession != null || activity.voiceSession != null) { Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction"); return; @@ -5033,7 +5039,7 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } // Keep track of the root activity of the task before we finish it - TaskRecord tr = r.task; + TaskRecord tr = r.getTask(); ActivityRecord rootR = tr.getRootActivity(); if (rootR == null) { Slog.w(TAG, "Finishing task with all activities already finished"); @@ -5170,7 +5176,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps // can finish. - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV && mStackSupervisor.isLastLockedTask(task) && task.getRootActivity() == r) { mStackSupervisor.showLockTaskToast(); @@ -7834,7 +7840,7 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } // An activity is consider to be in multi-window mode if its task isn't fullscreen. - return !r.task.mFullscreen; + return !r.getTask().mFullscreen; } } finally { Binder.restoreCallingIdentity(origId); @@ -9927,8 +9933,9 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { r.setTaskDescription(td); - r.task.updateTaskDescription(); - mTaskChangeNotificationController.notifyTaskDescriptionChanged(r.task.taskId, td); + final TaskRecord task = r.getTask(); + task.updateTaskDescription(); + mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td); } } } @@ -10378,8 +10385,8 @@ public class ActivityManagerService extends IActivityManager.Stub } if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r); - r.task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, - ANIMATE, !DEFER_RESUME, "exitFreeformMode"); + r.getTask().reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, + REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode"); } finally { Binder.restoreCallingIdentity(ident); } @@ -10760,7 +10767,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task != null) { startLockTaskModeLocked(task); } @@ -10859,7 +10866,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return; } - mStackSupervisor.showLockTaskEscapeMessageLocked(r.task); + mStackSupervisor.showLockTaskEscapeMessageLocked(r.getTask()); } } @@ -13121,9 +13128,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { return false; } - int index = r.task.mActivities.lastIndexOf(r); + final TaskRecord task = r.getTask(); + int index = task.mActivities.lastIndexOf(r); if (index > 0) { - ActivityRecord under = r.task.mActivities.get(index - 1); + ActivityRecord under = task.mActivities.get(index - 1); under.returningOptions = ActivityOptions.fromBundle(options); } final boolean translucentChanged = r.changeWindowTranslucency(false); @@ -13410,7 +13418,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (r == null) { throw new IllegalArgumentException(); } - return r.task.getTopActivity() == r; + return r.getTask().getTopActivity() == r; } } @@ -15928,8 +15936,9 @@ public class ActivityManagerService extends IActivityManager.Stub } needSep = true; synchronized (this) { - if (lastTask != r.task) { - lastTask = r.task; + final TaskRecord task = r.getTask(); + if (lastTask != task) { + lastTask = task; pw.print("TASK "); pw.print(lastTask.affinity); pw.print(" id="); pw.print(lastTask.taskId); pw.print(" userId="); pw.println(lastTask.userId); @@ -20561,8 +20570,9 @@ public class ActivityManagerService extends IActivityManager.Stub app.cached = false; app.empty = false; foregroundActivities = true; - if (r.task != null && minLayer > 0) { - final int layer = r.task.mLayerRank; + final TaskRecord task = r.getTask(); + if (task != null && minLayer > 0) { + final int layer = task.mLayerRank; if (layer >= 0 && minLayer > layer) { minLayer = layer; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index cbb51e1566c4..3a2941467cb6 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -229,7 +229,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private int theme; // resource identifier of activity's theme. private int realTheme; // actual theme resource we will use, never 0. private int windowFlags; // custom window flags for preview window. - TaskRecord task; // the task this is in. + private TaskRecord task; // the task this is in. private long createTime = System.currentTimeMillis(); long displayStartTime; // when we started launching this activity long fullyDrawnStartTime; // when we started launching this activity @@ -686,9 +686,48 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo @Override protected ConfigurationContainer getParent() { + return getTask(); + } + + TaskRecord getTask() { return task; } + /** + * Sets reference to the {@link TaskRecord} the {@link ActivityRecord} will treat as its parent. + * Note that this does not actually add the {@link ActivityRecord} as a {@link TaskRecord} + * children. However, this method will clean up references to this {@link ActivityRecord} in + * {@link ActivityStack}. + * @param task The new parent {@link TaskRecord}. + */ + void setTask(TaskRecord task) { + setTask(task, false /*reparenting*/); + } + + /** + * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}. + */ + void setTask(TaskRecord task, boolean reparenting) { + // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}. + if (task != null && task == getTask()) { + return; + } + + final ActivityStack stack = getStack(); + + // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this + // {@link ActivityRecord} from its current {@link ActivityStack}. + if (!reparenting && stack != null && (task == null || stack != task.getStack())) { + stack.onActivityRemovedFromStack(this); + } + + this.task = task; + + if (!reparenting) { + onParentChanged(); + } + } + static class Token extends IApplicationToken.Stub { private final WeakReference<ActivityRecord> weakActivity; @@ -925,8 +964,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // Must reparent first in window manager mWindowContainerController.reparent(newTask.getWindowContainerController(), position); - // Remove the activity from the old task and add it to the new task - prevTask.removeActivity(this); + // Remove the activity from the old task and add it to the new task. + prevTask.removeActivity(this, true /*reparenting*/); newTask.addActivityAtIndex(position, this); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 3d50b7cc729d..f13b11e65a88 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -116,6 +116,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; @@ -725,7 +726,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (r == null) { return null; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = r.getStack(); if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) { if (stack != this) Slog.w(TAG, @@ -934,7 +935,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls=" + taskIntent.getComponent().flattenToShortString() - + "/aff=" + r.task.rootAffinity + " to new cls=" + + "/aff=" + r.getTask().rootAffinity + " to new cls=" + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity); // TODO Refactor to remove duplications. Check if logic can be simplified. if (taskIntent != null && taskIntent.getComponent() != null && @@ -1049,8 +1050,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai void addRecentActivityLocked(ActivityRecord r) { if (r != null) { - mRecentTasks.addLocked(r.task); - r.task.touchActiveTime(); + final TaskRecord task = r.getTask(); + mRecentTasks.addLocked(task); + task.touchActiveTime(); } } @@ -1226,11 +1228,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null; prev.state = ActivityState.PAUSING; - prev.task.touchActiveTime(); + prev.getTask().touchActiveTime(); clearLaunchTime(prev); final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(); if (mService.mHasRecents - && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) { + && (next == null || next.noDisplay || next.getTask() != prev.getTask() + || uiSleeping)) { prev.mUpdateTaskThumbnailWhenHidden = true; } stopFullyDrawnTraceIfNeeded(); @@ -1457,7 +1460,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Find the first visible activity above the passed activity and if it is translucent return it // otherwise return null; ActivityRecord findNextTranslucentActivity(ActivityRecord r) { - TaskRecord task = r.task; + TaskRecord task = r.getTask(); if (task == null) { return null; } @@ -1604,7 +1607,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Otherwise, the docked stack is always visible, except in the case where the top // running activity task in the focus stack doesn't support any form of resizing but we // show it for the home task even though it's not resizable. - final TaskRecord task = r != null ? r.task : null; + final TaskRecord task = r != null ? r.getTask() : null; return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE : STACK_INVISIBLE; } @@ -2157,8 +2160,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mResumedActivity = r; r.state = ActivityState.RESUMED; mService.setResumedActivityUncheckLocked(r, reason); - r.task.touchActiveTime(); - mRecentTasks.addLocked(r.task); + final TaskRecord task = r.getTask(); + task.touchActiveTime(); + mRecentTasks.addLocked(task); } private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { @@ -2207,8 +2211,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } - final TaskRecord nextTask = next.task; - final TaskRecord prevTask = prev != null ? prev.task : null; + final TaskRecord nextTask = next.getTask(); + final TaskRecord prevTask = prev != null ? prev.getTask() : null; if (prevTask != null && prevTask.getStack() == this && prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) { if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); @@ -2373,7 +2377,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai anim = false; mWindowManager.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.task == next.task + mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE : TRANSIT_TASK_CLOSE, false); } @@ -2385,7 +2389,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai anim = false; mWindowManager.prepareAppTransition(TRANSIT_NONE, false); } else { - mWindowManager.prepareAppTransition(prev.task == next.task + mWindowManager.prepareAppTransition(prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND @@ -2522,7 +2526,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.notifyAppResumed(next.stopped, allowSavedSurface); EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, - System.identityHashCode(next), next.task.taskId, next.shortComponentName); + System.identityHashCode(next), next.getTask().taskId, + next.shortComponentName); next.sleeping = false; mService.showUnsupportedZoomDialogIfNeededLocked(next); @@ -2696,9 +2701,15 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - // If the task was launched from the assistant stack, set the return type to assistant final ActivityStack lastStack = mStackSupervisor.getLastStack(); - if (lastStack != null && lastStack.isAssistantStack()) { + + // If there is no last task, do not set task to return to + if (lastStack == null) { + return; + } + + // If the task was launched from the assistant stack, set the return type to assistant + if (lastStack.isAssistantStack()) { task.setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE); return; } @@ -2721,7 +2732,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { - TaskRecord rTask = r.task; + TaskRecord rTask = r.getTask(); final int taskId = rTask.taskId; // mLaunchTaskBehind tasks get placed at the back of the task stack. if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) { @@ -2740,7 +2751,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // All activities in task are finishing. continue; } - if (task == r.task) { + if (task == rTask) { // 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. @@ -2762,13 +2773,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // If we are not placing the new activity frontmost, we do not want to deliver the // onUserLeaving callback to the actual frontmost activity - if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { + final TaskRecord activityTask = r.getTask(); + if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) { mStackSupervisor.mUserLeaving = false; if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, "startActivity() behind front, mUserLeaving=false"); } - task = r.task; + task = activityTask; // Slot the activity into the history stack and proceed if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task, @@ -2830,11 +2842,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // "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 = r.task.topRunningActivityWithStartingWindowLocked(); + TaskRecord prevTask = r.getTask(); + ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked(); 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) { + if (prev.getTask() != prevTask) { prev = null; } // (2) The current activity is already displayed. @@ -2853,7 +2866,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) { - return topFocusedActivity != null && r.task != topFocusedActivity.task; + return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask(); } /** @@ -2920,20 +2933,20 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? mTaskHistory.get(0).mActivities.get(0) : null; if (bottom != null && target.taskAffinity != null - && target.taskAffinity.equals(bottom.task.affinity)) { + && target.taskAffinity.equals(bottom.getTask().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. - targetTask = bottom.task; + targetTask = bottom.getTask(); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target - + " out to bottom task " + bottom.task); + + " out to bottom task " + targetTask); } else { targetTask = createTaskRecord( mStackSupervisor.getNextTaskIdForUserLocked(target.userId), target.info, null, null, null, false, target.mActivityType); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target - + " out to new task " + target.task); + + " out to new task " + targetTask); } boolean noOptions = canMoveOptions; @@ -2955,7 +2968,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai "Removing activity " + p + " from task=" + task + " adding to task=" + targetTask + " Callers=" + Debug.getCallers(4)); if (DEBUG_TASKS) Slog.v(TAG_TASKS, - "Pushing next activity " + p + " out to target's task " + target.task); + "Pushing next activity " + p + " out to target's task " + target); p.reparent(targetTask, 0 /* position - bottom */, "resetTargetTaskIfNeeded"); } @@ -3126,13 +3139,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean forceReset = (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; if (ACTIVITY_INACTIVE_RESET_TIME > 0 - && taskTop.task.getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { + && taskTop.getTask().getInactiveDuration() > ACTIVITY_INACTIVE_RESET_TIME) { if ((newActivity.info.flags & ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE) == 0) { forceReset = true; } } - final TaskRecord task = taskTop.task; + final TaskRecord task = taskTop.getTask(); /** False until we evaluate the TaskRecord associated with taskTop. Switches to true * for remaining tasks. Used for later tasks to reparent to task. */ @@ -3215,7 +3228,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // stack as long as there is a running activity. return; } else { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final boolean isAssistantOrOverAssistant = task.getStack().isAssistantStack() || task.isOverAssistantStack(); if (r.frontOfTask && task == topTask() && @@ -3374,10 +3387,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - int taskNdx = mTaskHistory.indexOf(r.task); - int activityNdx = r.task.mActivities.indexOf(r); + finishedTask = r.getTask(); + int taskNdx = mTaskHistory.indexOf(finishedTask); + final TaskRecord task = finishedTask; + int activityNdx = task.mActivities.indexOf(r); finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); - finishedTask = r.task; + finishedTask = task; // Also terminate any 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. @@ -3447,7 +3462,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean finishActivityAffinityLocked(ActivityRecord r) { - ArrayList<ActivityRecord> activities = r.task.mActivities; + ArrayList<ActivityRecord> activities = r.getTask().mActivities; for (int index = activities.indexOf(r); index >= 0; --index) { ActivityRecord cur = activities.get(index); if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) { @@ -3512,7 +3527,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mWindowManager.deferSurfaceLayout(); try { r.makeFinishingLocked(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), task.taskId, r.shortComponentName, reason); @@ -3701,23 +3716,24 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) { // Basic case: for simple app-centric recents, we need to recreate // the task if the affinity has changed. - if (srec == null || srec.task.affinity == null || - !srec.task.affinity.equals(destAffinity)) { + if (srec == null || srec.getTask().affinity == null || + !srec.getTask().affinity.equals(destAffinity)) { return true; } // Document-centric case: an app may be split in to multiple documents; // they need to re-create their task if this current activity is the root // of a document, unless simply finishing it will return them to the the // correct app behind. - if (srec.frontOfTask && srec.task != null && srec.task.getBaseIntent() != null - && srec.task.getBaseIntent().isDocument()) { + final TaskRecord task = srec.getTask(); + if (srec.frontOfTask && task != null && task.getBaseIntent() != null + && task.getBaseIntent().isDocument()) { // Okay, this activity is at the root of its task. What to do, what to do... - if (srec.task.getTaskToReturnTo() != ActivityRecord.APPLICATION_ACTIVITY_TYPE) { + if (task.getTaskToReturnTo() != ActivityRecord.APPLICATION_ACTIVITY_TYPE) { // Finishing won't return to an application, so we need to recreate. return true; } // We now need to get the task below it to determine what to do. - int taskIdx = mTaskHistory.indexOf(srec.task); + int taskIdx = mTaskHistory.indexOf(task); if (taskIdx <= 0) { Slog.w(TAG, "shouldUpRecreateTask: task not in history for " + srec); return false; @@ -3727,7 +3743,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return true; } TaskRecord prevTask = mTaskHistory.get(taskIdx); - if (!srec.task.affinity.equals(prevTask.affinity)) { + if (!task.affinity.equals(prevTask.affinity)) { // These are different apps, so need to recreate. return true; } @@ -3737,7 +3753,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode, Intent resultData) { - final TaskRecord task = srec.task; + final TaskRecord task = srec.getTask(); final ArrayList<ActivityRecord> activities = task.mActivities; final int start = activities.indexOf(srec); if (!mTaskHistory.contains(task) || (start < 0)) { @@ -3816,6 +3832,22 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Binder.restoreCallingIdentity(origId); return foundParentInTask; } + + /** + * Remove any state associated with the {@link ActivityRecord}. This should be called whenever + * an activity moves away from the stack. + */ + void onActivityRemovedFromStack(ActivityRecord r) { + if (mResumedActivity == r) { + mResumedActivity = null; + } + if (mPausingActivity == r) { + mPausingActivity = null; + } + + removeTimeoutsForActivityLocked(r); + } + /** * Perform the common clean-up of an activity record. This is called both * as part of destroyActivityLocked() (when destroying the client-side @@ -3826,12 +3858,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * Note: Call before #removeActivityFromHistoryLocked. */ private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) { - if (mResumedActivity == r) { - mResumedActivity = null; - } - if (mPausingActivity == r) { - mPausingActivity = null; - } + onActivityRemovedFromStack(r); r.deferRelaunchUntilPaused = false; r.frozenBeforeDestroy = false; @@ -3898,7 +3925,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r); r.app = null; r.removeWindowContainer(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final boolean lastActivity = task != null ? task.removeActivity(r) : false; // If we are removing the last activity in the task, not including task overlay activities, // then fall through into the block below to remove the entire task itself @@ -4044,7 +4071,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai + ", app=" + (r.app != null ? r.app.processName : "(null)")); EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY, r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, reason); + r.getTask().taskId, r.shortComponentName, reason); boolean removedFromHistory = false; @@ -4276,7 +4303,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.userId, System.identityHashCode(r), - r.task.taskId, r.shortComponentName, + r.getTask().taskId, r.shortComponentName, "proc died without state saved"); if (r.state == ActivityState.RESUMED) { mService.updateUsageStats(r, false); @@ -4530,7 +4557,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null; + final TaskRecord task = mResumedActivity != null ? mResumedActivity.getTask() : null; if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) { if (!mService.mBooting && !mService.mBooted) { // Not ready yet! @@ -4571,7 +4598,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - final TaskRecord startTask = start.task; + final TaskRecord startTask = start.getTask(); boolean behindFullscreen = false; boolean updatedConfig = false; @@ -4579,7 +4606,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final TaskRecord task = mTaskHistory.get(taskIndex); final ArrayList<ActivityRecord> activities = task.mActivities; int activityIndex = - (start.task == task) ? activities.indexOf(start) : activities.size() - 1; + (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1; for (; activityIndex >= 0; --activityIndex) { final ActivityRecord r = activities.get(activityIndex); updatedConfig |= r.ensureActivityConfigurationLocked(0 /* globalChanges */, @@ -4740,7 +4767,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || filterByClasses.contains(r.realActivity.getClassName()))) || (packageName == null && r.userId == userId); if ((userId == UserHandle.USER_ALL || r.userId == userId) - && (sameComponent || r.task == lastTask) + && (sameComponent || r.getTask() == lastTask) && (r.app == null || evenPersistent || !r.app.persistent)) { if (!doit) { if (r.finishing) { @@ -4766,7 +4793,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } r.app = null; } - lastTask = r.task; + lastTask = r.getTask(); if (finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop", true)) { // r has been deleted from mActivities, accommodate. @@ -4817,7 +4844,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_ALL) Slog.v( TAG, r.intent.getComponent().flattenToShortString() - + ": task=" + r.task); + + ": task=" + r.getTask()); } RunningTaskInfo ci = new RunningTaskInfo(); @@ -4833,8 +4860,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai topTask = false; } - if (top.task != null) { - ci.description = top.task.lastDescription; + if (top.getTask() != null) { + ci.description = top.getTask().lastDescription; } ci.numActivities = numActivities; ci.numRunning = numRunning; @@ -4983,9 +5010,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai task.removeWindowContainer(); } - final ActivityRecord r = mResumedActivity; - if (r != null && r.task == task) { - mResumedActivity = null; + for (ActivityRecord record : task.mActivities) { + onActivityRemovedFromStack(record); } final int taskNdx = mTaskHistory.indexOf(task); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index f16849ddc672..b72cd73e85cf 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -164,6 +164,7 @@ import android.view.Display; import android.view.InputEvent; import android.view.Surface; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -173,6 +174,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.am.ActivityStack.ActivityState; +import com.android.server.wm.StackWindowController; import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; @@ -553,9 +555,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - public ActivityStackSupervisor(ActivityManagerService service) { + public ActivityStackSupervisor(ActivityManagerService service, Looper looper) { mService = service; - mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper()); + mHandler = new ActivityStackSupervisorHandler(looper); mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext); mKeyguardController = new KeyguardController(service, this); } @@ -722,7 +724,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } if (prev != null) { - prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); + prev.getTask().setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE); } mHomeStack.moveHomeStackTaskToTop(); @@ -1314,7 +1316,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.updateLruProcessLocked(app, true, null); mService.updateOomAdjLocked(); - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) { setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE", false); @@ -2622,7 +2624,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } for (int k = 0; k < proc.activities.size(); k++) { - TaskRecord otherTask = proc.activities.get(k).task; + TaskRecord otherTask = proc.activities.get(k).getTask(); if (tr.taskId != otherTask.taskId && otherTask.inRecents) { // Don't kill process(es) that has an activity in a different task that is // also in recents. @@ -2837,7 +2839,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP); try { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); if (r == task.getStack().getVisibleBehindActivity()) { // An activity can't be pinned and visible behind at the same time. Go ahead and @@ -2910,7 +2912,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = r.getStack(); if (stack == null) { Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: r=" @@ -3203,7 +3205,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); final ActivityStack stack = task.getStack(); r.mLaunchTaskBehind = false; @@ -3216,7 +3218,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // task has been shown briefly final ActivityRecord top = stack.topActivity(); if (top != null) { - top.task.touchActiveTime(); + top.getTask().touchActiveTime(); } } @@ -3315,17 +3317,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r); continue; } - if (r.task != null) { - if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + r.task + + final TaskRecord task = r.getTask(); + if (task != null) { + if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task + " from " + r); if (firstTask == null) { - firstTask = r.task; - } else if (firstTask != r.task) { + firstTask = task; + } else if (firstTask != task) { if (tasks == null) { tasks = new ArraySet<>(); tasks.add(firstTask); } - tasks.add(r.task); + tasks.add(task); } } } @@ -3664,8 +3668,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D pw.println(header2); header2 = null; } - if (lastTask != r.task) { - lastTask = r.task; + if (lastTask != r.getTask()) { + lastTask = r.getTask(); pw.print(prefix); pw.print(full ? "* " : " "); pw.println(lastTask); @@ -4080,7 +4084,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } final ActivityRecord r = topRunningActivityLocked(); - final TaskRecord task = r != null ? r.task : null; + final TaskRecord task = r != null ? r.getTask() : null; if (mLockTaskModeTasks.isEmpty() && task != null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { // This task must have just been authorized. @@ -4390,19 +4394,24 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { mStackId = stackId; mActivityDisplay = activityDisplay; - switch (mStackId) { - case PINNED_STACK_ID: - new PinnedActivityStack(this, mRecentTasks, onTop); - break; - default: - new ActivityStack(this, mRecentTasks, onTop); - break; - } mIdString = "ActivtyContainer{" + mStackId + "}"; + + createStack(stackId, onTop); if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this); } } + protected void createStack(int stackId, boolean onTop) { + switch (stackId) { + case PINNED_STACK_ID: + new PinnedActivityStack(this, mRecentTasks, onTop); + break; + default: + new ActivityStack(this, mRecentTasks, onTop); + break; + } + } + /** * Adds the stack to specified display. Also calls WindowManager to do the same from * {@link ActivityStack#reparent(ActivityDisplay, boolean)}. @@ -4926,7 +4935,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(), ActivityManager.START_TASK_TO_FRONT, - sourceRecord != null ? sourceRecord.task.getStackId() : INVALID_STACK_ID, + sourceRecord != null ? sourceRecord.getTask().getStackId() : INVALID_STACK_ID, sourceRecord, task.getStack()); return ActivityManager.START_TASK_TO_FRONT; } diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java index 547161ac4169..cafc4f0ecc96 100644 --- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java @@ -188,10 +188,10 @@ class ActivityStartInterceptor { } ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity(); - if (homeActivityRecord != null && homeActivityRecord.task != null) { + if (homeActivityRecord != null && homeActivityRecord.getTask() != null) { // Showing credential confirmation activity in home task to avoid stopping multi-windowed // mode after showing the full-screen credential confirmation activity. - mActivityOptions.setLaunchTaskId(homeActivityRecord.task.taskId); + mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId); } final UserInfo parent = mUserManager.getProfileParent(mUserId); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 18e74080490d..b4085697f2da 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -79,7 +79,6 @@ import static com.android.server.am.ActivityStack.ActivityState.RESUMED; import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; -import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS; @@ -87,8 +86,6 @@ import static com.android.server.am.EventLogTags.AM_NEW_INTENT; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; -import static java.lang.Integer.MAX_VALUE; - import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityOptions; @@ -335,7 +332,7 @@ class ActivityStarter { } if (err == ActivityManager.START_SUCCESS && sourceRecord != null - && sourceRecord.task.voiceSession != null) { + && sourceRecord.getTask().voiceSession != null) { // If this activity is being launched as part of a voice session, we need // to ensure that it is safe to do so. If the upcoming activity will also // be part of the voice session, we can only launch it if it has explicitly @@ -575,7 +572,7 @@ class ActivityStarter { // visibility instead of using this flag. final boolean noDisplayActivityOverHome = sourceRecord != null && sourceRecord.noDisplay - && sourceRecord.task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE; + && sourceRecord.getTask().getTaskToReturnTo() == HOME_ACTIVITY_TYPE; if (startedActivityStackId == DOCKED_STACK_ID && (prevFocusedStackId == HOME_STACK_ID || noDisplayActivityOverHome)) { final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID); @@ -625,7 +622,7 @@ class ActivityStarter { FLAG_ACTIVITY_TASK_ON_HOME); ActivityOptions options = (optionsBundle != null ? new ActivityOptions(optionsBundle) : ActivityOptions.makeBasic()); - options.setLaunchTaskId(mSupervisor.getHomeActivity().task.taskId); + options.setLaunchTaskId(mSupervisor.getHomeActivity().getTask().taskId); mService.mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); } @@ -763,7 +760,7 @@ class ActivityStarter { newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName); newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, - hist.task.taskId); + hist.getTask().taskId); } newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP, aInfo.packageName); @@ -967,8 +964,8 @@ class ActivityStarter { // If we are not able to proceed, disassociate the activity from the task. Leaving an // activity in an incomplete state can lead to issues, such as performing operations // without a window container. - if (result != START_SUCCESS && mStartActivity.task != null) { - mStartActivity.task.removeActivity(mStartActivity); + if (result != START_SUCCESS && mStartActivity.getTask() != null) { + mStartActivity.getTask().removeActivity(mStartActivity); } mService.mWindowManager.continueSurfaceLayout(); } @@ -1002,7 +999,7 @@ class ActivityStarter { // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but // still needs to be a lock task mode violation since the task gets cleared out and // the device would otherwise leave the locked task. - if (mSupervisor.isLockTaskModeViolation(mReusedActivity.task, + if (mSupervisor.isLockTaskModeViolation(mReusedActivity.getTask(), (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) { mSupervisor.showLockTaskToast(); @@ -1010,13 +1007,13 @@ class ActivityStarter { return START_RETURN_LOCK_TASK_MODE_VIOLATION; } - if (mStartActivity.task == null) { - mStartActivity.task = mReusedActivity.task; + if (mStartActivity.getTask() == null) { + mStartActivity.setTask(mReusedActivity.getTask()); } - if (mReusedActivity.task.intent == null) { + if (mReusedActivity.getTask().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. - mReusedActivity.task.setIntent(mStartActivity); + mReusedActivity.getTask().setIntent(mStartActivity); } // This code path leads to delivering a new intent, we want to make sure we schedule it @@ -1025,7 +1022,7 @@ class ActivityStarter { if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || isDocumentLaunchesIntoExisting(mLaunchFlags) || mLaunchSingleInstance || mLaunchSingleTask) { - final TaskRecord task = mReusedActivity.task; + final TaskRecord task = mReusedActivity.getTask(); // 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 @@ -1037,17 +1034,17 @@ class ActivityStarter { // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The // task reference is needed in the call below to // {@link setTargetStackAndMoveToFrontIfNeeded}. - if (mReusedActivity.task == null) { - mReusedActivity.task = task; + if (mReusedActivity.getTask() == null) { + mReusedActivity.setTask(task); } 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(mStartActivity); + top.getTask().setIntent(mStartActivity); } - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); } @@ -1098,7 +1095,7 @@ class ActivityStarter { && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop || mLaunchSingleTask); if (dontStart) { - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); // For paranoia, make sure we have correctly resumed the top activity. topStack.mLastPausedActivity = null; if (mDoResume) { @@ -1116,14 +1113,14 @@ class ActivityStarter { // Don't use mStartActivity.task to show the toast. We're not starting a new activity // but reusing 'top'. Fields in mStartActivity may not be fully initialized. mSupervisor.handleNonResizableTaskIfNeeded( - top.task, preferredLaunchStackId, topStack.mStackId); + top.getTask(), preferredLaunchStackId, topStack.mStackId); return START_DELIVERED_TO_TOP; } boolean newTask = false; final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null) - ? mSourceRecord.task : null; + ? mSourceRecord.getTask() : null; // Should this be considered a new task? int result = START_SUCCESS; @@ -1150,14 +1147,15 @@ class ActivityStarter { mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent, mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid)); if (mSourceRecord != null) { - mStartActivity.task.setTaskToReturnTo(mSourceRecord); + mStartActivity.getTask().setTaskToReturnTo(mSourceRecord); } if (newTask) { EventLog.writeEvent( - EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId); + EventLogTags.AM_CREATE_TASK, mStartActivity.userId, + mStartActivity.getTask().taskId); } ActivityStack.logStartActivity( - EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task); + EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask()); mTargetStack.mLastPausedActivity = null; sendPowerHintForLaunchStartIfNeeded(false /* forceSend */); @@ -1165,7 +1163,8 @@ class ActivityStarter { mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition, mOptions); if (mDoResume) { - final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked(); + final ActivityRecord topTaskActivity = + mStartActivity.getTask().topRunningActivityLocked(); if (!mTargetStack.isFocusable() || (topTaskActivity != null && topTaskActivity.mTaskOverlay && mStartActivity != topTaskActivity)) { @@ -1197,7 +1196,7 @@ class ActivityStarter { mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded( - mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId); + mStartActivity.getTask(), preferredLaunchStackId, mTargetStack.mStackId); return START_SUCCESS; } @@ -1424,7 +1423,7 @@ class ActivityStarter { + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent); mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK; mNewTaskInfo = mSourceRecord.info; - mNewTaskIntent = mSourceRecord.task.intent; + mNewTaskIntent = mSourceRecord.getTask().intent; } mSourceRecord = null; mSourceStack = null; @@ -1516,15 +1515,16 @@ class ActivityStarter { ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); - if (curTop != null - && (curTop.task != intentActivity.task || curTop.task != focusStack.topTask()) + final TaskRecord topTask = curTop != null ? curTop.getTask() : null; + if (topTask != null + && (topTask != intentActivity.getTask() || topTask != focusStack.topTask()) && !mAvoidMoveToFront) { mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); if (mSourceRecord == null || (mSourceStack.topActivity() != null && - mSourceStack.topActivity().task == mSourceRecord.task)) { + mSourceStack.topActivity().getTask() == mSourceRecord.getTask())) { // We really do want to push this one into the user's face, right now. if (mLaunchTaskBehind && mSourceRecord != null) { - intentActivity.setTaskToAffiliateWith(mSourceRecord.task); + intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask()); } mMovedOtherTask = true; @@ -1539,13 +1539,13 @@ class ActivityStarter { == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); if (!willClearTask) { final ActivityStack launchStack = getLaunchStack( - mStartActivity, mLaunchFlags, mStartActivity.task, mOptions); + mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions); + final TaskRecord intentTask = intentActivity.getTask(); if (launchStack == null || launchStack == mTargetStack) { // We only want to move to the front, if we aren't going to launch on a // different stack. If we launch on a different stack, we will put the // task on top there. - mTargetStack.moveTaskToFrontLocked( - intentActivity.task, mNoAnimation, mOptions, + mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront"); mMovedToFront = true; } else if (launchStack.mStackId == DOCKED_STACK_ID @@ -1553,7 +1553,7 @@ class ActivityStarter { if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // If we want to launch adjacent and mTargetStack is not the computed // launch stack - move task to top of computed stack. - intentActivity.task.reparent(launchStack.mStackId, ON_TOP, + intentTask.reparent(launchStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "launchToSide"); } else { @@ -1561,8 +1561,8 @@ class ActivityStarter { // We choose to move task to front instead of launching it adjacent // when specific stack was requested explicitly and it appeared to be // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set. - mTargetStack.moveTaskToFrontLocked(intentActivity.task, mNoAnimation, - mOptions, mStartActivity.appTimeTracker, + mTargetStack.moveTaskToFrontLocked(intentTask, + mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringToFrontInsteadOfAdjacentLaunch"); } mMovedToFront = true; @@ -1570,7 +1570,7 @@ class ActivityStarter { // Target and computed stacks are on different displays and we've // found a matching task - move the existing instance to that display and // move it to front. - intentActivity.task.reparent(launchStack.mStackId, ON_TOP, + intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentToDisplay"); mMovedToFront = true; @@ -1582,7 +1582,7 @@ class ActivityStarter { intentActivity.showStartingWindow(null /* prev */, false /* newTask */, true /* taskSwitch */); } - updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack); + updateTaskReturnToType(intentActivity.getTask(), mLaunchFlags, focusStack); } } if (!mMovedToFront && mDoResume) { @@ -1591,7 +1591,7 @@ class ActivityStarter { mTargetStack.moveToFront("intentActivityFound"); } - mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.task, INVALID_STACK_ID, + mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID, mTargetStack.mStackId); // If the caller has requested that the target task be reset, then do so. @@ -1635,7 +1635,7 @@ class ActivityStarter { // launching another activity. // TODO(b/36119896): We shouldn't trigger activity launches in this path since we are // already launching one. - final TaskRecord task = intentActivity.task; + final TaskRecord task = intentActivity.getTask(); task.performClearTaskLocked(); mReuseTask = task; mReuseTask.setIntent(mStartActivity); @@ -1646,7 +1646,7 @@ class ActivityStarter { mMovedOtherTask = true; } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 || mLaunchSingleInstance || mLaunchSingleTask) { - ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity, + ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity, mLaunchFlags); if (top == null) { // A special case: we need to start the activity because it is not currently @@ -1655,11 +1655,11 @@ class ActivityStarter { mAddingToTask = true; // We are no longer placing the activity in the task we previously thought we were. - mStartActivity.task = null; + mStartActivity.setTask(null); // Now pretend like this activity is being started by the top of its task, so it // is put in the right place. mSourceRecord = intentActivity; - final TaskRecord task = mSourceRecord.task; + final TaskRecord task = mSourceRecord.getTask(); if (task != null && task.getStack() == null) { // Target stack got cleared when we all activities were removed above. // Go ahead and reset it. @@ -1669,7 +1669,7 @@ class ActivityStarter { !mLaunchTaskBehind /* toTop */, "startActivityUnchecked"); } } - } else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) { + } else if (mStartActivity.realActivity.equals(intentActivity.getTask().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 @@ -1677,13 +1677,13 @@ class ActivityStarter { if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop) && intentActivity.realActivity.equals(mStartActivity.realActivity)) { ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, - intentActivity.task); + intentActivity.getTask()); if (intentActivity.frontOfTask) { - intentActivity.task.setIntent(mStartActivity); + intentActivity.getTask().setIntent(mStartActivity); } intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); - } else if (!intentActivity.task.isSameIntentFilter(mStartActivity)) { + } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) { // 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. mAddingToTask = true; @@ -1696,13 +1696,13 @@ class ActivityStarter { // current task. mAddingToTask = true; mSourceRecord = intentActivity; - } else if (!intentActivity.task.rootWasReset) { + } else if (!intentActivity.getTask().rootWasReset) { // In this case we are launching into 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. - intentActivity.task.setIntent(mStartActivity); + intentActivity.getTask().setIntent(mStartActivity); } } @@ -1736,11 +1736,11 @@ class ActivityStarter { mService.resizeStack( stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1); } else { - mStartActivity.task.updateOverrideConfiguration(mLaunchBounds); + mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds); } } if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new task " + mStartActivity.task); + + " in new task " + mStartActivity.getTask()); } else { addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask"); } @@ -1749,7 +1749,7 @@ class ActivityStarter { mStartActivity.setTaskToAffiliateWith(taskToAffiliate); } - if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) { + if (mSupervisor.isLockTaskModeViolation(mStartActivity.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } @@ -1758,7 +1758,7 @@ class ActivityStarter { // If stack id is specified in activity options, usually it means that activity is // launched not from currently focused stack (e.g. from SysUI or from shell) - in // that case we check the target stack. - updateTaskReturnToType(mStartActivity.task, mLaunchFlags, + updateTaskReturnToType(mStartActivity.getTask(), mLaunchFlags, preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack); } if (mDoResume) { @@ -1768,19 +1768,19 @@ class ActivityStarter { } private int setTaskFromSourceRecord() { - if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) { + if (mSupervisor.isLockTaskModeViolation(mSourceRecord.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); return START_RETURN_LOCK_TASK_MODE_VIOLATION; } - final TaskRecord sourceTask = mSourceRecord.task; + final TaskRecord sourceTask = mSourceRecord.getTask(); final ActivityStack sourceStack = mSourceRecord.getStack(); // We only want to allow changing stack if the target task is not the top one, // otherwise we would move the launching task to the other side, rather than show // two side by side. final boolean moveStackAllowed = sourceStack.topTask() != sourceTask; if (moveStackAllowed) { - mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.task, + mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions); } @@ -1805,7 +1805,7 @@ class ActivityStarter { ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags); mKeepCurTransition = true; if (top != null) { - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); // For paranoia, make sure we have correctly resumed the top activity. mTargetStack.mLastPausedActivity = null; @@ -1821,7 +1821,7 @@ class ActivityStarter { // stack if so. final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity); if (top != null) { - final TaskRecord task = top.task; + final TaskRecord task = top.getTask(); task.moveActivityToFrontLocked(top); top.updateOptionsLocked(mOptions); ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task); @@ -1838,7 +1838,7 @@ class ActivityStarter { // the same task as the one that is starting it. addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in existing task " + mStartActivity.task + " from source " + mSourceRecord); + + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord); return START_SUCCESS; } @@ -1861,7 +1861,7 @@ class ActivityStarter { || mLaunchSingleTop || mLaunchSingleTask) { mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront"); - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task); + ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { // 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! @@ -1901,7 +1901,7 @@ class ActivityStarter { addOrReparentStartingActivity(mInTask, "setTaskFromInTask"); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in explicit task " + mStartActivity.task); + + " in explicit task " + mStartActivity.getTask()); return START_SUCCESS; } @@ -1913,17 +1913,17 @@ class ActivityStarter { mTargetStack.moveToFront("addingToTopTask"); } final ActivityRecord prev = mTargetStack.topActivity(); - final TaskRecord task = (prev != null) ? prev.task : mTargetStack.createTaskRecord( + final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info, mIntent, null, null, true, mStartActivity.mActivityType); addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask"); mTargetStack.positionChildWindowContainerAtTop(task); if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity - + " in new guessed " + mStartActivity.task); + + " in new guessed " + mStartActivity.getTask()); } private void addOrReparentStartingActivity(TaskRecord parent, String reason) { - if (mStartActivity.task == null || mStartActivity.task == parent) { + if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { parent.addActivityToTop(mStartActivity); } else { mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason); @@ -1973,7 +1973,7 @@ class ActivityStarter { private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds, int launchFlags, ActivityOptions aOptions) { - final TaskRecord task = r.task; + final TaskRecord task = r.getTask(); ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions); if (stack != null) { return stack; diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 27a2461a8827..0c2c2043fb15 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -660,17 +660,6 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta // we are coming from in WM before we reparent because it became empty. mWindowContainerController.reparent(toStack.getWindowContainerController(), position); - // Reset the resumed activity on the previous stack - if (wasResumed) { - sourceStack.mResumedActivity = null; - } - - // Reset the paused activity on the previous stack - if (wasPaused) { - sourceStack.mPausingActivity = null; - sourceStack.removeTimeoutsForActivityLocked(r); - } - // Move the task sourceStack.removeTask(this, reason, REMOVE_TASK_MODE_MOVING); toStack.addTask(this, position, false /* schedulePictureInPictureModeChange */, reason); @@ -1212,14 +1201,13 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta * be in the current task or unparented to any task. */ void addActivityAtIndex(int index, ActivityRecord r) { - if (r.task != null && r.task != this) { + TaskRecord task = r.getTask(); + if (task != null && task != this) { throw new IllegalArgumentException("Can not add r=" + " to task=" + this - + " current parent=" + r.task); + + " current parent=" + task); } - // TODO(b/36505427): Maybe make task private to ActivityRecord so we can also do - // onParentChanged() within the setter? - r.task = this; - r.onParentChanged(); + + r.setTask(this); // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. if (!mActivities.remove(r) && r.fullscreen) { @@ -1274,15 +1262,21 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta } /** - * @return true if this was the last activity in the task + * Removes the specified activity from this task. + * @param r The {@link ActivityRecord} to remove. + * @return true if this was the last activity in the task. */ boolean removeActivity(ActivityRecord r) { - if (r.task != this) { + return removeActivity(r, false /*reparenting*/); + } + + boolean removeActivity(ActivityRecord r, boolean reparenting) { + if (r.getTask() != this) { throw new IllegalArgumentException( "Activity=" + r + " does not belong to task=" + this); } - r.task = null; + r.setTask(null /*task*/, reparenting); if (mActivities.remove(r) && r.fullscreen) { // Was previously in list. @@ -1437,7 +1431,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta TaskThumbnail getTaskThumbnailLocked() { if (mStack != null) { final ActivityRecord resumedActivity = mStack.mResumedActivity; - if (resumedActivity != null && resumedActivity.task == this) { + if (resumedActivity != null && resumedActivity.getTask() == this) { final Bitmap thumbnail = resumedActivity.screenshotActivityLocked(); setLastThumbnailLocked(thumbnail); } @@ -1953,7 +1947,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta task.updateOverrideConfiguration(bounds); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { - activities.get(activityNdx).task = task; + activities.get(activityNdx).setTask(task); } if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java new file mode 100644 index 000000000000..54ecab3af542 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 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 static org.junit.Assert.assertTrue; + +import android.content.ComponentName; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.MediumTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.runner.RunWith; +import org.junit.Test; + +/** + * Tests for the {@link ActivityRecord} class. + * + * Build/Install/Run: + * bit FrameworksServicesTests:com.android.server.am.ActivityRecordTests + */ +@MediumTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class ActivityRecordTests extends ActivityTestsBase { + private final ComponentName testActivityComponent = + ComponentName.unflattenFromString("com.foo/.BarActivity"); + @Test + public void testStackCleanupOnClearingTask() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord task = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, task); + + record.setTask(null); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + } + + @Test + public void testStackCleanupOnActivityRemoval() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord task = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, task); + + task.removeActivity(record); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + } + + @Test + public void testStackCleanupOnTaskRemoval() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord task = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, task); + + testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1); + } + + @Test + public void testNoCleanupMovingActivityInSameStack() throws Exception { + final ActivityManagerService service = createActivityManagerService(); + final TestActivityStack testStack = new ActivityStackBuilder(service).build(); + final TaskRecord oldTask = createTask(service, testActivityComponent, testStack); + final ActivityRecord record = createActivity(service, testActivityComponent, oldTask); + final TaskRecord newTask = createTask(service, testActivityComponent, testStack); + + record.reparent(newTask, 0, null /*reason*/); + assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java new file mode 100644 index 000000000000..c5cc2ff22abd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2017 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 static org.mockito.Mockito.mock; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.support.test.InstrumentationRegistry; +import com.android.server.AttributeCache; +import com.android.server.wm.AppWindowContainerController; +import com.android.server.wm.StackWindowController; + +import com.android.server.wm.WindowManagerService; +import com.android.server.wm.WindowTestUtils; +import org.junit.Before; +import org.mockito.MockitoAnnotations; + +/** + * A base class to handle common operations in activity related unit tests. + */ +public class ActivityTestsBase { + private final Context mContext = InstrumentationRegistry.getContext(); + private static boolean sLooperPrepared; + private Handler mHandler; + + // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must + // be called at before any tests. + private final WindowManagerService mWms = WindowTestUtils.getWindowManagerService(mContext); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + if (!sLooperPrepared) { + sLooperPrepared = true; + Looper.prepare(); + } + } + + protected ActivityManagerService createActivityManagerService() { + return new TestActivityManagerService(mContext); + } + + protected static TestActivityStack createActivityStack(ActivityManagerService service, + int stackId, int displayId, boolean onTop) { + if (service.mStackSupervisor instanceof TestActivityStackSupervisor) { + final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor) + .createTestStack(stackId, onTop); + return stack; + } + + return null; + } + + protected static ActivityRecord createActivity(ActivityManagerService service, + ComponentName component, TaskRecord task) { + Intent intent = new Intent(); + intent.setComponent(component); + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = component.getPackageName(); + AttributeCache.init(service.mContext); + final ActivityRecord activity = new ActivityRecord(service, null /* caller */, + 0 /* launchedFromPid */, 0, null, intent, null, + aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, + 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, + service.mStackSupervisor, null /* container */, null /* options */, + null /* sourceRecord */); + activity.mWindowContainerController = mock(AppWindowContainerController.class); + + if (task != null) { + task.addActivityToTop(activity); + } + + return activity; + } + + protected static TaskRecord createTask(ActivityManagerService service, + ComponentName component, ActivityStack stack) { + final ActivityInfo aInfo = new ActivityInfo(); + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.packageName = component.getPackageName(); + + Intent intent = new Intent(); + intent.setComponent(component); + + final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/, + null /*_taskDescription*/, null /*thumbnailInfo*/); + stack.addTask(task, true, "creating test task"); + task.setStack(stack); + task.createWindowContainer(true, true); + + return task; + } + + /** + * An {@link ActivityManagerService} subclass which provides a test + * {@link ActivityStackSupervisor}. + */ + protected static class TestActivityManagerService extends ActivityManagerService { + public TestActivityManagerService(Context context) { + super(context); + } + + @Override + protected ActivityStackSupervisor createStackSupervisor() { + return new TestActivityStackSupervisor(this, new Handler().getLooper()); + } + } + + /** + * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on + * setup not available in the test environment. Also specifies an injector for + */ + protected static class TestActivityStackSupervisor extends ActivityStackSupervisor { + public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) { + super(service, looper); + } + + // Invoked during {@link ActivityStack} creation. + @Override + void updateUIDsPresentOnDisplay() { + } + + public TestActivityStack createTestStack(int stackId, boolean onTop) { + final ActivityDisplay display = new ActivityDisplay(); + final TestActivityContainer container = + new TestActivityContainer(stackId, display, onTop); + return container.getStack(); + } + + private class TestActivityContainer extends ActivityContainer { + private TestActivityStack mStack; + TestActivityContainer(int stackId, ActivityDisplay activityDisplay, boolean onTop) { + super(stackId, activityDisplay, onTop); + } + + @Override + protected void createStack(int stackId, boolean onTop) { + mStack = new TestActivityStack(this, null /*recentTasks*/, onTop); + } + + public TestActivityStack getStack() { + return mStack; + } + } + } + + /** + * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a + * method is called. Note that its functionality depends on the implementations of the + * construction arguments. + */ + protected static class TestActivityStack<T extends StackWindowController> + extends ActivityStack<T> { + private int mOnActivityRemovedFromStackCount = 0; + private T mContainerController; + TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer, + RecentTasks recentTasks, boolean onTop) { + super(activityContainer, recentTasks, onTop); + } + + @Override + void onActivityRemovedFromStack(ActivityRecord r) { + mOnActivityRemovedFromStackCount++; + super.onActivityRemovedFromStack(r); + } + + // Returns the number of times {@link #onActivityRemovedFromStack} has been called + public int onActivityRemovedFromStackInvocationCount() { + return mOnActivityRemovedFromStackCount; + } + + @Override + protected T createStackWindowController(int displayId, boolean onTop, + Rect outBounds) { + mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController(); + return mContainerController; + } + + @Override + T getWindowContainerController() { + return mContainerController; + } + } + + protected static class ActivityStackBuilder { + private boolean mOnTop = true; + private int mStackId = 0; + private int mDisplayId = 1; + + private final ActivityManagerService mService; + + public ActivityStackBuilder(ActivityManagerService ams) { + mService = ams; + } + + public ActivityStackBuilder setOnTop(boolean onTop) { + mOnTop = onTop; + return this; + } + + public ActivityStackBuilder setStackId(int id) { + mStackId = id; + return this; + } + + public ActivityStackBuilder setDisplayId(int id) { + mDisplayId = id; + return this; + } + + public TestActivityStack build() { + return createActivityStack(mService, mStackId, mDisplayId, mOnTop); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java index 2ccaefc4512e..25004de60676 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java @@ -44,7 +44,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); // Assert token was added to display. assertNotNull(sDisplayContent.getWindowToken(controller.mToken.asBinder())); @@ -61,7 +62,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testSetOrientation() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); // Assert orientation is unspecified to start. assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); @@ -92,7 +94,8 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testCreateRemoveStartingWindow() throws Exception { - final TestAppWindowContainerController controller = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller = + createAppWindowController(); controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); waitUntilHandlerIdle(); @@ -105,8 +108,10 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testTransferStartingWindow() throws Exception { - final TestAppWindowContainerController controller1 = createAppWindowController(); - final TestAppWindowContainerController controller2 = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller1 = + createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller2 = + createAppWindowController(); controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false); waitUntilHandlerIdle(); @@ -120,8 +125,10 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { @Test public void testTransferStartingWindowWhileCreating() throws Exception { - final TestAppWindowContainerController controller1 = createAppWindowController(); - final TestAppWindowContainerController controller2 = createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller1 = + createAppWindowController(); + final WindowTestUtils.TestAppWindowContainerController controller2 = + createAppWindowController(); sPolicy.setRunnableWhenAddingSplashScreen(() -> { // Surprise, ...! Transfer window in the middle of the creation flow. @@ -140,16 +147,16 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { public void testReparent() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController1 = - new TestTaskWindowContainerController(stackController); - final TestAppWindowContainerController appWindowController1 = createAppWindowController( - taskController1); - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stackController); - final TestAppWindowContainerController appWindowController2 = createAppWindowController( - taskController2); - final TestTaskWindowContainerController taskController3 = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController1 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestAppWindowContainerController appWindowController1 = + createAppWindowController(taskController1); + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestAppWindowContainerController appWindowController2 = + createAppWindowController(taskController2); + final WindowTestUtils.TestTaskWindowContainerController taskController3 = + new WindowTestUtils.TestTaskWindowContainerController(stackController); try { appWindowController1.reparent(taskController1, 0); @@ -169,16 +176,18 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { // Reparent the app window and ensure that it is moved appWindowController1.reparent(taskController2, 0); assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent()); - assertEquals(0, ((TestAppWindowToken) appWindowController1.mContainer).positionInParent()); - assertEquals(1, ((TestAppWindowToken) appWindowController2.mContainer).positionInParent()); + assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer) + .positionInParent()); + assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer) + .positionInParent()); } - private TestAppWindowContainerController createAppWindowController() { - return createAppWindowController(new TestTaskWindowContainerController()); + private WindowTestUtils.TestAppWindowContainerController createAppWindowController() { + return createAppWindowController(new WindowTestUtils.TestTaskWindowContainerController()); } - private TestAppWindowContainerController createAppWindowController( - TestTaskWindowContainerController taskController) { - return new TestAppWindowContainerController(taskController); + private WindowTestUtils.TestAppWindowContainerController createAppWindowController( + WindowTestUtils.TestTaskWindowContainerController taskController) { + return new WindowTestUtils.TestAppWindowContainerController(taskController); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 2003b91bcfad..7a7ca3f4000a 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -51,7 +51,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testAddWindow_Order() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); assertEquals(0, token.getWindowsCount()); @@ -78,7 +79,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testFindMainWindow() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); assertNull(token.findMainWindow()); @@ -102,12 +104,13 @@ public class AppWindowTokenTests extends WindowTestsBase { // Create an app window with token on a display. final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken appWindowToken = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken appWindowToken = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task.addChild(appWindowToken, 0); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken); appWindowToken.addWindow(appWindow); // Set initial orientation and update. @@ -137,12 +140,13 @@ public class AppWindowTokenTests extends WindowTestsBase { final DisplayContent defaultDisplayContent = sWm.getDefaultDisplayContentLocked(); final TaskStack stack = createTaskStackOnDisplay(defaultDisplayContent); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken appWindowToken = new TestAppWindowToken(defaultDisplayContent); + final WindowTestUtils.TestAppWindowToken appWindowToken = + new WindowTestUtils.TestAppWindowToken(defaultDisplayContent); task.addChild(appWindowToken, 0); final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( TYPE_BASE_APPLICATION); attrs.setTitle("AppWindow"); - final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, appWindowToken); appWindowToken.addWindow(appWindow); // Set initial orientation and update. @@ -165,7 +169,8 @@ public class AppWindowTokenTests extends WindowTestsBase { @Test public void testGetOrientation() throws Exception { - final TestAppWindowToken token = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken token = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); token.setFillsParent(false); diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java index e3ccd6eef105..d7d365e72480 100644 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java @@ -175,7 +175,7 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(dc, stack.getDisplayContent()); final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken token = new TestAppWindowToken(dc); + final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc); task.addChild(token, 0); assertEquals(dc, task.getDisplayContent()); assertEquals(dc, token.getDisplayContent()); diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java index b0eba0b99567..13098f64bfac 100644 --- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java @@ -16,12 +16,7 @@ package com.android.server.wm; -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - import android.graphics.Rect; -import android.hardware.display.DisplayManagerGlobal; -import android.view.Display; -import android.view.DisplayInfo; import org.junit.Test; import org.junit.runner.RunWith; @@ -48,8 +43,8 @@ public class StackWindowControllerTests extends WindowTestsBase { public void testRemoveContainer() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); final TaskStack stack = stackController.mContainer; final Task task = taskController.mContainer; @@ -68,11 +63,11 @@ public class StackWindowControllerTests extends WindowTestsBase { public void testRemoveContainer_deferRemoval() throws Exception { final StackWindowController stackController = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); final TaskStack stack = stackController.mContainer; - final TestTask task = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; // Stack removal is deferred if one of its child is animating. task.setLocalIsAnimating(true); @@ -96,9 +91,9 @@ public class StackWindowControllerTests extends WindowTestsBase { final StackWindowController stack1Controller = createStackControllerOnDisplay(sDisplayContent); final TaskStack stack1 = stack1Controller.mContainer; - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stack1Controller); - final TestTask task1 = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; task1.mOnDisplayChangedCalled = false; // Create second display and put second stack on it. diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java index 462bd68dc420..82ea2313e8d7 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java @@ -56,7 +56,7 @@ public class TaskStackContainersTests extends WindowTestsBase { // Stack should contain visible app window to be considered visible. final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); assertFalse(mPinnedStack.isVisible()); - final TestAppWindowToken pinnedApp = new TestAppWindowToken(sDisplayContent); + final WindowTestUtils.TestAppWindowToken pinnedApp = new WindowTestUtils.TestAppWindowToken(sDisplayContent); pinnedTask.addChild(pinnedApp, 0 /* addPos */); assertTrue(mPinnedStack.isVisible()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java index 9dbd8a617eec..267e5f77e709 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java @@ -16,9 +16,6 @@ package com.android.server.wm; -import android.content.pm.ActivityInfo; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; import org.junit.Test; import org.junit.runner.RunWith; @@ -67,12 +64,14 @@ public class TaskStackTests extends WindowTestsBase { public void testClosingAppDifferentStackOrientation() throws Exception { final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task1 = createTaskInStack(stack, 0 /* userId */); - TestAppWindowToken appWindowToken1 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task1.addChild(appWindowToken1, 0); appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); final Task task2 = createTaskInStack(stack, 1 /* userId */); - TestAppWindowToken appWindowToken2 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task2.addChild(appWindowToken2, 0); appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); @@ -85,12 +84,14 @@ public class TaskStackTests extends WindowTestsBase { public void testMoveTaskToBackDifferentStackOrientation() throws Exception { final TaskStack stack = createTaskStackOnDisplay(sDisplayContent); final Task task1 = createTaskInStack(stack, 0 /* userId */); - TestAppWindowToken appWindowToken1 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task1.addChild(appWindowToken1, 0); appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); final Task task2 = createTaskInStack(stack, 1 /* userId */); - TestAppWindowToken appWindowToken2 = new TestAppWindowToken(sDisplayContent); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + new WindowTestUtils.TestAppWindowToken(sDisplayContent); task2.addChild(appWindowToken2, 0); appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java index f79908e906c7..1819c56735eb 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java @@ -16,14 +16,9 @@ package com.android.server.wm; -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - -import android.hardware.display.DisplayManagerGlobal; import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.Display; -import android.view.DisplayInfo; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,10 +40,10 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer() throws Exception { - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(); - final TestAppWindowContainerController appController = - new TestAppWindowContainerController(taskController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); taskController.removeContainer(); // Assert that the container was removed. @@ -58,12 +53,12 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { @Test public void testRemoveContainer_deferRemoval() throws Exception { - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(); - final TestAppWindowContainerController appController = - new TestAppWindowContainerController(taskController); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); - final TestTask task = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; final AppWindowToken app = appController.mContainer; task.mShouldDeferRemoval = true; @@ -85,12 +80,12 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { public void testReparent() throws Exception { final StackWindowController stackController1 = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stackController1); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController1); final StackWindowController stackController2 = createStackControllerOnDisplay(sDisplayContent); - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stackController2); + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stackController2); boolean gotException = false; try { @@ -114,8 +109,8 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { taskController.reparent(stackController2, 0); assertEquals(stackController2.mContainer, taskController.mContainer.getParent()); - assertEquals(0, ((TestTask) taskController.mContainer).positionInParent()); - assertEquals(1, ((TestTask) taskController2.mContainer).positionInParent()); + assertEquals(0, ((WindowTestUtils.TestTask) taskController.mContainer).positionInParent()); + assertEquals(1, ((WindowTestUtils.TestTask) taskController2.mContainer).positionInParent()); } @Test @@ -124,9 +119,9 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { final StackWindowController stack1Controller = createStackControllerOnDisplay(sDisplayContent); final TaskStack stack1 = stack1Controller.mContainer; - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(stack1Controller); - final TestTask task1 = (TestTask) taskController.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; task1.mOnDisplayChangedCalled = false; assertEquals(sDisplayContent, stack1.getDisplayContent()); @@ -134,9 +129,10 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); final StackWindowController stack2Controller = createStackControllerOnDisplay(dc); final TaskStack stack2 = stack2Controller.mContainer; - final TestTaskWindowContainerController taskController2 = - new TestTaskWindowContainerController(stack2Controller); - final TestTask task2 = (TestTask) taskController2.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stack2Controller); + final WindowTestUtils.TestTask task2 = + (WindowTestUtils.TestTask) taskController2.mContainer; // Reparent and check state taskController.reparent(stack2Controller, 0); diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index cf8af6716d34..0eaf5bc3e940 100644 --- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -46,7 +46,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testFlow() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token); sWm.mUnknownAppVisibilityController.notifyRelayouted(token); @@ -58,8 +58,8 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testMultiple() throws Exception { - final AppWindowToken token1 = new TestAppWindowToken(sDisplayContent); - final AppWindowToken token2 = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token1 = new WindowTestUtils.TestAppWindowToken(sDisplayContent); + final AppWindowToken token2 = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token1); sWm.mUnknownAppVisibilityController.notifyAppResumedFinished(token1); sWm.mUnknownAppVisibilityController.notifyLaunched(token2); @@ -74,7 +74,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testClear() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.clear();; assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); @@ -82,7 +82,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { @Test public void testAppRemoved() throws Exception { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); sWm.mUnknownAppVisibilityController.notifyLaunched(token); sWm.mUnknownAppVisibilityController.appRemoved(token); assertTrue(sWm.mUnknownAppVisibilityController.allResolved()); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index 2e6eac091ba8..a2aa058ac063 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -102,7 +102,7 @@ public class WindowFrameTests extends WindowTestsBase { // Just any non zero value. sWm.mSystemDecorLayer = 10000; - mWindowToken = new TestAppWindowToken(sWm.getDefaultDisplayContentLocked()); + mWindowToken = new WindowTestUtils.TestAppWindowToken(sWm.getDefaultDisplayContentLocked()); mStubStack = new TaskStack(sWm, 0); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java new file mode 100644 index 000000000000..3a443575332e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2017 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.wm; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; +import android.view.IApplicationToken; +import android.view.IWindow; +import android.view.WindowManager; + +import static android.app.AppOpsManager.OP_NONE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.res.Configuration.EMPTY; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static org.mockito.Mockito.mock; + +/** + * A collection of static functions that can be referenced by other test packages to provide access + * to WindowManager related test functionality. + */ +public class WindowTestUtils { + public static int sNextTaskId = 0; + + /** + * Retrieves an instance of {@link WindowManagerService}, creating it if necessary. + */ + public static WindowManagerService getWindowManagerService(Context context) { + return TestWindowManagerPolicy.getWindowManagerService(context); + } + + /** + * Creates a mock instance of {@link StackWindowController}. + */ + public static StackWindowController createMockStackWindowContainerController() { + StackWindowController controller = mock(StackWindowController.class); + controller.mContainer = mock(TestTaskStack.class); + return controller; + } + + /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ + public static Task createTaskInStack(WindowManagerService service, TaskStack stack, + int userId) { + final Task newTask = new Task(WindowTestUtils.sNextTaskId++, stack, userId, service, null, + EMPTY, 0, false, + false, new ActivityManager.TaskDescription(), null); + stack.addTask(newTask, POSITION_TOP); + return newTask; + } + + /** + * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not + * normally be mocked out. + */ + public static class TestTaskStack extends TaskStack { + TestTaskStack(WindowManagerService service, int stackId) { + super(service, stackId); + } + + @Override + void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { + // Do nothing. + } + } + + /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ + public static class TestAppWindowToken extends AppWindowToken { + + TestAppWindowToken(DisplayContent dc) { + super(WindowTestsBase.sWm, null, false, dc, true /* fillsParent */, + null /* overrideConfig */, null /* bounds */); + } + + TestAppWindowToken(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller, + Configuration overrideConfig, Rect bounds) { + super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, + showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, + launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + WindowState getFirstChild() { + return mChildren.getFirst(); + } + + WindowState getLastChild() { + return mChildren.getLast(); + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + } + + /* Used so we can gain access to some protected members of the {@link WindowToken} class */ + public static class TestWindowToken extends WindowToken { + int adj = 0; + + TestWindowToken(int type, DisplayContent dc) { + this(type, dc, false /* persistOnEmpty */); + } + + TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { + super(WindowTestsBase.sWm, mock(IBinder.class), type, persistOnEmpty, dc, + false /* ownerCanManageAppTokens */); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + @Override + int getAnimLayerAdjustment() { + return adj; + } + } + + /* Used so we can gain access to some protected members of the {@link Task} class */ + public static class TestTask extends Task { + boolean mShouldDeferRemoval = false; + boolean mOnDisplayChangedCalled = false; + private boolean mUseLocalIsAnimating = false; + private boolean mIsAnimating = false; + + TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, + Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, + boolean homeTask, TaskWindowContainerController controller) { + super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode, + supportsPictureInPicture, homeTask, new ActivityManager.TaskDescription(), + controller); + } + + boolean shouldDeferRemoval() { + return mShouldDeferRemoval; + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + + @Override + void onDisplayChanged(DisplayContent dc) { + super.onDisplayChanged(dc); + mOnDisplayChangedCalled = true; + } + + @Override + boolean isAnimating() { + return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating(); + } + + void setLocalIsAnimating(boolean isAnimating) { + mUseLocalIsAnimating = true; + mIsAnimating = isAnimating; + } + } + + /** + * Used so we can gain access to some protected members of {@link TaskWindowContainerController} + * class. + */ + public static class TestTaskWindowContainerController extends TaskWindowContainerController { + + TestTaskWindowContainerController() { + this(WindowTestsBase.createStackControllerOnDisplay(WindowTestsBase.sDisplayContent)); + } + + TestTaskWindowContainerController(StackWindowController stackController) { + super(sNextTaskId++, new TaskWindowContainerListener() { + @Override + public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) { + + } + + @Override + public void requestResize(Rect bounds, int resizeMode) { + + } + }, stackController, 0 /* userId */, null /* bounds */, + EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, + false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/, + true /* showForAllUsers */, new ActivityManager.TaskDescription(), WindowTestsBase.sWm); + } + + @Override + TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, + Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, + boolean homeTask, ActivityManager.TaskDescription taskDescription) { + return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode, + supportsPictureInPicture, homeTask, this); + } + } + + public static class TestAppWindowContainerController extends AppWindowContainerController { + + final IApplicationToken mToken; + + TestAppWindowContainerController(TestTaskWindowContainerController taskController) { + this(taskController, new TestIApplicationToken()); + } + + TestAppWindowContainerController(TestTaskWindowContainerController taskController, + IApplicationToken token) { + super(taskController, token, null /* listener */, 0 /* index */, + SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */, + true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, + false /* launchTaskBehind */, false /* alwaysFocusable */, + 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, + 0 /* inputDispatchingTimeoutNanos */, WindowTestsBase.sWm, null /* overrideConfig */, + null /* bounds */); + mToken = token; + } + + @Override + AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller, + Configuration overrideConfig, Rect bounds) { + return new TestAppWindowToken(service, token, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, + orientation, + rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, + controller, overrideConfig, bounds); + } + + AppWindowToken getAppWindowToken() { + return (AppWindowToken) WindowTestsBase.sDisplayContent.getWindowToken(mToken.asBinder()); + } + } + + public static class TestIApplicationToken implements IApplicationToken { + + private final Binder mBinder = new Binder(); + @Override + public IBinder asBinder() { + return mBinder; + } + } + + /** Used to track resize reports. */ + public static class TestWindowState extends WindowState { + boolean resizeReported; + + TestWindowState(WindowManagerService service, Session session, IWindow window, + WindowManager.LayoutParams attrs, WindowToken token) { + super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, + false /* ownerCanAddInternalSystemWindow */); + } + + @Override + void reportResized() { + super.reportResized(); + resizeReported = true; + } + + @Override + public boolean isGoneForLayoutLw() { + return false; + } + + @Override + void updateResizingWindowIfNeeded() { + // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive + // the system that it can actually update the window. + boolean hadSurface = mHasSurface; + mHasSurface = true; + + super.updateResizingWindowIfNeeded(); + + mHasSurface = hadSurface; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index a9d930f5c893..eaf4ac4baf4f 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -19,21 +19,16 @@ package com.android.server.wm; import static android.view.View.VISIBLE; import android.app.ActivityManager.TaskDescription; -import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; -import android.os.Binder; import android.view.Display; import android.view.DisplayInfo; -import android.view.IApplicationToken; import org.junit.Assert; import org.junit.After; import org.junit.Before; import org.mockito.MockitoAnnotations; -import android.app.ActivityManager.TaskSnapshot; import android.content.Context; -import android.os.IBinder; import android.support.test.InstrumentationRegistry; import android.view.IWindow; import android.view.WindowManager; @@ -41,10 +36,6 @@ import android.view.WindowManager; import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.AppOpsManager.OP_NONE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; -import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -58,7 +49,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.mockito.Mockito.mock; import com.android.server.AttributeCache; @@ -78,8 +68,7 @@ class WindowTestsBase { // make sure we don't collide with any existing display. If we run into no other display, the // added display should be treated as default. This cannot be the default display private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1; - static int sNextStackId = FIRST_DYNAMIC_STACK_ID; - private static int sNextTaskId = 0; + private static int sNextStackId = FIRST_DYNAMIC_STACK_ID; private static boolean sOneTimeSetupDone = false; static DisplayContent sDisplayContent; @@ -184,14 +173,14 @@ class WindowTestsBase { private static WindowToken createWindowToken(DisplayContent dc, int stackId, int type) { if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { - return new TestWindowToken(type, dc); + return new WindowTestUtils.TestWindowToken(type, dc); } final TaskStack stack = stackId == INVALID_STACK_ID ? createTaskStackOnDisplay(dc) : createStackControllerOnStackOnDisplay(stackId, dc).mContainer; final Task task = createTaskInStack(stack, 0 /* userId */); - final TestAppWindowToken token = new TestAppWindowToken(dc); + final WindowTestUtils.TestAppWindowToken token = new WindowTestUtils.TestAppWindowToken(dc); task.addChild(token, 0); return token; } @@ -209,7 +198,7 @@ class WindowTestsBase { } WindowState createAppWindow(Task task, int type, String name) { - final AppWindowToken token = new TestAppWindowToken(sDisplayContent); + final AppWindowToken token = new WindowTestUtils.TestAppWindowToken(sDisplayContent); task.addChild(token, 0); return createWindow(null, type, token, name); } @@ -260,10 +249,7 @@ class WindowTestsBase { /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ static Task createTaskInStack(TaskStack stack, int userId) { - final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, 0, false, - false, new TaskDescription(), null); - stack.addTask(newTask, POSITION_TOP); - return newTask; + return WindowTestUtils.createTaskInStack(sWm, stack, userId); } /** Creates a {@link DisplayContent} and adds it to the system. */ @@ -274,227 +260,10 @@ class WindowTestsBase { return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm)); } - /* Used so we can gain access to some protected members of the {@link WindowToken} class */ - static class TestWindowToken extends WindowToken { - int adj = 0; - - TestWindowToken(int type, DisplayContent dc) { - this(type, dc, false /* persistOnEmpty */); - } - - TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { - super(sWm, mock(IBinder.class), type, persistOnEmpty, dc, - false /* ownerCanManageAppTokens */); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - @Override - int getAnimLayerAdjustment() { - return adj; - } - } - - /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ - static class TestAppWindowToken extends AppWindowToken { - - TestAppWindowToken(DisplayContent dc) { - super(sWm, null, false, dc, true /* fillsParent */, null /* overrideConfig */, - null /* bounds */); - } - - TestAppWindowToken(WindowManagerService service, IApplicationToken token, - boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, - Configuration overrideConfig, Rect bounds) { - super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, - showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, - launchTaskBehind, alwaysFocusable, controller, overrideConfig, bounds); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - WindowState getFirstChild() { - return mChildren.getFirst(); - } - - WindowState getLastChild() { - return mChildren.getLast(); - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - } - - /* Used so we can gain access to some protected members of the {@link Task} class */ - class TestTask extends Task { - - boolean mShouldDeferRemoval = false; - boolean mOnDisplayChangedCalled = false; - private boolean mUseLocalIsAnimating = false; - private boolean mIsAnimating = false; - - TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, - Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, - boolean homeTask, TaskWindowContainerController controller) { - super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode, - supportsPictureInPicture, homeTask, new TaskDescription(), controller); - } - - boolean shouldDeferRemoval() { - return mShouldDeferRemoval; - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - super.onDisplayChanged(dc); - mOnDisplayChangedCalled = true; - } - - @Override - boolean isAnimating() { - return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating(); - } - - void setLocalIsAnimating(boolean isAnimating) { - mUseLocalIsAnimating = true; - mIsAnimating = isAnimating; - } - } - - /** - * Used so we can gain access to some protected members of {@link TaskWindowContainerController} - * class. - */ - class TestTaskWindowContainerController extends TaskWindowContainerController { - - TestTaskWindowContainerController() { - this(createStackControllerOnDisplay(sDisplayContent)); - } - - TestTaskWindowContainerController(StackWindowController stackController) { - super(sNextTaskId++, new TaskWindowContainerListener() { - @Override - public void onSnapshotChanged(TaskSnapshot snapshot) { - - } - - @Override - public void requestResize(Rect bounds, int resizeMode) { - - } - }, stackController, 0 /* userId */, null /* bounds */, - EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, - false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/, - true /* showForAllUsers */, new TaskDescription(), sWm); - } - - @Override - TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, - Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, - boolean homeTask, TaskDescription taskDescription) { - return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode, - supportsPictureInPicture, homeTask, this); - } - } - - class TestAppWindowContainerController extends AppWindowContainerController { - - final IApplicationToken mToken; - - TestAppWindowContainerController(TestTaskWindowContainerController taskController) { - this(taskController, new TestIApplicationToken()); - } - - TestAppWindowContainerController(TestTaskWindowContainerController taskController, - IApplicationToken token) { - super(taskController, token, null /* listener */, 0 /* index */, - SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */, - true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, - false /* launchTaskBehind */, false /* alwaysFocusable */, - 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, - 0 /* inputDispatchingTimeoutNanos */, sWm, null /* overrideConfig */, - null /* bounds */); - mToken = token; - } - - @Override - AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, - boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, - Configuration overrideConfig, Rect bounds) { - return new TestAppWindowToken(service, token, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, - rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller, overrideConfig, bounds); - } - - AppWindowToken getAppWindowToken() { - return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder()); - } + /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */ + WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs, + WindowToken token) { + return new WindowTestUtils.TestWindowState(sWm, sMockSession, sIWindow, attrs, token); } - class TestIApplicationToken implements IApplicationToken { - - private final Binder mBinder = new Binder(); - @Override - public IBinder asBinder() { - return mBinder; - } - } - - /** Used to track resize reports. */ - class TestWindowState extends WindowState { - boolean resizeReported; - - TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) { - super(sWm, sMockSession, sIWindow, token, null, OP_NONE, 0, attrs, 0, 0, - false /* ownerCanAddInternalSystemWindow */); - } - - @Override - void reportResized() { - super.reportResized(); - resizeReported = true; - } - - @Override - public boolean isGoneForLayoutLw() { - return false; - } - - @Override - void updateResizingWindowIfNeeded() { - // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive - // the system that it can actually update the window. - boolean hadSurface = mHasSurface; - mHasSurface = true; - - super.updateResizingWindowIfNeeded(); - - mHasSurface = hadSurface; - } - } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java index babb6d9db31f..4f7ad41050f1 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java @@ -46,7 +46,8 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testAddWindow() throws Exception { - final TestWindowToken token = new TestWindowToken(0, sDisplayContent); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(0, sDisplayContent); assertEquals(0, token.getWindowsCount()); @@ -76,7 +77,7 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testChildRemoval() throws Exception { final DisplayContent dc = sDisplayContent; - final TestWindowToken token = new TestWindowToken(0, dc); + final WindowTestUtils.TestWindowToken token = new WindowTestUtils.TestWindowToken(0, dc); assertEquals(token, dc.getWindowToken(token.token)); @@ -95,7 +96,8 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testAdjustAnimLayer() throws Exception { - final TestWindowToken token = new TestWindowToken(0, sDisplayContent); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(0, sDisplayContent); final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11"); final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12"); @@ -135,8 +137,9 @@ public class WindowTokenTests extends WindowTestsBase { */ @Test public void testTokenRemovalProcess() throws Exception { - final TestWindowToken token = - new TestWindowToken(TYPE_TOAST, sDisplayContent, true /* persistOnEmpty */); + final WindowTestUtils.TestWindowToken token = + new WindowTestUtils.TestWindowToken(TYPE_TOAST, sDisplayContent, + true /* persistOnEmpty */); // Verify that the token is on the display assertNotNull(sDisplayContent.getWindowToken(token.token)); |