Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d9c7be8 authored by Craig Mautner's avatar Craig Mautner
Browse files

Begin switch over to task based history.

- Introduce the task history and add to and remove from it with
verification.

Change-Id: If97e74f5a13f85acdb1521fc6d0b066a7e8584ae
parent 278dac91
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -471,6 +471,7 @@ final class ActivityRecord {
    void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
        if (inHistory && !finishing) {
            if (task != null) {
                task.removeActivity(this);
                task.numActivities--;
            }
            if (newTask != null) {
@@ -505,6 +506,7 @@ final class ActivityRecord {
            inHistory = false;
            if (task != null && !finishing) {
                task.numActivities--;
                task = null;
            }
            clearOptionsLocked();
        }
@@ -539,7 +541,7 @@ final class ActivityRecord {
        ActivityResult r = new ActivityResult(from, resultWho,
        		requestCode, resultCode, resultData);
        if (results == null) {
            results = new ArrayList();
            results = new ArrayList<ResultInfo>();
        }
        results.add(r);
    }
@@ -950,6 +952,8 @@ final class ActivityRecord {
        StringBuilder sb = new StringBuilder(128);
        sb.append("ActivityRecord{");
        sb.append(Integer.toHexString(System.identityHashCode(this)));
        sb.append(" t");
        sb.append(task.taskId);
        sb.append(" u");
        sb.append(userId);
        sb.append(' ');
+329 −11
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -66,6 +67,7 @@ import android.os.UserHandle;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;

import java.io.FileDescriptor;
@@ -75,6 +77,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

/**
 * State and management of a single stack of activities.
@@ -98,6 +101,7 @@ final class ActivityStack {
    static final boolean DEBUG_APP = false;

    static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
    static final boolean VALIDATE_TASK_REPLACE = true;

    // How long we wait until giving up on the last activity telling us it
    // is idle.
@@ -138,6 +142,9 @@ final class ActivityStack {
    // is being started.
    static final boolean SHOW_APP_STARTING_PREVIEW = true;

    static final boolean FORWARD_ITERATOR = false;
    static final boolean REVERSE_ITERATOR = true;

    enum ActivityState {
        INITIALIZING,
        RESUMED,
@@ -161,6 +168,17 @@ final class ActivityStack {
     */
    private final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();

    /**
     * The back history of all previous (and possibly still
     * running) activities.  It contains #TaskRecord objects.
     */
    private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>();

    /**
     * Mapping from taskId to TaskRecord
     */
    private SparseArray<TaskRecord> mTaskIdToTaskRecord = new SparseArray<TaskRecord>();

    /**
     * Used for validating app tokens with window manager.
     */
@@ -289,6 +307,12 @@ final class ActivityStack {
     */
    boolean mDismissKeyguardOnNextActivity = false;

    /** So we don't have to keep constructing a new object for utility non-nested use. */
    final ActivityIterator mTmpActivityIterator = new ActivityIterator(FORWARD_ITERATOR, true);

    /** So we don't have to keep constructing a new object for utility non-nested use. */
    final TaskIterator mTmpTaskIterator = new TaskIterator();

    /**
     * Save the most recent screenshot for reuse. This keeps Recents from taking two identical
     * screenshots, one for the Recents thumbnail and one for the pauseActivity thumbnail.
@@ -508,29 +532,68 @@ final class ActivityStack {
    }

    final ActivityRecord isInStackLocked(IBinder token) {
        ActivityRecord newAr = newIsInStackLocked(token);

        ActivityRecord r = ActivityRecord.forToken(token);
        if (mHistory.contains(r)) {
            if (VALIDATE_TASK_REPLACE && newAr != r) Slog.w(TAG,
                    "isInStackLocked: mismatch: newAr=" + newAr + " r=" + r);
            return r;
        }
        if (VALIDATE_TASK_REPLACE && newAr != null) Slog.w(TAG,
                "isInStackLocked: mismatch: newAr!=null");
        return null;
    }

    final ActivityRecord newIsInStackLocked(IBinder token) {
        final ActivityRecord r = ActivityRecord.forToken(token);
        if (r != null) {
            final TaskRecord task = r.task;
            if (mTaskHistory.contains(task) && task.mActivities.contains(r)) {
                return r;
            }
        }
        return null;
    }

    int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
        int newTaskId = newGetTaskForActivityLocked(token, onlyRoot);

        TaskRecord lastTask = null;
        final int N = mHistory.size();
        for (int i = 0; i < N; i++) {
            ActivityRecord r = mHistory.get(i);
            if (r.appToken == token) {
                if (!onlyRoot || lastTask != r.task) {
                    if (VALIDATE_TASK_REPLACE && newTaskId != r.task.taskId) Slog.w(TAG,
                            "getTaskForActivityLocked: mismatch: new=" + newTaskId
                            + " taskId=" + r.task.taskId);
                    return r.task.taskId;
                }
                if (VALIDATE_TASK_REPLACE && newTaskId != -1) Slog.w(TAG,
                    "getTaskForActivityLocked: mismatch: newTaskId=" + newTaskId + " not -1.");
                return -1;
            }
            lastTask = r.task;
        }

        if (VALIDATE_TASK_REPLACE && newTaskId != -1) Slog.w(TAG,
            "getTaskForActivityLocked: mismatch at end: newTaskId=" + newTaskId + " not -1.");
        return -1;
    }

    int newGetTaskForActivityLocked(IBinder token, boolean onlyRoot) {
        final ActivityRecord r = ActivityRecord.forToken(token);
        if (r == null) {
            return -1;
        }
        final TaskRecord task = r.task;
        switch (task.mActivities.indexOf(r)) {
            case -1: return -1;
            case 0: return task.taskId;
            default: return onlyRoot ? -1 : task.taskId;
        }
    }

    private final boolean updateLRUListLocked(ActivityRecord r) {
        final boolean hadit = mLRUActivities.remove(r);
@@ -624,17 +687,29 @@ final class ActivityStack {
     * @return whether there are any activities for the specified user.
     */
    final boolean switchUserLocked(int userId, UserStartedState uss) {
        if (VALIDATE_TOKENS) {
            validateAppTokensLocked();
        }
        final boolean newResult = newSwitchUserLocked(userId, uss);

        mCurrentUser = userId;
        mStartingUsers.add(uss);

        // Only one activity? Nothing to do...
        if (mHistory.size() < 2)
        if (mHistory.size() < 2) {
            if (VALIDATE_TASK_REPLACE && newResult) Slog.w(TAG,
                    "switchUserLocked: mismatch: " + newResult + " " + false);
            return false;
        }

        boolean haveActivities = false;
        // Check if the top activity is from the new user.
        ActivityRecord top = mHistory.get(mHistory.size() - 1);
        if (top.userId == userId) return true;
        if (top.userId == userId) {
            if (VALIDATE_TASK_REPLACE && !newResult) Slog.w(TAG,
                    "switchUserLocked: mismatch: " + newResult + " " + true);
            return true;
        }
        // Otherwise, move the user's activities to the top.
        int N = mHistory.size();
        int i = 0;
@@ -651,7 +726,44 @@ final class ActivityStack {
            }
        }
        // Transition from the old top to the new top
        if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
                "switchUserLocked: calling resumeTopActivity " + top);
        resumeTopActivityLocked(top);
        if (VALIDATE_TASK_REPLACE && (newResult != haveActivities)) Slog.w(TAG,
                    "switchUserLocked: mismatch: " + newResult + " " + haveActivities);
        return haveActivities;
    }

    /*
     * Move the activities around in the stack to bring a user to the foreground.
     * @return whether there are any activities for the specified user.
     */
    final boolean newSwitchUserLocked(int userId, UserStartedState uss) {
//        mStartingUsers.add(uss);
        if (mCurrentUser == userId) {
            return true;
        }
        mCurrentUser = userId;

        // Move userId's tasks to the top.
        boolean haveActivities = false;
        TaskRecord task = null;
        int index = mTaskHistory.size();
        for (int i = 0; i < index; ++i) {
            task = mTaskHistory.get(i);
            if (task.userId == userId) {
                haveActivities = true;
                mTaskHistory.remove(i);
                mTaskHistory.add(task);
                --index;
            }
        }

        // task is now the original topmost TaskRecord. Transition from the old top to the new top.
        ActivityRecord top = task != null ? task.getTopActivity() : null;
        if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
                "newSwitchUserLocked: would call resumeTopActivity " + top);
//        resumeTopActivityLocked(top);
        return haveActivities;
    }

@@ -889,6 +1001,9 @@ final class ActivityStack {
            mGoingToSleep.release();
        }
        // Ensure activities are no longer sleeping.
        if (VALIDATE_TASK_REPLACE) {
            verifyActivityRecords();
        }
        for (int i=mHistory.size()-1; i>=0; i--) {
            ActivityRecord r = mHistory.get(i);
            r.setSleeping(false);
@@ -933,6 +1048,9 @@ final class ActivityStack {

            // Make sure any stopped but visible activities are now sleeping.
            // This ensures that the activity's onStop() is called.
            if (VALIDATE_TASK_REPLACE) {
                verifyActivityRecords();
            }
            for (int i=mHistory.size()-1; i>=0; i--) {
                ActivityRecord r = mHistory.get(i);
                if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
@@ -1077,6 +1195,9 @@ final class ActivityStack {
        ActivityRecord r = null;

        synchronized (mService) {
            if (VALIDATE_TASK_REPLACE) {
                verifyActivityRecords();
            }
            int index = indexOfTokenLocked(token);
            if (index >= 0) {
                r = mHistory.get(index);
@@ -1094,6 +1215,9 @@ final class ActivityStack {
        ActivityRecord r = null;

        synchronized (mService) {
            if (VALIDATE_TASK_REPLACE) {
                verifyActivityRecords();
            }
            int index = indexOfTokenLocked(token);
            if (index >= 0) {
                r = mHistory.get(index);
@@ -1306,6 +1430,9 @@ final class ActivityStack {

        // If the top activity is not fullscreen, then we need to
        // make sure any activities under it are now visible.
        if (VALIDATE_TASK_REPLACE) {
            verifyActivityRecords();
        }
        final int count = mHistory.size();
        int i = count-1;
        while (mHistory.get(i) != top) {
@@ -1868,6 +1995,7 @@ final class ActivityStack {
                            Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos,
                                    here);
                        }
                        r.task.addActivityToTop(r);
                        mHistory.add(addPos, r);
                        r.putInHistory();
                        mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
@@ -1907,6 +2035,7 @@ final class ActivityStack {
            here.fillInStackTrace();
            Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos, here);
        }
        r.task.addActivityToTop(r);
        mHistory.add(addPos, r);
        r.putInHistory();
        r.frontOfTask = newTask;
@@ -1986,6 +2115,9 @@ final class ActivityStack {
        if (doResume) {
            resumeTopActivityLocked(null);
        }
        if (VALIDATE_TASK_REPLACE) {
            verifyActivityRecords();
        }
    }

    final void validateAppTokensLocked() {
@@ -2107,8 +2239,8 @@ final class ActivityStack {
                            if (mService.mCurTask <= 0) {
                                mService.mCurTask = 1;
                            }
                            target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
                                    null, false);
                            target.setTask(createTaskRecord(mService.mCurTask, target.info, null,
                                    false), null, false);
                            target.task.affinityIntent = target.intent;
                            if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
                                    + " out to new task " + target.task);
@@ -2172,8 +2304,7 @@ final class ActivityStack {
                            // like these are all in the reply chain.
                            replyChainEnd = targetI+1;
                            while (replyChainEnd < mHistory.size() &&
                                    (mHistory.get(
                                                replyChainEnd)).task == task) {
                                    (mHistory.get(replyChainEnd)).task == task) {
                                replyChainEnd++;
                            }
                            replyChainEnd--;
@@ -2346,6 +2477,9 @@ final class ActivityStack {
            }
        }

        if (VALIDATE_TASK_REPLACE) {
            verifyActivityRecords();
        }
        return taskTop;
    }
    
@@ -3011,7 +3145,7 @@ final class ActivityStack {
                if (mService.mCurTask <= 0) {
                    mService.mCurTask = 1;
                }
                r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
                r.setTask(createTaskRecord(mService.mCurTask, r.info, intent, true), null, true);
                if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                        + " in new task " + r.task);
            } else {
@@ -3075,7 +3209,7 @@ final class ActivityStack {
                N > 0 ? mHistory.get(N-1) : null;
            r.setTask(prev != null
                    ? prev.task
                    : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
                    : createTaskRecord(mService.mCurTask, r.info, intent, true), null, true);
            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                    + " in new guessed " + r.task);
        }
@@ -4160,6 +4294,9 @@ final class ActivityStack {
            here.fillInStackTrace();
            Slog.i(TAG, "Removing activity " + r + " from stack");
        }
        if (r.task != null) {
            r.task.removeActivity(r);
        }
        mHistory.remove(r);
        r.takeFromHistory();
        removeTimeoutsForActivityLocked(r);
@@ -5169,4 +5306,185 @@ final class ActivityStack {

        return starting;
    }

    void verifyActivityRecords() {
        /* Until we have activity movement implemented for tasks just do the simple test
        ActivityIterator iterator = new ActivityIterator();
        int i;
        int N = mHistory.size();
        for (i = 0; i < N && iterator.hasNext(); ++i) {
            ActivityRecord r1 = mHistory.get(i);
            ActivityRecord r2 = iterator.next();
            if (r1 != r2) {
                break;
            }
        }
        if (i != N || iterator.hasNext()) {
            Slog.w(TAG, "verifyActivityRecords mHistory=" + mHistory
                + " mTaskHistory=" + iterator + " Callers=" + Debug.getCallers(2));
        } */
        // Simple test
        ActivityIterator iterator = new ActivityIterator();
        while (iterator.hasNext()) {
            ActivityRecord r = iterator.next();
            if (!mHistory.contains(r)) {
                break;
            }
        }
        if (iterator.size() != mHistory.size() || iterator.hasNext()) {
            Slog.w(TAG, "verifyActivityRecords mHistory=" + mHistory
                + " mTaskHistory=" + iterator + " Callers=" + Debug.getCallers(2));
        }
    }

    private TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
            boolean toTop) {
        TaskRecord oldTask = mTaskIdToTaskRecord.get(taskId);
        if (oldTask != null) {
            Slog.w(TAG, "createTaskRecord: Reusing taskId=" + taskId + " without removing");
            mTaskHistory.remove(oldTask);
        }
        TaskRecord task = new TaskRecord(taskId, info, intent);
        mTaskIdToTaskRecord.put(taskId, task);
        if (toTop) {
            mTaskHistory.add(task);
        } else {
            mTaskHistory.add(0, task);
        }
        return task;
    }

    class TaskIterator implements Iterator<TaskRecord> {
        private int mCur;
        private boolean mReverse;

        TaskIterator() {
            this(FORWARD_ITERATOR);
        }

        TaskIterator(boolean reverse) {
            reset(reverse);
        }

        public void reset(boolean reverse) {
            mReverse = reverse;
            mCur = reverse ? mTaskHistory.size() - 1 : 0;
        }

        @Override
        public boolean hasNext() {
            if (mReverse) {
                return mCur >= 0;
            }
            return mCur < mTaskHistory.size();
        }

        @Override
        public TaskRecord next() {
            if (hasNext()) {
                TaskRecord task = mTaskHistory.get(mCur);
                mCur += (mReverse ? -1 : 1);
                return task;
            }
            throw new NoSuchElementException();
        }

        @Override
        public void remove() {
            throw new IllegalArgumentException();
        }
    }

    class ActivityIterator implements Iterator<ActivityRecord> {
        final TaskIterator mIterator;
        boolean mReverse;
        int mCur;
        TaskRecord mTaskRecord;
        final boolean mSkipFinishing;

        public ActivityIterator() {
            this(FORWARD_ITERATOR);
        }

        public ActivityIterator(boolean reverse) {
            this(reverse, false);
        }

        public ActivityIterator(boolean reverse, boolean skipFinishing) {
            mSkipFinishing = skipFinishing;
            mIterator = new TaskIterator();
            reset(reverse);
        }

        public void reset(boolean reverse) {
            mReverse = reverse;
            mIterator.reset(reverse);
            getNextTaskRecord();
        }

        private void getNextTaskRecord() {
            if (mIterator.hasNext()) {
                mTaskRecord = mIterator.next();
                mCur = mReverse ? mTaskRecord.mActivities.size() - 1 : 0;
            }
        }

        @Override
        public boolean hasNext() {
            if (mTaskRecord == null) {
                return false;
            }
            if (mReverse) {
                return mCur >= 0;
            }
            return mCur < mTaskRecord.mActivities.size();
        }

        @Override
        public ActivityRecord next() {
            while (hasNext()) {
                ActivityRecord r = mTaskRecord.mActivities.get(mCur);
                mCur += mReverse ? -1 : 1;
                if (!hasNext()) {
                    getNextTaskRecord();
                }
                if (mSkipFinishing && r.finishing) {
                    continue;
                }
                return r;
            }
            throw new NoSuchElementException();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

        int size() {
            int size = 0;
            final TaskIterator iterator = new TaskIterator();
            while (iterator.hasNext()) {
                size += iterator.next().mActivities.size();
            }
            return size;
        }

        ActivityRecord peek() {
            if (mTaskRecord != null && mCur >= 0 && mCur < mTaskRecord.mActivities.size()) {
                return mTaskRecord.mActivities.get(mCur);
            }
            return null;
        }

        @Override
        public String toString() {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < mTaskHistory.size(); ++i) {
                final TaskRecord task = mTaskHistory.get(i);
                sb.append("task_").append(i).append("-").append(task.mActivities).append(" ");
            }
            return sb.toString();
        }
    }
}
+53 −7
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.os.UserHandle;
import android.util.Slog;

import java.io.PrintWriter;
import java.util.ArrayList;

class TaskRecord extends ThumbnailHolder {
    final int taskId;       // Unique identifier for this task.
@@ -40,6 +41,10 @@ class TaskRecord extends ThumbnailHolder {
    String stringName;      // caching of toString() result.
    int userId;             // user for which this task was created

    int numFullscreen;      // Number of fullscreen activities.

    final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();

    TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
        taskId = _taskId;
        affinity = info.taskAffinity;
@@ -105,11 +110,48 @@ class TaskRecord extends ThumbnailHolder {
        }
    }

    ActivityRecord getTopActivity() {
        for (int i = mActivities.size() - 1; i >= 0; --i) {
            final ActivityRecord r = mActivities.get(i);
            if (r.finishing) {
                continue;
            }
            return r;
        }
        return null;
    }

    void addActivityAtBottom(ActivityRecord r) {
        if (!mActivities.remove(r) && r.fullscreen) {
            // Was not previously in list.
            numFullscreen++;
        }
        mActivities.add(0, r);
    }

    void addActivityToTop(ActivityRecord r) {
        if (!mActivities.remove(r) && r.fullscreen) {
            // Was not previously in list.
            numFullscreen++;
        }
        mActivities.add(r);
    }

    /** @return true if this was the last activity in the task */
    boolean removeActivity(ActivityRecord r) {
        if (mActivities.remove(r) && r.fullscreen) {
            // Was previously in list.
            numFullscreen--;
        }
        return mActivities.size() == 0;
    }

    void dump(PrintWriter pw, String prefix) {
        if (numActivities != 0 || rootWasReset || userId != 0) {
            pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
                    pw.print(" rootWasReset="); pw.print(rootWasReset);
                    pw.print(" userId="); pw.println(userId);
                    pw.print(" userId="); pw.print(userId);
                    pw.print(" numFullscreen="); pw.println(numFullscreen);
        }
        if (affinity != null) {
            pw.print(prefix); pw.print("affinity="); pw.println(affinity);
@@ -136,6 +178,7 @@ class TaskRecord extends ThumbnailHolder {
            pw.print(prefix); pw.print("realActivity=");
            pw.println(realActivity.flattenToShortString());
        }
        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
        if (!askedCompatMode) {
            pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
        }
@@ -146,6 +189,7 @@ class TaskRecord extends ThumbnailHolder {
                pw.print((getInactiveDuration()/1000)); pw.println("s)");
    }

    @Override
    public String toString() {
        if (stringName != null) {
            return stringName;
@@ -156,19 +200,21 @@ class TaskRecord extends ThumbnailHolder {
        sb.append(" #");
        sb.append(taskId);
        if (affinity != null) {
            sb.append(" A ");
            sb.append(" A=");
            sb.append(affinity);
        } else if (intent != null) {
            sb.append(" I ");
            sb.append(" I=");
            sb.append(intent.getComponent().flattenToShortString());
        } else if (affinityIntent != null) {
            sb.append(" aI ");
            sb.append(" aI=");
            sb.append(affinityIntent.getComponent().flattenToShortString());
        } else {
            sb.append(" ??");
        }
        sb.append(" U ");
        sb.append(" U=");
        sb.append(userId);
        sb.append(" sz=");
        sb.append(mActivities.size());
        sb.append('}');
        return stringName = sb.toString();
    }
+8 −0
Original line number Diff line number Diff line
@@ -228,6 +228,10 @@ class DisplayContent {
        public void remove() {
            throw new IllegalArgumentException();
        }

        @Override public String toString() {
            return mTaskLists.toString();
        }
    }

    class AppTokenIterator implements Iterator<AppWindowToken> {
@@ -294,6 +298,10 @@ class DisplayContent {
            }
            return size;
        }

        @Override public String toString() {
            return mIterator.toString();
        }
    }

    public void dump(String prefix, PrintWriter pw) {
+5 −0
Original line number Diff line number Diff line
@@ -23,4 +23,9 @@ import java.util.ArrayList;
public class TaskGroup {
    public int taskId = -1;
    public ArrayList<IApplicationToken> tokens = new ArrayList<IApplicationToken>();

    @Override
    public String toString() {
        return "id=" + taskId + " tokens=" + tokens;
    }
}
Loading