1 /* 2 * Copyright (C) 2021 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 17 package com.android.wm.shell.transition; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.ActivityTaskManager; 22 import android.os.IBinder; 23 import android.os.RemoteException; 24 import android.util.ArrayMap; 25 import android.util.Log; 26 import android.util.Pair; 27 import android.util.Slog; 28 import android.view.SurfaceControl; 29 import android.window.IRemoteTransition; 30 import android.window.IRemoteTransitionFinishedCallback; 31 import android.window.RemoteTransition; 32 import android.window.TransitionFilter; 33 import android.window.TransitionInfo; 34 import android.window.TransitionRequestInfo; 35 import android.window.WindowContainerTransaction; 36 37 import androidx.annotation.BinderThread; 38 39 import com.android.internal.protolog.common.ProtoLog; 40 import com.android.wm.shell.common.ShellExecutor; 41 import com.android.wm.shell.protolog.ShellProtoLogGroup; 42 43 import java.util.ArrayList; 44 45 /** 46 * Handler that deals with RemoteTransitions. It will only request to handle a transition 47 * if the request includes a specific remote. 48 */ 49 public class RemoteTransitionHandler implements Transitions.TransitionHandler { 50 private static final String TAG = "RemoteTransitionHandler"; 51 52 private final ShellExecutor mMainExecutor; 53 54 /** Includes remotes explicitly requested by, eg, ActivityOptions */ 55 private final ArrayMap<IBinder, RemoteTransition> mRequestedRemotes = new ArrayMap<>(); 56 57 /** Ordered by specificity. Last filters will be checked first */ 58 private final ArrayList<Pair<TransitionFilter, RemoteTransition>> mFilters = 59 new ArrayList<>(); 60 61 private final ArrayMap<IBinder, RemoteDeathHandler> mDeathHandlers = new ArrayMap<>(); 62 RemoteTransitionHandler(@onNull ShellExecutor mainExecutor)63 RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) { 64 mMainExecutor = mainExecutor; 65 } 66 addFiltered(TransitionFilter filter, RemoteTransition remote)67 void addFiltered(TransitionFilter filter, RemoteTransition remote) { 68 handleDeath(remote.asBinder(), null /* finishCallback */); 69 mFilters.add(new Pair<>(filter, remote)); 70 } 71 removeFiltered(RemoteTransition remote)72 void removeFiltered(RemoteTransition remote) { 73 boolean removed = false; 74 for (int i = mFilters.size() - 1; i >= 0; --i) { 75 if (mFilters.get(i).second.asBinder().equals(remote.asBinder())) { 76 mFilters.remove(i); 77 removed = true; 78 } 79 } 80 if (removed) { 81 unhandleDeath(remote.asBinder(), null /* finishCallback */); 82 } 83 } 84 85 @Override onTransitionMerged(@onNull IBinder transition)86 public void onTransitionMerged(@NonNull IBinder transition) { 87 mRequestedRemotes.remove(transition); 88 } 89 90 @Override startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback)91 public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 92 @NonNull SurfaceControl.Transaction startTransaction, 93 @NonNull SurfaceControl.Transaction finishTransaction, 94 @NonNull Transitions.TransitionFinishCallback finishCallback) { 95 RemoteTransition pendingRemote = mRequestedRemotes.get(transition); 96 if (pendingRemote == null) { 97 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have " 98 + "explicit remote, search filters for match for %s", transition, info); 99 // If no explicit remote, search filters until one matches 100 for (int i = mFilters.size() - 1; i >= 0; --i) { 101 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Checking filter %s", 102 mFilters.get(i)); 103 if (mFilters.get(i).first.matches(info)) { 104 Slog.d(TAG, "Found filter" + mFilters.get(i)); 105 pendingRemote = mFilters.get(i).second; 106 // Add to requested list so that it can be found for merge requests. 107 mRequestedRemotes.put(transition, pendingRemote); 108 break; 109 } 110 } 111 } 112 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for %s to %s", 113 transition, pendingRemote); 114 115 if (pendingRemote == null) return false; 116 117 final RemoteTransition remote = pendingRemote; 118 IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { 119 @Override 120 public void onTransitionFinished(WindowContainerTransaction wct, 121 SurfaceControl.Transaction sct) { 122 unhandleDeath(remote.asBinder(), finishCallback); 123 mMainExecutor.execute(() -> { 124 if (sct != null) { 125 finishTransaction.merge(sct); 126 } 127 mRequestedRemotes.remove(transition); 128 finishCallback.onTransitionFinished(wct, null /* wctCB */); 129 }); 130 } 131 }; 132 try { 133 handleDeath(remote.asBinder(), finishCallback); 134 try { 135 ActivityTaskManager.getService().setRunningRemoteTransitionDelegate( 136 remote.getAppThread()); 137 } catch (SecurityException e) { 138 Log.e(Transitions.TAG, "Unable to boost animation thread. This should only happen" 139 + " during unit tests"); 140 } 141 remote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb); 142 } catch (RemoteException e) { 143 Log.e(Transitions.TAG, "Error running remote transition.", e); 144 unhandleDeath(remote.asBinder(), finishCallback); 145 mRequestedRemotes.remove(transition); 146 mMainExecutor.execute( 147 () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */)); 148 } 149 return true; 150 } 151 152 @Override mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback)153 public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 154 @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, 155 @NonNull Transitions.TransitionFinishCallback finishCallback) { 156 final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget).getRemoteTransition(); 157 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s", 158 transition, remote); 159 if (remote == null) return; 160 161 IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { 162 @Override 163 public void onTransitionFinished(WindowContainerTransaction wct, 164 SurfaceControl.Transaction sct) { 165 mMainExecutor.execute(() -> { 166 if (!mRequestedRemotes.containsKey(mergeTarget)) { 167 Log.e(TAG, "Merged transition finished after it's mergeTarget (the " 168 + "transition it was supposed to merge into). This usually means " 169 + "that the mergeTarget's RemoteTransition impl erroneously " 170 + "accepted/ran the merge request after finishing the mergeTarget"); 171 } 172 finishCallback.onTransitionFinished(wct, null /* wctCB */); 173 }); 174 } 175 }; 176 try { 177 remote.mergeAnimation(transition, info, t, mergeTarget, cb); 178 } catch (RemoteException e) { 179 Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e); 180 } 181 } 182 183 @Override 184 @Nullable handleRequest(@onNull IBinder transition, @Nullable TransitionRequestInfo request)185 public WindowContainerTransaction handleRequest(@NonNull IBinder transition, 186 @Nullable TransitionRequestInfo request) { 187 RemoteTransition remote = request.getRemoteTransition(); 188 if (remote == null) return null; 189 mRequestedRemotes.put(transition, remote); 190 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested" 191 + " for %s: %s", transition, remote); 192 return new WindowContainerTransaction(); 193 } 194 handleDeath(@onNull IBinder remote, @Nullable Transitions.TransitionFinishCallback finishCallback)195 private void handleDeath(@NonNull IBinder remote, 196 @Nullable Transitions.TransitionFinishCallback finishCallback) { 197 synchronized (mDeathHandlers) { 198 RemoteDeathHandler deathHandler = mDeathHandlers.get(remote); 199 if (deathHandler == null) { 200 deathHandler = new RemoteDeathHandler(remote); 201 try { 202 remote.linkToDeath(deathHandler, 0 /* flags */); 203 } catch (RemoteException e) { 204 Slog.e(TAG, "Failed to link to death"); 205 return; 206 } 207 mDeathHandlers.put(remote, deathHandler); 208 } 209 deathHandler.addUser(finishCallback); 210 } 211 } 212 unhandleDeath(@onNull IBinder remote, @Nullable Transitions.TransitionFinishCallback finishCallback)213 private void unhandleDeath(@NonNull IBinder remote, 214 @Nullable Transitions.TransitionFinishCallback finishCallback) { 215 synchronized (mDeathHandlers) { 216 RemoteDeathHandler deathHandler = mDeathHandlers.get(remote); 217 if (deathHandler == null) return; 218 deathHandler.removeUser(finishCallback); 219 if (deathHandler.getUserCount() == 0) { 220 if (!deathHandler.mPendingFinishCallbacks.isEmpty()) { 221 throw new IllegalStateException("Unhandling death for binder that still has" 222 + " pending finishCallback(s)."); 223 } 224 remote.unlinkToDeath(deathHandler, 0 /* flags */); 225 mDeathHandlers.remove(remote); 226 } 227 } 228 } 229 230 /** NOTE: binder deaths can alter the filter order */ 231 private class RemoteDeathHandler implements IBinder.DeathRecipient { 232 private final IBinder mRemote; 233 private final ArrayList<Transitions.TransitionFinishCallback> mPendingFinishCallbacks = 234 new ArrayList<>(); 235 private int mUsers = 0; 236 RemoteDeathHandler(IBinder remote)237 RemoteDeathHandler(IBinder remote) { 238 mRemote = remote; 239 } 240 addUser(@ullable Transitions.TransitionFinishCallback finishCallback)241 void addUser(@Nullable Transitions.TransitionFinishCallback finishCallback) { 242 if (finishCallback != null) { 243 mPendingFinishCallbacks.add(finishCallback); 244 } 245 ++mUsers; 246 } 247 removeUser(@ullable Transitions.TransitionFinishCallback finishCallback)248 void removeUser(@Nullable Transitions.TransitionFinishCallback finishCallback) { 249 if (finishCallback != null) { 250 mPendingFinishCallbacks.remove(finishCallback); 251 } 252 --mUsers; 253 } 254 getUserCount()255 int getUserCount() { 256 return mUsers; 257 } 258 259 @Override 260 @BinderThread binderDied()261 public void binderDied() { 262 mMainExecutor.execute(() -> { 263 for (int i = mFilters.size() - 1; i >= 0; --i) { 264 if (mRemote.equals(mFilters.get(i).second.asBinder())) { 265 mFilters.remove(i); 266 } 267 } 268 for (int i = mRequestedRemotes.size() - 1; i >= 0; --i) { 269 if (mRemote.equals(mRequestedRemotes.valueAt(i).asBinder())) { 270 mRequestedRemotes.removeAt(i); 271 } 272 } 273 for (int i = mPendingFinishCallbacks.size() - 1; i >= 0; --i) { 274 mPendingFinishCallbacks.get(i).onTransitionFinished( 275 null /* wct */, null /* wctCB */); 276 } 277 mPendingFinishCallbacks.clear(); 278 }); 279 } 280 } 281 } 282