1 /*
2  * Copyright (C) 2016 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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
21 
22 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
23 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
26 
27 import android.annotation.Nullable;
28 import android.os.Bundle;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.view.animation.Animation;
32 
33 import com.android.internal.protolog.common.ProtoLog;
34 
35 import java.util.function.Consumer;
36 
37 /**
38  * A token that represents a set of wallpaper windows.
39  */
40 class WallpaperWindowToken extends WindowToken {
41 
42     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperWindowToken" : TAG_WM;
43 
44     private boolean mShowWhenLocked = false;
45 
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens)46     WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
47             DisplayContent dc, boolean ownerCanManageAppTokens) {
48         this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */);
49     }
50 
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options)51     WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
52             DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) {
53         super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens,
54                 false /* roundedCornerOverlay */, false /* fromClientToken */, options);
55         dc.mWallpaperController.addWallpaperToken(this);
56         setWindowingMode(WINDOWING_MODE_FULLSCREEN);
57     }
58 
59     @Override
asWallpaperToken()60     WallpaperWindowToken asWallpaperToken() {
61         return this;
62     }
63 
64     @Override
setExiting(boolean animateExit)65     void setExiting(boolean animateExit) {
66         super.setExiting(animateExit);
67         mDisplayContent.mWallpaperController.removeWallpaperToken(this);
68     }
69 
70     /**
71      * Controls whether this wallpaper shows underneath the keyguard or is hidden and only
72      * revealed once keyguard is dismissed.
73      */
setShowWhenLocked(boolean showWhenLocked)74     void setShowWhenLocked(boolean showWhenLocked) {
75         if (showWhenLocked == mShowWhenLocked) {
76             return;
77         }
78         mShowWhenLocked = showWhenLocked;
79         if (mDisplayContent.mWallpaperController.mIsLockscreenLiveWallpaperEnabled) {
80             // Move the window token to the front (private) or back (showWhenLocked). This is
81             // possible
82             // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER
83             // windows.
84             final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
85 
86             // Note: Moving all the way to the front or back breaks ordering based on addition
87             // times.
88             // We should never have more than one non-animating token of each type.
89             getParent().positionChildAt(position, this /* child */, false /*includingParents */);
90         }
91     }
92 
canShowWhenLocked()93     boolean canShowWhenLocked() {
94         return mShowWhenLocked;
95     }
96 
sendWindowWallpaperCommand( String action, int x, int y, int z, Bundle extras, boolean sync)97     void sendWindowWallpaperCommand(
98             String action, int x, int y, int z, Bundle extras, boolean sync) {
99         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
100             final WindowState wallpaper = mChildren.get(wallpaperNdx);
101             try {
102                 wallpaper.mClient.dispatchWallpaperCommand(action, x, y, z, extras, sync);
103                 // We only want to be synchronous with one wallpaper.
104                 sync = false;
105             } catch (RemoteException e) {
106             }
107         }
108     }
109 
updateWallpaperOffset(boolean sync)110     void updateWallpaperOffset(boolean sync) {
111         final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
112         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
113             final WindowState wallpaper = mChildren.get(wallpaperNdx);
114             if (wallpaperController.updateWallpaperOffset(wallpaper, sync)) {
115                 // We only want to be synchronous with one wallpaper.
116                 sync = false;
117             }
118         }
119     }
120 
121     /**
122      * Starts {@param anim} on all children.
123      */
startAnimation(Animation anim)124     void startAnimation(Animation anim) {
125         for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
126             final WindowState windowState = mChildren.get(ndx);
127             windowState.startAnimation(anim);
128         }
129     }
130 
updateWallpaperWindows(boolean visible)131     void updateWallpaperWindows(boolean visible) {
132         if (mVisibleRequested != visible) {
133             ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
134                     token, visible);
135             setVisibility(visible);
136         }
137 
138         final WindowState wallpaperTarget =
139                 mDisplayContent.mWallpaperController.getWallpaperTarget();
140 
141         if (visible && wallpaperTarget != null) {
142             final RecentsAnimationController recentsAnimationController =
143                     mWmService.getRecentsAnimationController();
144             if (recentsAnimationController != null
145                     && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) {
146                 // If the Recents animation is running, and the wallpaper target is the animating
147                 // task we want the wallpaper to be rotated in the same orientation as the
148                 // RecentsAnimation's target (e.g the launcher)
149                 recentsAnimationController.linkFixedRotationTransformIfNeeded(this);
150             } else if ((wallpaperTarget.mActivityRecord == null
151                     // Ignore invisible activity because it may be moving to background.
152                     || wallpaperTarget.mActivityRecord.isVisibleRequested())
153                     && wallpaperTarget.mToken.hasFixedRotationTransform()) {
154                 // If the wallpaper target has a fixed rotation, we want the wallpaper to follow its
155                 // rotation
156                 linkFixedRotationTransform(wallpaperTarget.mToken);
157             }
158         }
159         if (mTransitionController.inTransition(this)) {
160             // If wallpaper is in transition, setVisible() will be called from commitVisibility()
161             // when finishing transition. Otherwise commitVisibility() is already called from above
162             // setVisibility().
163             return;
164         }
165 
166         setVisible(visible);
167     }
168 
setVisible(boolean visible)169     private void setVisible(boolean visible) {
170         final boolean wasClientVisible = isClientVisible();
171         setClientVisible(visible);
172         if (visible && !wasClientVisible) {
173             for (int i = mChildren.size() - 1; i >= 0; i--) {
174                 final WindowState wallpaper = mChildren.get(i);
175                 wallpaper.requestUpdateWallpaperIfNeeded();
176             }
177         }
178     }
179 
180     /**
181      * Sets the requested visibility of this token. The visibility may not be if this is part of a
182      * transition. In that situation, make sure to call {@link #commitVisibility} when done.
183      */
setVisibility(boolean visible)184     void setVisibility(boolean visible) {
185         if (mVisibleRequested != visible) {
186             // Before setting mVisibleRequested so we can track changes.
187             mTransitionController.collect(this);
188 
189             setVisibleRequested(visible);
190         }
191 
192         // If in a transition, defer commits for activities that are going invisible
193         if (!visible && (mTransitionController.inTransition()
194                 || getDisplayContent().mAppTransition.isRunning())) {
195             return;
196         }
197 
198         commitVisibility(visible);
199     }
200 
201     /** Commits the visibility of this token. This will directly update the visibility. */
commitVisibility(boolean visible)202     void commitVisibility(boolean visible) {
203         if (visible == isVisible()) return;
204 
205         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
206                 "commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
207                 isVisible(), mVisibleRequested);
208 
209         setVisibleRequested(visible);
210         setVisible(visible);
211     }
212 
hasVisibleNotDrawnWallpaper()213     boolean hasVisibleNotDrawnWallpaper() {
214         if (!isVisible()) return false;
215         for (int j = mChildren.size() - 1; j >= 0; --j) {
216             final WindowState wallpaper = mChildren.get(j);
217             if (!wallpaper.isDrawn() && wallpaper.isVisible()) {
218                 return true;
219             }
220         }
221         return false;
222     }
223 
224     @Override
forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback)225     void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) {
226         callback.accept(this);
227     }
228 
229     @Override
fillsParent()230     boolean fillsParent() {
231         return true;
232     }
233 
234     @Override
showWallpaper()235     boolean showWallpaper() {
236         return false;
237     }
238 
239     @Override
setVisibleRequested(boolean visible)240     protected boolean setVisibleRequested(boolean visible) {
241         if (!super.setVisibleRequested(visible)) return false;
242         setInsetsFrozen(!visible);
243         return true;
244     }
245 
246     @Override
onChildVisibleRequestedChanged(@ullable WindowContainer child)247     protected boolean onChildVisibleRequestedChanged(@Nullable WindowContainer child) {
248         // Wallpaper manages visibleRequested directly (it's not determined by children)
249         return false;
250     }
251 
252     @Override
isVisible()253     boolean isVisible() {
254         return isClientVisible();
255     }
256 
257     @Override
toString()258     public String toString() {
259         if (stringName == null) {
260             StringBuilder sb = new StringBuilder();
261             sb.append("WallpaperWindowToken{");
262             sb.append(Integer.toHexString(System.identityHashCode(this)));
263             sb.append(" token="); sb.append(token); sb.append('}');
264             stringName = sb.toString();
265         }
266         return stringName;
267     }
268 }
269