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