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.SystemClock;
24 import android.os.UserHandle;
25 import android.util.ArraySet;
26 
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.function.Consumer;
30 
31 /**
32  * Class for resolving the set of running tasks in the system.
33  */
34 class RunningTasks implements Consumer<Task> {
35 
36     static final int FLAG_FILTER_ONLY_VISIBLE_RECENTS = 1;
37     static final int FLAG_ALLOWED = 1 << 1;
38     static final int FLAG_CROSS_USERS = 1 << 2;
39     static final int FLAG_KEEP_INTENT_EXTRA = 1 << 3;
40 
41     // Tasks are sorted in order {focusedVisibleTasks, visibleTasks, invisibleTasks}.
42     private final ArrayList<Task> mTmpSortedTasks = new ArrayList<>();
43     // mTmpVisibleTasks, mTmpInvisibleTasks and mTmpFocusedTasks are sorted from top
44     // to bottom.
45     private final ArrayList<Task> mTmpVisibleTasks = new ArrayList<>();
46     private final ArrayList<Task> mTmpInvisibleTasks = new ArrayList<>();
47     private final ArrayList<Task> mTmpFocusedTasks = new ArrayList<>();
48 
49     private int mCallingUid;
50     private int mUserId;
51     private boolean mCrossUser;
52     private ArraySet<Integer> mProfileIds;
53     private boolean mAllowed;
54     private boolean mFilterOnlyVisibleRecents;
55     private RecentTasks mRecentTasks;
56     private boolean mKeepIntentExtra;
57 
getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks, WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds)58     void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
59             WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
60         // Return early if there are no tasks to fetch
61         if (maxNum <= 0) {
62             return;
63         }
64 
65         mCallingUid = callingUid;
66         mUserId = UserHandle.getUserId(callingUid);
67         mCrossUser = (flags & FLAG_CROSS_USERS) == FLAG_CROSS_USERS;
68         mProfileIds = profileIds;
69         mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
70         mFilterOnlyVisibleRecents =
71                 (flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
72         mRecentTasks = recentTasks;
73         mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
74 
75         if (root instanceof RootWindowContainer) {
76             ((RootWindowContainer) root).forAllDisplays(dc -> {
77                 final Task focusedTask = dc.mFocusedApp != null ? dc.mFocusedApp.getTask() : null;
78                 if (focusedTask != null) {
79                     mTmpFocusedTasks.add(focusedTask);
80                 }
81                 processTaskInWindowContainer(dc);
82             });
83         } else {
84             final DisplayContent dc = root.getDisplayContent();
85             final Task focusedTask = dc != null
86                     ? (dc.mFocusedApp != null ? dc.mFocusedApp.getTask() : null)
87                     : null;
88             // May not be include focusedTask if root is DisplayArea.
89             final boolean rootContainsFocusedTask = focusedTask != null
90                     && focusedTask.isDescendantOf(root);
91             if (rootContainsFocusedTask) {
92                 mTmpFocusedTasks.add(focusedTask);
93             }
94             processTaskInWindowContainer(root);
95         }
96 
97         final int visibleTaskCount = mTmpVisibleTasks.size();
98         for (int i = 0; i < mTmpFocusedTasks.size(); i++) {
99             final Task focusedTask = mTmpFocusedTasks.get(i);
100             final boolean containsFocusedTask = mTmpVisibleTasks.remove(focusedTask);
101             if (containsFocusedTask) {
102                 // Put the visible focused task at the first position.
103                 mTmpSortedTasks.add(focusedTask);
104             }
105         }
106         if (!mTmpVisibleTasks.isEmpty()) {
107             mTmpSortedTasks.addAll(mTmpVisibleTasks);
108         }
109         if (!mTmpInvisibleTasks.isEmpty()) {
110             mTmpSortedTasks.addAll(mTmpInvisibleTasks);
111         }
112 
113         // Take the first {@param maxNum} tasks and create running task infos for them
114         final int size = Math.min(maxNum, mTmpSortedTasks.size());
115         final long now = SystemClock.elapsedRealtime();
116         for (int i = 0; i < size; i++) {
117             final Task task = mTmpSortedTasks.get(i);
118             // Override the last active to current time for the visible tasks because the visible
119             // tasks can be considered to be currently active, the values are descending as
120             // the item order.
121             final long visibleActiveTime = i < visibleTaskCount ? now + size - i : -1;
122             list.add(createRunningTaskInfo(task, visibleActiveTime));
123         }
124 
125         mTmpFocusedTasks.clear();
126         mTmpVisibleTasks.clear();
127         mTmpInvisibleTasks.clear();
128         mTmpSortedTasks.clear();
129     }
130 
131     private void processTaskInWindowContainer(WindowContainer wc) {
132         wc.forAllLeafTasks(this, true /* traverseTopToBottom */);
133     }
134 
135     @Override
136     public void accept(Task task) {
137         if (task.getTopNonFinishingActivity() == null) {
138             // Skip if there are no activities in the task
139             return;
140         }
141         if (task.effectiveUid != mCallingUid) {
142             if (task.mUserId != mUserId && !mCrossUser && !mProfileIds.contains(task.mUserId)) {
143                 // Skip if the caller does not have cross user permission or cannot access
144                 // the task's profile
145                 return;
146             }
147             if (!mAllowed) {
148                 // Skip if the caller isn't allowed to fetch this task
149                 return;
150             }
151         }
152         if (mFilterOnlyVisibleRecents
153                 && task.getActivityType() != ACTIVITY_TYPE_HOME
154                 && task.getActivityType() != ACTIVITY_TYPE_RECENTS
155                 && !mRecentTasks.isVisibleRecentTask(task)) {
156             // Skip if this task wouldn't be visibile (ever) from recents, with an exception for the
157             // home & recent tasks
158             return;
159         }
160         if (task.isVisibleRequested()) {
161             mTmpVisibleTasks.add(task);
162         } else {
163             mTmpInvisibleTasks.add(task);
164         }
165     }
166 
167     /** Constructs a {@link RunningTaskInfo} from a given {@param task}. */
168     private RunningTaskInfo createRunningTaskInfo(Task task, long visibleActiveTime) {
169         final RunningTaskInfo rti = new RunningTaskInfo();
170         task.fillTaskInfo(rti, !mKeepIntentExtra);
171         if (visibleActiveTime > 0) {
172             rti.lastActiveTime = visibleActiveTime;
173         }
174         // Fill in some deprecated values
175         rti.id = rti.taskId;
176 
177         if (!mAllowed) {
178             Task.trimIneffectiveInfo(task, rti);
179         }
180         return rti;
181     }
182 }
183