• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.ScreenRotationAnimationProto.ANIMATION_RUNNING;
20 import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
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 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
26 import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
27 
28 import android.content.Context;
29 import android.graphics.Matrix;
30 import android.graphics.Rect;
31 import android.util.Slog;
32 import android.util.proto.ProtoOutputStream;
33 import android.view.Display;
34 import android.view.DisplayInfo;
35 import android.view.Surface;
36 import android.view.Surface.OutOfResourcesException;
37 import android.view.SurfaceControl;
38 import android.view.animation.Animation;
39 import android.view.animation.AnimationUtils;
40 import android.view.animation.Transformation;
41 
42 import java.io.PrintWriter;
43 
44 class ScreenRotationAnimation {
45     static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
46     static final boolean DEBUG_STATE = false;
47     static final boolean DEBUG_TRANSFORMS = false;
48     static final boolean TWO_PHASE_ANIMATION = false;
49     static final boolean USE_CUSTOM_BLACK_FRAME = false;
50 
51     /*
52      * Layers for screen rotation animation. We put these layers above
53      * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
54      */
55     static final int SCREEN_FREEZE_LAYER_BASE       = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
56     static final int SCREEN_FREEZE_LAYER_ENTER      = SCREEN_FREEZE_LAYER_BASE;
57     static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
58     static final int SCREEN_FREEZE_LAYER_EXIT       = SCREEN_FREEZE_LAYER_BASE + 2;
59     static final int SCREEN_FREEZE_LAYER_CUSTOM     = SCREEN_FREEZE_LAYER_BASE + 3;
60 
61     final Context mContext;
62     final DisplayContent mDisplayContent;
63     SurfaceControl mSurfaceControl;
64     BlackFrame mCustomBlackFrame;
65     BlackFrame mExitingBlackFrame;
66     BlackFrame mEnteringBlackFrame;
67     int mWidth, mHeight;
68 
69     int mOriginalRotation;
70     int mOriginalWidth, mOriginalHeight;
71     int mCurRotation;
72     Rect mOriginalDisplayRect = new Rect();
73     Rect mCurrentDisplayRect = new Rect();
74 
75     // For all animations, "exit" is for the UI elements that are going
76     // away (that is the snapshot of the old screen), and "enter" is for
77     // the new UI elements that are appearing (that is the active windows
78     // in their final orientation).
79 
80     // The starting animation for the exiting and entering elements.  This
81     // animation applies a transformation while the rotation is in progress.
82     // It is started immediately, before the new entering UI is ready.
83     Animation mStartExitAnimation;
84     final Transformation mStartExitTransformation = new Transformation();
85     Animation mStartEnterAnimation;
86     final Transformation mStartEnterTransformation = new Transformation();
87     Animation mStartFrameAnimation;
88     final Transformation mStartFrameTransformation = new Transformation();
89 
90     // The finishing animation for the exiting and entering elements.  This
91     // animation needs to undo the transformation of the starting animation.
92     // It starts running once the new rotation UI elements are ready to be
93     // displayed.
94     Animation mFinishExitAnimation;
95     final Transformation mFinishExitTransformation = new Transformation();
96     Animation mFinishEnterAnimation;
97     final Transformation mFinishEnterTransformation = new Transformation();
98     Animation mFinishFrameAnimation;
99     final Transformation mFinishFrameTransformation = new Transformation();
100 
101     // The current active animation to move from the old to the new rotated
102     // state.  Which animation is run here will depend on the old and new
103     // rotations.
104     Animation mRotateExitAnimation;
105     final Transformation mRotateExitTransformation = new Transformation();
106     Animation mRotateEnterAnimation;
107     final Transformation mRotateEnterTransformation = new Transformation();
108     Animation mRotateFrameAnimation;
109     final Transformation mRotateFrameTransformation = new Transformation();
110 
111     // A previously running rotate animation.  This will be used if we need
112     // to switch to a new rotation before finishing the previous one.
113     Animation mLastRotateExitAnimation;
114     final Transformation mLastRotateExitTransformation = new Transformation();
115     Animation mLastRotateEnterAnimation;
116     final Transformation mLastRotateEnterTransformation = new Transformation();
117     Animation mLastRotateFrameAnimation;
118     final Transformation mLastRotateFrameTransformation = new Transformation();
119 
120     // Complete transformations being applied.
121     final Transformation mExitTransformation = new Transformation();
122     final Transformation mEnterTransformation = new Transformation();
123     final Transformation mFrameTransformation = new Transformation();
124 
125     boolean mStarted;
126     boolean mAnimRunning;
127     boolean mFinishAnimReady;
128     long mFinishAnimStartTime;
129     boolean mForceDefaultOrientation;
130 
131     final Matrix mFrameInitialMatrix = new Matrix();
132     final Matrix mSnapshotInitialMatrix = new Matrix();
133     final Matrix mSnapshotFinalMatrix = new Matrix();
134     final Matrix mExitFrameFinalMatrix = new Matrix();
135     final Matrix mTmpMatrix = new Matrix();
136     final float[] mTmpFloats = new float[9];
137     private boolean mMoreRotateEnter;
138     private boolean mMoreRotateExit;
139     private boolean mMoreRotateFrame;
140     private boolean mMoreFinishEnter;
141     private boolean mMoreFinishExit;
142     private boolean mMoreFinishFrame;
143     private boolean mMoreStartEnter;
144     private boolean mMoreStartExit;
145     private boolean mMoreStartFrame;
146     long mHalfwayPoint;
147 
148     private final WindowManagerService mService;
149 
printTo(String prefix, PrintWriter pw)150     public void printTo(String prefix, PrintWriter pw) {
151         pw.print(prefix); pw.print("mSurface="); pw.print(mSurfaceControl);
152                 pw.print(" mWidth="); pw.print(mWidth);
153                 pw.print(" mHeight="); pw.println(mHeight);
154         if (USE_CUSTOM_BLACK_FRAME) {
155             pw.print(prefix); pw.print("mCustomBlackFrame="); pw.println(mCustomBlackFrame);
156             if (mCustomBlackFrame != null) {
157                 mCustomBlackFrame.printTo(prefix + "  ", pw);
158             }
159         }
160         pw.print(prefix); pw.print("mExitingBlackFrame="); pw.println(mExitingBlackFrame);
161         if (mExitingBlackFrame != null) {
162             mExitingBlackFrame.printTo(prefix + "  ", pw);
163         }
164         pw.print(prefix); pw.print("mEnteringBlackFrame="); pw.println(mEnteringBlackFrame);
165         if (mEnteringBlackFrame != null) {
166             mEnteringBlackFrame.printTo(prefix + "  ", pw);
167         }
168         pw.print(prefix); pw.print("mCurRotation="); pw.print(mCurRotation);
169                 pw.print(" mOriginalRotation="); pw.println(mOriginalRotation);
170         pw.print(prefix); pw.print("mOriginalWidth="); pw.print(mOriginalWidth);
171                 pw.print(" mOriginalHeight="); pw.println(mOriginalHeight);
172         pw.print(prefix); pw.print("mStarted="); pw.print(mStarted);
173                 pw.print(" mAnimRunning="); pw.print(mAnimRunning);
174                 pw.print(" mFinishAnimReady="); pw.print(mFinishAnimReady);
175                 pw.print(" mFinishAnimStartTime="); pw.println(mFinishAnimStartTime);
176         pw.print(prefix); pw.print("mStartExitAnimation="); pw.print(mStartExitAnimation);
177                 pw.print(" "); mStartExitTransformation.printShortString(pw); pw.println();
178         pw.print(prefix); pw.print("mStartEnterAnimation="); pw.print(mStartEnterAnimation);
179                 pw.print(" "); mStartEnterTransformation.printShortString(pw); pw.println();
180         pw.print(prefix); pw.print("mStartFrameAnimation="); pw.print(mStartFrameAnimation);
181                 pw.print(" "); mStartFrameTransformation.printShortString(pw); pw.println();
182         pw.print(prefix); pw.print("mFinishExitAnimation="); pw.print(mFinishExitAnimation);
183                 pw.print(" "); mFinishExitTransformation.printShortString(pw); pw.println();
184         pw.print(prefix); pw.print("mFinishEnterAnimation="); pw.print(mFinishEnterAnimation);
185                 pw.print(" "); mFinishEnterTransformation.printShortString(pw); pw.println();
186         pw.print(prefix); pw.print("mFinishFrameAnimation="); pw.print(mFinishFrameAnimation);
187                 pw.print(" "); mFinishFrameTransformation.printShortString(pw); pw.println();
188         pw.print(prefix); pw.print("mRotateExitAnimation="); pw.print(mRotateExitAnimation);
189                 pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
190         pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
191                 pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
192         pw.print(prefix); pw.print("mRotateFrameAnimation="); pw.print(mRotateFrameAnimation);
193                 pw.print(" "); mRotateFrameTransformation.printShortString(pw); pw.println();
194         pw.print(prefix); pw.print("mExitTransformation=");
195                 mExitTransformation.printShortString(pw); pw.println();
196         pw.print(prefix); pw.print("mEnterTransformation=");
197                 mEnterTransformation.printShortString(pw); pw.println();
198         pw.print(prefix); pw.print("mFrameTransformation=");
199                 mFrameTransformation.printShortString(pw); pw.println();
200         pw.print(prefix); pw.print("mFrameInitialMatrix=");
201                 mFrameInitialMatrix.printShortString(pw);
202                 pw.println();
203         pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
204                 mSnapshotInitialMatrix.printShortString(pw);
205                 pw.print(" mSnapshotFinalMatrix="); mSnapshotFinalMatrix.printShortString(pw);
206                 pw.println();
207         pw.print(prefix); pw.print("mExitFrameFinalMatrix=");
208                 mExitFrameFinalMatrix.printShortString(pw);
209                 pw.println();
210         pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
211         if (mForceDefaultOrientation) {
212             pw.print(" mOriginalDisplayRect="); pw.print(mOriginalDisplayRect.toShortString());
213             pw.print(" mCurrentDisplayRect="); pw.println(mCurrentDisplayRect.toShortString());
214         }
215     }
216 
writeToProto(ProtoOutputStream proto, long fieldId)217     public void writeToProto(ProtoOutputStream proto, long fieldId) {
218         final long token = proto.start(fieldId);
219         proto.write(STARTED, mStarted);
220         proto.write(ANIMATION_RUNNING, mAnimRunning);
221         proto.end(token);
222     }
223 
ScreenRotationAnimation(Context context, DisplayContent displayContent, boolean fixedToUserRotation, boolean isSecure, WindowManagerService service)224     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
225             boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
226         mService = service;
227         mContext = context;
228         mDisplayContent = displayContent;
229         displayContent.getBounds(mOriginalDisplayRect);
230 
231         // Screenshot does NOT include rotation!
232         final Display display = displayContent.getDisplay();
233         int originalRotation = display.getRotation();
234         final int originalWidth;
235         final int originalHeight;
236         DisplayInfo displayInfo = displayContent.getDisplayInfo();
237         if (fixedToUserRotation) {
238             // Emulated orientation.
239             mForceDefaultOrientation = true;
240             originalWidth = displayContent.mBaseDisplayWidth;
241             originalHeight = displayContent.mBaseDisplayHeight;
242         } else {
243             // Normal situation
244             originalWidth = displayInfo.logicalWidth;
245             originalHeight = displayInfo.logicalHeight;
246         }
247         if (originalRotation == Surface.ROTATION_90
248                 || originalRotation == Surface.ROTATION_270) {
249             mWidth = originalHeight;
250             mHeight = originalWidth;
251         } else {
252             mWidth = originalWidth;
253             mHeight = originalHeight;
254         }
255 
256         mOriginalRotation = originalRotation;
257         mOriginalWidth = originalWidth;
258         mOriginalHeight = originalHeight;
259 
260         final SurfaceControl.Transaction t = mService.mTransactionFactory.make();
261         try {
262             mSurfaceControl = displayContent.makeOverlay()
263                     .setName("ScreenshotSurface")
264                     .setBufferSize(mWidth, mHeight)
265                     .setSecure(isSecure)
266                     .build();
267 
268             // In case display bounds change, screenshot buffer and surface may mismatch so set a
269             // scaling mode.
270             SurfaceControl.Transaction t2 = mService.mTransactionFactory.make();
271             t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
272             t2.apply(true /* sync */);
273 
274             // Capture a screenshot into the surface we just created.
275             final int displayId = display.getDisplayId();
276             final Surface surface = mService.mSurfaceFactory.make();
277             surface.copyFrom(mSurfaceControl);
278             SurfaceControl.ScreenshotGraphicBuffer gb =
279                 mService.mDisplayManagerInternal.screenshot(displayId);
280             if (gb != null) {
281                 try {
282                     surface.attachAndQueueBuffer(gb.getGraphicBuffer());
283                 } catch (RuntimeException e) {
284                     Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
285                 }
286                 // If the screenshot contains secure layers, we have to make sure the
287                 // screenshot surface we display it in also has FLAG_SECURE so that
288                 // the user can not screenshot secure layers via the screenshot surface.
289                 if (gb.containsSecureLayers()) {
290                     t.setSecure(mSurfaceControl, true);
291                 }
292                 t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT);
293                 t.setAlpha(mSurfaceControl, 0);
294                 t.show(mSurfaceControl);
295             } else {
296                 Slog.w(TAG, "Unable to take screenshot of display " + displayId);
297             }
298             surface.destroy();
299         } catch (OutOfResourcesException e) {
300             Slog.w(TAG, "Unable to allocate freeze surface", e);
301         }
302 
303         if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
304                 "  FREEZE " + mSurfaceControl + ": CREATE");
305         setRotation(t, originalRotation);
306         t.apply();
307     }
308 
hasScreenshot()309     boolean hasScreenshot() {
310         return mSurfaceControl != null;
311     }
312 
setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha)313     private void setSnapshotTransform(SurfaceControl.Transaction t, Matrix matrix, float alpha) {
314         if (mSurfaceControl != null) {
315             matrix.getValues(mTmpFloats);
316             float x = mTmpFloats[Matrix.MTRANS_X];
317             float y = mTmpFloats[Matrix.MTRANS_Y];
318             if (mForceDefaultOrientation) {
319                 mDisplayContent.getBounds(mCurrentDisplayRect);
320                 x -= mCurrentDisplayRect.left;
321                 y -= mCurrentDisplayRect.top;
322             }
323             t.setPosition(mSurfaceControl, x, y);
324             t.setMatrix(mSurfaceControl,
325                     mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
326                     mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
327             t.setAlpha(mSurfaceControl, alpha);
328             if (DEBUG_TRANSFORMS) {
329                 float[] srcPnts = new float[] { 0, 0, mWidth, mHeight };
330                 float[] dstPnts = new float[4];
331                 matrix.mapPoints(dstPnts, srcPnts);
332                 Slog.i(TAG, "Original  : (" + srcPnts[0] + "," + srcPnts[1]
333                         + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")");
334                 Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1]
335                         + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")");
336             }
337         }
338     }
339 
createRotationMatrix(int rotation, int width, int height, Matrix outMatrix)340     public static void createRotationMatrix(int rotation, int width, int height,
341             Matrix outMatrix) {
342         switch (rotation) {
343             case Surface.ROTATION_0:
344                 outMatrix.reset();
345                 break;
346             case Surface.ROTATION_90:
347                 outMatrix.setRotate(90, 0, 0);
348                 outMatrix.postTranslate(height, 0);
349                 break;
350             case Surface.ROTATION_180:
351                 outMatrix.setRotate(180, 0, 0);
352                 outMatrix.postTranslate(width, height);
353                 break;
354             case Surface.ROTATION_270:
355                 outMatrix.setRotate(270, 0, 0);
356                 outMatrix.postTranslate(0, width);
357                 break;
358         }
359     }
360 
setRotation(SurfaceControl.Transaction t, int rotation)361     private void setRotation(SurfaceControl.Transaction t, int rotation) {
362         mCurRotation = rotation;
363 
364         // Compute the transformation matrix that must be applied
365         // to the snapshot to make it stay in the same original position
366         // with the current screen rotation.
367         int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
368         createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
369 
370         if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
371         setSnapshotTransform(t, mSnapshotInitialMatrix, 1.0f);
372     }
373 
setRotation(SurfaceControl.Transaction t, int rotation, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight)374     public boolean setRotation(SurfaceControl.Transaction t, int rotation,
375             long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
376         setRotation(t, rotation);
377         if (TWO_PHASE_ANIMATION) {
378             return startAnimation(t, maxAnimationDuration, animationScale,
379                     finalWidth, finalHeight, false, 0, 0);
380         }
381 
382         // Don't start animation yet.
383         return false;
384     }
385 
386     /**
387      * Returns true if animating.
388      */
startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, boolean dismissing, int exitAnim, int enterAnim)389     private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
390             float animationScale, int finalWidth, int finalHeight, boolean dismissing,
391             int exitAnim, int enterAnim) {
392         if (mSurfaceControl == null) {
393             // Can't do animation.
394             return false;
395         }
396         if (mStarted) {
397             return true;
398         }
399 
400         mStarted = true;
401 
402         boolean firstStart = false;
403 
404         // Figure out how the screen has moved from the original rotation.
405         int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);
406 
407         if (TWO_PHASE_ANIMATION && mFinishExitAnimation == null
408                 && (!dismissing || delta != Surface.ROTATION_0)) {
409             if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
410             firstStart = true;
411             mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
412                     com.android.internal.R.anim.screen_rotate_start_exit);
413             mStartEnterAnimation = AnimationUtils.loadAnimation(mContext,
414                     com.android.internal.R.anim.screen_rotate_start_enter);
415             if (USE_CUSTOM_BLACK_FRAME) {
416                 mStartFrameAnimation = AnimationUtils.loadAnimation(mContext,
417                         com.android.internal.R.anim.screen_rotate_start_frame);
418             }
419             mFinishExitAnimation = AnimationUtils.loadAnimation(mContext,
420                     com.android.internal.R.anim.screen_rotate_finish_exit);
421             mFinishEnterAnimation = AnimationUtils.loadAnimation(mContext,
422                     com.android.internal.R.anim.screen_rotate_finish_enter);
423             if (USE_CUSTOM_BLACK_FRAME) {
424                 mFinishFrameAnimation = AnimationUtils.loadAnimation(mContext,
425                         com.android.internal.R.anim.screen_rotate_finish_frame);
426             }
427         }
428 
429         if (DEBUG_STATE) Slog.v(TAG, "Rotation delta: " + delta + " finalWidth="
430                 + finalWidth + " finalHeight=" + finalHeight
431                 + " origWidth=" + mOriginalWidth + " origHeight=" + mOriginalHeight);
432 
433         final boolean customAnim;
434         if (exitAnim != 0 && enterAnim != 0) {
435             customAnim = true;
436             mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
437             mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
438         } else {
439             customAnim = false;
440             switch (delta) {
441                 case Surface.ROTATION_0:
442                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
443                             com.android.internal.R.anim.screen_rotate_0_exit);
444                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
445                             com.android.internal.R.anim.screen_rotate_0_enter);
446                     if (USE_CUSTOM_BLACK_FRAME) {
447                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
448                                 com.android.internal.R.anim.screen_rotate_0_frame);
449                     }
450                     break;
451                 case Surface.ROTATION_90:
452                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
453                             com.android.internal.R.anim.screen_rotate_plus_90_exit);
454                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
455                             com.android.internal.R.anim.screen_rotate_plus_90_enter);
456                     if (USE_CUSTOM_BLACK_FRAME) {
457                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
458                                 com.android.internal.R.anim.screen_rotate_plus_90_frame);
459                     }
460                     break;
461                 case Surface.ROTATION_180:
462                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
463                             com.android.internal.R.anim.screen_rotate_180_exit);
464                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
465                             com.android.internal.R.anim.screen_rotate_180_enter);
466                     if (USE_CUSTOM_BLACK_FRAME) {
467                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
468                                 com.android.internal.R.anim.screen_rotate_180_frame);
469                     }
470                     break;
471                 case Surface.ROTATION_270:
472                     mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
473                             com.android.internal.R.anim.screen_rotate_minus_90_exit);
474                     mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
475                             com.android.internal.R.anim.screen_rotate_minus_90_enter);
476                     if (USE_CUSTOM_BLACK_FRAME) {
477                         mRotateFrameAnimation = AnimationUtils.loadAnimation(mContext,
478                                 com.android.internal.R.anim.screen_rotate_minus_90_frame);
479                     }
480                     break;
481             }
482         }
483 
484         // Initialize the animations.  This is a hack, redefining what "parent"
485         // means to allow supplying the last and next size.  In this definition
486         // "%p" is the original (let's call it "previous") size, and "%" is the
487         // screen's current/new size.
488         if (TWO_PHASE_ANIMATION && firstStart) {
489             // Compute partial steps between original and final sizes.  These
490             // are used for the dimensions of the exiting and entering elements,
491             // so they are never stretched too significantly.
492             final int halfWidth = (finalWidth + mOriginalWidth) / 2;
493             final int halfHeight = (finalHeight + mOriginalHeight) / 2;
494 
495             if (DEBUG_STATE) Slog.v(TAG, "Initializing start and finish animations");
496             mStartEnterAnimation.initialize(finalWidth, finalHeight,
497                     halfWidth, halfHeight);
498             mStartExitAnimation.initialize(halfWidth, halfHeight,
499                     mOriginalWidth, mOriginalHeight);
500             mFinishEnterAnimation.initialize(finalWidth, finalHeight,
501                     halfWidth, halfHeight);
502             mFinishExitAnimation.initialize(halfWidth, halfHeight,
503                     mOriginalWidth, mOriginalHeight);
504             if (USE_CUSTOM_BLACK_FRAME) {
505                 mStartFrameAnimation.initialize(finalWidth, finalHeight,
506                         mOriginalWidth, mOriginalHeight);
507                 mFinishFrameAnimation.initialize(finalWidth, finalHeight,
508                         mOriginalWidth, mOriginalHeight);
509             }
510         }
511         mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
512         mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
513         if (USE_CUSTOM_BLACK_FRAME) {
514             mRotateFrameAnimation.initialize(finalWidth, finalHeight, mOriginalWidth,
515                     mOriginalHeight);
516         }
517         mAnimRunning = false;
518         mFinishAnimReady = false;
519         mFinishAnimStartTime = -1;
520 
521         if (TWO_PHASE_ANIMATION && firstStart) {
522             mStartExitAnimation.restrictDuration(maxAnimationDuration);
523             mStartExitAnimation.scaleCurrentDuration(animationScale);
524             mStartEnterAnimation.restrictDuration(maxAnimationDuration);
525             mStartEnterAnimation.scaleCurrentDuration(animationScale);
526             mFinishExitAnimation.restrictDuration(maxAnimationDuration);
527             mFinishExitAnimation.scaleCurrentDuration(animationScale);
528             mFinishEnterAnimation.restrictDuration(maxAnimationDuration);
529             mFinishEnterAnimation.scaleCurrentDuration(animationScale);
530             if (USE_CUSTOM_BLACK_FRAME) {
531                 mStartFrameAnimation.restrictDuration(maxAnimationDuration);
532                 mStartFrameAnimation.scaleCurrentDuration(animationScale);
533                 mFinishFrameAnimation.restrictDuration(maxAnimationDuration);
534                 mFinishFrameAnimation.scaleCurrentDuration(animationScale);
535             }
536         }
537         mRotateExitAnimation.restrictDuration(maxAnimationDuration);
538         mRotateExitAnimation.scaleCurrentDuration(animationScale);
539         mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
540         mRotateEnterAnimation.scaleCurrentDuration(animationScale);
541         if (USE_CUSTOM_BLACK_FRAME) {
542             mRotateFrameAnimation.restrictDuration(maxAnimationDuration);
543             mRotateFrameAnimation.scaleCurrentDuration(animationScale);
544         }
545 
546         final int layerStack = mDisplayContent.getDisplay().getLayerStack();
547         if (USE_CUSTOM_BLACK_FRAME && mCustomBlackFrame == null) {
548             // Compute the transformation matrix that must be applied
549             // the the black frame to make it stay in the initial position
550             // before the new screen rotation.  This is different than the
551             // snapshot transformation because the snapshot is always based
552             // of the native orientation of the screen, not the orientation
553             // we were last in.
554             createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
555 
556             try {
557                 Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
558                         mOriginalWidth*2, mOriginalHeight*2);
559                 Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
560                 mCustomBlackFrame = new BlackFrame(t, outer, inner,
561                         SCREEN_FREEZE_LAYER_CUSTOM, mDisplayContent, false);
562                 mCustomBlackFrame.setMatrix(t, mFrameInitialMatrix);
563             } catch (OutOfResourcesException e) {
564                 Slog.w(TAG, "Unable to allocate black surface", e);
565             }
566         }
567 
568         if (!customAnim && mExitingBlackFrame == null) {
569             try {
570                 // Compute the transformation matrix that must be applied
571                 // the the black frame to make it stay in the initial position
572                 // before the new screen rotation.  This is different than the
573                 // snapshot transformation because the snapshot is always based
574                 // of the native orientation of the screen, not the orientation
575                 // we were last in.
576                 createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
577 
578                 final Rect outer;
579                 final Rect inner;
580                 if (mForceDefaultOrientation) {
581                     // Going from a smaller Display to a larger Display, add curtains to sides
582                     // or top and bottom. Going from a larger to smaller display will result in
583                     // no BlackSurfaces being constructed.
584                     outer = mCurrentDisplayRect;
585                     inner = mOriginalDisplayRect;
586                 } else {
587                     outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
588                             mOriginalWidth*2, mOriginalHeight*2);
589                     inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
590                 }
591                 mExitingBlackFrame = new BlackFrame(t, outer, inner,
592                         SCREEN_FREEZE_LAYER_EXIT, mDisplayContent, mForceDefaultOrientation);
593                 mExitingBlackFrame.setMatrix(t, mFrameInitialMatrix);
594             } catch (OutOfResourcesException e) {
595                 Slog.w(TAG, "Unable to allocate black surface", e);
596             }
597         }
598 
599         if (customAnim && mEnteringBlackFrame == null) {
600             try {
601                 Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
602                         finalWidth*2, finalHeight*2);
603                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
604                 mEnteringBlackFrame = new BlackFrame(t, outer, inner,
605                         SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false);
606             } catch (OutOfResourcesException e) {
607                 Slog.w(TAG, "Unable to allocate black surface", e);
608             }
609         }
610 
611         return true;
612     }
613 
614     /**
615      * Returns true if animating.
616      */
dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim)617     public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
618             float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
619         if (DEBUG_STATE) Slog.v(TAG, "Dismiss!");
620         if (mSurfaceControl == null) {
621             // Can't do animation.
622             return false;
623         }
624         if (!mStarted) {
625             startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
626                     true, exitAnim, enterAnim);
627         }
628         if (!mStarted) {
629             return false;
630         }
631         if (DEBUG_STATE) Slog.v(TAG, "Setting mFinishAnimReady = true");
632         mFinishAnimReady = true;
633         return true;
634     }
635 
kill()636     public void kill() {
637         if (DEBUG_STATE) Slog.v(TAG, "Kill!");
638         if (mSurfaceControl != null) {
639             if (SHOW_TRANSACTIONS ||
640                     SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
641                             "  FREEZE " + mSurfaceControl + ": DESTROY");
642             mSurfaceControl.remove();
643             mSurfaceControl = null;
644         }
645         if (mCustomBlackFrame != null) {
646             mCustomBlackFrame.kill();
647             mCustomBlackFrame = null;
648         }
649         if (mExitingBlackFrame != null) {
650             mExitingBlackFrame.kill();
651             mExitingBlackFrame = null;
652         }
653         if (mEnteringBlackFrame != null) {
654             mEnteringBlackFrame.kill();
655             mEnteringBlackFrame = null;
656         }
657         if (TWO_PHASE_ANIMATION) {
658             if (mStartExitAnimation != null) {
659                 mStartExitAnimation.cancel();
660                 mStartExitAnimation = null;
661             }
662             if (mStartEnterAnimation != null) {
663                 mStartEnterAnimation.cancel();
664                 mStartEnterAnimation = null;
665             }
666             if (mFinishExitAnimation != null) {
667                 mFinishExitAnimation.cancel();
668                 mFinishExitAnimation = null;
669             }
670             if (mFinishEnterAnimation != null) {
671                 mFinishEnterAnimation.cancel();
672                 mFinishEnterAnimation = null;
673             }
674         }
675         if (USE_CUSTOM_BLACK_FRAME) {
676             if (mStartFrameAnimation != null) {
677                 mStartFrameAnimation.cancel();
678                 mStartFrameAnimation = null;
679             }
680             if (mRotateFrameAnimation != null) {
681                 mRotateFrameAnimation.cancel();
682                 mRotateFrameAnimation = null;
683             }
684             if (mFinishFrameAnimation != null) {
685                 mFinishFrameAnimation.cancel();
686                 mFinishFrameAnimation = null;
687             }
688         }
689         if (mRotateExitAnimation != null) {
690             mRotateExitAnimation.cancel();
691             mRotateExitAnimation = null;
692         }
693         if (mRotateEnterAnimation != null) {
694             mRotateEnterAnimation.cancel();
695             mRotateEnterAnimation = null;
696         }
697     }
698 
isAnimating()699     public boolean isAnimating() {
700         return hasAnimations() || (TWO_PHASE_ANIMATION && mFinishAnimReady);
701     }
702 
isRotating()703     public boolean isRotating() {
704         return mCurRotation != mOriginalRotation;
705     }
706 
hasAnimations()707     private boolean hasAnimations() {
708         return (TWO_PHASE_ANIMATION &&
709                     (mStartEnterAnimation != null || mStartExitAnimation != null
710                     || mFinishEnterAnimation != null || mFinishExitAnimation != null))
711                 || (USE_CUSTOM_BLACK_FRAME &&
712                         (mStartFrameAnimation != null || mRotateFrameAnimation != null
713                         || mFinishFrameAnimation != null))
714                 || mRotateEnterAnimation != null || mRotateExitAnimation != null;
715     }
716 
stepAnimation(long now)717     private boolean stepAnimation(long now) {
718         if (now > mHalfwayPoint) {
719             mHalfwayPoint = Long.MAX_VALUE;
720         }
721         if (mFinishAnimReady && mFinishAnimStartTime < 0) {
722             if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
723             mFinishAnimStartTime = now;
724         }
725 
726         if (TWO_PHASE_ANIMATION) {
727             mMoreStartExit = false;
728             if (mStartExitAnimation != null) {
729                 mMoreStartExit = mStartExitAnimation.getTransformation(now, mStartExitTransformation);
730                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start exit: " + mStartExitTransformation);
731             }
732 
733             mMoreStartEnter = false;
734             if (mStartEnterAnimation != null) {
735                 mMoreStartEnter = mStartEnterAnimation.getTransformation(now, mStartEnterTransformation);
736                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start enter: " + mStartEnterTransformation);
737             }
738         }
739         if (USE_CUSTOM_BLACK_FRAME) {
740             mMoreStartFrame = false;
741             if (mStartFrameAnimation != null) {
742                 mMoreStartFrame = mStartFrameAnimation.getTransformation(now, mStartFrameTransformation);
743                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped start frame: " + mStartFrameTransformation);
744             }
745         }
746 
747         long finishNow = mFinishAnimReady ? (now - mFinishAnimStartTime) : 0;
748         if (DEBUG_STATE) Slog.v(TAG, "Step: finishNow=" + finishNow);
749 
750         if (TWO_PHASE_ANIMATION) {
751             mMoreFinishExit = false;
752             if (mFinishExitAnimation != null) {
753                 mMoreFinishExit = mFinishExitAnimation.getTransformation(finishNow, mFinishExitTransformation);
754                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish exit: " + mFinishExitTransformation);
755             }
756 
757             mMoreFinishEnter = false;
758             if (mFinishEnterAnimation != null) {
759                 mMoreFinishEnter = mFinishEnterAnimation.getTransformation(finishNow, mFinishEnterTransformation);
760                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish enter: " + mFinishEnterTransformation);
761             }
762         }
763         if (USE_CUSTOM_BLACK_FRAME) {
764             mMoreFinishFrame = false;
765             if (mFinishFrameAnimation != null) {
766                 mMoreFinishFrame = mFinishFrameAnimation.getTransformation(finishNow, mFinishFrameTransformation);
767                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped finish frame: " + mFinishFrameTransformation);
768             }
769         }
770 
771         mMoreRotateExit = false;
772         if (mRotateExitAnimation != null) {
773             mMoreRotateExit = mRotateExitAnimation.getTransformation(now, mRotateExitTransformation);
774             if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate exit: " + mRotateExitTransformation);
775         }
776 
777         mMoreRotateEnter = false;
778         if (mRotateEnterAnimation != null) {
779             mMoreRotateEnter = mRotateEnterAnimation.getTransformation(now, mRotateEnterTransformation);
780             if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate enter: " + mRotateEnterTransformation);
781         }
782 
783         if (USE_CUSTOM_BLACK_FRAME) {
784             mMoreRotateFrame = false;
785             if (mRotateFrameAnimation != null) {
786                 mMoreRotateFrame = mRotateFrameAnimation.getTransformation(now, mRotateFrameTransformation);
787                 if (DEBUG_TRANSFORMS) Slog.v(TAG, "Stepped rotate frame: " + mRotateFrameTransformation);
788             }
789         }
790 
791         if (!mMoreRotateExit && (!TWO_PHASE_ANIMATION || (!mMoreStartExit && !mMoreFinishExit))) {
792             if (TWO_PHASE_ANIMATION) {
793                 if (mStartExitAnimation != null) {
794                     if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing start exit anim!");
795                     mStartExitAnimation.cancel();
796                     mStartExitAnimation = null;
797                     mStartExitTransformation.clear();
798                 }
799                 if (mFinishExitAnimation != null) {
800                     if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing finish exit anim!");
801                     mFinishExitAnimation.cancel();
802                     mFinishExitAnimation = null;
803                     mFinishExitTransformation.clear();
804                 }
805             }
806             if (mRotateExitAnimation != null) {
807                 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, clearing rotate exit anim!");
808                 mRotateExitAnimation.cancel();
809                 mRotateExitAnimation = null;
810                 mRotateExitTransformation.clear();
811             }
812         }
813 
814         if (!mMoreRotateEnter && (!TWO_PHASE_ANIMATION || (!mMoreStartEnter && !mMoreFinishEnter))) {
815             if (TWO_PHASE_ANIMATION) {
816                 if (mStartEnterAnimation != null) {
817                     if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing start enter anim!");
818                     mStartEnterAnimation.cancel();
819                     mStartEnterAnimation = null;
820                     mStartEnterTransformation.clear();
821                 }
822                 if (mFinishEnterAnimation != null) {
823                     if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing finish enter anim!");
824                     mFinishEnterAnimation.cancel();
825                     mFinishEnterAnimation = null;
826                     mFinishEnterTransformation.clear();
827                 }
828             }
829             if (mRotateEnterAnimation != null) {
830                 if (DEBUG_STATE) Slog.v(TAG, "Enter animations done, clearing rotate enter anim!");
831                 mRotateEnterAnimation.cancel();
832                 mRotateEnterAnimation = null;
833                 mRotateEnterTransformation.clear();
834             }
835         }
836 
837         if (USE_CUSTOM_BLACK_FRAME && !mMoreStartFrame && !mMoreRotateFrame && !mMoreFinishFrame) {
838             if (mStartFrameAnimation != null) {
839                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing start frame anim!");
840                 mStartFrameAnimation.cancel();
841                 mStartFrameAnimation = null;
842                 mStartFrameTransformation.clear();
843             }
844             if (mFinishFrameAnimation != null) {
845                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing finish frame anim!");
846                 mFinishFrameAnimation.cancel();
847                 mFinishFrameAnimation = null;
848                 mFinishFrameTransformation.clear();
849             }
850             if (mRotateFrameAnimation != null) {
851                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, clearing rotate frame anim!");
852                 mRotateFrameAnimation.cancel();
853                 mRotateFrameAnimation = null;
854                 mRotateFrameTransformation.clear();
855             }
856         }
857 
858         mExitTransformation.set(mRotateExitTransformation);
859         mEnterTransformation.set(mRotateEnterTransformation);
860         if (TWO_PHASE_ANIMATION) {
861             mExitTransformation.compose(mStartExitTransformation);
862             mExitTransformation.compose(mFinishExitTransformation);
863 
864             mEnterTransformation.compose(mStartEnterTransformation);
865             mEnterTransformation.compose(mFinishEnterTransformation);
866         }
867 
868         if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final exit: " + mExitTransformation);
869         if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final enter: " + mEnterTransformation);
870 
871         if (USE_CUSTOM_BLACK_FRAME) {
872             //mFrameTransformation.set(mRotateExitTransformation);
873             //mFrameTransformation.compose(mStartExitTransformation);
874             //mFrameTransformation.compose(mFinishExitTransformation);
875             mFrameTransformation.set(mRotateFrameTransformation);
876             mFrameTransformation.compose(mStartFrameTransformation);
877             mFrameTransformation.compose(mFinishFrameTransformation);
878             mFrameTransformation.getMatrix().preConcat(mFrameInitialMatrix);
879             if (DEBUG_TRANSFORMS) Slog.v(TAG, "Final frame: " + mFrameTransformation);
880         }
881 
882         final boolean more = (TWO_PHASE_ANIMATION
883                     && (mMoreStartEnter || mMoreStartExit || mMoreFinishEnter || mMoreFinishExit))
884                 || (USE_CUSTOM_BLACK_FRAME
885                         && (mMoreStartFrame || mMoreRotateFrame || mMoreFinishFrame))
886                 || mMoreRotateEnter || mMoreRotateExit
887                 || !mFinishAnimReady;
888 
889         mSnapshotFinalMatrix.setConcat(mExitTransformation.getMatrix(), mSnapshotInitialMatrix);
890 
891         if (DEBUG_STATE) Slog.v(TAG, "Step: more=" + more);
892 
893         return more;
894     }
895 
updateSurfaces(SurfaceControl.Transaction t)896     void updateSurfaces(SurfaceControl.Transaction t) {
897         if (!mStarted) {
898             return;
899         }
900 
901         if (mSurfaceControl != null) {
902             if (!mMoreStartExit && !mMoreFinishExit && !mMoreRotateExit) {
903                 if (DEBUG_STATE) Slog.v(TAG, "Exit animations done, hiding screenshot surface");
904                 t.hide(mSurfaceControl);
905             }
906         }
907 
908         if (mCustomBlackFrame != null) {
909             if (!mMoreStartFrame && !mMoreFinishFrame && !mMoreRotateFrame) {
910                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding black frame");
911                 mCustomBlackFrame.hide(t);
912             } else {
913                 mCustomBlackFrame.setMatrix(t, mFrameTransformation.getMatrix());
914             }
915         }
916 
917         if (mExitingBlackFrame != null) {
918             if (!mMoreStartExit && !mMoreFinishExit && !mMoreRotateExit) {
919                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding exiting frame");
920                 mExitingBlackFrame.hide(t);
921             } else {
922                 mExitFrameFinalMatrix.setConcat(mExitTransformation.getMatrix(), mFrameInitialMatrix);
923                 mExitingBlackFrame.setMatrix(t, mExitFrameFinalMatrix);
924                 if (mForceDefaultOrientation) {
925                     mExitingBlackFrame.setAlpha(t, mExitTransformation.getAlpha());
926                 }
927             }
928         }
929 
930         if (mEnteringBlackFrame != null) {
931             if (!mMoreStartEnter && !mMoreFinishEnter && !mMoreRotateEnter) {
932                 if (DEBUG_STATE) Slog.v(TAG, "Frame animations done, hiding entering frame");
933                 mEnteringBlackFrame.hide(t);
934             } else {
935                 mEnteringBlackFrame.setMatrix(t, mEnterTransformation.getMatrix());
936             }
937         }
938 
939         t.setEarlyWakeup();
940         setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha());
941     }
942 
stepAnimationLocked(long now)943     public boolean stepAnimationLocked(long now) {
944         if (!hasAnimations()) {
945             if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
946             mFinishAnimReady = false;
947             return false;
948         }
949 
950         if (!mAnimRunning) {
951             if (DEBUG_STATE) Slog.v(TAG, "Step: starting start, finish, rotate");
952             if (TWO_PHASE_ANIMATION) {
953                 if (mStartEnterAnimation != null) {
954                     mStartEnterAnimation.setStartTime(now);
955                 }
956                 if (mStartExitAnimation != null) {
957                     mStartExitAnimation.setStartTime(now);
958                 }
959                 if (mFinishEnterAnimation != null) {
960                     mFinishEnterAnimation.setStartTime(0);
961                 }
962                 if (mFinishExitAnimation != null) {
963                     mFinishExitAnimation.setStartTime(0);
964                 }
965             }
966             if (USE_CUSTOM_BLACK_FRAME) {
967                 if (mStartFrameAnimation != null) {
968                     mStartFrameAnimation.setStartTime(now);
969                 }
970                 if (mFinishFrameAnimation != null) {
971                     mFinishFrameAnimation.setStartTime(0);
972                 }
973                 if (mRotateFrameAnimation != null) {
974                     mRotateFrameAnimation.setStartTime(now);
975                 }
976             }
977             if (mRotateEnterAnimation != null) {
978                 mRotateEnterAnimation.setStartTime(now);
979             }
980             if (mRotateExitAnimation != null) {
981                 mRotateExitAnimation.setStartTime(now);
982             }
983             mAnimRunning = true;
984             mHalfwayPoint = now + mRotateEnterAnimation.getDuration() / 2;
985         }
986 
987         return stepAnimation(now);
988     }
989 
getEnterTransformation()990     public Transformation getEnterTransformation() {
991         return mEnterTransformation;
992     }
993 }
994