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

Commit 2ade126e authored by Craig Mautner's avatar Craig Mautner
Browse files

Rework the task affiliate chain validation

The old chain was problematic. It started verification at the end of
the chain and then went no further. Then it went back down the
chain to the beginning with no checks. It also removed entries and
tried to add them back past the end of the ArrayList during boot
which caused the system process to shut down.

This method uses the ordering imposed by taskIds which is the same
order that the tasks were created. It fixes up and notes dropped
links and then reconstructs the list correctly.

Fixes bug 17467284.

Change-Id: I746ec2cdcc7ac6403278cfefc7a6db9b5549d226
parent df980618
Loading
Loading
Loading
Loading
+83 −45
Original line number Diff line number Diff line
@@ -412,7 +412,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     * List of intents that were used to start the most recent tasks.
     */
    ArrayList<TaskRecord> mRecentTasks;
    ArraySet<TaskRecord> mTmpRecents = new ArraySet<TaskRecord>();
    ArrayList<TaskRecord> mTmpRecents = new ArrayList<TaskRecord>();
    /**
     * For addAppTask: cached of the last activity component that was added.
@@ -3841,6 +3841,86 @@ public final class ActivityManagerService extends ActivityManagerNative
        mTaskPersister.wakeup(null, true);
    }
    // Sort by taskId
    private Comparator<TaskRecord> mTaskRecordComparator = new Comparator<TaskRecord>() {
        @Override
        public int compare(TaskRecord lhs, TaskRecord rhs) {
            return rhs.taskId - lhs.taskId;
        }
    };
    // Extract the affiliates of the chain containing mRecentTasks[start].
    private int processNextAffiliateChain(int start) {
        final TaskRecord startTask = mRecentTasks.get(start);
        final int affiliateId = startTask.mAffiliatedTaskId;
        // Quick identification of isolated tasks. I.e. those not launched behind.
        if (startTask.taskId == affiliateId && startTask.mPrevAffiliate == null &&
                startTask.mNextAffiliate == null) {
            // There is still a slim chance that there are other tasks that point to this task
            // and that the chain is so messed up that this task no longer points to them but
            // the gain of this optimization outweighs the risk.
            startTask.inRecents = true;
            return start + 1;
        }
        // Remove all tasks that are affiliated to affiliateId and put them in mTmpRecents.
        mTmpRecents.clear();
        for (int i = mRecentTasks.size() - 1; i >= start; --i) {
            final TaskRecord task = mRecentTasks.get(i);
            if (task.mAffiliatedTaskId == affiliateId) {
                mRecentTasks.remove(i);
                mTmpRecents.add(task);
            }
        }
        // Sort them all by taskId. That is the order they were create in and that order will
        // always be correct.
        Collections.sort(mTmpRecents, mTaskRecordComparator);
        // Go through and fix up the linked list.
        // The first one is the end of the chain and has no next.
        final TaskRecord first = mTmpRecents.get(0);
        first.inRecents = true;
        if (first.mNextAffiliate != null) {
            Slog.w(TAG, "Link error 1 first.next=" + first.mNextAffiliate);
            first.setNextAffiliate(null);
            mTaskPersister.wakeup(first, false);
        }
        // Everything in the middle is doubly linked from next to prev.
        final int tmpSize = mTmpRecents.size();
        for (int i = 0; i < tmpSize - 1; ++i) {
            final TaskRecord next = mTmpRecents.get(i);
            final TaskRecord prev = mTmpRecents.get(i + 1);
            if (next.mPrevAffiliate != prev) {
                Slog.w(TAG, "Link error 2 next=" + next + " prev=" + next.mPrevAffiliate +
                        " setting prev=" + prev);
                next.setPrevAffiliate(prev);
                mTaskPersister.wakeup(next, false);
            }
            if (prev.mNextAffiliate != next) {
                Slog.w(TAG, "Link error 3 prev=" + prev + " next=" + prev.mNextAffiliate +
                        " setting next=" + next);
                prev.setNextAffiliate(next);
                mTaskPersister.wakeup(prev, false);
            }
            prev.inRecents = true;
        }
        // The last one is the beginning of the list and has no prev.
        final TaskRecord last = mTmpRecents.get(tmpSize - 1);
        if (last.mPrevAffiliate != null) {
            Slog.w(TAG, "Link error 4 last.prev=" + last.mPrevAffiliate);
            last.setPrevAffiliate(null);
            mTaskPersister.wakeup(last, false);
        }
        // Insert the group back into mRecentTasks at start.
        mRecentTasks.addAll(start, mTmpRecents);
        // Let the caller know where we left off.
        return start + tmpSize;
    }
    /**
     * Update the recent tasks lists: make sure tasks should still be here (their
     * applications / activities still exist), update their availability, fixup ordering
@@ -3953,51 +4033,9 @@ public final class ActivityManagerService extends ActivityManagerNative
        }
        // Verify the affiliate chain for each task.
        for (int i = 0; i < N; ) {
            TaskRecord task = mRecentTasks.remove(i);
            if (mTmpRecents.contains(task)) {
                continue;
            }
            int affiliatedTaskId = task.mAffiliatedTaskId;
            while (true) {
                TaskRecord next = task.mNextAffiliate;
                if (next == null) {
                    break;
                }
                if (next.mAffiliatedTaskId != affiliatedTaskId) {
                    Slog.e(TAG, "Error in Recents: next.affiliatedTaskId=" +
                            next.mAffiliatedTaskId + " affiliatedTaskId=" + affiliatedTaskId);
                    task.setNextAffiliate(null);
                    if (next.mPrevAffiliate == task) {
                        next.setPrevAffiliate(null);
                    }
                    break;
                }
                if (next.mPrevAffiliate != task) {
                    Slog.e(TAG, "Error in Recents chain prev.mNextAffiliate=" +
                            next.mPrevAffiliate + " task=" + task);
                    next.setPrevAffiliate(null);
                    task.setNextAffiliate(null);
                    break;
                }
                if (!mRecentTasks.contains(next)) {
                    Slog.e(TAG, "Error in Recents: next=" + next + " not in mRecentTasks");
                    task.setNextAffiliate(null);
                    // We know that next.mPrevAffiliate is always task, from above, so clear
                    // its previous affiliate.
                    next.setPrevAffiliate(null);
                    break;
                }
                task = next;
            }
            // task is now the end of the list
            do {
                mRecentTasks.remove(task);
                mRecentTasks.add(i++, task);
                mTmpRecents.add(task);
                task.inRecents = true;
            } while ((task = task.mPrevAffiliate) != null);
        for (int i = 0; i < N; i = processNextAffiliateChain(i)) {
        }
        mTmpRecents.clear();
        // mRecentTasks is now in sorted, affiliated order.
    }