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