OSDN Git Service

Fix NPE when animation doesn't get started
authorJorim Jaggi <jjaggi@google.com>
Thu, 22 Mar 2018 15:30:56 +0000 (16:30 +0100)
committerJorim Jaggi <jjaggi@google.com>
Thu, 22 Mar 2018 15:46:56 +0000 (16:46 +0100)
If animation start is delayed, there is no guarantee that
startAnimation is called on the adapter. Thus, protect
RemoteAnimationController against this case, as leash and
finishedCallback would be null.

Furthermore don't even try to start remote animation that is
delayed as it would leave it hanging forever.

Test: SurfaceAnimatorTest/RemoteAnimationControllerTest
Change-Id: I72c492c4fb6ca8eeae481d2c281e9c1fee95f921
Fixes: 76096628

services/core/java/com/android/server/wm/AppWindowToken.java
services/core/java/com/android/server/wm/RemoteAnimationController.java
services/core/java/com/android/server/wm/SurfaceAnimator.java
services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java

index a76857e..85436da 100644 (file)
@@ -1698,7 +1698,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
                 stack.getBounds(mTmpRect);
                 mTmpRect.offsetTo(0, 0);
             }
-            if (mService.mAppTransition.getRemoteAnimationController() != null) {
+
+            // Delaying animation start isn't compatible with remote animations at all.
+            if (mService.mAppTransition.getRemoteAnimationController() != null
+                    && !mSurfaceAnimator.isAnimationStartDelayed()) {
                 adapter = mService.mAppTransition.getRemoteAnimationController()
                         .createAnimationAdapter(this, mTmpPoint, mTmpRect);
             } else {
index d7f480b..c590067 100644 (file)
@@ -99,6 +99,10 @@ class RemoteAnimationController {
         mFinishedCallback = new FinishedCallback(this);
 
         final RemoteAnimationTarget[] animations = createAnimations();
+        if (animations.length == 0) {
+            onAnimationFinished();
+            return;
+        }
         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             try {
                 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
@@ -132,6 +136,8 @@ class RemoteAnimationController {
                     mPendingAnimations.get(i).createRemoteAppAnimation();
             if (target != null) {
                 targets.add(target);
+            } else {
+                mPendingAnimations.remove(i);
             }
         }
         return targets.toArray(new RemoteAnimationTarget[targets.size()]);
@@ -225,10 +231,8 @@ class RemoteAnimationController {
         RemoteAnimationTarget createRemoteAppAnimation() {
             final Task task = mAppWindowToken.getTask();
             final WindowState mainWindow = mAppWindowToken.findMainWindow();
-            if (task == null) {
-                return null;
-            }
-            if (mainWindow == null) {
+            if (task == null || mainWindow == null || mCapturedFinishCallback == null
+                    || mCapturedLeash == null) {
                 return null;
             }
             mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
index e5928b1..ba3d091 100644 (file)
@@ -234,6 +234,10 @@ class SurfaceAnimator {
         mService.mAnimationTransferMap.put(mAnimation, this);
     }
 
+    boolean isAnimationStartDelayed() {
+        return mAnimationStartDelayed;
+    }
+
     /**
      * Cancels the animation, and resets the leash.
      *
index 26a7313..64501e4 100644 (file)
@@ -68,6 +68,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
         super.setUp();
         MockitoAnnotations.initMocks(this);
         mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
+        mAdapter.setCallingPid(123);
         sWm.mH.runWithScissors(() -> {
             mHandler = new TestHandler(null, mClock);
         }, 0);
@@ -83,7 +84,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
                     new Point(50, 100), new Rect(50, 100, 150, 150));
             adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
             mController.goodToGo();
-
+            sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
@@ -167,4 +168,33 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
         mController.goodToGo();
         verifyZeroInteractions(mMockRunner);
     }
+
+    @Test
+    public void testNotReallyStarted() throws Exception {
+        final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+        mController.createAnimationAdapter(win.mAppToken,
+                new Point(50, 100), new Rect(50, 100, 150, 150));
+        mController.goodToGo();
+        verifyZeroInteractions(mMockRunner);
+    }
+
+    @Test
+    public void testOneNotStarted() throws Exception {
+        final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
+        final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
+        mController.createAnimationAdapter(win1.mAppToken,
+                new Point(50, 100), new Rect(50, 100, 150, 150));
+        final AnimationAdapter adapter = mController.createAnimationAdapter(win2.mAppToken,
+                new Point(50, 100), new Rect(50, 100, 150, 150));
+        adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+        mController.goodToGo();
+        sWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+        final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+                ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+        verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+        assertEquals(1, appsCaptor.getValue().length);
+        assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
+    }
 }
index 6506872..16b8458 100644 (file)
@@ -134,6 +134,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
         verifyZeroInteractions(mSpec);
         assertAnimating(mAnimatable);
+        assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed());
         mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
         verify(mSpec).startAnimation(any(), any(), any());
     }