1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
21 
22 import android.app.ActivityManager.RunningTaskInfo;
23 import android.os.UserHandle;
24 import android.util.ArraySet;
25 
26 import com.android.internal.util.function.pooled.PooledConsumer;
27 import com.android.internal.util.function.pooled.PooledLambda;
28 
29 import java.util.Comparator;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.TreeSet;
33 
34 /**
35  * Class for resolving the set of running tasks in the system.
36  */
37 class RunningTasks {
38 
39     static final int FLAG_FILTER_ONLY_VISIBLE_RECENTS = 1;
40     static final int FLAG_ALLOWED = 1 << 1;
41     static final int FLAG_CROSS_USERS = 1 << 2;
42     static final int FLAG_KEEP_INTENT_EXTRA = 1 << 3;
43 
44     // Comparator to sort by last active time (descending)
45     private static final Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR =
46             (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
47 
48     private final TreeSet<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
49 
50     private int mCallingUid;
51     private int mUserId;
52     private boolean mCrossUser;
53     private ArraySet<Integer> mProfileIds;
54     private boolean mAllowed;
55     private boolean mFilterOnlyVisibleRecents;
56     private Task mTopDisplayFocusRootTask;
57     private Task mTopDisplayAdjacentTask;
58     private RecentTasks mRecentTasks;
59     private boolean mKeepIntentExtra;
60 
getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds)61     void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
62             RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
63         // Return early if there are no tasks to fetch
64         if (maxNum <= 0) {
65             return;
66         }
67 
68         // Gather all of the tasks across all of the tasks, and add them to the sorted set
69         mTmpSortedSet.clear();
70         mCallingUid = callingUid;
71         mUserId = UserHandle.getUserId(callingUid);
72         mCrossUser = (flags & FLAG_CROSS_USERS) == FLAG_CROSS_USERS;
73         mProfileIds = profileIds;
74         mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
75         mFilterOnlyVisibleRecents =
76                 (flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
77         mTopDisplayFocusRootTask = root.getTopDisplayFocusedRootTask();
78         mRecentTasks = root.mService.getRecentTasks();
79         mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
80 
81         if (mTopDisplayFocusRootTask.getAdjacentTaskFragment() != null) {
82             mTopDisplayAdjacentTask = mTopDisplayFocusRootTask.getAdjacentTaskFragment().asTask();
83         } else {
84             mTopDisplayAdjacentTask = null;
85         }
86 
87         final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
88                 PooledLambda.__(Task.class));
89         root.forAllLeafTasks(c, false);
90         c.recycle();
91 
92         // Take the first {@param maxNum} tasks and create running task infos for them
93         final Iterator<Task> iter = mTmpSortedSet.iterator();
94         while (iter.hasNext()) {
95             if (maxNum == 0) {
96                 break;
97             }
98 
99             final Task task = iter.next();
100             list.add(createRunningTaskInfo(task));
101             maxNum--;
102         }
103     }
104 
processTask(Task task)105     private void processTask(Task task) {
106         if (task.getTopNonFinishingActivity() == null) {
107             // Skip if there are no activities in the task
108             return;
109         }
110         if (task.effectiveUid != mCallingUid) {
111             if (task.mUserId != mUserId && !mCrossUser && !mProfileIds.contains(task.mUserId)) {
112                 // Skip if the caller does not have cross user permission or cannot access
113                 // the task's profile
114                 return;
115             }
116             if (!mAllowed) {
117                 // Skip if the caller isn't allowed to fetch this task
118                 return;
119             }
120         }
121         if (mFilterOnlyVisibleRecents
122                 && task.getActivityType() != ACTIVITY_TYPE_HOME
123                 && task.getActivityType() != ACTIVITY_TYPE_RECENTS
124                 && !mRecentTasks.isVisibleRecentTask(task)) {
125             // Skip if this task wouldn't be visibile (ever) from recents, with an exception for the
126             // home & recent tasks
127             return;
128         }
129 
130         final Task rootTask = task.getRootTask();
131         if (rootTask == mTopDisplayFocusRootTask && rootTask.getTopMostTask() == task) {
132             // For the focused top root task, update the last root task active time so that it
133             // can be used to determine the order of the tasks (it may not be set for newly
134             // created tasks)
135             task.touchActiveTime();
136         } else if (rootTask == mTopDisplayAdjacentTask && rootTask.getTopMostTask() == task) {
137             // The short-term workaround for launcher could get suitable running task info in
138             // split screen.
139             task.touchActiveTime();
140             // TreeSet doesn't allow same value and make sure this task is lower than focus one.
141             task.lastActiveTime--;
142         }
143 
144         mTmpSortedSet.add(task);
145     }
146 
147     /** Constructs a {@link RunningTaskInfo} from a given {@param task}. */
createRunningTaskInfo(Task task)148     private RunningTaskInfo createRunningTaskInfo(Task task) {
149         final RunningTaskInfo rti = new RunningTaskInfo();
150         task.fillTaskInfo(rti, !mKeepIntentExtra);
151         // Fill in some deprecated values
152         rti.id = rti.taskId;
153         return rti;
154     }
155 }
156