1 /*
2  * Copyright (C) 2011 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.FLAG_SHOW_WALLPAPER;
20 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
21 
22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
23 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
24 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
26 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
27 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
28 import static com.android.server.wm.WindowTokenProto.HASH_CODE;
29 import static com.android.server.wm.WindowTokenProto.HIDDEN;
30 import static com.android.server.wm.WindowTokenProto.PAUSED;
31 import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
32 import static com.android.server.wm.WindowTokenProto.WINDOWS;
33 import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
34 
35 import android.annotation.CallSuper;
36 import android.os.Debug;
37 import android.os.IBinder;
38 import android.util.Slog;
39 import android.util.proto.ProtoOutputStream;
40 
41 import java.io.PrintWriter;
42 import java.util.Comparator;
43 
44 /**
45  * Container of a set of related windows in the window manager. Often this is an AppWindowToken,
46  * which is the handle for an Activity that it uses to display windows. For nested windows, there is
47  * a WindowToken created for the parent window to manage its children.
48  */
49 class WindowToken extends WindowContainer<WindowState> {
50     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;
51 
52     // The actual token.
53     final IBinder token;
54 
55     // The type of window this token is for, as per WindowManager.LayoutParams.
56     final int windowType;
57 
58     /** {@code true} if this holds the rounded corner overlay */
59     final boolean mRoundedCornerOverlay;
60 
61     // Set if this token was explicitly added by a client, so should
62     // persist (not be removed) when all windows are removed.
63     boolean mPersistOnEmpty;
64 
65     // For printing.
66     String stringName;
67 
68     // Is key dispatching paused for this token?
69     boolean paused = false;
70 
71     // Should this token's windows be hidden?
72     private boolean mHidden;
73 
74     // Temporary for finding which tokens no longer have visible windows.
75     boolean hasVisible;
76 
77     // Set to true when this token is in a pending transaction where it
78     // will be shown.
79     boolean waitingToShow;
80 
81     // Set to true when this token is in a pending transaction where its
82     // windows will be put to the bottom of the list.
83     boolean sendingToBottom;
84 
85     /** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */
86     final boolean mOwnerCanManageAppTokens;
87 
88     /**
89      * Compares two child window of this token and returns -1 if the first is lesser than the
90      * second in terms of z-order and 1 otherwise.
91      */
92     private final Comparator<WindowState> mWindowComparator =
93             (WindowState newWindow, WindowState existingWindow) -> {
94         final WindowToken token = WindowToken.this;
95         if (newWindow.mToken != token) {
96             throw new IllegalArgumentException("newWindow=" + newWindow
97                     + " is not a child of token=" + token);
98         }
99 
100         if (existingWindow.mToken != token) {
101             throw new IllegalArgumentException("existingWindow=" + existingWindow
102                     + " is not a child of token=" + token);
103         }
104 
105         return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
106     };
107 
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens)108     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
109             DisplayContent dc, boolean ownerCanManageAppTokens) {
110         this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
111                 false /* roundedCornersOverlay */);
112     }
113 
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay)114     WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
115             DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
116         super(service);
117         token = _token;
118         windowType = type;
119         mPersistOnEmpty = persistOnEmpty;
120         mOwnerCanManageAppTokens = ownerCanManageAppTokens;
121         mRoundedCornerOverlay = roundedCornerOverlay;
122         onDisplayChanged(dc);
123     }
124 
setHidden(boolean hidden)125     void setHidden(boolean hidden) {
126         if (hidden != mHidden) {
127             mHidden = hidden;
128         }
129     }
130 
isHidden()131     boolean isHidden() {
132         return mHidden;
133     }
134 
removeAllWindowsIfPossible()135     void removeAllWindowsIfPossible() {
136         for (int i = mChildren.size() - 1; i >= 0; --i) {
137             final WindowState win = mChildren.get(i);
138             if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM,
139                     "removeAllWindowsIfPossible: removing win=" + win);
140             win.removeIfPossible();
141         }
142     }
143 
setExiting()144     void setExiting() {
145         if (mChildren.size() == 0) {
146             super.removeImmediately();
147             return;
148         }
149 
150         // This token is exiting, so allow it to be removed when it no longer contains any windows.
151         mPersistOnEmpty = false;
152 
153         if (mHidden) {
154             return;
155         }
156 
157         final int count = mChildren.size();
158         boolean changed = false;
159         boolean delayed = false;
160 
161         for (int i = 0; i < count; i++) {
162             final WindowState win = mChildren.get(i);
163             if (win.isAnimating()) {
164                 delayed = true;
165             }
166             changed |= win.onSetAppExiting();
167         }
168 
169         setHidden(true);
170 
171         if (changed) {
172             mWmService.mWindowPlacerLocked.performSurfacePlacement();
173             mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
174         }
175 
176         if (delayed) {
177             mDisplayContent.mExitingTokens.add(this);
178         }
179     }
180 
181     /**
182      * @return The scale for applications running in compatibility mode. Multiply the size in the
183      *         application by this scale will be the size in the screen.
184      */
getSizeCompatScale()185     float getSizeCompatScale() {
186         return mDisplayContent.mCompatibleScreenScale;
187     }
188 
189     /**
190      * Returns true if the new window is considered greater than the existing window in terms of
191      * z-order.
192      */
isFirstChildWindowGreaterThanSecond(WindowState newWindow, WindowState existingWindow)193     protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
194             WindowState existingWindow) {
195         // New window is considered greater if it has a higher or equal base layer.
196         return newWindow.mBaseLayer >= existingWindow.mBaseLayer;
197     }
198 
addWindow(final WindowState win)199     void addWindow(final WindowState win) {
200         if (DEBUG_FOCUS) Slog.d(TAG_WM,
201                 "addWindow: win=" + win + " Callers=" + Debug.getCallers(5));
202 
203         if (win.isChildWindow()) {
204             // Child windows are added to their parent windows.
205             return;
206         }
207         if (!mChildren.contains(win)) {
208             if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
209             addChild(win, mWindowComparator);
210             mWmService.mWindowsChanged = true;
211             // TODO: Should we also be setting layout needed here and other places?
212         }
213     }
214 
215     /** Returns true if the token windows list is empty. */
isEmpty()216     boolean isEmpty() {
217         return mChildren.isEmpty();
218     }
219 
getReplacingWindow()220     WindowState getReplacingWindow() {
221         for (int i = mChildren.size() - 1; i >= 0; i--) {
222             final WindowState win = mChildren.get(i);
223             final WindowState replacing = win.getReplacingWindow();
224             if (replacing != null) {
225                 return replacing;
226             }
227         }
228         return null;
229     }
230 
231     /** Return true if this token has a window that wants the wallpaper displayed behind it. */
windowsCanBeWallpaperTarget()232     boolean windowsCanBeWallpaperTarget() {
233         for (int j = mChildren.size() - 1; j >= 0; j--) {
234             final WindowState w = mChildren.get(j);
235             if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
236                 return true;
237             }
238         }
239 
240         return false;
241     }
242 
asAppWindowToken()243     AppWindowToken asAppWindowToken() {
244         // TODO: Not sure if this is the best way to handle this vs. using instanceof and casting.
245         // I am not an app window token!
246         return null;
247     }
248 
249     @Override
removeImmediately()250     void removeImmediately() {
251         if (mDisplayContent != null) {
252             mDisplayContent.removeWindowToken(token);
253         }
254         // Needs to occur after the token is removed from the display above to avoid attempt at
255         // duplicate removal of this window container from it's parent.
256         super.removeImmediately();
257     }
258 
259     @Override
onDisplayChanged(DisplayContent dc)260     void onDisplayChanged(DisplayContent dc) {
261         dc.reParentWindowToken(this);
262 
263         // TODO(b/36740756): One day this should perhaps be hooked
264         // up with goodToGo, so we don't move a window
265         // to another display before the window behind
266         // it is ready.
267 
268         super.onDisplayChanged(dc);
269     }
270 
271     @CallSuper
272     @Override
writeToProto(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)273     public void writeToProto(ProtoOutputStream proto, long fieldId,
274             @WindowTraceLogLevel int logLevel) {
275         if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
276             return;
277         }
278 
279         final long token = proto.start(fieldId);
280         super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
281         proto.write(HASH_CODE, System.identityHashCode(this));
282         for (int i = 0; i < mChildren.size(); i++) {
283             final WindowState w = mChildren.get(i);
284             w.writeToProto(proto, WINDOWS, logLevel);
285         }
286         proto.write(HIDDEN, mHidden);
287         proto.write(WAITING_TO_SHOW, waitingToShow);
288         proto.write(PAUSED, paused);
289         proto.end(token);
290     }
291 
dump(PrintWriter pw, String prefix, boolean dumpAll)292     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
293         super.dump(pw, prefix, dumpAll);
294         pw.print(prefix); pw.print("windows="); pw.println(mChildren);
295         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
296                 pw.print(" hidden="); pw.print(mHidden);
297                 pw.print(" hasVisible="); pw.println(hasVisible);
298         if (waitingToShow || sendingToBottom) {
299             pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
300                     pw.print(" sendingToBottom="); pw.print(sendingToBottom);
301         }
302     }
303 
304     @Override
toString()305     public String toString() {
306         if (stringName == null) {
307             StringBuilder sb = new StringBuilder();
308             sb.append("WindowToken{");
309             sb.append(Integer.toHexString(System.identityHashCode(this)));
310             sb.append(" "); sb.append(token); sb.append('}');
311             stringName = sb.toString();
312         }
313         return stringName;
314     }
315 
316     @Override
getName()317     String getName() {
318         return toString();
319     }
320 
okToDisplay()321     boolean okToDisplay() {
322         return mDisplayContent != null && mDisplayContent.okToDisplay();
323     }
324 
okToAnimate()325     boolean okToAnimate() {
326         return mDisplayContent != null && mDisplayContent.okToAnimate();
327     }
328 
329     /**
330      * Return whether windows from this token can layer above the
331      * system bars, or in other words extend outside of the "Decor Frame"
332      */
canLayerAboveSystemBars()333     boolean canLayerAboveSystemBars() {
334         int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
335                 mOwnerCanManageAppTokens);
336         int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
337                 mOwnerCanManageAppTokens);
338         return mOwnerCanManageAppTokens && (layer > navLayer);
339     }
340 }
341