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