RemoteAnimationController.java revision d3d139ab1eb841b1713016598fdca7a31b302ed8
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 sendRunningRemoteAnimation(true); 107 } 108 109 private RemoteAnimationTarget[] createAnimations() { 110 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); 111 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 112 final RemoteAnimationTarget target = 113 mPendingAnimations.get(i).createRemoteAppAnimation(); 114 if (target != null) { 115 targets.add(target); 116 } 117 } 118 return targets.toArray(new RemoteAnimationTarget[targets.size()]); 119 } 120 121 private void onAnimationFinished() { 122 mHandler.removeCallbacks(mTimeoutRunnable); 123 synchronized (mService.mWindowMap) { 124 releaseFinishedCallback(); 125 mService.openSurfaceTransaction(); 126 try { 127 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 128 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i); 129 adapter.mCapturedFinishCallback.onAnimationFinished(adapter); 130 } 131 } finally { 132 mService.closeSurfaceTransaction("RemoteAnimationController#finished"); 133 } 134 } 135 sendRunningRemoteAnimation(false); 136 } 137 138 private void invokeAnimationCancelled() { 139 try { 140 mRemoteAnimationAdapter.getRunner().onAnimationCancelled(); 141 } catch (RemoteException e) { 142 Slog.e(TAG, "Failed to notify cancel", e); 143 } 144 } 145 146 private void releaseFinishedCallback() { 147 if (mFinishedCallback != null) { 148 mFinishedCallback.release(); 149 mFinishedCallback = null; 150 } 151 } 152 153 private void sendRunningRemoteAnimation(boolean running) { 154 final int pid = mRemoteAnimationAdapter.getCallingPid(); 155 if (pid == 0) { 156 throw new RuntimeException("Calling pid of remote animation was null"); 157 } 158 mService.sendSetRunningRemoteAnimation(pid, running); 159 } 160 161 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub { 162 163 RemoteAnimationController mOuter; 164 165 FinishedCallback(RemoteAnimationController outer) { 166 mOuter = outer; 167 } 168 169 @Override 170 public void onAnimationFinished() throws RemoteException { 171 if (mOuter != null) { 172 mOuter.onAnimationFinished(); 173 174 // In case the client holds on to the finish callback, make sure we don't leak 175 // RemoteAnimationController which in turn would leak the runner on the client. 176 mOuter = null; 177 } 178 } 179 180 /** 181 * Marks this callback as not be used anymore by releasing the reference to the outer class 182 * to prevent memory leak. 183 */ 184 void release() { 185 mOuter = null; 186 } 187 }; 188 189 private class RemoteAnimationAdapterWrapper implements AnimationAdapter { 190 191 private final AppWindowToken mAppWindowToken; 192 private SurfaceControl mCapturedLeash; 193 private OnAnimationFinishedCallback mCapturedFinishCallback; 194 private final Point mPosition = new Point(); 195 private final Rect mStackBounds = new Rect(); 196 197 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position, 198 Rect stackBounds) { 199 mAppWindowToken = appWindowToken; 200 mPosition.set(position.x, position.y); 201 mStackBounds.set(stackBounds); 202 } 203 204 RemoteAnimationTarget createRemoteAppAnimation() { 205 final Task task = mAppWindowToken.getTask(); 206 final WindowState mainWindow = mAppWindowToken.findMainWindow(); 207 if (task == null) { 208 return null; 209 } 210 if (mainWindow == null) { 211 return null; 212 } 213 return new RemoteAnimationTarget(task.mTaskId, getMode(), 214 mCapturedLeash, !mAppWindowToken.fillsParent(), 215 mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets, 216 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds, 217 task.getWindowConfiguration()); 218 } 219 220 private int getMode() { 221 if (mService.mOpeningApps.contains(mAppWindowToken)) { 222 return RemoteAnimationTarget.MODE_OPENING; 223 } else { 224 return RemoteAnimationTarget.MODE_CLOSING; 225 } 226 } 227 228 @Override 229 public boolean getDetachWallpaper() { 230 return false; 231 } 232 233 @Override 234 public boolean getShowWallpaper() { 235 return false; 236 } 237 238 @Override 239 public int getBackgroundColor() { 240 return 0; 241 } 242 243 @Override 244 public void startAnimation(SurfaceControl animationLeash, Transaction t, 245 OnAnimationFinishedCallback finishCallback) { 246 247 // Restore z-layering, position and stack crop until client has a chance to modify it. 248 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex()); 249 t.setPosition(animationLeash, mPosition.x, mPosition.y); 250 mTmpRect.set(mStackBounds); 251 mTmpRect.offsetTo(0, 0); 252 t.setWindowCrop(animationLeash, mTmpRect); 253 mCapturedLeash = animationLeash; 254 mCapturedFinishCallback = finishCallback; 255 } 256 257 @Override 258 public void onAnimationCancelled(SurfaceControl animationLeash) { 259 mPendingAnimations.remove(this); 260 if (mPendingAnimations.isEmpty()) { 261 mHandler.removeCallbacks(mTimeoutRunnable); 262 releaseFinishedCallback(); 263 invokeAnimationCancelled(); 264 sendRunningRemoteAnimation(false); 265 } 266 } 267 268 @Override 269 public long getDurationHint() { 270 return mRemoteAnimationAdapter.getDuration(); 271 } 272 273 @Override 274 public long getStatusBarTransitionsStartTime() { 275 return SystemClock.uptimeMillis() 276 + mRemoteAnimationAdapter.getStatusBarTransitionDelay(); 277 } 278 } 279} 280