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.graphics.Rect; 29 import android.os.Bundle; 30 import android.os.IBinder; 31 import android.os.RemoteException; 32 import android.util.SparseArray; 33 34 import com.android.internal.protolog.common.ProtoLog; 35 import com.android.window.flags.Flags; 36 37 import java.util.function.Consumer; 38 39 /** 40 * A token that represents a set of wallpaper windows. 41 */ 42 class WallpaperWindowToken extends WindowToken { 43 44 private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperWindowToken" : TAG_WM; 45 46 private boolean mShowWhenLocked = false; 47 float mWallpaperX = -1; 48 float mWallpaperY = -1; 49 float mWallpaperXStep = -1; 50 float mWallpaperYStep = -1; 51 int mWallpaperDisplayOffsetX = Integer.MIN_VALUE; 52 int mWallpaperDisplayOffsetY = Integer.MIN_VALUE; 53 54 /** 55 * Map from {@link android.app.WallpaperManager.ScreenOrientation} to crop rectangles. 56 * Crop rectangles represent the part of the wallpaper displayed for each screen orientation. 57 */ 58 private SparseArray<Rect> mCropHints = new SparseArray<>(); 59 WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens)60 WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, 61 DisplayContent dc, boolean ownerCanManageAppTokens) { 62 this(service, token, explicit, dc, ownerCanManageAppTokens, null /* options */); 63 } 64 WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options)65 WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit, 66 DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) { 67 super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, 68 false /* roundedCornerOverlay */, false /* fromClientToken */, options); 69 dc.mWallpaperController.addWallpaperToken(this); 70 setWindowingMode(WINDOWING_MODE_FULLSCREEN); 71 } 72 73 @Override asWallpaperToken()74 WallpaperWindowToken asWallpaperToken() { 75 return this; 76 } 77 78 @Override setExiting(boolean animateExit)79 void setExiting(boolean animateExit) { 80 super.setExiting(animateExit); 81 mDisplayContent.mWallpaperController.removeWallpaperToken(this); 82 } 83 84 @Override prepareSurfaces()85 public void prepareSurfaces() { 86 super.prepareSurfaces(); 87 88 if (Flags.ensureWallpaperInTransitions()) { 89 // Similar to Task.prepareSurfaces, outside of transitions we need to apply visibility 90 // changes directly. In transitions the transition player will take care of applying the 91 // visibility change. 92 if (!mTransitionController.inTransition(this)) { 93 getSyncTransaction().setVisibility(mSurfaceControl, isVisible()); 94 } 95 } 96 } 97 98 /** 99 * Controls whether this wallpaper shows underneath the keyguard or is hidden and only 100 * revealed once keyguard is dismissed. 101 */ setShowWhenLocked(boolean showWhenLocked)102 void setShowWhenLocked(boolean showWhenLocked) { 103 if (showWhenLocked == mShowWhenLocked) { 104 return; 105 } 106 mShowWhenLocked = showWhenLocked; 107 // Move the window token to the front (private) or back (showWhenLocked). This is possible 108 // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER windows. 109 final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP; 110 111 // Note: Moving all the way to the front or back breaks ordering based on addition times. 112 // There should never have more than one non-animating token of each type. 113 getParent().positionChildAt(position, this /* child */, false /*includingParents */); 114 mDisplayContent.mWallpaperController.onWallpaperTokenReordered(); 115 } 116 canShowWhenLocked()117 boolean canShowWhenLocked() { 118 return mShowWhenLocked; 119 } 120 setCropHints(SparseArray<Rect> cropHints)121 void setCropHints(SparseArray<Rect> cropHints) { 122 mCropHints = cropHints.clone(); 123 } 124 getCropHints()125 SparseArray<Rect> getCropHints() { 126 return mCropHints; 127 } 128 sendWindowWallpaperCommand( String action, int x, int y, int z, Bundle extras, boolean sync)129 void sendWindowWallpaperCommand( 130 String action, int x, int y, int z, Bundle extras, boolean sync) { 131 for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) { 132 final WindowState wallpaper = mChildren.get(wallpaperNdx); 133 try { 134 wallpaper.mClient.dispatchWallpaperCommand(action, x, y, z, extras, sync); 135 // We only want to be synchronous with one wallpaper. 136 sync = false; 137 } catch (RemoteException e) { 138 } 139 } 140 } 141 updateWallpaperOffset(boolean sync)142 void updateWallpaperOffset(boolean sync) { 143 final WallpaperController wallpaperController = mDisplayContent.mWallpaperController; 144 for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) { 145 final WindowState wallpaper = mChildren.get(wallpaperNdx); 146 if (wallpaperController.updateWallpaperOffset(wallpaper, 147 sync && !mWmService.mFlags.mWallpaperOffsetAsync)) { 148 // We only want to be synchronous with one wallpaper. 149 sync = false; 150 } 151 } 152 } 153 updateWallpaperWindows(boolean visible)154 void updateWallpaperWindows(boolean visible) { 155 if (mVisibleRequested != visible) { 156 ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b", 157 token, visible); 158 setVisibility(visible); 159 } 160 161 final WindowState wallpaperTarget = 162 mDisplayContent.mWallpaperController.getWallpaperTarget(); 163 164 if (visible && wallpaperTarget != null) { 165 final RecentsAnimationController recentsAnimationController = 166 mWmService.getRecentsAnimationController(); 167 if (recentsAnimationController != null 168 && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) { 169 // If the Recents animation is running, and the wallpaper target is the animating 170 // task we want the wallpaper to be rotated in the same orientation as the 171 // RecentsAnimation's target (e.g the launcher) 172 recentsAnimationController.linkFixedRotationTransformIfNeeded(this); 173 } else if ((wallpaperTarget.mActivityRecord == null 174 // Ignore invisible activity because it may be moving to background. 175 || wallpaperTarget.mActivityRecord.isVisibleRequested()) 176 && wallpaperTarget.mToken.hasFixedRotationTransform()) { 177 // If the wallpaper target has a fixed rotation, we want the wallpaper to follow its 178 // rotation 179 linkFixedRotationTransform(wallpaperTarget.mToken); 180 } 181 } 182 if (mTransitionController.inTransition(this)) { 183 // If wallpaper is in transition, setVisible() will be called from commitVisibility() 184 // when finishing transition. Otherwise commitVisibility() is already called from above 185 // setVisibility(). 186 return; 187 } 188 189 setVisible(visible); 190 } 191 setVisible(boolean visible)192 private void setVisible(boolean visible) { 193 final boolean wasClientVisible = isClientVisible(); 194 setClientVisible(visible); 195 if (visible && !wasClientVisible) { 196 for (int i = mChildren.size() - 1; i >= 0; i--) { 197 final WindowState wallpaper = mChildren.get(i); 198 wallpaper.requestUpdateWallpaperIfNeeded(); 199 } 200 } 201 } 202 203 /** 204 * Sets the requested visibility of this token. The visibility may not be if this is part of a 205 * transition. In that situation, make sure to call {@link #commitVisibility} when done. 206 */ setVisibility(boolean visible)207 void setVisibility(boolean visible) { 208 if (mVisibleRequested != visible) { 209 // Before setting mVisibleRequested so we can track changes. 210 final WindowState wpTarget = mDisplayContent.mWallpaperController.getWallpaperTarget(); 211 final boolean isTargetNotCollectedActivity = wpTarget == null 212 || (wpTarget.mActivityRecord != null 213 && !mTransitionController.isCollecting(wpTarget.mActivityRecord)); 214 // Skip collecting requesting-invisible wallpaper if the wallpaper target is empty or 215 // a non-collected activity. Because the visibility change may be called after the 216 // transition of activity is finished, e.g. WallpaperController#hideWallpapers from 217 // hiding surface of the target. Then if there is a next transition, the wallpaper 218 // change may be collected into the unrelated transition and cause a weird animation. 219 if (!isTargetNotCollectedActivity || visible) { 220 mTransitionController.collect(this); 221 } 222 223 setVisibleRequested(visible); 224 } 225 226 // If in a transition, defer commits for activities that are going invisible 227 if (!visible && (mTransitionController.inTransition() 228 || getDisplayContent().mAppTransition.isRunning())) { 229 return; 230 } 231 232 commitVisibility(visible); 233 } 234 235 /** Commits the visibility of this token. This will directly update the visibility. */ commitVisibility(boolean visible)236 void commitVisibility(boolean visible) { 237 if (visible == isVisible()) return; 238 239 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 240 "commitVisibility: %s: visible=%b mVisibleRequested=%b", this, 241 isVisible(), mVisibleRequested); 242 243 setVisibleRequested(visible); 244 setVisible(visible); 245 } 246 hasVisibleNotDrawnWallpaper()247 boolean hasVisibleNotDrawnWallpaper() { 248 if (!isVisible()) return false; 249 for (int j = mChildren.size() - 1; j >= 0; --j) { 250 final WindowState wallpaper = mChildren.get(j); 251 if (!wallpaper.isDrawn() && wallpaper.isVisible()) { 252 return true; 253 } 254 } 255 return false; 256 } 257 258 @Override forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback)259 void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) { 260 callback.accept(this); 261 } 262 263 @Override fillsParent()264 boolean fillsParent() { 265 return true; 266 } 267 268 @Override showWallpaper()269 boolean showWallpaper() { 270 return false; 271 } 272 273 @Override setVisibleRequested(boolean visible)274 protected boolean setVisibleRequested(boolean visible) { 275 if (!super.setVisibleRequested(visible)) return false; 276 setInsetsFrozen(!visible); 277 return true; 278 } 279 280 @Override onChildVisibleRequestedChanged(@ullable WindowContainer child)281 protected boolean onChildVisibleRequestedChanged(@Nullable WindowContainer child) { 282 // Wallpaper manages visibleRequested directly (it's not determined by children) 283 return false; 284 } 285 286 @Override isVisible()287 boolean isVisible() { 288 return isClientVisible(); 289 } 290 291 @Override isSyncFinished(BLASTSyncEngine.SyncGroup group)292 boolean isSyncFinished(BLASTSyncEngine.SyncGroup group) { 293 // TODO(b/233286785): Support sync state for wallpaper. See WindowState#prepareSync. 294 return !mVisibleRequested || !hasVisibleNotDrawnWallpaper(); 295 } 296 297 @Override toString()298 public String toString() { 299 if (stringName == null) { 300 StringBuilder sb = new StringBuilder(); 301 sb.append("WallpaperWindowToken{"); 302 sb.append(Integer.toHexString(System.identityHashCode(this))); 303 sb.append(" token="); sb.append(token); sb.append('}'); 304 stringName = sb.toString(); 305 } 306 return stringName; 307 } 308 } 309