1 /* 2 * Copyright (C) 2014 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 com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER; 20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE; 21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC; 22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 25 26 import android.graphics.PixelFormat; 27 import android.graphics.Rect; 28 import android.os.SystemClock; 29 import android.util.Slog; 30 import android.view.DisplayInfo; 31 import android.view.SurfaceControl; 32 33 import java.io.PrintWriter; 34 35 public class DimLayer { 36 private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM; 37 private final WindowManagerService mService; 38 39 /** Actual surface that dims */ 40 private SurfaceControl mDimSurface; 41 42 /** Last value passed to mDimSurface.setAlpha() */ 43 private float mAlpha = 0; 44 45 /** Last value passed to mDimSurface.setLayer() */ 46 private int mLayer = -1; 47 48 /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */ 49 private final Rect mBounds = new Rect(); 50 51 /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */ 52 private final Rect mLastBounds = new Rect(); 53 54 /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */ 55 private boolean mShowing = false; 56 57 /** Value of mAlpha when beginning transition to mTargetAlpha */ 58 private float mStartAlpha = 0; 59 60 /** Final value of mAlpha following transition */ 61 private float mTargetAlpha = 0; 62 63 /** Time in units of SystemClock.uptimeMillis() at which the current transition started */ 64 private long mStartTime; 65 66 /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */ 67 private long mDuration; 68 69 private boolean mDestroyed = false; 70 71 private final int mDisplayId; 72 73 74 /** Interface implemented by users of the dim layer */ 75 interface DimLayerUser { 76 /** Returns true if the dim should be fullscreen. */ 77 boolean dimFullscreen(); 78 /** Returns the display info. of the dim layer user. */ 79 DisplayInfo getDisplayInfo(); 80 /** Returns true if the dim layer user is currently attached to a display */ 81 boolean isAttachedToDisplay(); 82 /** Gets the bounds of the dim layer user. */ 83 void getDimBounds(Rect outBounds); 84 /** Returns the layer to place a dim layer. */ 85 default int getLayerForDim(WindowStateAnimator animator, int layerOffset, 86 int defaultLayer) { 87 return defaultLayer; 88 } 89 90 String toShortString(); 91 } 92 /** The user of this dim layer. */ 93 private final DimLayerUser mUser; 94 95 private final String mName; 96 97 DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) { 98 mUser = user; 99 mDisplayId = displayId; 100 mService = service; 101 mName = name; 102 if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId); 103 } 104 105 private void constructSurface(WindowManagerService service) { 106 service.openSurfaceTransaction(); 107 try { 108 if (DEBUG_SURFACE_TRACE) { 109 mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession, 110 "DimSurface", 111 16, 16, PixelFormat.OPAQUE, 112 SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN); 113 } else { 114 mDimSurface = new SurfaceControl(service.mFxSession, mName, 115 16, 16, PixelFormat.OPAQUE, 116 SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN); 117 } 118 if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG, 119 " DIM " + mDimSurface + ": CREATE"); 120 mDimSurface.setLayerStack(mDisplayId); 121 adjustBounds(); 122 adjustAlpha(mAlpha); 123 adjustLayer(mLayer); 124 } catch (Exception e) { 125 Slog.e(TAG_WM, "Exception creating Dim surface", e); 126 } finally { 127 service.closeSurfaceTransaction(); 128 } 129 } 130 131 /** Return true if dim layer is showing */ 132 boolean isDimming() { 133 return mTargetAlpha != 0; 134 } 135 136 /** Return true if in a transition period */ 137 boolean isAnimating() { 138 return mTargetAlpha != mAlpha; 139 } 140 141 float getTargetAlpha() { 142 return mTargetAlpha; 143 } 144 145 void setLayer(int layer) { 146 if (mLayer == layer) { 147 return; 148 } 149 mLayer = layer; 150 adjustLayer(layer); 151 } 152 153 private void adjustLayer(int layer) { 154 if (mDimSurface != null) { 155 mDimSurface.setLayer(layer); 156 } 157 } 158 159 int getLayer() { 160 return mLayer; 161 } 162 163 private void setAlpha(float alpha) { 164 if (mAlpha == alpha) { 165 return; 166 } 167 mAlpha = alpha; 168 adjustAlpha(alpha); 169 } 170 171 private void adjustAlpha(float alpha) { 172 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha); 173 try { 174 if (mDimSurface != null) { 175 mDimSurface.setAlpha(alpha); 176 } 177 if (alpha == 0 && mShowing) { 178 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding"); 179 if (mDimSurface != null) { 180 mDimSurface.hide(); 181 mShowing = false; 182 } 183 } else if (alpha > 0 && !mShowing) { 184 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing"); 185 if (mDimSurface != null) { 186 mDimSurface.show(); 187 mShowing = true; 188 } 189 } 190 } catch (RuntimeException e) { 191 Slog.w(TAG, "Failure setting alpha immediately", e); 192 } 193 } 194 195 /** 196 * NOTE: Must be called with Surface transaction open. 197 */ 198 private void adjustBounds() { 199 if (mUser.dimFullscreen()) { 200 getBoundsForFullscreen(mBounds); 201 } 202 203 if (mDimSurface != null) { 204 mDimSurface.setPosition(mBounds.left, mBounds.top); 205 mDimSurface.setSize(mBounds.width(), mBounds.height()); 206 if (DEBUG_DIM_LAYER) Slog.v(TAG, 207 "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds); 208 } 209 210 mLastBounds.set(mBounds); 211 } 212 213 private void getBoundsForFullscreen(Rect outBounds) { 214 final int dw, dh; 215 final float xPos, yPos; 216 // Set surface size to screen size. 217 final DisplayInfo info = mUser.getDisplayInfo(); 218 // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose 219 // a corner. 220 dw = (int) (info.logicalWidth * 1.5); 221 dh = (int) (info.logicalHeight * 1.5); 222 // back off position so 1/4 of Surface is before and 1/4 is after. 223 xPos = -1 * dw / 6; 224 yPos = -1 * dh / 6; 225 outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh); 226 } 227 228 void setBoundsForFullscreen() { 229 getBoundsForFullscreen(mBounds); 230 setBounds(mBounds); 231 } 232 233 /** @param bounds The new bounds to set */ 234 void setBounds(Rect bounds) { 235 mBounds.set(bounds); 236 if (isDimming() && !mLastBounds.equals(bounds)) { 237 try { 238 mService.openSurfaceTransaction(); 239 adjustBounds(); 240 } catch (RuntimeException e) { 241 Slog.w(TAG, "Failure setting size", e); 242 } finally { 243 mService.closeSurfaceTransaction(); 244 } 245 } 246 } 247 248 /** 249 * @param duration The time to test. 250 * @return True if the duration would lead to an earlier end to the current animation. 251 */ 252 private boolean durationEndsEarlier(long duration) { 253 return SystemClock.uptimeMillis() + duration < mStartTime + mDuration; 254 } 255 256 /** Jump to the end of the animation. 257 * NOTE: Must be called with Surface transaction open. */ 258 void show() { 259 if (isAnimating()) { 260 if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate"); 261 show(mLayer, mTargetAlpha, 0); 262 } 263 } 264 265 /** 266 * Begin an animation to a new dim value. 267 * NOTE: Must be called with Surface transaction open. 268 * 269 * @param layer The layer to set the surface to. 270 * @param alpha The dim value to end at. 271 * @param duration How long to take to get there in milliseconds. 272 */ 273 void show(int layer, float alpha, long duration) { 274 if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha 275 + " duration=" + duration + ", mDestroyed=" + mDestroyed); 276 if (mDestroyed) { 277 Slog.e(TAG, "show: no Surface"); 278 // Make sure isAnimating() returns false. 279 mTargetAlpha = mAlpha = 0; 280 return; 281 } 282 283 if (mDimSurface == null) { 284 constructSurface(mService); 285 } 286 287 if (!mLastBounds.equals(mBounds)) { 288 adjustBounds(); 289 } 290 setLayer(layer); 291 292 long curTime = SystemClock.uptimeMillis(); 293 final boolean animating = isAnimating(); 294 if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration))) 295 || (!animating && mAlpha != alpha)) { 296 if (duration <= 0) { 297 // No animation required, just set values. 298 setAlpha(alpha); 299 } else { 300 // Start or continue animation with new parameters. 301 mStartAlpha = mAlpha; 302 mStartTime = curTime; 303 mDuration = duration; 304 } 305 } 306 mTargetAlpha = alpha; 307 if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime=" 308 + mStartTime + " mTargetAlpha=" + mTargetAlpha); 309 } 310 311 /** Immediate hide. 312 * NOTE: Must be called with Surface transaction open. */ 313 void hide() { 314 if (mShowing) { 315 if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate"); 316 hide(0); 317 } 318 } 319 320 /** 321 * Gradually fade to transparent. 322 * NOTE: Must be called with Surface transaction open. 323 * 324 * @param duration Time to fade in milliseconds. 325 */ 326 void hide(long duration) { 327 if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) { 328 if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration); 329 show(mLayer, 0, duration); 330 } 331 } 332 333 /** 334 * Advance the dimming per the last #show(int, float, long) call. 335 * NOTE: Must be called with Surface transaction open. 336 * 337 * @return True if animation is still required after this step. 338 */ 339 boolean stepAnimation() { 340 if (mDestroyed) { 341 Slog.e(TAG, "stepAnimation: surface destroyed"); 342 // Ensure that isAnimating() returns false; 343 mTargetAlpha = mAlpha = 0; 344 return false; 345 } 346 if (isAnimating()) { 347 final long curTime = SystemClock.uptimeMillis(); 348 final float alphaDelta = mTargetAlpha - mStartAlpha; 349 float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration; 350 if (alphaDelta > 0 && alpha > mTargetAlpha || 351 alphaDelta < 0 && alpha < mTargetAlpha) { 352 // Don't exceed limits. 353 alpha = mTargetAlpha; 354 } 355 if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha); 356 setAlpha(alpha); 357 } 358 359 return isAnimating(); 360 } 361 362 /** Cleanup */ 363 void destroySurface() { 364 if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface."); 365 if (mDimSurface != null) { 366 mDimSurface.destroy(); 367 mDimSurface = null; 368 } 369 mDestroyed = true; 370 } 371 372 public void printTo(String prefix, PrintWriter pw) { 373 pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface); 374 pw.print(" mLayer="); pw.print(mLayer); 375 pw.print(" mAlpha="); pw.println(mAlpha); 376 pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString()); 377 pw.print(" mBounds="); pw.println(mBounds.toShortString()); 378 pw.print(prefix); pw.print("Last animation: "); 379 pw.print(" mDuration="); pw.print(mDuration); 380 pw.print(" mStartTime="); pw.print(mStartTime); 381 pw.print(" curTime="); pw.println(SystemClock.uptimeMillis()); 382 pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha); 383 pw.print(" mTargetAlpha="); pw.println(mTargetAlpha); 384 } 385 } 386