| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wm; |
| |
| import static android.view.Display.DEFAULT_DISPLAY; |
| import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; |
| import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; |
| import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; |
| import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; |
| |
| import android.content.Context; |
| import android.os.Trace; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.util.TimeUtils; |
| import android.view.Choreographer; |
| |
| import com.android.server.AnimationThread; |
| import com.android.server.policy.WindowManagerPolicy; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| |
| /** |
| * Singleton class that carries out the animations and Surface operations in a separate task |
| * on behalf of WindowManagerService. |
| */ |
| public class WindowAnimator { |
| private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowAnimator" : TAG_WM; |
| |
| final WindowManagerService mService; |
| final Context mContext; |
| final WindowManagerPolicy mPolicy; |
| |
| /** Is any window animating? */ |
| private boolean mAnimating; |
| private boolean mLastRootAnimating; |
| |
| final Choreographer.FrameCallback mAnimationFrameCallback; |
| |
| /** Time of current animation step. Reset on each iteration */ |
| long mCurrentTime; |
| |
| boolean mAppWindowAnimating; |
| /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this |
| * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ |
| int mAnimTransactionSequence; |
| |
| /** Window currently running an animation that has requested it be detached |
| * from the wallpaper. This means we need to ensure the wallpaper is |
| * visible behind it in case it animates in a way that would allow it to be |
| * seen. If multiple windows satisfy this, use the lowest window. */ |
| WindowState mWindowDetachedWallpaper = null; |
| |
| int mBulkUpdateParams = 0; |
| Object mLastWindowFreezeSource; |
| |
| SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2); |
| |
| private boolean mInitialized = false; |
| |
| // When set to true the animator will go over all windows after an animation frame is posted and |
| // check if some got replaced and can be removed. |
| private boolean mRemoveReplacedWindows = false; |
| |
| private Choreographer mChoreographer; |
| |
| /** |
| * Indicates whether we have an animation frame callback scheduled, which will happen at |
| * vsync-app and then schedule the animation tick at the right time (vsync-sf). |
| */ |
| private boolean mAnimationFrameCallbackScheduled; |
| |
| /** |
| * A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is |
| * executed and the corresponding transaction is closed and applied. |
| */ |
| private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>(); |
| private boolean mInExecuteAfterPrepareSurfacesRunnables; |
| |
| WindowAnimator(final WindowManagerService service) { |
| mService = service; |
| mContext = service.mContext; |
| mPolicy = service.mPolicy; |
| AnimationThread.getHandler().runWithScissors( |
| () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */); |
| |
| mAnimationFrameCallback = frameTimeNs -> { |
| synchronized (mService.mWindowMap) { |
| mAnimationFrameCallbackScheduled = false; |
| } |
| animate(frameTimeNs); |
| }; |
| } |
| |
| void addDisplayLocked(final int displayId) { |
| // Create the DisplayContentsAnimator object by retrieving it if the associated |
| // {@link DisplayContent} exists. |
| getDisplayContentsAnimatorLocked(displayId); |
| if (displayId == DEFAULT_DISPLAY) { |
| mInitialized = true; |
| } |
| } |
| |
| void removeDisplayLocked(final int displayId) { |
| final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId); |
| if (displayAnimator != null) { |
| if (displayAnimator.mScreenRotationAnimation != null) { |
| displayAnimator.mScreenRotationAnimation.kill(); |
| displayAnimator.mScreenRotationAnimation = null; |
| } |
| } |
| |
| mDisplayContentsAnimators.delete(displayId); |
| } |
| |
| /** |
| * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes |
| * an animation transaction, that might be blocking until the next sf-vsync, so we want to make |
| * sure other threads can make progress if this happens. |
| */ |
| private void animate(long frameTimeNs) { |
| |
| synchronized (mService.mWindowMap) { |
| if (!mInitialized) { |
| return; |
| } |
| |
| // Schedule next frame already such that back-pressure happens continuously |
| scheduleAnimation(); |
| } |
| |
| synchronized (mService.mWindowMap) { |
| mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS; |
| mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE; |
| mAnimating = false; |
| if (DEBUG_WINDOW_TRACE) { |
| Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime); |
| } |
| |
| if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION animate"); |
| mService.openSurfaceTransaction(); |
| try { |
| final AccessibilityController accessibilityController = |
| mService.mAccessibilityController; |
| final int numDisplays = mDisplayContentsAnimators.size(); |
| for (int i = 0; i < numDisplays; i++) { |
| final int displayId = mDisplayContentsAnimators.keyAt(i); |
| final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); |
| DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i); |
| |
| final ScreenRotationAnimation screenRotationAnimation = |
| displayAnimator.mScreenRotationAnimation; |
| if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { |
| if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) { |
| setAnimating(true); |
| } else { |
| mBulkUpdateParams |= SET_UPDATE_ROTATION; |
| screenRotationAnimation.kill(); |
| displayAnimator.mScreenRotationAnimation = null; |
| |
| //TODO (multidisplay): Accessibility supported only for the default |
| // display. |
| if (accessibilityController != null && dc.isDefaultDisplay) { |
| // We just finished rotation animation which means we did not |
| // announce the rotation and waited for it to end, announce now. |
| accessibilityController.onRotationChangedLocked( |
| mService.getDefaultDisplayContentLocked()); |
| } |
| } |
| } |
| |
| // Update animations of all applications, including those |
| // associated with exiting/removed apps |
| ++mAnimTransactionSequence; |
| dc.updateWindowsForAnimator(this); |
| dc.updateWallpaperForAnimator(this); |
| dc.prepareSurfaces(); |
| } |
| |
| for (int i = 0; i < numDisplays; i++) { |
| final int displayId = mDisplayContentsAnimators.keyAt(i); |
| final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); |
| |
| dc.checkAppWindowsReadyToShow(); |
| |
| final ScreenRotationAnimation screenRotationAnimation = |
| mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation; |
| if (screenRotationAnimation != null) { |
| screenRotationAnimation.updateSurfacesInTransaction(); |
| } |
| orAnimating(dc.getDockedDividerController().animate(mCurrentTime)); |
| //TODO (multidisplay): Magnification is supported only for the default display. |
| if (accessibilityController != null && dc.isDefaultDisplay) { |
| accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(); |
| } |
| } |
| |
| if (!mAnimating) { |
| cancelAnimation(); |
| } |
| |
| if (mService.mWatermark != null) { |
| mService.mWatermark.drawIfNeeded(); |
| } |
| } catch (RuntimeException e) { |
| Slog.wtf(TAG, "Unhandled exception in Window Manager", e); |
| } finally { |
| mService.closeSurfaceTransaction("WindowAnimator"); |
| if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate"); |
| } |
| |
| final int numDisplays = mDisplayContentsAnimators.size(); |
| for (int i = 0; i < numDisplays; i++) { |
| final int displayId = mDisplayContentsAnimators.keyAt(i); |
| final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); |
| dc.onPendingTransactionApplied(); |
| } |
| |
| boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this); |
| boolean doRequest = false; |
| if (mBulkUpdateParams != 0) { |
| doRequest = mService.mRoot.copyAnimToLayoutParams(); |
| } |
| |
| if (hasPendingLayoutChanges || doRequest) { |
| mService.mWindowPlacerLocked.requestTraversal(); |
| } |
| |
| final boolean rootAnimating = mService.mRoot.isSelfOrChildAnimating(); |
| if (rootAnimating && !mLastRootAnimating) { |
| |
| // Usually app transitions but quite a load onto the system already (with all the |
| // things happening in app), so pause task snapshot persisting to not increase the |
| // load. |
| mService.mTaskSnapshotController.setPersisterPaused(true); |
| Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0); |
| } |
| if (!rootAnimating && mLastRootAnimating) { |
| mService.mWindowPlacerLocked.requestTraversal(); |
| mService.mTaskSnapshotController.setPersisterPaused(false); |
| Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0); |
| } |
| |
| mLastRootAnimating = rootAnimating; |
| |
| if (mRemoveReplacedWindows) { |
| mService.mRoot.removeReplacedWindows(); |
| mRemoveReplacedWindows = false; |
| } |
| |
| mService.destroyPreservedSurfaceLocked(); |
| mService.mWindowPlacerLocked.destroyPendingSurfaces(); |
| |
| executeAfterPrepareSurfacesRunnables(); |
| |
| if (DEBUG_WINDOW_TRACE) { |
| Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating |
| + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams) |
| + " mPendingLayoutChanges(DEFAULT_DISPLAY)=" |
| + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY))); |
| } |
| } |
| } |
| |
| private static String bulkUpdateParamsToString(int bulkUpdateParams) { |
| StringBuilder builder = new StringBuilder(128); |
| if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) { |
| builder.append(" UPDATE_ROTATION"); |
| } |
| if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) { |
| builder.append(" WALLPAPER_MAY_CHANGE"); |
| } |
| if ((bulkUpdateParams & WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED) != 0) { |
| builder.append(" FORCE_HIDING_CHANGED"); |
| } |
| if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) { |
| builder.append(" ORIENTATION_CHANGE_COMPLETE"); |
| } |
| return builder.toString(); |
| } |
| |
| public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) { |
| final String subPrefix = " " + prefix; |
| final String subSubPrefix = " " + subPrefix; |
| |
| for (int i = 0; i < mDisplayContentsAnimators.size(); i++) { |
| pw.print(prefix); pw.print("DisplayContentsAnimator #"); |
| pw.print(mDisplayContentsAnimators.keyAt(i)); |
| pw.println(":"); |
| final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i); |
| final DisplayContent dc = |
| mService.mRoot.getDisplayContentOrCreate(mDisplayContentsAnimators.keyAt(i)); |
| dc.dumpWindowAnimators(pw, subPrefix); |
| if (displayAnimator.mScreenRotationAnimation != null) { |
| pw.print(subPrefix); pw.println("mScreenRotationAnimation:"); |
| displayAnimator.mScreenRotationAnimation.printTo(subSubPrefix, pw); |
| } else if (dumpAll) { |
| pw.print(subPrefix); pw.println("no ScreenRotationAnimation "); |
| } |
| pw.println(); |
| } |
| |
| pw.println(); |
| |
| if (dumpAll) { |
| pw.print(prefix); pw.print("mAnimTransactionSequence="); |
| pw.print(mAnimTransactionSequence); |
| pw.print(prefix); pw.print("mCurrentTime="); |
| pw.println(TimeUtils.formatUptime(mCurrentTime)); |
| } |
| if (mBulkUpdateParams != 0) { |
| pw.print(prefix); pw.print("mBulkUpdateParams=0x"); |
| pw.print(Integer.toHexString(mBulkUpdateParams)); |
| pw.println(bulkUpdateParamsToString(mBulkUpdateParams)); |
| } |
| if (mWindowDetachedWallpaper != null) { |
| pw.print(prefix); pw.print("mWindowDetachedWallpaper="); |
| pw.println(mWindowDetachedWallpaper); |
| } |
| } |
| |
| int getPendingLayoutChanges(final int displayId) { |
| if (displayId < 0) { |
| return 0; |
| } |
| final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId); |
| return (displayContent != null) ? displayContent.pendingLayoutChanges : 0; |
| } |
| |
| void setPendingLayoutChanges(final int displayId, final int changes) { |
| if (displayId < 0) { |
| return; |
| } |
| final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId); |
| if (displayContent != null) { |
| displayContent.pendingLayoutChanges |= changes; |
| } |
| } |
| |
| private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) { |
| if (displayId < 0) { |
| return null; |
| } |
| |
| DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId); |
| |
| // It is possible that this underlying {@link DisplayContent} has been removed. In this |
| // case, we do not want to create an animator associated with it as {link #animate} will |
| // fail. |
| if (displayAnimator == null && mService.mRoot.getDisplayContent(displayId) != null) { |
| displayAnimator = new DisplayContentsAnimator(); |
| mDisplayContentsAnimators.put(displayId, displayAnimator); |
| } |
| return displayAnimator; |
| } |
| |
| void setScreenRotationAnimationLocked(int displayId, ScreenRotationAnimation animation) { |
| final DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId); |
| |
| if (animator != null) { |
| animator.mScreenRotationAnimation = animation; |
| } |
| } |
| |
| ScreenRotationAnimation getScreenRotationAnimationLocked(int displayId) { |
| if (displayId < 0) { |
| return null; |
| } |
| |
| DisplayContentsAnimator animator = getDisplayContentsAnimatorLocked(displayId); |
| return animator != null? animator.mScreenRotationAnimation : null; |
| } |
| |
| void requestRemovalOfReplacedWindows(WindowState win) { |
| mRemoveReplacedWindows = true; |
| } |
| |
| void scheduleAnimation() { |
| if (!mAnimationFrameCallbackScheduled) { |
| mAnimationFrameCallbackScheduled = true; |
| mChoreographer.postFrameCallback(mAnimationFrameCallback); |
| } |
| } |
| |
| private void cancelAnimation() { |
| if (mAnimationFrameCallbackScheduled) { |
| mAnimationFrameCallbackScheduled = false; |
| mChoreographer.removeFrameCallback(mAnimationFrameCallback); |
| } |
| } |
| |
| private class DisplayContentsAnimator { |
| ScreenRotationAnimation mScreenRotationAnimation = null; |
| } |
| |
| boolean isAnimating() { |
| return mAnimating; |
| } |
| |
| boolean isAnimationScheduled() { |
| return mAnimationFrameCallbackScheduled; |
| } |
| |
| Choreographer getChoreographer() { |
| return mChoreographer; |
| } |
| |
| void setAnimating(boolean animating) { |
| mAnimating = animating; |
| } |
| |
| void orAnimating(boolean animating) { |
| mAnimating |= animating; |
| } |
| |
| /** |
| * Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and |
| * the corresponding transaction is closed and applied. |
| */ |
| void addAfterPrepareSurfacesRunnable(Runnable r) { |
| mAfterPrepareSurfacesRunnables.add(r); |
| scheduleAnimation(); |
| } |
| |
| void executeAfterPrepareSurfacesRunnables() { |
| |
| // Don't even think about to start recursing! |
| if (mInExecuteAfterPrepareSurfacesRunnables) { |
| return; |
| } |
| mInExecuteAfterPrepareSurfacesRunnables = true; |
| |
| // Traverse in order they were added. |
| final int size = mAfterPrepareSurfacesRunnables.size(); |
| for (int i = 0; i < size; i++) { |
| mAfterPrepareSurfacesRunnables.get(i).run(); |
| } |
| mAfterPrepareSurfacesRunnables.clear(); |
| mInExecuteAfterPrepareSurfacesRunnables = false; |
| } |
| } |