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_INPUT_METHOD; 20 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 21 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; 22 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; 23 24 import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; 25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 26 27 import android.annotation.Nullable; 28 import android.util.Slog; 29 30 import com.android.server.policy.WindowManagerPolicy; 31 32 import java.util.ArrayList; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.Map; 36 37 /** 38 * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root 39 * of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the 40 * logical display. 41 */ 42 class RootDisplayArea extends DisplayArea.Dimmable { 43 44 /** {@link Feature} that are supported in this {@link DisplayArea} hierarchy. */ 45 List<DisplayAreaPolicyBuilder.Feature> mFeatures; 46 47 /** 48 * Mapping from policy supported {@link Feature} to list of {@link DisplayArea} created to cover 49 * all the window types that the {@link Feature} will be applied to. 50 */ 51 Map<Feature, List<DisplayArea<WindowContainer>>> mFeatureToDisplayAreas; 52 53 /** Mapping from window layer to {@link DisplayArea.Tokens} that holds windows on that layer. */ 54 private DisplayArea.Tokens[] mAreaForLayer; 55 56 /** Whether the hierarchy has been built. */ 57 private boolean mHasBuiltHierarchy; 58 RootDisplayArea(WindowManagerService wms, String name, int featureId)59 RootDisplayArea(WindowManagerService wms, String name, int featureId) { 60 super(wms, Type.ANY, name, featureId); 61 } 62 63 @Override getRootDisplayArea()64 RootDisplayArea getRootDisplayArea() { 65 return this; 66 } 67 68 @Override asRootDisplayArea()69 RootDisplayArea asRootDisplayArea() { 70 return this; 71 } 72 73 /** Whether the orientation (based on dimensions) of this root is different from the Display. */ isOrientationDifferentFromDisplay()74 boolean isOrientationDifferentFromDisplay() { 75 return false; 76 } 77 78 /** 79 * Places the IME container below this root, so that it's bounds and config will be updated to 80 * match the root. 81 * 82 * @return {@code true} if the IME container is reparented to this root. 83 */ placeImeContainer(DisplayArea.Tokens imeContainer)84 boolean placeImeContainer(DisplayArea.Tokens imeContainer) { 85 final RootDisplayArea previousRoot = imeContainer.getRootDisplayArea(); 86 87 List<Feature> features = mFeatures; 88 for (int i = 0; i < features.size(); i++) { 89 Feature feature = features.get(i); 90 if (feature.getId() == FEATURE_IME_PLACEHOLDER) { 91 List<DisplayArea<WindowContainer>> imeDisplayAreas = 92 mFeatureToDisplayAreas.get(feature); 93 if (imeDisplayAreas.size() != 1) { 94 throw new IllegalStateException("There must be exactly one DisplayArea for the " 95 + "FEATURE_IME_PLACEHOLDER"); 96 } 97 98 previousRoot.updateImeContainerForLayers(null /* imeContainer */); 99 imeContainer.reparent(imeDisplayAreas.get(0), POSITION_TOP); 100 updateImeContainerForLayers(imeContainer); 101 return true; 102 } 103 } 104 105 // Some device UX may not have the need to update the IME bounds and position for IME target 106 // in a child DisplayArea, so instead of throwing exception, we just allow the IME container 107 // to remain in its previous root. 108 if (!isDescendantOf(previousRoot)) { 109 // When this RootDisplayArea is a descendant of the current RootDisplayArea, it will be 110 // at the APPLICATION_LAYER, and the IME container will always be on top and have bounds 111 // equal or larger than the input target. 112 // If it is not a descendant, the DisplayAreaPolicy owner should make sure the IME is 113 // working correctly. Print a warning in case they are not. 114 Slog.w(TAG_WM, "The IME target is not in the same root as the IME container, but there " 115 + "is no DisplayArea of FEATURE_IME_PLACEHOLDER in the target RootDisplayArea"); 116 } 117 return false; 118 } 119 120 /** 121 * Finds the {@link DisplayArea.Tokens} in {@code mAreaForLayer} that this type of window 122 * should be attached to. 123 * <p> 124 * Note that in most cases, users are expected to call 125 * {@link DisplayContent#findAreaForToken(WindowToken)} to find a {@link DisplayArea} in 126 * {@link DisplayContent} level instead of calling this inner method. 127 * </p> 128 */ 129 @Nullable findAreaForTokenInLayer(WindowToken token)130 DisplayArea.Tokens findAreaForTokenInLayer(WindowToken token) { 131 return findAreaForWindowTypeInLayer(token.windowType, token.mOwnerCanManageAppTokens, 132 token.mRoundedCornerOverlay); 133 } 134 135 /** @see #findAreaForTokenInLayer(WindowToken) */ 136 @Nullable findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay)137 DisplayArea.Tokens findAreaForWindowTypeInLayer(int windowType, boolean ownerCanManageAppTokens, 138 boolean roundedCornerOverlay) { 139 int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, 140 ownerCanManageAppTokens, roundedCornerOverlay); 141 if (windowLayerFromType == APPLICATION_LAYER) { 142 throw new IllegalArgumentException( 143 "There shouldn't be WindowToken on APPLICATION_LAYER"); 144 } 145 return mAreaForLayer[windowLayerFromType]; 146 } 147 148 /** Callback after {@link DisplayArea} hierarchy has been built. */ onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer, Map<Feature, List<DisplayArea<WindowContainer>>> featureToDisplayAreas)149 void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer, 150 Map<Feature, List<DisplayArea<WindowContainer>>> featureToDisplayAreas) { 151 if (mHasBuiltHierarchy) { 152 throw new IllegalStateException("Root should only build the hierarchy once"); 153 } 154 mHasBuiltHierarchy = true; 155 mFeatures = Collections.unmodifiableList(features); 156 mAreaForLayer = areaForLayer; 157 mFeatureToDisplayAreas = featureToDisplayAreas; 158 } 159 updateImeContainerForLayers(@ullable DisplayArea.Tokens imeContainer)160 private void updateImeContainerForLayers(@Nullable DisplayArea.Tokens imeContainer) { 161 final WindowManagerPolicy policy = mWmService.mPolicy; 162 mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)] = imeContainer; 163 mAreaForLayer[policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)] = imeContainer; 164 } 165 } 166