RemoteAnimationController.java revision 91f9f348dc6a8e062e2f2dee9e687ae9ca558ac9
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.lang.ref.WeakReference; 38import java.util.ArrayList; 39 40/** 41 * Helper class to run app animations in a remote process. 42 */ 43class RemoteAnimationController { 44 private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM; 45 private static final long TIMEOUT_MS = 2000; 46 47 private final WindowManagerService mService; 48 private final RemoteAnimationAdapter mRemoteAnimationAdapter; 49 private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>(); 50 private final Rect mTmpRect = new Rect(); 51 private final Handler mHandler; 52 private FinishedCallback mFinishedCallback; 53 54 private final Runnable mTimeoutRunnable = () -> { 55 onAnimationFinished(); 56 invokeAnimationCancelled(); 57 }; 58 59 RemoteAnimationController(WindowManagerService service, 60 RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) { 61 mService = service; 62 mRemoteAnimationAdapter = remoteAnimationAdapter; 63 mHandler = handler; 64 } 65 66 /** 67 * Creates an animation for each individual {@link AppWindowToken}. 68 * 69 * @param appWindowToken The app to animate. 70 * @param position The position app bounds, in screen coordinates. 71 * @param stackBounds The stack bounds of the app. 72 * @return The adapter to be run on the app. 73 */ 74 AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position, 75 Rect stackBounds) { 76 final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper( 77 appWindowToken, position, stackBounds); 78 mPendingAnimations.add(adapter); 79 return adapter; 80 } 81 82 /** 83 * Called when the transition is ready to be started, and all leashes have been set up. 84 */ 85 void goodToGo() { 86 if (mPendingAnimations.isEmpty()) { 87 onAnimationFinished(); 88 return; 89 } 90 91 // Scale the timeout with the animator scale the controlling app is using. 92 mHandler.postDelayed(mTimeoutRunnable, 93 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale())); 94 mFinishedCallback = new FinishedCallback(this); 95 96 final RemoteAnimationTarget[] animations = createAnimations(); 97 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 98 try { 99 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, 100 mFinishedCallback); 101 } catch (RemoteException e) { 102 Slog.e(TAG, "Failed to start remote animation", e); 103 onAnimationFinished(); 104 } 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 releaseFinishedCallback(); 124 mService.openSurfaceTransaction(); 125 try { 126 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 127 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i); 128 adapter.mCapturedFinishCallback.onAnimationFinished(adapter); 129 } 130 } finally { 131 mService.closeSurfaceTransaction("RemoteAnimationController#finished"); 132 } 133 } 134 } 135 136 private void invokeAnimationCancelled() { 137 try { 138 mRemoteAnimationAdapter.getRunner().onAnimationCancelled(); 139 } catch (RemoteException e) { 140 Slog.e(TAG, "Failed to notify cancel", e); 141 } 142 } 143 144 private void releaseFinishedCallback() { 145 if (mFinishedCallback != null) { 146 mFinishedCallback.release(); 147 mFinishedCallback = null; 148 } 149 } 150 151 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub { 152 153 RemoteAnimationController mOuter; 154 155 FinishedCallback(RemoteAnimationController outer) { 156 mOuter = outer; 157 } 158 159 @Override 160 public void onAnimationFinished() throws RemoteException { 161 if (mOuter != null) { 162 mOuter.onAnimationFinished(); 163 164 // In case the client holds on to the finish callback, make sure we don't leak 165 // RemoteAnimationController which in turn would leak the runner on the client. 166 mOuter = null; 167 } 168 } 169 170 /** 171 * Marks this callback as not be used anymore by releasing the reference to the outer class 172 * to prevent memory leak. 173 */ 174 void release() { 175 mOuter = null; 176 } 177 }; 178 179 private class RemoteAnimationAdapterWrapper implements AnimationAdapter { 180 181 private final AppWindowToken mAppWindowToken; 182 private SurfaceControl mCapturedLeash; 183 private OnAnimationFinishedCallback mCapturedFinishCallback; 184 private final Point mPosition = new Point(); 185 private final Rect mStackBounds = new Rect(); 186 187 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position, 188 Rect stackBounds) { 189 mAppWindowToken = appWindowToken; 190 mPosition.set(position.x, position.y); 191 mStackBounds.set(stackBounds); 192 } 193 194 RemoteAnimationTarget createRemoteAppAnimation() { 195 final Task task = mAppWindowToken.getTask(); 196 final WindowState mainWindow = mAppWindowToken.findMainWindow(); 197 if (task == null) { 198 return null; 199 } 200 if (mainWindow == null) { 201 return null; 202 } 203 return new RemoteAnimationTarget(task.mTaskId, getMode(), 204 mCapturedLeash, !mAppWindowToken.fillsParent(), 205 mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets, 206 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds, 207 task.getWindowConfiguration()); 208 } 209 210 private int getMode() { 211 if (mService.mOpeningApps.contains(mAppWindowToken)) { 212 return RemoteAnimationTarget.MODE_OPENING; 213 } else { 214 return RemoteAnimationTarget.MODE_CLOSING; 215 } 216 } 217 218 @Override 219 public boolean getDetachWallpaper() { 220 return false; 221 } 222 223 @Override 224 public int getBackgroundColor() { 225 return 0; 226 } 227 228 @Override 229 public void startAnimation(SurfaceControl animationLeash, Transaction t, 230 OnAnimationFinishedCallback finishCallback) { 231 232 // Restore z-layering, position and stack crop until client has a chance to modify it. 233 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex()); 234 t.setPosition(animationLeash, mPosition.x, mPosition.y); 235 mTmpRect.set(mStackBounds); 236 mTmpRect.offsetTo(0, 0); 237 t.setWindowCrop(animationLeash, mTmpRect); 238 mCapturedLeash = animationLeash; 239 mCapturedFinishCallback = finishCallback; 240 } 241 242 @Override 243 public void onAnimationCancelled(SurfaceControl animationLeash) { 244 mPendingAnimations.remove(this); 245 if (mPendingAnimations.isEmpty()) { 246 mHandler.removeCallbacks(mTimeoutRunnable); 247 releaseFinishedCallback(); 248 invokeAnimationCancelled(); 249 } 250 } 251 252 @Override 253 public long getDurationHint() { 254 return mRemoteAnimationAdapter.getDuration(); 255 } 256 257 @Override 258 public long getStatusBarTransitionsStartTime() { 259 return SystemClock.uptimeMillis() 260 + mRemoteAnimationAdapter.getStatusBarTransitionDelay(); 261 } 262 } 263} 264