RemoteAnimationController.java revision da10c37b6d8497338dcab82550ee37b374c3c9e2
1/* 2 * Copyright (C) 2018 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 com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 21 22import android.graphics.Point; 23import android.graphics.Rect; 24import android.os.Handler; 25import android.os.RemoteException; 26import android.os.SystemClock; 27import android.util.Slog; 28import android.view.IRemoteAnimationFinishedCallback; 29import android.view.IRemoteAnimationFinishedCallback.Stub; 30import android.view.RemoteAnimationAdapter; 31import android.view.RemoteAnimationTarget; 32import android.view.SurfaceControl; 33import android.view.SurfaceControl.Transaction; 34 35import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 36 37import java.util.ArrayList; 38 39/** 40 * Helper class to run app animations in a remote process. 41 */ 42class RemoteAnimationController { 43 private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM; 44 private static final long TIMEOUT_MS = 2000; 45 46 private final WindowManagerService mService; 47 private final RemoteAnimationAdapter mRemoteAnimationAdapter; 48 private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>(); 49 private final Rect mTmpRect = new Rect(); 50 private final Handler mHandler; 51 52 private final IRemoteAnimationFinishedCallback mFinishedCallback = new Stub() { 53 @Override 54 public void onAnimationFinished() throws RemoteException { 55 RemoteAnimationController.this.onAnimationFinished(); 56 } 57 }; 58 59 private final Runnable mTimeoutRunnable = () -> { 60 onAnimationFinished(); 61 invokeAnimationCancelled(); 62 }; 63 64 RemoteAnimationController(WindowManagerService service, 65 RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) { 66 mService = service; 67 mRemoteAnimationAdapter = remoteAnimationAdapter; 68 mHandler = handler; 69 } 70 71 /** 72 * Creates an animation for each individual {@link AppWindowToken}. 73 * 74 * @param appWindowToken The app to animate. 75 * @param position The position app bounds, in screen coordinates. 76 * @param stackBounds The stack bounds of the app. 77 * @return The adapter to be run on the app. 78 */ 79 AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position, 80 Rect stackBounds) { 81 final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper( 82 appWindowToken, position, stackBounds); 83 mPendingAnimations.add(adapter); 84 return adapter; 85 } 86 87 /** 88 * Called when the transition is ready to be started, and all leashes have been set up. 89 */ 90 void goodToGo() { 91 if (mPendingAnimations.isEmpty()) { 92 onAnimationFinished(); 93 return; 94 } 95 96 // Scale the timeout with the animator scale the controlling app is using. 97 mHandler.postDelayed(mTimeoutRunnable, 98 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale())); 99 try { 100 mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(), 101 mFinishedCallback); 102 } catch (RemoteException e) { 103 Slog.e(TAG, "Failed to start remote animation", e); 104 onAnimationFinished(); 105 } 106 } 107 108 private RemoteAnimationTarget[] createAnimations() { 109 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); 110 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 111 final RemoteAnimationTarget target = 112 mPendingAnimations.get(i).createRemoteAppAnimation(); 113 if (target != null) { 114 targets.add(target); 115 } 116 } 117 return targets.toArray(new RemoteAnimationTarget[targets.size()]); 118 } 119 120 private void onAnimationFinished() { 121 mHandler.removeCallbacks(mTimeoutRunnable); 122 synchronized (mService.mWindowMap) { 123 mService.openSurfaceTransaction(); 124 try { 125 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 126 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i); 127 adapter.mCapturedFinishCallback.onAnimationFinished(adapter); 128 } 129 } finally { 130 mService.closeSurfaceTransaction("RemoteAnimationController#finished"); 131 } 132 } 133 } 134 135 private void invokeAnimationCancelled() { 136 try { 137 mRemoteAnimationAdapter.getRunner().onAnimationCancelled(); 138 } catch (RemoteException e) { 139 Slog.e(TAG, "Failed to notify cancel", e); 140 } 141 } 142 143 private class RemoteAnimationAdapterWrapper implements AnimationAdapter { 144 145 private final AppWindowToken mAppWindowToken; 146 private SurfaceControl mCapturedLeash; 147 private OnAnimationFinishedCallback mCapturedFinishCallback; 148 private final Point mPosition = new Point(); 149 private final Rect mStackBounds = new Rect(); 150 151 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position, 152 Rect stackBounds) { 153 mAppWindowToken = appWindowToken; 154 mPosition.set(position.x, position.y); 155 mStackBounds.set(stackBounds); 156 } 157 158 RemoteAnimationTarget createRemoteAppAnimation() { 159 final Task task = mAppWindowToken.getTask(); 160 final WindowState mainWindow = mAppWindowToken.findMainWindow(); 161 if (task == null) { 162 return null; 163 } 164 if (mainWindow == null) { 165 return null; 166 } 167 return new RemoteAnimationTarget(task.mTaskId, getMode(), 168 mCapturedLeash, !mAppWindowToken.fillsParent(), 169 mainWindow.mWinAnimator.mLastClipRect, 170 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds, 171 task.getWindowConfiguration()); 172 } 173 174 private int getMode() { 175 if (mService.mOpeningApps.contains(mAppWindowToken)) { 176 return RemoteAnimationTarget.MODE_OPENING; 177 } else { 178 return RemoteAnimationTarget.MODE_CLOSING; 179 } 180 } 181 182 @Override 183 public boolean getDetachWallpaper() { 184 return false; 185 } 186 187 @Override 188 public int getBackgroundColor() { 189 return 0; 190 } 191 192 @Override 193 public void startAnimation(SurfaceControl animationLeash, Transaction t, 194 OnAnimationFinishedCallback finishCallback) { 195 196 // Restore z-layering, position and stack crop until client has a chance to modify it. 197 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex()); 198 t.setPosition(animationLeash, mPosition.x, mPosition.y); 199 mTmpRect.set(mStackBounds); 200 mTmpRect.offsetTo(0, 0); 201 t.setWindowCrop(animationLeash, mTmpRect); 202 mCapturedLeash = animationLeash; 203 mCapturedFinishCallback = finishCallback; 204 } 205 206 @Override 207 public void onAnimationCancelled(SurfaceControl animationLeash) { 208 mPendingAnimations.remove(this); 209 if (mPendingAnimations.isEmpty()) { 210 mHandler.removeCallbacks(mTimeoutRunnable); 211 invokeAnimationCancelled(); 212 } 213 } 214 215 @Override 216 public long getDurationHint() { 217 return mRemoteAnimationAdapter.getDuration(); 218 } 219 220 @Override 221 public long getStatusBarTransitionsStartTime() { 222 return SystemClock.uptimeMillis() 223 + mRemoteAnimationAdapter.getStatusBarTransitionDelay(); 224 } 225 } 226} 227