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
17package com.android.server.wm;
18
19import static android.view.Display.DEFAULT_DISPLAY;
20import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
21import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
22import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
23import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
25import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
26
27import android.content.Context;
28import android.os.Trace;
29import android.util.Slog;
30import android.util.SparseArray;
31import android.util.TimeUtils;
32import android.view.Choreographer;
33import android.view.SurfaceControl;
34import android.view.WindowManagerPolicy;
35
36import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
37import com.android.server.AnimationThread;
38
39import java.io.PrintWriter;
40
41/**
42 * Singleton class that carries out the animations and Surface operations in a separate task
43 * on behalf of WindowManagerService.
44 */
45public class WindowAnimator {
46    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowAnimator" : TAG_WM;
47
48    final WindowManagerService mService;
49    final Context mContext;
50    final WindowManagerPolicy mPolicy;
51    private final WindowSurfacePlacer mWindowPlacerLocked;
52
53    /** Is any window animating? */
54    private boolean mAnimating;
55    private boolean mLastAnimating;
56
57    /** Is any app window animating? */
58    boolean mAppWindowAnimating;
59
60    final Choreographer.FrameCallback mAnimationFrameCallback;
61
62    /** Time of current animation step. Reset on each iteration */
63    long mCurrentTime;
64
65    /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
66     * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
67    int mAnimTransactionSequence;
68
69    /** Window currently running an animation that has requested it be detached
70     * from the wallpaper.  This means we need to ensure the wallpaper is
71     * visible behind it in case it animates in a way that would allow it to be
72     * seen. If multiple windows satisfy this, use the lowest window. */
73    WindowState mWindowDetachedWallpaper = null;
74
75    int mBulkUpdateParams = 0;
76    Object mLastWindowFreezeSource;
77
78    SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2);
79
80    boolean mInitialized = false;
81
82    // When set to true the animator will go over all windows after an animation frame is posted and
83    // check if some got replaced and can be removed.
84    private boolean mRemoveReplacedWindows = false;
85
86    private Choreographer mChoreographer;
87
88    /**
89     * Indicates whether we have an animation frame callback scheduled, which will happen at
90     * vsync-app and then schedule the animation tick at the right time (vsync-sf).
91     */
92    private boolean mAnimationFrameCallbackScheduled;
93
94    WindowAnimator(final WindowManagerService service) {
95        mService = service;
96        mContext = service.mContext;
97        mPolicy = service.mPolicy;
98        mWindowPlacerLocked = service.mWindowPlacerLocked;
99        AnimationThread.getHandler().runWithScissors(
100                () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
101
102        mAnimationFrameCallback = frameTimeNs -> {
103            synchronized (mService.mWindowMap) {
104                mAnimationFrameCallbackScheduled = false;
105            }
106            animate(frameTimeNs);
107        };
108    }
109
110    void addDisplayLocked(final int displayId) {
111        // Create the DisplayContentsAnimator object by retrieving it if the associated
112        // {@link DisplayContent} exists.
113        getDisplayContentsAnimatorLocked(displayId);
114        if (displayId == DEFAULT_DISPLAY) {
115            mInitialized = true;
116        }
117    }
118
119    void removeDisplayLocked(final int displayId) {
120        final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
121        if (displayAnimator != null) {
122            if (displayAnimator.mScreenRotationAnimation != null) {
123                displayAnimator.mScreenRotationAnimation.kill();
124                displayAnimator.mScreenRotationAnimation = null;
125            }
126        }
127
128        mDisplayContentsAnimators.delete(displayId);
129    }
130
131    /**
132     * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
133     * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
134     * sure other threads can make progress if this happens.
135     */
136    private void animate(long frameTimeNs) {
137        boolean transactionOpen = false;
138        try {
139            synchronized (mService.mWindowMap) {
140                if (!mInitialized) {
141                    return;
142                }
143
144                mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
145                mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
146                mAnimating = false;
147                mAppWindowAnimating = false;
148                if (DEBUG_WINDOW_TRACE) {
149                    Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
150                }
151
152                if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate");
153                mService.openSurfaceTransaction();
154                transactionOpen = true;
155                SurfaceControl.setAnimationTransaction();
156
157                final AccessibilityController accessibilityController =
158                        mService.mAccessibilityController;
159                final int numDisplays = mDisplayContentsAnimators.size();
160                for (int i = 0; i < numDisplays; i++) {
161                    final int displayId = mDisplayContentsAnimators.keyAt(i);
162                    final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
163                    dc.stepAppWindowsAnimation(mCurrentTime);
164                    DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
165
166                    final ScreenRotationAnimation screenRotationAnimation =
167                            displayAnimator.mScreenRotationAnimation;
168                    if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
169                        if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
170                            setAnimating(true);
171                        } else {
172                            mBulkUpdateParams |= SET_UPDATE_ROTATION;
173                            screenRotationAnimation.kill();
174                            displayAnimator.mScreenRotationAnimation = null;
175
176                            //TODO (multidisplay): Accessibility supported only for the default
177                            // display.
178                            if (accessibilityController != null && dc.isDefaultDisplay) {
179                                // We just finished rotation animation which means we did not
180                                // announce the rotation and waited for it to end, announce now.
181                                accessibilityController.onRotationChangedLocked(
182                                        mService.getDefaultDisplayContentLocked());
183                            }
184                        }
185                    }
186
187                    // Update animations of all applications, including those
188                    // associated with exiting/removed apps
189                    ++mAnimTransactionSequence;
190                    dc.updateWindowsForAnimator(this);
191                    dc.updateWallpaperForAnimator(this);
192                    dc.prepareWindowSurfaces();
193                }
194
195                for (int i = 0; i < numDisplays; i++) {
196                    final int displayId = mDisplayContentsAnimators.keyAt(i);
197                    final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
198
199                    dc.checkAppWindowsReadyToShow();
200
201                    final ScreenRotationAnimation screenRotationAnimation =
202                            mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
203                    if (screenRotationAnimation != null) {
204                        screenRotationAnimation.updateSurfacesInTransaction();
205                    }
206
207                    orAnimating(dc.animateDimLayers());
208                    orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
209                    //TODO (multidisplay): Magnification is supported only for the default display.
210                    if (accessibilityController != null && dc.isDefaultDisplay) {
211                        accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
212                    }
213                }
214
215                if (mService.mDragState != null) {
216                    mAnimating |= mService.mDragState.stepAnimationLocked(mCurrentTime);
217                }
218
219                if (mAnimating) {
220                    mService.scheduleAnimationLocked();
221                }
222
223                if (mService.mWatermark != null) {
224                    mService.mWatermark.drawIfNeeded();
225                }
226            }
227        } catch (RuntimeException e) {
228            Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
229        } finally {
230            if (transactionOpen) {
231
232                // Do not hold window manager lock while closing the transaction, as this might be
233                // blocking until the next frame, which can lead to total lock starvation.
234                mService.closeSurfaceTransaction(false /* withLockHeld */);
235                if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate");
236            }
237        }
238
239        synchronized (mService.mWindowMap) {
240            boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
241            boolean doRequest = false;
242            if (mBulkUpdateParams != 0) {
243                doRequest = mService.mRoot.copyAnimToLayoutParams();
244            }
245
246            if (hasPendingLayoutChanges || doRequest) {
247                mWindowPlacerLocked.requestTraversal();
248            }
249
250            if (mAnimating && !mLastAnimating) {
251
252                // Usually app transitions but quite a load onto the system already (with all the
253                // things happening in app), so pause task snapshot persisting to not increase the
254                // load.
255                mService.mTaskSnapshotController.setPersisterPaused(true);
256                Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
257            }
258            if (!mAnimating && mLastAnimating) {
259                mWindowPlacerLocked.requestTraversal();
260                mService.mTaskSnapshotController.setPersisterPaused(false);
261                Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
262            }
263
264            mLastAnimating = mAnimating;
265
266            if (mRemoveReplacedWindows) {
267                mService.mRoot.removeReplacedWindows();
268                mRemoveReplacedWindows = false;
269            }
270
271            mService.stopUsingSavedSurfaceLocked();
272            mService.destroyPreservedSurfaceLocked();
273            mService.mWindowPlacerLocked.destroyPendingSurfaces();
274
275            if (DEBUG_WINDOW_TRACE) {
276                Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
277                        + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
278                        + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
279                        + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
280            }
281        }
282    }
283
284    private static String bulkUpdateParamsToString(int bulkUpdateParams) {
285        StringBuilder builder = new StringBuilder(128);
286        if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
287            builder.append(" UPDATE_ROTATION");
288        }
289        if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
290            builder.append(" WALLPAPER_MAY_CHANGE");
291        }
292        if ((bulkUpdateParams & WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED) != 0) {
293            builder.append(" FORCE_HIDING_CHANGED");
294        }
295        if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
296            builder.append(" ORIENTATION_CHANGE_COMPLETE");
297        }
298        if ((bulkUpdateParams & WindowSurfacePlacer.SET_TURN_ON_SCREEN) != 0) {
299            builder.append(" TURN_ON_SCREEN");
300        }
301        return builder.toString();
302    }
303
304    public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
305        final String subPrefix = "  " + prefix;
306        final String subSubPrefix = "  " + subPrefix;
307
308        for (int i = 0; i < mDisplayContentsAnimators.size(); i++) {
309            pw.print(prefix); pw.print("DisplayContentsAnimator #");
310                    pw.print(mDisplayContentsAnimators.keyAt(i));
311                    pw.println(":");
312            final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
313            final DisplayContent dc =
314                    mService.mRoot.getDisplayContentOrCreate(mDisplayContentsAnimators.keyAt(i));
315            dc.dumpWindowAnimators(pw, subPrefix);
316            if (displayAnimator.mScreenRotationAnimation != null) {
317                pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
318                displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw);
319            } else if (dumpAll) {
320                pw.print(subPrefix); pw.println("no ScreenRotationAnimation ");
321            }
322            pw.println();
323        }
324
325        pw.println();
326
327        if (dumpAll) {
328            pw.print(prefix); pw.print("mAnimTransactionSequence=");
329                    pw.print(mAnimTransactionSequence);
330            pw.print(prefix); pw.print("mCurrentTime=");
331                    pw.println(TimeUtils.formatUptime(mCurrentTime));
332        }
333        if (mBulkUpdateParams != 0) {
334            pw.print(prefix); pw.print("mBulkUpdateParams=0x");
335                    pw.print(Integer.toHexString(mBulkUpdateParams));
336                    pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
337        }
338        if (mWindowDetachedWallpaper != null) {
339            pw.print(prefix); pw.print("mWindowDetachedWallpaper=");
340                pw.println(mWindowDetachedWallpaper);
341        }
342    }
343
344    int getPendingLayoutChanges(final int displayId) {
345        if (displayId < 0) {
346            return 0;
347        }
348        final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId);
349        return (displayContent != null) ? displayContent.pendingLayoutChanges : 0;
350    }
351
352    void setPendingLayoutChanges(final int displayId, final int changes) {
353        if (displayId < 0) {
354            return;
355        }
356        final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId);
357        if (displayContent != null) {
358            displayContent.pendingLayoutChanges |= changes;
359        }
360    }
361
362    private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
363        if (displayId < 0) {
364            return null;
365        }
366
367        DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
368
369        // It is possible that this underlying {@link DisplayContent} has been removed. In this
370        // case, we do not want to create an animator associated with it as {link #animate} will
371        // fail.
372        if (displayAnimator == null && mService.mRoot.getDisplayContent(displayId) != null) {
373            displayAnimator = new DisplayContentsAnimator();
374            mDisplayContentsAnimators.put(displayId, displayAnimator);
375        }
376        return displayAnimator;
377    }
378
379    void setScreenRotationAnimationLocked(int displayId, ScreenRotationAnimation animation) {
380        final DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
381
382        if (animator != null) {
383            animator.mScreenRotationAnimation = animation;
384        }
385    }
386
387    ScreenRotationAnimation getScreenRotationAnimationLocked(int displayId) {
388        if (displayId < 0) {
389            return null;
390        }
391
392        DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId);
393        return animator != null? animator.mScreenRotationAnimation : null;
394    }
395
396    void requestRemovalOfReplacedWindows(WindowState win) {
397        mRemoveReplacedWindows = true;
398    }
399
400    void scheduleAnimation() {
401        if (!mAnimationFrameCallbackScheduled) {
402            mAnimationFrameCallbackScheduled = true;
403            mChoreographer.postFrameCallback(mAnimationFrameCallback);
404        }
405    }
406
407    private class DisplayContentsAnimator {
408        ScreenRotationAnimation mScreenRotationAnimation = null;
409    }
410
411    boolean isAnimating() {
412        return mAnimating;
413    }
414
415    boolean isAnimationScheduled() {
416        return mAnimationFrameCallbackScheduled;
417    }
418
419    Choreographer getChoreographer() {
420        return mChoreographer;
421    }
422
423    void setAnimating(boolean animating) {
424        mAnimating = animating;
425    }
426
427    void orAnimating(boolean animating) {
428        mAnimating |= animating;
429    }
430}
431