1 /*
2  * Copyright (C) 2020 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.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST;
20 
21 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
22 import static com.android.server.wm.DisplayArea.Type.ANY;
23 
24 import android.annotation.Nullable;
25 import android.content.pm.ParceledListSlice;
26 import android.os.Binder;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Slog;
30 import android.view.SurfaceControl;
31 import android.window.DisplayAreaAppearedInfo;
32 import android.window.IDisplayAreaOrganizer;
33 import android.window.IDisplayAreaOrganizerController;
34 import android.window.WindowContainerToken;
35 
36 import com.android.internal.protolog.common.ProtoLog;
37 
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.List;
41 
42 public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
43     private static final String TAG = "DisplayAreaOrganizerController";
44 
45     /**
46      * Next available feature id for a runtime task display area.
47      * @see #createTaskDisplayArea(IDisplayAreaOrganizer organizer, int, int, String)
48      */
49     private int mNextTaskDisplayAreaFeatureId = FEATURE_RUNTIME_TASK_CONTAINER_FIRST;
50 
51     final ActivityTaskManagerService mService;
52     private final WindowManagerGlobalLock mGlobalLock;
53     private final HashMap<Integer, DisplayAreaOrganizerState> mOrganizersByFeatureIds =
54             new HashMap();
55 
56     private class DeathRecipient implements IBinder.DeathRecipient {
57         int mFeature;
58         IDisplayAreaOrganizer mOrganizer;
59 
DeathRecipient(IDisplayAreaOrganizer organizer, int feature)60         DeathRecipient(IDisplayAreaOrganizer organizer, int feature) {
61             mOrganizer = organizer;
62             mFeature = feature;
63         }
64 
65         @Override
binderDied()66         public void binderDied() {
67             synchronized (mGlobalLock) {
68                 IDisplayAreaOrganizer featureOrganizer = getOrganizerByFeature(mFeature);
69                 if (featureOrganizer != null) {
70                     IBinder organizerBinder = featureOrganizer.asBinder();
71                     if (!organizerBinder.equals(mOrganizer.asBinder()) &&
72                                organizerBinder.isBinderAlive()) {
73                         Slog.d(TAG, "Dead organizer replaced for feature=" + mFeature);
74                         return;
75                     }
76                     mOrganizersByFeatureIds.remove(mFeature).destroy();
77                 }
78             }
79         }
80     }
81 
82     private class DisplayAreaOrganizerState {
83         private final IDisplayAreaOrganizer mOrganizer;
84         private final DeathRecipient mDeathRecipient;
85 
DisplayAreaOrganizerState(IDisplayAreaOrganizer organizer, int feature)86         DisplayAreaOrganizerState(IDisplayAreaOrganizer organizer, int feature) {
87             mOrganizer = organizer;
88             mDeathRecipient = new DeathRecipient(organizer, feature);
89             try {
90                 organizer.asBinder().linkToDeath(mDeathRecipient, 0);
91             } catch (RemoteException e) {
92                 // Oh well...
93             }
94         }
95 
destroy()96         void destroy() {
97             IBinder organizerBinder = mOrganizer.asBinder();
98             mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
99                 if (da.mOrganizer != null && da.mOrganizer.asBinder().equals(organizerBinder)) {
100                     if (da.isTaskDisplayArea() && da.asTaskDisplayArea().mCreatedByOrganizer) {
101                         // Delete the organizer created TDA when unregister.
102                         deleteTaskDisplayArea(da.asTaskDisplayArea());
103                     } else {
104                         da.setOrganizer(null);
105                     }
106                 }
107             });
108             organizerBinder.unlinkToDeath(mDeathRecipient, 0);
109         }
110     }
111 
DisplayAreaOrganizerController(ActivityTaskManagerService atm)112     DisplayAreaOrganizerController(ActivityTaskManagerService atm) {
113         mService = atm;
114         mGlobalLock = atm.mGlobalLock;
115     }
116 
enforceTaskPermission(String func)117     private void enforceTaskPermission(String func) {
118         mService.enforceTaskPermission(func);
119     }
120 
121     @Nullable
getOrganizerByFeature(int featureId)122     IDisplayAreaOrganizer getOrganizerByFeature(int featureId) {
123         final DisplayAreaOrganizerState state = mOrganizersByFeatureIds.get(featureId);
124         return state != null ? state.mOrganizer : null;
125     }
126 
127     @Override
registerOrganizer( IDisplayAreaOrganizer organizer, int feature)128     public ParceledListSlice<DisplayAreaAppearedInfo> registerOrganizer(
129             IDisplayAreaOrganizer organizer, int feature) {
130         enforceTaskPermission("registerOrganizer()");
131         final long uid = Binder.getCallingUid();
132         final long origId = Binder.clearCallingIdentity();
133         try {
134             synchronized (mGlobalLock) {
135                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d",
136                         organizer.asBinder(), uid);
137                 if (mOrganizersByFeatureIds.get(feature) != null) {
138                     mOrganizersByFeatureIds.remove(feature).destroy();
139                     Slog.d(TAG, "Replacing dead organizer for feature=" + feature);
140                 }
141 
142                 final DisplayAreaOrganizerState state = new DisplayAreaOrganizerState(organizer,
143                         feature);
144                 final List<DisplayAreaAppearedInfo> displayAreaInfos = new ArrayList<>();
145                 mService.mRootWindowContainer.forAllDisplays(dc -> {
146                     if (!dc.isTrusted()) {
147                         ProtoLog.w(WM_DEBUG_WINDOW_ORGANIZER,
148                                 "Don't organize or trigger events for untrusted displayId=%d",
149                                 dc.getDisplayId());
150                         return;
151                     }
152                     dc.forAllDisplayAreas((da) -> {
153                         if (da.mFeatureId != feature) return;
154                         displayAreaInfos.add(organizeDisplayArea(organizer, da,
155                                 "DisplayAreaOrganizerController.registerOrganizer"));
156                     });
157                 });
158 
159                 mOrganizersByFeatureIds.put(feature, state);
160                 return new ParceledListSlice<>(displayAreaInfos);
161             }
162         } finally {
163             Binder.restoreCallingIdentity(origId);
164         }
165     }
166 
167     @Override
unregisterOrganizer(IDisplayAreaOrganizer organizer)168     public void unregisterOrganizer(IDisplayAreaOrganizer organizer) {
169         enforceTaskPermission("unregisterTaskOrganizer()");
170         final long uid = Binder.getCallingUid();
171         final long origId = Binder.clearCallingIdentity();
172         try {
173             synchronized (mGlobalLock) {
174                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister display organizer=%s uid=%d",
175                         organizer.asBinder(), uid);
176                 mOrganizersByFeatureIds.entrySet().removeIf((entry) -> {
177                     final boolean matches = entry.getValue().mOrganizer.asBinder()
178                             .equals(organizer.asBinder());
179                     if (matches) {
180                         entry.getValue().destroy();
181                     }
182                     return matches;
183                 });
184             }
185         } finally {
186             Binder.restoreCallingIdentity(origId);
187         }
188     }
189 
190     @Override
createTaskDisplayArea(IDisplayAreaOrganizer organizer, int displayId, int parentFeatureId, String name)191     public DisplayAreaAppearedInfo createTaskDisplayArea(IDisplayAreaOrganizer organizer,
192             int displayId, int parentFeatureId, String name) {
193         enforceTaskPermission("createTaskDisplayArea()");
194         final long uid = Binder.getCallingUid();
195         final long origId = Binder.clearCallingIdentity();
196         try {
197             synchronized (mGlobalLock) {
198                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create TaskDisplayArea uid=%d", uid);
199 
200                 final DisplayContent display =
201                         mService.mRootWindowContainer.getDisplayContent(displayId);
202                 if (display == null) {
203                     throw new IllegalArgumentException("createTaskDisplayArea unknown displayId="
204                             + displayId);
205                 }
206                 if (!display.isTrusted()) {
207                     throw new IllegalArgumentException("createTaskDisplayArea untrusted displayId="
208                             + displayId);
209                 }
210 
211                 // The parentFeatureId can be either a RootDisplayArea or a TaskDisplayArea.
212                 // Check if there is a RootDisplayArea with the given parentFeatureId.
213                 final RootDisplayArea parentRoot = display.getItemFromDisplayAreas(da ->
214                         da.asRootDisplayArea() != null && da.mFeatureId == parentFeatureId
215                                 ? da.asRootDisplayArea()
216                                 : null);
217                 final TaskDisplayArea parentTda;
218                 if (parentRoot == null) {
219                     // There is no RootDisplayArea matching the parentFeatureId.
220                     // Check if there is a TaskDisplayArea with the given parentFeatureId.
221                     parentTda = display.getItemFromTaskDisplayAreas(taskDisplayArea ->
222                             taskDisplayArea.mFeatureId == parentFeatureId
223                                     ? taskDisplayArea
224                                     : null);
225                 } else {
226                     parentTda = null;
227                 }
228                 if (parentRoot == null && parentTda == null) {
229                     throw new IllegalArgumentException(
230                             "Can't find a parent DisplayArea with featureId=" + parentFeatureId);
231                 }
232 
233                 final int taskDisplayAreaFeatureId = mNextTaskDisplayAreaFeatureId++;
234                 final DisplayAreaOrganizerState state = new DisplayAreaOrganizerState(organizer,
235                         taskDisplayAreaFeatureId);
236 
237                 final TaskDisplayArea tda = parentRoot != null
238                         ? createTaskDisplayArea(parentRoot, name, taskDisplayAreaFeatureId)
239                         : createTaskDisplayArea(parentTda, name, taskDisplayAreaFeatureId);
240                 final DisplayAreaAppearedInfo tdaInfo = organizeDisplayArea(organizer, tda,
241                         "DisplayAreaOrganizerController.createTaskDisplayArea");
242                 mOrganizersByFeatureIds.put(taskDisplayAreaFeatureId, state);
243                 return tdaInfo;
244             }
245         } finally {
246             Binder.restoreCallingIdentity(origId);
247         }
248     }
249 
250     @Override
deleteTaskDisplayArea(WindowContainerToken token)251     public void deleteTaskDisplayArea(WindowContainerToken token) {
252         enforceTaskPermission("deleteTaskDisplayArea()");
253         final long uid = Binder.getCallingUid();
254         final long origId = Binder.clearCallingIdentity();
255         try {
256             synchronized (mGlobalLock) {
257                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete TaskDisplayArea uid=%d", uid);
258 
259                 final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
260                 if (wc == null || wc.asTaskDisplayArea() == null) {
261                     throw new IllegalArgumentException("Can't resolve TaskDisplayArea from token");
262                 }
263                 final TaskDisplayArea taskDisplayArea = wc.asTaskDisplayArea();
264                 if (!taskDisplayArea.mCreatedByOrganizer) {
265                     throw new IllegalArgumentException(
266                             "Attempt to delete TaskDisplayArea not created by organizer "
267                                     + "TaskDisplayArea=" + taskDisplayArea);
268                 }
269 
270                 mOrganizersByFeatureIds.remove(taskDisplayArea.mFeatureId).destroy();
271             }
272         } finally {
273             Binder.restoreCallingIdentity(origId);
274         }
275     }
276 
onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da)277     void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
278         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName());
279         try {
280             SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl(),
281                     "DisplayAreaOrganizerController.onDisplayAreaAppeared");
282             organizer.onDisplayAreaAppeared(da.getDisplayAreaInfo(), outSurfaceControl);
283         } catch (RemoteException e) {
284             // Oh well...
285         }
286     }
287 
onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da)288     void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) {
289         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea vanished name=%s", da.getName());
290         if (!organizer.asBinder().isBinderAlive()) {
291             Slog.d(TAG, "Organizer died before sending onDisplayAreaVanished");
292             return;
293         }
294         try {
295             organizer.onDisplayAreaVanished(da.getDisplayAreaInfo());
296         } catch (RemoteException e) {
297             // Oh well...
298         }
299     }
300 
onDisplayAreaInfoChanged(IDisplayAreaOrganizer organizer, DisplayArea da)301     void onDisplayAreaInfoChanged(IDisplayAreaOrganizer organizer, DisplayArea da) {
302         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea info changed name=%s", da.getName());
303         try {
304             organizer.onDisplayAreaInfoChanged(da.getDisplayAreaInfo());
305         } catch (RemoteException e) {
306             // Oh well...
307         }
308     }
309 
organizeDisplayArea(IDisplayAreaOrganizer organizer, DisplayArea displayArea, String callsite)310     private DisplayAreaAppearedInfo organizeDisplayArea(IDisplayAreaOrganizer organizer,
311             DisplayArea displayArea, String callsite) {
312         displayArea.setOrganizer(organizer, true /* skipDisplayAreaAppeared */);
313         return new DisplayAreaAppearedInfo(displayArea.getDisplayAreaInfo(),
314                 new SurfaceControl(displayArea.getSurfaceControl(), callsite));
315     }
316 
317     /**
318      * Creates a {@link TaskDisplayArea} as the topmost TDA below the given {@link RootDisplayArea}.
319      */
createTaskDisplayArea(RootDisplayArea root, String name, int taskDisplayAreaFeatureId)320     private TaskDisplayArea createTaskDisplayArea(RootDisplayArea root, String name,
321             int taskDisplayAreaFeatureId) {
322         final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(root.mDisplayContent,
323                 root.mWmService, name, taskDisplayAreaFeatureId, true /* createdByOrganizer */);
324 
325         // Find the top most DA that can contain Task (either a TDA or a DisplayAreaGroup).
326         final DisplayArea topTaskContainer = root.getItemFromDisplayAreas(da -> {
327             if (da.mType != ANY) {
328                 return null;
329             }
330 
331             final RootDisplayArea rootDA = da.getRootDisplayArea();
332             if (rootDA == root || rootDA == da) {
333                 // Either it is the top TDA below the root or it is a DisplayAreaGroup.
334                 return da;
335             }
336             return null;
337         });
338         if (topTaskContainer == null) {
339             throw new IllegalStateException("Root must either contain TDA or DAG root=" + root);
340         }
341 
342         // Insert the TaskDisplayArea as the top Task container.
343         final WindowContainer parent = topTaskContainer.getParent();
344         final int index = parent.mChildren.indexOf(topTaskContainer) + 1;
345         parent.addChild(taskDisplayArea, index);
346 
347         return taskDisplayArea;
348     }
349 
350     /**
351      * Creates a {@link TaskDisplayArea} as the topmost child of the given {@link TaskDisplayArea}.
352      */
createTaskDisplayArea(TaskDisplayArea parentTda, String name, int taskDisplayAreaFeatureId)353     private TaskDisplayArea createTaskDisplayArea(TaskDisplayArea parentTda, String name,
354             int taskDisplayAreaFeatureId) {
355         final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(parentTda.mDisplayContent,
356                 parentTda.mWmService, name, taskDisplayAreaFeatureId,
357                 true /* createdByOrganizer */);
358 
359         // Insert the TaskDisplayArea on the top.
360         parentTda.addChild(taskDisplayArea, WindowContainer.POSITION_TOP);
361 
362         return taskDisplayArea;
363     }
364 
deleteTaskDisplayArea(TaskDisplayArea taskDisplayArea)365     private void deleteTaskDisplayArea(TaskDisplayArea taskDisplayArea) {
366         taskDisplayArea.setOrganizer(null);
367         mService.mRootWindowContainer.mTaskSupervisor.beginDeferResume();
368 
369         // TaskDisplayArea#remove() move the stacks to the default TaskDisplayArea.
370         Task lastReparentedRootTask;
371         try {
372             lastReparentedRootTask = taskDisplayArea.remove();
373         } finally {
374             mService.mRootWindowContainer.mTaskSupervisor.endDeferResume();
375         }
376 
377         taskDisplayArea.removeImmediately();
378 
379         // Only update focus/visibility for the last one because there may be many root tasks are
380         // reparented and the intermediate states are unnecessary.
381         if (lastReparentedRootTask != null) {
382             lastReparentedRootTask.resumeNextFocusAfterReparent();
383         }
384     }
385 }
386