Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 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.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; |
| 20 | import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; |
| 21 | import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; |
| 22 | import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; |
| 23 | import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; |
| 24 | import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; |
| 25 | import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; |
| 26 | |
| 27 | import com.android.internal.annotations.VisibleForTesting; |
| 28 | import com.android.server.policy.WindowManagerPolicy; |
| 29 | |
| 30 | import java.util.ArrayList; |
| 31 | import java.util.Arrays; |
| 32 | import java.util.Comparator; |
| 33 | import java.util.HashMap; |
| 34 | import java.util.List; |
| 35 | import java.util.Map; |
| 36 | |
| 37 | /** |
| 38 | * A builder for instantiating a complex {@link DisplayAreaPolicy} |
| 39 | * |
| 40 | * <p>Given a set of features (that each target a set of window types), it builds the necessary |
| 41 | * DisplayArea hierarchy. |
| 42 | * |
| 43 | * <p>Example: <br /> |
| 44 | * |
| 45 | * <pre> |
| 46 | * // Feature for targeting everything below the magnification overlay: |
| 47 | * new DisplayAreaPolicyBuilder(...) |
| 48 | * .addFeature(new Feature.Builder(..., "Magnification") |
| 49 | * .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) |
| 50 | * .build()) |
| 51 | * .build(...) |
| 52 | * |
| 53 | * // Builds a policy with the following hierarchy: |
| 54 | * - DisplayArea.Root |
| 55 | * - Magnification |
| 56 | * - DisplayArea.Tokens (Wallpapers are attached here) |
Andrii Kulian | 4c0fd0d | 2020-03-29 13:32:14 -0700 | [diff] [blame] | 57 | * - TaskDisplayArea |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 58 | * - DisplayArea.Tokens (windows above Tasks up to IME are attached here) |
| 59 | * - ImeContainers |
| 60 | * - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here) |
| 61 | * - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up are attached here) |
| 62 | * |
| 63 | * </pre> |
| 64 | * |
| 65 | * // TODO(display-area): document more complex scenarios where we need multiple areas per feature. |
| 66 | */ |
| 67 | class DisplayAreaPolicyBuilder { |
| 68 | |
| 69 | private final ArrayList<Feature> mFeatures = new ArrayList<>(); |
| 70 | |
| 71 | /** |
| 72 | * A feature that requires {@link DisplayArea DisplayArea(s)}. |
| 73 | */ |
| 74 | static class Feature { |
| 75 | private final String mName; |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 76 | private final int mId; |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 77 | private final boolean[] mWindowLayers; |
| 78 | |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 79 | private Feature(String name, int id, boolean[] windowLayers) { |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 80 | mName = name; |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 81 | mId = id; |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 82 | mWindowLayers = windowLayers; |
| 83 | } |
| 84 | |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 85 | /** |
| 86 | * Returns the id of the feature. |
| 87 | * |
| 88 | * Must be unique among the features added to a {@link DisplayAreaPolicyBuilder}. |
| 89 | * |
Wale Ogunwale | adf116e | 2020-03-27 16:36:01 -0700 | [diff] [blame] | 90 | * @see android.window.DisplayAreaOrganizer#FEATURE_SYSTEM_FIRST |
| 91 | * @see android.window.DisplayAreaOrganizer#FEATURE_VENDOR_FIRST |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 92 | */ |
| 93 | public int getId() { |
| 94 | return mId; |
| 95 | } |
| 96 | |
| 97 | @Override |
| 98 | public String toString() { |
| 99 | return "Feature(\"" + mName + "\", " + mId + '}'; |
| 100 | } |
| 101 | |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 102 | static class Builder { |
| 103 | private final WindowManagerPolicy mPolicy; |
| 104 | private final String mName; |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 105 | private final int mId; |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 106 | private final boolean[] mLayers; |
| 107 | |
| 108 | /** |
| 109 | * Build a new feature that applies to a set of window types as specified by the builder |
| 110 | * methods. |
| 111 | * |
| 112 | * <p>The set of types is updated iteratively in the order of the method invocations. |
| 113 | * For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should |
| 114 | * apply to all types except TYPE_STATUS_BAR. |
| 115 | * |
| 116 | * The builder starts out with the feature not applying to any types. |
| 117 | * |
| 118 | * @param name the name of the feature. |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 119 | * @param id of the feature. {@see Feature#getId} |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 120 | */ |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 121 | Builder(WindowManagerPolicy policy, String name, int id) { |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 122 | mPolicy = policy; |
| 123 | mName = name; |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 124 | mId = id; |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 125 | mLayers = new boolean[mPolicy.getMaxWindowLayer()]; |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * Set that the feature applies to all window types. |
| 130 | */ |
| 131 | Builder all() { |
| 132 | Arrays.fill(mLayers, true); |
| 133 | return this; |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * Set that the feature applies to the given window types. |
| 138 | */ |
| 139 | Builder and(int... types) { |
| 140 | for (int i = 0; i < types.length; i++) { |
| 141 | int type = types[i]; |
| 142 | set(type, true); |
| 143 | } |
| 144 | return this; |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * Set that the feature does not apply to the given window types. |
| 149 | */ |
| 150 | Builder except(int... types) { |
| 151 | for (int i = 0; i < types.length; i++) { |
| 152 | int type = types[i]; |
| 153 | set(type, false); |
| 154 | } |
| 155 | return this; |
| 156 | } |
| 157 | |
| 158 | /** |
| 159 | * Set that the feature applies window types that are layerd at or below the layer of |
| 160 | * the given window type. |
| 161 | */ |
| 162 | Builder upTo(int typeInclusive) { |
| 163 | final int max = layerFromType(typeInclusive, false); |
| 164 | for (int i = 0; i < max; i++) { |
| 165 | mLayers[i] = true; |
| 166 | } |
| 167 | set(typeInclusive, true); |
| 168 | return this; |
| 169 | } |
| 170 | |
| 171 | Feature build() { |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 172 | return new Feature(mName, mId, mLayers.clone()); |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | private void set(int type, boolean value) { |
| 176 | mLayers[layerFromType(type, true)] = value; |
| 177 | if (type == TYPE_APPLICATION_OVERLAY) { |
| 178 | mLayers[layerFromType(type, true)] = value; |
| 179 | mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value; |
| 180 | mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value; |
| 181 | mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value; |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | private int layerFromType(int type, boolean internalWindows) { |
| 186 | return mPolicy.getWindowLayerFromTypeLw(type, internalWindows); |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | static class Result extends DisplayAreaPolicy { |
| 192 | private static final int LEAF_TYPE_TASK_CONTAINERS = 1; |
| 193 | private static final int LEAF_TYPE_IME_CONTAINERS = 2; |
| 194 | private static final int LEAF_TYPE_TOKENS = 0; |
| 195 | |
| 196 | private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer(); |
| 197 | |
| 198 | private final ArrayList<Feature> mFeatures; |
| 199 | private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas; |
| 200 | private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer]; |
| 201 | |
| 202 | Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, |
| 203 | DisplayArea<? extends WindowContainer> imeContainer, |
Andrii Kulian | 44b3c56 | 2020-04-01 12:49:56 -0700 | [diff] [blame] | 204 | List<TaskDisplayArea> taskDisplayAreas, ArrayList<Feature> features) { |
| 205 | super(wmService, content, root, imeContainer, taskDisplayAreas); |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 206 | mFeatures = features; |
| 207 | mAreas = new HashMap<>(features.size()); |
| 208 | for (int i = 0; i < mFeatures.size(); i++) { |
| 209 | mAreas.put(mFeatures.get(i), new ArrayList<>()); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | @Override |
| 214 | public void attachDisplayAreas() { |
| 215 | // This method constructs the layer hierarchy with the following properties: |
| 216 | // (1) Every feature maps to a set of DisplayAreas |
| 217 | // (2) After adding a window, for every feature the window's type belongs to, |
| 218 | // it is a descendant of one of the corresponding DisplayAreas of the feature. |
| 219 | // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows |
| 220 | // within a DisplayArea: |
| 221 | // for every pair of DisplayArea siblings (a,b), where a is below b, it holds that |
| 222 | // max(z-range(a)) <= min(z-range(b)) |
| 223 | // |
| 224 | // The algorithm below iteratively creates such a hierarchy: |
| 225 | // - Initially, all windows are attached to the root. |
| 226 | // - For each feature we create a set of DisplayAreas, by looping over the layers |
| 227 | // - if the feature does apply to the current layer, we need to find a DisplayArea |
| 228 | // for it to satisfy (2) |
| 229 | // - we can re-use the previous layer's area if: |
| 230 | // the current feature also applies to the previous layer, (to satisfy (3)) |
| 231 | // and the last feature that applied to the previous layer is the same as |
| 232 | // the last feature that applied to the current layer (to satisfy (2)) |
| 233 | // - otherwise we create a new DisplayArea below the last feature that applied |
| 234 | // to the current layer |
| 235 | |
| 236 | |
| 237 | PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer]; |
| 238 | final PendingArea root = new PendingArea(null, 0, null); |
| 239 | Arrays.fill(areaForLayer, root); |
| 240 | |
| 241 | final int size = mFeatures.size(); |
| 242 | for (int i = 0; i < size; i++) { |
| 243 | PendingArea featureArea = null; |
| 244 | for (int layer = 0; layer < mMaxWindowLayer; layer++) { |
| 245 | final Feature feature = mFeatures.get(i); |
| 246 | if (feature.mWindowLayers[layer]) { |
| 247 | if (featureArea == null || featureArea.mParent != areaForLayer[layer]) { |
| 248 | // No suitable DisplayArea - create a new one under the previous area |
| 249 | // for this layer. |
| 250 | featureArea = new PendingArea(feature, layer, areaForLayer[layer]); |
| 251 | areaForLayer[layer].mChildren.add(featureArea); |
| 252 | } |
| 253 | areaForLayer[layer] = featureArea; |
| 254 | } else { |
| 255 | featureArea = null; |
| 256 | } |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | PendingArea leafArea = null; |
| 261 | int leafType = LEAF_TYPE_TOKENS; |
| 262 | for (int layer = 0; layer < mMaxWindowLayer; layer++) { |
| 263 | int type = typeOfLayer(mWmService.mPolicy, layer); |
| 264 | if (leafArea == null || leafArea.mParent != areaForLayer[layer] |
| 265 | || type != leafType) { |
| 266 | leafArea = new PendingArea(null, layer, areaForLayer[layer]); |
| 267 | areaForLayer[layer].mChildren.add(leafArea); |
| 268 | leafType = type; |
| 269 | if (leafType == LEAF_TYPE_TASK_CONTAINERS) { |
Andrii Kulian | 44b3c56 | 2020-04-01 12:49:56 -0700 | [diff] [blame] | 270 | addTaskDisplayAreasToLayer(areaForLayer[layer], layer); |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 271 | } else if (leafType == LEAF_TYPE_IME_CONTAINERS) { |
| 272 | leafArea.mExisting = mImeContainer; |
| 273 | } |
| 274 | } |
| 275 | leafArea.mMaxLayer = layer; |
| 276 | } |
| 277 | root.computeMaxLayer(); |
| 278 | root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas); |
| 279 | } |
| 280 | |
Andrii Kulian | 44b3c56 | 2020-04-01 12:49:56 -0700 | [diff] [blame] | 281 | /** Adds all task display areas to the specified layer */ |
| 282 | private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea, int layer) { |
| 283 | final int count = mTaskDisplayAreas.size(); |
| 284 | for (int i = 0; i < count; i++) { |
| 285 | PendingArea leafArea = new PendingArea(null, layer, parentPendingArea); |
| 286 | leafArea.mExisting = mTaskDisplayAreas.get(i); |
| 287 | leafArea.mMaxLayer = layer; |
| 288 | parentPendingArea.mChildren.add(leafArea); |
| 289 | } |
| 290 | } |
| 291 | |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 292 | @Override |
| 293 | public void addWindow(WindowToken token) { |
| 294 | DisplayArea.Tokens area = findAreaForToken(token); |
| 295 | area.addChild(token); |
| 296 | } |
| 297 | |
| 298 | @VisibleForTesting |
| 299 | DisplayArea.Tokens findAreaForToken(WindowToken token) { |
| 300 | int windowLayerFromType = token.getWindowLayerFromType(); |
| 301 | if (windowLayerFromType == APPLICATION_LAYER) { |
| 302 | // TODO(display-area): Better handle AboveAppWindows in APPLICATION_LAYER |
| 303 | windowLayerFromType += 1; |
| 304 | } else if (token.mRoundedCornerOverlay) { |
| 305 | windowLayerFromType = mMaxWindowLayer - 1; |
| 306 | } |
| 307 | return mAreaForLayer[windowLayerFromType]; |
| 308 | } |
| 309 | |
| 310 | public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) { |
| 311 | return mAreas.get(feature); |
| 312 | } |
| 313 | |
| 314 | private static int typeOfLayer(WindowManagerPolicy policy, int layer) { |
| 315 | if (layer == APPLICATION_LAYER) { |
| 316 | return LEAF_TYPE_TASK_CONTAINERS; |
| 317 | } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD) |
| 318 | || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) { |
| 319 | return LEAF_TYPE_IME_CONTAINERS; |
| 320 | } else { |
| 321 | return LEAF_TYPE_TOKENS; |
| 322 | } |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | DisplayAreaPolicyBuilder addFeature(Feature feature) { |
| 327 | mFeatures.add(feature); |
| 328 | return this; |
| 329 | } |
| 330 | |
Andrii Kulian | 44b3c56 | 2020-04-01 12:49:56 -0700 | [diff] [blame] | 331 | protected List<Feature> getFeatures() { |
| 332 | return mFeatures; |
| 333 | } |
| 334 | |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 335 | Result build(WindowManagerService wmService, |
| 336 | DisplayContent content, DisplayArea.Root root, |
| 337 | DisplayArea<? extends WindowContainer> imeContainer, |
Andrii Kulian | 44b3c56 | 2020-04-01 12:49:56 -0700 | [diff] [blame] | 338 | List<TaskDisplayArea> taskDisplayAreas) { |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 339 | |
Andrii Kulian | 44b3c56 | 2020-04-01 12:49:56 -0700 | [diff] [blame] | 340 | return new Result(wmService, content, root, imeContainer, taskDisplayAreas, new ArrayList<>( |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 341 | mFeatures)); |
| 342 | } |
| 343 | |
| 344 | static class PendingArea { |
| 345 | final int mMinLayer; |
| 346 | final ArrayList<PendingArea> mChildren = new ArrayList<>(); |
| 347 | final Feature mFeature; |
| 348 | final PendingArea mParent; |
| 349 | int mMaxLayer; |
| 350 | DisplayArea mExisting; |
| 351 | |
| 352 | PendingArea(Feature feature, |
| 353 | int minLayer, |
| 354 | PendingArea parent) { |
| 355 | mMinLayer = minLayer; |
| 356 | mFeature = feature; |
| 357 | mParent = parent; |
| 358 | } |
| 359 | |
| 360 | int computeMaxLayer() { |
| 361 | for (int i = 0; i < mChildren.size(); i++) { |
| 362 | mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer()); |
| 363 | } |
| 364 | return mMaxLayer; |
| 365 | } |
| 366 | |
| 367 | void instantiateChildren(DisplayArea<DisplayArea> parent, |
| 368 | DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<? |
| 369 | extends WindowContainer>>> areas) { |
| 370 | mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer)); |
| 371 | for (int i = 0; i < mChildren.size(); i++) { |
| 372 | final PendingArea child = mChildren.get(i); |
| 373 | final DisplayArea area = child.createArea(parent, areaForLayer); |
| 374 | parent.addChild(area, WindowContainer.POSITION_TOP); |
| 375 | if (mFeature != null) { |
| 376 | areas.get(mFeature).add(area); |
| 377 | } |
| 378 | child.instantiateChildren(area, areaForLayer, level + 1, areas); |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | private DisplayArea createArea(DisplayArea<DisplayArea> parent, |
| 383 | DisplayArea.Tokens[] areaForLayer) { |
| 384 | if (mExisting != null) { |
| 385 | return mExisting; |
| 386 | } |
| 387 | DisplayArea.Type type; |
| 388 | if (mMinLayer > APPLICATION_LAYER) { |
| 389 | type = DisplayArea.Type.ABOVE_TASKS; |
| 390 | } else if (mMaxLayer < APPLICATION_LAYER) { |
| 391 | type = DisplayArea.Type.BELOW_TASKS; |
| 392 | } else { |
| 393 | type = DisplayArea.Type.ANY; |
| 394 | } |
| 395 | if (mFeature == null) { |
| 396 | final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type, |
| 397 | "Leaf:" + mMinLayer + ":" + mMaxLayer); |
| 398 | for (int i = mMinLayer; i <= mMaxLayer; i++) { |
| 399 | areaForLayer[i] = leaf; |
| 400 | } |
| 401 | return leaf; |
| 402 | } else { |
| 403 | return new DisplayArea(parent.mWmService, type, mFeature.mName + ":" |
Wale Ogunwale | dec3408 | 2020-03-22 09:45:00 -0700 | [diff] [blame] | 404 | + mMinLayer + ":" + mMaxLayer, mFeature.mId); |
Adrian Roos | 8116358 | 2020-01-08 23:21:16 +0100 | [diff] [blame] | 405 | } |
| 406 | } |
| 407 | } |
| 408 | } |