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 android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
20 
21 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
23 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
24 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
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.TYPE_LAYER_OFFSET;
28 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
29 
30 import android.graphics.Matrix;
31 import android.util.Slog;
32 import android.util.TimeUtils;
33 import android.view.Choreographer;
34 import android.view.Display;
35 import android.view.SurfaceControl;
36 import android.view.animation.Animation;
37 import android.view.animation.Transformation;
38 
39 import java.io.PrintWriter;
40 import java.util.ArrayList;
41 
42 public class AppWindowAnimator {
43     static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowAnimator" : TAG_WM;
44 
45     private static final int PROLONG_ANIMATION_DISABLED = 0;
46     static final int PROLONG_ANIMATION_AT_END = 1;
47     static final int PROLONG_ANIMATION_AT_START = 2;
48 
49     final AppWindowToken mAppToken;
50     final WindowManagerService mService;
51     final WindowAnimator mAnimator;
52 
53     boolean animating;
54     boolean wasAnimating;
55     Animation animation;
56     boolean hasTransformation;
57     final Transformation transformation = new Transformation();
58 
59     // Have we been asked to have this token keep the screen frozen?
60     // Protect with mAnimator.
61     boolean freezingScreen;
62 
63     /**
64      * How long we last kept the screen frozen.
65      */
66     int lastFreezeDuration;
67 
68     // Offset to the window of all layers in the token, for use by
69     // AppWindowToken animations.
70     int animLayerAdjustment;
71 
72     // Propagated from AppWindowToken.allDrawn, to determine when
73     // the state changes.
74     boolean allDrawn;
75 
76     // Special surface for thumbnail animation.  If deferThumbnailDestruction is enabled, then we
77     // will make sure that the thumbnail is destroyed after the other surface is completed.  This
78     // requires that the duration of the two animations are the same.
79     SurfaceControl thumbnail;
80     int thumbnailTransactionSeq;
81     // TODO(b/62029108): combine both members into a private one. Create a member function to set
82     // the thumbnail layer to +1 to the highest layer position and replace all setter instances
83     // with this function. Remove all unnecessary calls to both variables in other classes.
84     int thumbnailLayer;
85     int thumbnailForceAboveLayer;
86     Animation thumbnailAnimation;
87     final Transformation thumbnailTransformation = new Transformation();
88     // This flag indicates that the destruction of the thumbnail surface is synchronized with
89     // another animation, so defer the destruction of this thumbnail surface for a single frame
90     // after the secondary animation completes.
91     boolean deferThumbnailDestruction;
92     // This flag is set if the animator has deferThumbnailDestruction set and has reached the final
93     // frame of animation.  It will extend the animation by one frame and then clean up afterwards.
94     boolean deferFinalFrameCleanup;
95     // If true when the animation hits the last frame, it will keep running on that last frame.
96     // This is used to synchronize animation with Recents and we wait for Recents to tell us to
97     // finish or for a new animation be set as fail-safe mechanism.
98     private int mProlongAnimation;
99     // Whether the prolong animation can be removed when animation is set. The purpose of this is
100     // that if recents doesn't tell us to remove the prolonged animation, we will get rid of it
101     // when new animation is set.
102     private boolean mClearProlongedAnimation;
103     private int mTransit;
104     private int mTransitFlags;
105 
106     /** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */
107     ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<>();
108 
109     /** True if the current animation was transferred from another AppWindowAnimator.
110      *  See {@link #transferCurrentAnimation}*/
111     boolean usingTransferredAnimation = false;
112 
113     private boolean mSkipFirstFrame = false;
114     private int mStackClip = STACK_CLIP_BEFORE_ANIM;
115 
116     static final Animation sDummyAnimation = new DummyAnimation();
117 
AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service)118     public AppWindowAnimator(final AppWindowToken atoken, WindowManagerService service) {
119         mAppToken = atoken;
120         mService = service;
121         mAnimator = mService.mAnimator;
122     }
123 
setAnimation(Animation anim, int width, int height, int parentWidth, int parentHeight, boolean skipFirstFrame, int stackClip, int transit, int transitFlags)124     public void setAnimation(Animation anim, int width, int height, int parentWidth,
125             int parentHeight, boolean skipFirstFrame, int stackClip, int transit,
126             int transitFlags) {
127         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
128                 + ": " + anim + " wxh=" + width + "x" + height
129                 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay());
130         animation = anim;
131         animating = false;
132         if (!anim.isInitialized()) {
133             anim.initialize(width, height, parentWidth, parentHeight);
134         }
135         anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
136         anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
137         int zorder = anim.getZAdjustment();
138         int adj = 0;
139         if (zorder == Animation.ZORDER_TOP) {
140             adj = TYPE_LAYER_OFFSET;
141         } else if (zorder == Animation.ZORDER_BOTTOM) {
142             adj = -TYPE_LAYER_OFFSET;
143         }
144 
145         if (animLayerAdjustment != adj) {
146             animLayerAdjustment = adj;
147             updateLayers();
148         }
149         // Start out animation gone if window is gone, or visible if window is visible.
150         transformation.clear();
151         transformation.setAlpha(mAppToken.hasContentToDisplay() ? 1 : 0);
152         hasTransformation = true;
153         mStackClip = stackClip;
154 
155         mSkipFirstFrame = skipFirstFrame;
156         mTransit = transit;
157         mTransitFlags = transitFlags;
158 
159         if (!mAppToken.fillsParent()) {
160             anim.setBackgroundColor(0);
161         }
162         if (mClearProlongedAnimation) {
163             mProlongAnimation = PROLONG_ANIMATION_DISABLED;
164         } else {
165             mClearProlongedAnimation = true;
166         }
167     }
168 
setDummyAnimation()169     public void setDummyAnimation() {
170         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting dummy animation in " + mAppToken
171                 + " hasContentToDisplay=" + mAppToken.hasContentToDisplay());
172         animation = sDummyAnimation;
173         hasTransformation = true;
174         transformation.clear();
175         transformation.setAlpha(mAppToken.hasContentToDisplay() ? 1 : 0);
176     }
177 
setNullAnimation()178     void setNullAnimation() {
179         animation = null;
180         usingTransferredAnimation = false;
181     }
182 
clearAnimation()183     public void clearAnimation() {
184         if (animation != null) {
185             animating = true;
186         }
187         clearThumbnail();
188         setNullAnimation();
189         if (mAppToken.deferClearAllDrawn) {
190             mAppToken.clearAllDrawn();
191         }
192         mStackClip = STACK_CLIP_BEFORE_ANIM;
193         mTransit = TRANSIT_UNSET;
194         mTransitFlags = 0;
195     }
196 
isAnimating()197     public boolean isAnimating() {
198         return animation != null || mAppToken.inPendingTransaction;
199     }
200 
getTransit()201     public int getTransit() {
202         return mTransit;
203     }
204 
getTransitFlags()205     int getTransitFlags() {
206         return mTransitFlags;
207     }
208 
clearThumbnail()209     public void clearThumbnail() {
210         if (thumbnail != null) {
211             thumbnail.hide();
212             mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail);
213             thumbnail = null;
214         }
215         deferThumbnailDestruction = false;
216     }
217 
getStackClip()218     int getStackClip() {
219         return mStackClip;
220     }
221 
transferCurrentAnimation( AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator)222     void transferCurrentAnimation(
223             AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
224 
225         if (animation != null) {
226             toAppAnimator.animation = animation;
227             toAppAnimator.animating = animating;
228             toAppAnimator.animLayerAdjustment = animLayerAdjustment;
229             setNullAnimation();
230             animLayerAdjustment = 0;
231             toAppAnimator.updateLayers();
232             updateLayers();
233             toAppAnimator.usingTransferredAnimation = true;
234             toAppAnimator.mTransit = mTransit;
235         }
236         if (transferWinAnimator != null) {
237             mAllAppWinAnimators.remove(transferWinAnimator);
238             toAppAnimator.mAllAppWinAnimators.add(transferWinAnimator);
239             toAppAnimator.hasTransformation = transferWinAnimator.mAppAnimator.hasTransformation;
240             if (toAppAnimator.hasTransformation) {
241                 toAppAnimator.transformation.set(transferWinAnimator.mAppAnimator.transformation);
242             } else {
243                 toAppAnimator.transformation.clear();
244             }
245             transferWinAnimator.mAppAnimator = toAppAnimator;
246         }
247     }
248 
updateLayers()249     private void updateLayers() {
250         mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
251         thumbnailLayer = mAppToken.getHighestAnimLayer();
252     }
253 
stepThumbnailAnimation(long currentTime)254     private void stepThumbnailAnimation(long currentTime) {
255         thumbnailTransformation.clear();
256         final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime);
257         thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation);
258 
259         ScreenRotationAnimation screenRotationAnimation =
260                 mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
261         final boolean screenAnimation = screenRotationAnimation != null
262                 && screenRotationAnimation.isAnimating();
263         if (screenAnimation) {
264             thumbnailTransformation.postCompose(screenRotationAnimation.getEnterTransformation());
265         }
266         // cache often used attributes locally
267         final float tmpFloats[] = mService.mTmpFloats;
268         thumbnailTransformation.getMatrix().getValues(tmpFloats);
269         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
270                 "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
271                 + ", " + tmpFloats[Matrix.MTRANS_Y]);
272         thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
273         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
274                 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
275                 + " layer=" + thumbnailLayer
276                 + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
277                 + "," + tmpFloats[Matrix.MSKEW_Y]
278                 + "][" + tmpFloats[Matrix.MSKEW_X]
279                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
280         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
281         if (thumbnailForceAboveLayer > 0) {
282             thumbnail.setLayer(thumbnailForceAboveLayer + 1);
283         } else {
284             // The thumbnail is layered below the window immediately above this
285             // token's anim layer.
286             thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
287                     - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
288         }
289         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
290                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
291         thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
292     }
293 
294     /**
295      * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
296      * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
297      * and keep producing the first frame of the animation.
298      */
getAnimationFrameTime(Animation animation, long currentTime)299     private long getAnimationFrameTime(Animation animation, long currentTime) {
300         if (mProlongAnimation == PROLONG_ANIMATION_AT_START) {
301             animation.setStartTime(currentTime);
302             return currentTime + 1;
303         }
304         return currentTime;
305     }
306 
stepAnimation(long currentTime)307     private boolean stepAnimation(long currentTime) {
308         if (animation == null) {
309             return false;
310         }
311         transformation.clear();
312         final long animationFrameTime = getAnimationFrameTime(animation, currentTime);
313         boolean hasMoreFrames = animation.getTransformation(animationFrameTime, transformation);
314         if (!hasMoreFrames) {
315             if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
316                 // We are deferring the thumbnail destruction, so extend the animation for one more
317                 // (dummy) frame before we clean up
318                 deferFinalFrameCleanup = true;
319                 hasMoreFrames = true;
320             } else {
321                 if (false && DEBUG_ANIM) Slog.v(TAG,
322                         "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
323                         ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation);
324                 deferFinalFrameCleanup = false;
325                 if (mProlongAnimation == PROLONG_ANIMATION_AT_END) {
326                     hasMoreFrames = true;
327                 } else {
328                     setNullAnimation();
329                     clearThumbnail();
330                     if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ "
331                             + currentTime);
332                 }
333             }
334         }
335         hasTransformation = hasMoreFrames;
336         return hasMoreFrames;
337     }
338 
getStartTimeCorrection()339     private long getStartTimeCorrection() {
340         if (mSkipFirstFrame) {
341 
342             // If the transition is an animation in which the first frame doesn't change the screen
343             // contents at all, we can just skip it and start at the second frame. So we shift the
344             // start time of the animation forward by minus the frame duration.
345             return -Choreographer.getInstance().getFrameIntervalNanos() / TimeUtils.NANOS_PER_MS;
346         } else {
347             return 0;
348         }
349     }
350 
351     // This must be called while inside a transaction.
stepAnimationLocked(long currentTime)352     boolean stepAnimationLocked(long currentTime) {
353         if (mService.okToDisplay()) {
354             // We will run animations as long as the display isn't frozen.
355 
356             if (animation == sDummyAnimation) {
357                 // This guy is going to animate, but not yet.  For now count
358                 // it as not animating for purposes of scheduling transactions;
359                 // when it is really time to animate, this will be set to
360                 // a real animation and the next call will execute normally.
361                 return false;
362             }
363 
364             if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
365                     && animation != null) {
366                 if (!animating) {
367                     if (DEBUG_ANIM) Slog.v(TAG,
368                         "Starting animation in " + mAppToken +
369                         " @ " + currentTime + " scale="
370                         + mService.getTransitionAnimationScaleLocked()
371                         + " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
372                     long correction = getStartTimeCorrection();
373                     animation.setStartTime(currentTime + correction);
374                     animating = true;
375                     if (thumbnail != null) {
376                         thumbnail.show();
377                         thumbnailAnimation.setStartTime(currentTime + correction);
378                     }
379                     mSkipFirstFrame = false;
380                 }
381                 if (stepAnimation(currentTime)) {
382                     // animation isn't over, step any thumbnail and that's
383                     // it for now.
384                     if (thumbnail != null) {
385                         stepThumbnailAnimation(currentTime);
386                     }
387                     return true;
388                 }
389             }
390         } else if (animation != null) {
391             // If the display is frozen, and there is a pending animation,
392             // clear it and make sure we run the cleanup code.
393             animating = true;
394             animation = null;
395         }
396 
397         hasTransformation = false;
398 
399         if (!animating && animation == null) {
400             return false;
401         }
402 
403         mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "AppWindowToken");
404 
405         clearAnimation();
406         animating = false;
407         if (animLayerAdjustment != 0) {
408             animLayerAdjustment = 0;
409             updateLayers();
410         }
411         if (mService.mInputMethodTarget != null
412                 && mService.mInputMethodTarget.mAppToken == mAppToken) {
413             mAppToken.getDisplayContent().computeImeTarget(true /* updateImeTarget */);
414         }
415 
416         if (DEBUG_ANIM) Slog.v(TAG, "Animation done in " + mAppToken
417                 + ": reportedVisible=" + mAppToken.reportedVisible
418                 + " okToDisplay=" + mService.okToDisplay()
419                 + " startingDisplayed=" + mAppToken.startingDisplayed);
420 
421         transformation.clear();
422 
423         final int numAllAppWinAnimators = mAllAppWinAnimators.size();
424         for (int i = 0; i < numAllAppWinAnimators; i++) {
425             mAllAppWinAnimators.get(i).mWin.onExitAnimationDone();
426         }
427         mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
428         return false;
429     }
430 
431     // This must be called while inside a transaction.
showAllWindowsLocked()432     boolean showAllWindowsLocked() {
433         boolean isAnimating = false;
434         final int NW = mAllAppWinAnimators.size();
435         for (int i=0; i<NW; i++) {
436             WindowStateAnimator winAnimator = mAllAppWinAnimators.get(i);
437             if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + winAnimator);
438             winAnimator.mWin.performShowLocked();
439             isAnimating |= winAnimator.isAnimationSet();
440         }
441         return isAnimating;
442     }
443 
dump(PrintWriter pw, String prefix, boolean dumpAll)444     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
445         pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
446         pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
447         pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
448                 pw.print(" allDrawn="); pw.print(allDrawn);
449                 pw.print(" animLayerAdjustment="); pw.println(animLayerAdjustment);
450         if (lastFreezeDuration != 0) {
451             pw.print(prefix); pw.print("lastFreezeDuration=");
452                     TimeUtils.formatDuration(lastFreezeDuration, pw); pw.println();
453         }
454         if (animating || animation != null) {
455             pw.print(prefix); pw.print("animating="); pw.println(animating);
456             pw.print(prefix); pw.print("animation="); pw.println(animation);
457             pw.print(prefix); pw.print("mTransit="); pw.println(mTransit);
458             pw.print(prefix); pw.print("mTransitFlags="); pw.println(mTransitFlags);
459         }
460         if (hasTransformation) {
461             pw.print(prefix); pw.print("XForm: ");
462                     transformation.printShortString(pw);
463                     pw.println();
464         }
465         if (thumbnail != null) {
466             pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
467                     pw.print(" layer="); pw.println(thumbnailLayer);
468             pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
469             pw.print(prefix); pw.print("thumbnailTransformation=");
470                     pw.println(thumbnailTransformation.toShortString());
471         }
472         for (int i=0; i<mAllAppWinAnimators.size(); i++) {
473             WindowStateAnimator wanim = mAllAppWinAnimators.get(i);
474             pw.print(prefix); pw.print("App Win Anim #"); pw.print(i);
475                     pw.print(": "); pw.println(wanim);
476         }
477     }
478 
startProlongAnimation(int prolongType)479     void startProlongAnimation(int prolongType) {
480         mProlongAnimation = prolongType;
481         mClearProlongedAnimation = false;
482     }
483 
endProlongedAnimation()484     void endProlongedAnimation() {
485         mProlongAnimation = PROLONG_ANIMATION_DISABLED;
486     }
487 
488     // This is an animation that does nothing: it just immediately finishes
489     // itself every time it is called.  It is used as a stub animation in cases
490     // where we want to synchronize multiple things that may be animating.
491     static final class DummyAnimation extends Animation {
492         @Override
getTransformation(long currentTime, Transformation outTransformation)493         public boolean getTransformation(long currentTime, Transformation outTransformation) {
494             return false;
495         }
496     }
497 
498 }
499