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