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