1 /* 2 * Copyright (C) 2020 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.server.wm; 18 19 import static android.view.WindowManager.TRANSIT_CLOSE; 20 import static android.view.WindowManager.TRANSIT_OPEN; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.app.ActivityManager; 25 import android.os.IBinder; 26 import android.os.RemoteException; 27 import android.util.Slog; 28 import android.view.WindowManager; 29 import android.window.IRemoteTransition; 30 import android.window.ITransitionPlayer; 31 import android.window.TransitionRequestInfo; 32 33 import com.android.internal.protolog.ProtoLogGroup; 34 import com.android.internal.protolog.common.ProtoLog; 35 36 import java.util.ArrayList; 37 38 /** 39 * Handles all the aspects of recording and synchronizing transitions. 40 */ 41 class TransitionController { 42 private static final String TAG = "TransitionController"; 43 44 private ITransitionPlayer mTransitionPlayer; 45 final ActivityTaskManagerService mAtm; 46 47 /** 48 * Currently playing transitions (in the order they were started). When finished, records are 49 * removed from this list. 50 */ 51 private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>(); 52 53 private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> { 54 // clean-up/finish any playing transitions. 55 for (int i = 0; i < mPlayingTransitions.size(); ++i) { 56 mPlayingTransitions.get(i).cleanUpOnFailure(); 57 } 58 mPlayingTransitions.clear(); 59 mTransitionPlayer = null; 60 }; 61 62 /** The transition currently being constructed (collecting participants). */ 63 private Transition mCollectingTransition = null; 64 TransitionController(ActivityTaskManagerService atm)65 TransitionController(ActivityTaskManagerService atm) { 66 mAtm = atm; 67 } 68 69 /** @see #createTransition(int, int) */ 70 @NonNull createTransition(int type)71 Transition createTransition(int type) { 72 return createTransition(type, 0 /* flags */); 73 } 74 75 /** 76 * Creates a transition. It can immediately collect participants. 77 */ 78 @NonNull createTransition(@indowManager.TransitionType int type, @WindowManager.TransitionFlags int flags)79 Transition createTransition(@WindowManager.TransitionType int type, 80 @WindowManager.TransitionFlags int flags) { 81 if (mTransitionPlayer == null) { 82 throw new IllegalStateException("Shell Transitions not enabled"); 83 } 84 if (mCollectingTransition != null) { 85 throw new IllegalStateException("Simultaneous transitions not supported yet."); 86 } 87 mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine); 88 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s", 89 mCollectingTransition); 90 return mCollectingTransition; 91 } 92 registerTransitionPlayer(@ullable ITransitionPlayer player)93 void registerTransitionPlayer(@Nullable ITransitionPlayer player) { 94 try { 95 if (mTransitionPlayer != null) { 96 mTransitionPlayer.asBinder().unlinkToDeath(mTransitionPlayerDeath, 0); 97 mTransitionPlayer = null; 98 } 99 player.asBinder().linkToDeath(mTransitionPlayerDeath, 0); 100 mTransitionPlayer = player; 101 } catch (RemoteException e) { 102 throw new RuntimeException("Unable to set transition player"); 103 } 104 } 105 getTransitionPlayer()106 @Nullable ITransitionPlayer getTransitionPlayer() { 107 return mTransitionPlayer; 108 } 109 isShellTransitionsEnabled()110 boolean isShellTransitionsEnabled() { 111 return mTransitionPlayer != null; 112 } 113 114 /** 115 * @return {@code true} if transition is actively collecting changes. This is {@code false} 116 * once a transition is playing 117 */ isCollecting()118 boolean isCollecting() { 119 return mCollectingTransition != null; 120 } 121 122 /** 123 * @return {@code true} if transition is actively collecting changes and `wc` is one of them. 124 * This is {@code false} once a transition is playing. 125 */ isCollecting(@onNull WindowContainer wc)126 boolean isCollecting(@NonNull WindowContainer wc) { 127 return mCollectingTransition != null && mCollectingTransition.mParticipants.contains(wc); 128 } 129 130 /** 131 * @return {@code true} if transition is actively playing. This is not necessarily {@code true} 132 * during collection. 133 */ isPlaying()134 boolean isPlaying() { 135 return !mPlayingTransitions.isEmpty(); 136 } 137 138 /** @return {@code true} if a transition is running */ inTransition()139 boolean inTransition() { 140 // TODO(shell-transitions): eventually properly support multiple 141 return isCollecting() || isPlaying(); 142 } 143 144 /** @return {@code true} if wc is in a participant subtree */ inTransition(@onNull WindowContainer wc)145 boolean inTransition(@NonNull WindowContainer wc) { 146 if (isCollecting(wc)) return true; 147 for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { 148 for (WindowContainer p = wc; p != null; p = p.getParent()) { 149 if (mPlayingTransitions.get(i).mParticipants.contains(p)) { 150 return true; 151 } 152 } 153 } 154 return false; 155 } 156 157 /** 158 * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition) 159 */ 160 @Nullable requestTransitionIfNeeded(@indowManager.TransitionType int type, @Nullable WindowContainer trigger)161 Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, 162 @Nullable WindowContainer trigger) { 163 return requestTransitionIfNeeded(type, 0 /* flags */, trigger); 164 } 165 166 /** 167 * @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition) 168 */ 169 @Nullable requestTransitionIfNeeded(@indowManager.TransitionType int type, @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger)170 Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, 171 @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) { 172 return requestTransitionIfNeeded(type, flags, trigger, null /* remote */); 173 } 174 isExistenceType(@indowManager.TransitionType int type)175 private static boolean isExistenceType(@WindowManager.TransitionType int type) { 176 return type == TRANSIT_OPEN || type == TRANSIT_CLOSE; 177 } 178 179 /** 180 * If a transition isn't requested yet, creates one and asks the TransitionPlayer (Shell) to 181 * start it. Collection can start immediately. 182 * @param trigger if non-null, this is the first container that will be collected 183 * @return the created transition if created or null otherwise. 184 */ 185 @Nullable requestTransitionIfNeeded(@indowManager.TransitionType int type, @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger, @Nullable IRemoteTransition remoteTransition)186 Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type, 187 @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger, 188 @Nullable IRemoteTransition remoteTransition) { 189 if (mTransitionPlayer == null) { 190 return null; 191 } 192 Transition newTransition = null; 193 if (isCollecting()) { 194 // Make the collecting transition wait until this request is ready. 195 mCollectingTransition.setReady(false); 196 } else { 197 newTransition = requestStartTransition(createTransition(type, flags), 198 trigger != null ? trigger.asTask() : null, remoteTransition); 199 } 200 if (trigger != null) { 201 if (isExistenceType(type)) { 202 collectExistenceChange(trigger); 203 } else { 204 collect(trigger); 205 } 206 } 207 return newTransition; 208 } 209 210 /** Asks the transition player (shell) to start a created but not yet started transition. */ 211 @NonNull requestStartTransition(@onNull Transition transition, @Nullable Task startTask, @Nullable IRemoteTransition remoteTransition)212 Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask, 213 @Nullable IRemoteTransition remoteTransition) { 214 try { 215 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, 216 "Requesting StartTransition: %s", transition); 217 ActivityManager.RunningTaskInfo info = null; 218 if (startTask != null) { 219 info = new ActivityManager.RunningTaskInfo(); 220 startTask.fillTaskInfo(info); 221 } 222 mTransitionPlayer.requestStartTransition(transition, new TransitionRequestInfo( 223 transition.mType, info, remoteTransition)); 224 } catch (RemoteException e) { 225 Slog.e(TAG, "Error requesting transition", e); 226 transition.start(); 227 } 228 return transition; 229 } 230 231 /** @see Transition#collect */ collect(@onNull WindowContainer wc)232 void collect(@NonNull WindowContainer wc) { 233 if (mCollectingTransition == null) return; 234 mCollectingTransition.collect(wc); 235 } 236 237 /** @see Transition#collectExistenceChange */ collectExistenceChange(@onNull WindowContainer wc)238 void collectExistenceChange(@NonNull WindowContainer wc) { 239 if (mCollectingTransition == null) return; 240 mCollectingTransition.collectExistenceChange(wc); 241 } 242 243 /** @see Transition#setReady */ setReady(boolean ready)244 void setReady(boolean ready) { 245 if (mCollectingTransition == null) return; 246 mCollectingTransition.setReady(ready); 247 } 248 249 /** @see Transition#setReady */ setReady()250 void setReady() { 251 setReady(true); 252 } 253 254 /** @see Transition#finishTransition */ finishTransition(@onNull IBinder token)255 void finishTransition(@NonNull IBinder token) { 256 final Transition record = Transition.fromBinder(token); 257 if (record == null || !mPlayingTransitions.contains(record)) { 258 Slog.e(TAG, "Trying to finish a non-playing transition " + token); 259 return; 260 } 261 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record); 262 mPlayingTransitions.remove(record); 263 record.finishTransition(); 264 } 265 moveToPlaying(Transition transition)266 void moveToPlaying(Transition transition) { 267 if (transition != mCollectingTransition) { 268 throw new IllegalStateException("Trying to move non-collecting transition to playing"); 269 } 270 mCollectingTransition = null; 271 mPlayingTransitions.add(transition); 272 } 273 abort(Transition transition)274 void abort(Transition transition) { 275 if (transition != mCollectingTransition) { 276 throw new IllegalStateException("Too late to abort."); 277 } 278 transition.abort(); 279 mCollectingTransition = null; 280 } 281 282 } 283