• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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