RemoteAnimationController.java revision c4d29f2a1c6e8e9c3cdb3fc2bf8a8151fb24716b
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.DEBUG_APP_TRANSITIONS; 20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 22import static com.android.server.wm.AnimationAdapterProto.REMOTE; 23import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; 24 25import android.graphics.Point; 26import android.graphics.Rect; 27import android.os.Handler; 28import android.os.RemoteException; 29import android.os.SystemClock; 30import android.util.Slog; 31import android.util.proto.ProtoOutputStream; 32import android.view.IRemoteAnimationFinishedCallback; 33import android.view.RemoteAnimationAdapter; 34import android.view.RemoteAnimationTarget; 35import android.view.SurfaceControl; 36import android.view.SurfaceControl.Transaction; 37 38import com.android.internal.util.FastPrintWriter; 39import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 40 41import java.io.PrintWriter; 42import java.io.StringWriter; 43import java.util.ArrayList; 44 45/** 46 * Helper class to run app animations in a remote process. 47 */ 48class RemoteAnimationController { 49 private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM; 50 private static final long TIMEOUT_MS = 2000; 51 52 private final WindowManagerService mService; 53 private final RemoteAnimationAdapter mRemoteAnimationAdapter; 54 private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>(); 55 private final Rect mTmpRect = new Rect(); 56 private final Handler mHandler; 57 private FinishedCallback mFinishedCallback; 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 mFinishedCallback = new FinishedCallback(this); 100 101 final RemoteAnimationTarget[] animations = createAnimations(); 102 if (animations.length == 0) { 103 onAnimationFinished(); 104 return; 105 } 106 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 107 try { 108 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, 109 mFinishedCallback); 110 } catch (RemoteException e) { 111 Slog.e(TAG, "Failed to start remote animation", e); 112 onAnimationFinished(); 113 } 114 }); 115 sendRunningRemoteAnimation(true); 116 if (DEBUG_APP_TRANSITIONS) { 117 writeStartDebugStatement(); 118 } 119 } 120 121 private void writeStartDebugStatement() { 122 Slog.i(TAG, "Starting remote animation"); 123 final StringWriter sw = new StringWriter(); 124 final FastPrintWriter pw = new FastPrintWriter(sw); 125 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 126 mPendingAnimations.get(i).dump(pw, ""); 127 } 128 pw.close(); 129 Slog.i(TAG, sw.toString()); 130 } 131 132 private RemoteAnimationTarget[] createAnimations() { 133 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); 134 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 135 final RemoteAnimationTarget target = 136 mPendingAnimations.get(i).createRemoteAppAnimation(); 137 if (target != null) { 138 targets.add(target); 139 } else { 140 mPendingAnimations.remove(i); 141 } 142 } 143 return targets.toArray(new RemoteAnimationTarget[targets.size()]); 144 } 145 146 private void onAnimationFinished() { 147 mHandler.removeCallbacks(mTimeoutRunnable); 148 synchronized (mService.mWindowMap) { 149 releaseFinishedCallback(); 150 mService.openSurfaceTransaction(); 151 try { 152 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 153 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i); 154 adapter.mCapturedFinishCallback.onAnimationFinished(adapter); 155 } 156 } finally { 157 mService.closeSurfaceTransaction("RemoteAnimationController#finished"); 158 } 159 } 160 sendRunningRemoteAnimation(false); 161 if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation"); 162 } 163 164 private void invokeAnimationCancelled() { 165 try { 166 mRemoteAnimationAdapter.getRunner().onAnimationCancelled(); 167 } catch (RemoteException e) { 168 Slog.e(TAG, "Failed to notify cancel", e); 169 } 170 } 171 172 private void releaseFinishedCallback() { 173 if (mFinishedCallback != null) { 174 mFinishedCallback.release(); 175 mFinishedCallback = null; 176 } 177 } 178 179 private void sendRunningRemoteAnimation(boolean running) { 180 final int pid = mRemoteAnimationAdapter.getCallingPid(); 181 if (pid == 0) { 182 throw new RuntimeException("Calling pid of remote animation was null"); 183 } 184 mService.sendSetRunningRemoteAnimation(pid, running); 185 } 186 187 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub { 188 189 RemoteAnimationController mOuter; 190 191 FinishedCallback(RemoteAnimationController outer) { 192 mOuter = outer; 193 } 194 195 @Override 196 public void onAnimationFinished() throws RemoteException { 197 if (mOuter != null) { 198 mOuter.onAnimationFinished(); 199 200 // In case the client holds on to the finish callback, make sure we don't leak 201 // RemoteAnimationController which in turn would leak the runner on the client. 202 mOuter = null; 203 } 204 } 205 206 /** 207 * Marks this callback as not be used anymore by releasing the reference to the outer class 208 * to prevent memory leak. 209 */ 210 void release() { 211 mOuter = null; 212 } 213 }; 214 215 private class RemoteAnimationAdapterWrapper implements AnimationAdapter { 216 217 private final AppWindowToken mAppWindowToken; 218 private SurfaceControl mCapturedLeash; 219 private OnAnimationFinishedCallback mCapturedFinishCallback; 220 private final Point mPosition = new Point(); 221 private final Rect mStackBounds = new Rect(); 222 private RemoteAnimationTarget mTarget; 223 224 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position, 225 Rect stackBounds) { 226 mAppWindowToken = appWindowToken; 227 mPosition.set(position.x, position.y); 228 mStackBounds.set(stackBounds); 229 } 230 231 RemoteAnimationTarget createRemoteAppAnimation() { 232 final Task task = mAppWindowToken.getTask(); 233 final WindowState mainWindow = mAppWindowToken.findMainWindow(); 234 if (task == null || mainWindow == null || mCapturedFinishCallback == null 235 || mCapturedLeash == null) { 236 return null; 237 } 238 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(), 239 mCapturedLeash, !mAppWindowToken.fillsParent(), 240 mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets, 241 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds, 242 task.getWindowConfiguration(), false /*isNotInRecents*/); 243 return mTarget; 244 } 245 246 private int getMode() { 247 if (mService.mOpeningApps.contains(mAppWindowToken)) { 248 return RemoteAnimationTarget.MODE_OPENING; 249 } else { 250 return RemoteAnimationTarget.MODE_CLOSING; 251 } 252 } 253 254 @Override 255 public boolean getDetachWallpaper() { 256 return false; 257 } 258 259 @Override 260 public boolean getShowWallpaper() { 261 return false; 262 } 263 264 @Override 265 public int getBackgroundColor() { 266 return 0; 267 } 268 269 @Override 270 public void startAnimation(SurfaceControl animationLeash, Transaction t, 271 OnAnimationFinishedCallback finishCallback) { 272 273 // Restore z-layering, position and stack crop until client has a chance to modify it. 274 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex()); 275 t.setPosition(animationLeash, mPosition.x, mPosition.y); 276 mTmpRect.set(mStackBounds); 277 mTmpRect.offsetTo(0, 0); 278 t.setWindowCrop(animationLeash, mTmpRect); 279 mCapturedLeash = animationLeash; 280 mCapturedFinishCallback = finishCallback; 281 } 282 283 @Override 284 public void onAnimationCancelled(SurfaceControl animationLeash) { 285 mPendingAnimations.remove(this); 286 if (mPendingAnimations.isEmpty()) { 287 mHandler.removeCallbacks(mTimeoutRunnable); 288 releaseFinishedCallback(); 289 invokeAnimationCancelled(); 290 sendRunningRemoteAnimation(false); 291 } 292 } 293 294 @Override 295 public long getDurationHint() { 296 return mRemoteAnimationAdapter.getDuration(); 297 } 298 299 @Override 300 public long getStatusBarTransitionsStartTime() { 301 return SystemClock.uptimeMillis() 302 + mRemoteAnimationAdapter.getStatusBarTransitionDelay(); 303 } 304 305 @Override 306 public void dump(PrintWriter pw, String prefix) { 307 pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken); 308 if (mTarget != null) { 309 pw.print(prefix); pw.println("Target:"); 310 mTarget.dump(pw, prefix + " "); 311 } else { 312 pw.print(prefix); pw.println("Target: null"); 313 } 314 } 315 316 @Override 317 public void writeToProto(ProtoOutputStream proto) { 318 final long token = proto.start(REMOTE); 319 if (mTarget != null) { 320 mTarget.writeToProto(proto, TARGET); 321 } 322 proto.end(token); 323 } 324 } 325} 326