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 android.window;
18 
19 import static android.view.WindowManager.TRANSIT_CHANGE;
20 import static android.view.WindowManager.TRANSIT_CLOSE;
21 import static android.view.WindowManager.TRANSIT_NONE;
22 import static android.view.WindowManager.TRANSIT_OPEN;
23 
24 import android.annotation.CallSuper;
25 import android.annotation.IntDef;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.TestApi;
29 import android.os.Bundle;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.view.RemoteAnimationDefinition;
33 import android.view.WindowManager;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.util.concurrent.Executor;
38 
39 /**
40  * Interface for WindowManager to delegate control of {@code TaskFragment}.
41  * @hide
42  */
43 @TestApi
44 public class TaskFragmentOrganizer extends WindowOrganizer {
45 
46     /**
47      * Key to the {@link Throwable} in {@link TaskFragmentTransaction.Change#getErrorBundle()}.
48      */
49     public static final String KEY_ERROR_CALLBACK_THROWABLE = "fragment_throwable";
50 
51     /**
52      * Key to the {@link TaskFragmentInfo} in
53      * {@link TaskFragmentTransaction.Change#getErrorBundle()}.
54      */
55     public static final String KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO = "task_fragment_info";
56 
57     /**
58      * Key to the {@link TaskFragmentOperation.OperationType} in
59      * {@link TaskFragmentTransaction.Change#getErrorBundle()}.
60      */
61     public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type";
62 
63     /**
64      * No change set.
65      */
66     @WindowManager.TransitionType
67     @TaskFragmentTransitionType
68     public static final int TASK_FRAGMENT_TRANSIT_NONE = TRANSIT_NONE;
69 
70     /**
71      * A window that didn't exist before has been created and made visible.
72      */
73     @WindowManager.TransitionType
74     @TaskFragmentTransitionType
75     public static final int TASK_FRAGMENT_TRANSIT_OPEN = TRANSIT_OPEN;
76 
77     /**
78      * A window that was visible no-longer exists (was finished or destroyed).
79      */
80     @WindowManager.TransitionType
81     @TaskFragmentTransitionType
82     public static final int TASK_FRAGMENT_TRANSIT_CLOSE = TRANSIT_CLOSE;
83 
84     /**
85      * A window is visible before and after but changes in some way (eg. it resizes or changes
86      * windowing-mode).
87      */
88     @WindowManager.TransitionType
89     @TaskFragmentTransitionType
90     public static final int TASK_FRAGMENT_TRANSIT_CHANGE = TRANSIT_CHANGE;
91 
92     /**
93      * Introduced a sub set of {@link WindowManager.TransitionType} for the types that are used for
94      * TaskFragment transition.
95      *
96      * Doing this instead of exposing {@link WindowManager.TransitionType} because we want to keep
97      * the Shell transition API hidden until it comes fully stable.
98      * @hide
99      */
100     @IntDef(prefix = { "TASK_FRAGMENT_TRANSIT_" }, value = {
101             TASK_FRAGMENT_TRANSIT_NONE,
102             TASK_FRAGMENT_TRANSIT_OPEN,
103             TASK_FRAGMENT_TRANSIT_CLOSE,
104             TASK_FRAGMENT_TRANSIT_CHANGE,
105     })
106     @Retention(RetentionPolicy.SOURCE)
107     public @interface TaskFragmentTransitionType {}
108 
109     /**
110      * Creates a {@link Bundle} with an exception, operation type and TaskFragmentInfo (if any)
111      * that can be passed to {@link ITaskFragmentOrganizer#onTaskFragmentError}.
112      * @hide
113      */
putErrorInfoInBundle(@onNull Throwable exception, @Nullable TaskFragmentInfo info, @TaskFragmentOperation.OperationType int opType)114     public static @NonNull Bundle putErrorInfoInBundle(@NonNull Throwable exception,
115             @Nullable TaskFragmentInfo info, @TaskFragmentOperation.OperationType int opType) {
116         final Bundle errorBundle = new Bundle();
117         errorBundle.putSerializable(KEY_ERROR_CALLBACK_THROWABLE, exception);
118         if (info != null) {
119             errorBundle.putParcelable(KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, info);
120         }
121         errorBundle.putInt(KEY_ERROR_CALLBACK_OP_TYPE, opType);
122         return errorBundle;
123     }
124 
125     /**
126      * Callbacks from WM Core are posted on this executor.
127      */
128     private final Executor mExecutor;
129 
TaskFragmentOrganizer(@onNull Executor executor)130     public TaskFragmentOrganizer(@NonNull Executor executor) {
131         mExecutor = executor;
132     }
133 
134     /**
135      * Gets the executor to run callbacks on.
136      */
137     @NonNull
getExecutor()138     public Executor getExecutor() {
139         return mExecutor;
140     }
141 
142     /**
143      * Registers a TaskFragmentOrganizer to manage TaskFragments.
144      */
145     @CallSuper
registerOrganizer()146     public void registerOrganizer() {
147         try {
148             getController().registerOrganizer(mInterface);
149         } catch (RemoteException e) {
150             throw e.rethrowFromSystemServer();
151         }
152     }
153 
154     /**
155      * Unregisters a previously registered TaskFragmentOrganizer.
156      */
157     @CallSuper
unregisterOrganizer()158     public void unregisterOrganizer() {
159         try {
160             getController().unregisterOrganizer(mInterface);
161         } catch (RemoteException e) {
162             throw e.rethrowFromSystemServer();
163         }
164     }
165 
166     /**
167      * Registers remote animations per transition type for the organizer. It will override the
168      * animations if the transition only contains windows that belong to the organized
169      * TaskFragments, and at least one of the transition window is embedded (not filling the Task).
170      * @hide
171      */
172     @CallSuper
registerRemoteAnimations(@onNull RemoteAnimationDefinition definition)173     public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
174         try {
175             getController().registerRemoteAnimations(mInterface, definition);
176         } catch (RemoteException e) {
177             throw e.rethrowFromSystemServer();
178         }
179     }
180 
181     /**
182      * Unregisters remote animations per transition type for the organizer.
183      * @hide
184      */
185     @CallSuper
unregisterRemoteAnimations()186     public void unregisterRemoteAnimations() {
187         try {
188             getController().unregisterRemoteAnimations(mInterface);
189         } catch (RemoteException e) {
190             throw e.rethrowFromSystemServer();
191         }
192     }
193 
194     /**
195      * Notifies the server that the organizer has finished handling the given transaction. The
196      * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
197      *
198      * @param transactionToken  {@link TaskFragmentTransaction#getTransactionToken()} from
199      *                          {@link #onTransactionReady(TaskFragmentTransaction)}
200      * @param wct               {@link WindowContainerTransaction} that the server should apply for
201      *                          update of the transaction.
202      * @param transitionType    {@link TaskFragmentTransitionType} if it needs to start a
203      *                          transition.
204      * @param shouldApplyIndependently  If {@code true}, the {@code wct} will request a new
205      *                                  transition, which will be queued until the sync engine is
206      *                                  free if there is any other active sync. If {@code false},
207      *                                  the {@code wct} will be directly applied to the active sync.
208      * @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission
209      * for permission enforcement.
210      */
onTransactionHandled(@onNull IBinder transactionToken, @NonNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently)211     public void onTransactionHandled(@NonNull IBinder transactionToken,
212             @NonNull WindowContainerTransaction wct,
213             @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) {
214         wct.setTaskFragmentOrganizer(mInterface);
215         try {
216             getController().onTransactionHandled(transactionToken, wct, transitionType,
217                     shouldApplyIndependently);
218         } catch (RemoteException e) {
219             throw e.rethrowFromSystemServer();
220         }
221     }
222 
223     /**
224      * Must use {@link #applyTransaction(WindowContainerTransaction, int, boolean)} instead.
225      * @see #applyTransaction(WindowContainerTransaction, int, boolean)
226      */
227     @Override
applyTransaction(@onNull WindowContainerTransaction wct)228     public void applyTransaction(@NonNull WindowContainerTransaction wct) {
229         throw new RuntimeException("Not allowed!");
230     }
231 
232     /**
233      * Requests the server to apply the given {@link WindowContainerTransaction}.
234      *
235      * @param wct   {@link WindowContainerTransaction} to apply.
236      * @param transitionType    {@link TaskFragmentTransitionType} if it needs to start a
237      *                          transition.
238      * @param shouldApplyIndependently  If {@code true}, the {@code wct} will request a new
239      *                                  transition, which will be queued until the sync engine is
240      *                                  free if there is any other active sync. If {@code false},
241      *                                  the {@code wct} will be directly applied to the active sync.
242      * @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission
243      * for permission enforcement.
244      */
applyTransaction(@onNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently)245     public void applyTransaction(@NonNull WindowContainerTransaction wct,
246             @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) {
247         if (wct.isEmpty()) {
248             return;
249         }
250         wct.setTaskFragmentOrganizer(mInterface);
251         try {
252             getController().applyTransaction(wct, transitionType, shouldApplyIndependently);
253         } catch (RemoteException e) {
254             throw e.rethrowFromSystemServer();
255         }
256     }
257 
258     /**
259      * Called when the transaction is ready so that the organizer can update the TaskFragments based
260      * on the changes in transaction.
261      */
onTransactionReady(@onNull TaskFragmentTransaction transaction)262     public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
263         // Notify the server to finish the transaction.
264         onTransactionHandled(transaction.getTransactionToken(), new WindowContainerTransaction(),
265                 TASK_FRAGMENT_TRANSIT_NONE, false /* shouldApplyIndependently */);
266     }
267 
268     private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
269         @Override
270         public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
271             mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
272         }
273     };
274 
275     private final TaskFragmentOrganizerToken mToken = new TaskFragmentOrganizerToken(mInterface);
276 
277     @NonNull
getOrganizerToken()278     public TaskFragmentOrganizerToken getOrganizerToken() {
279         return mToken;
280     }
281 
getController()282     private ITaskFragmentOrganizerController getController() {
283         try {
284             return getWindowOrganizerController().getTaskFragmentOrganizerController();
285         } catch (RemoteException e) {
286             return null;
287         }
288     }
289 
290     /**
291      * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
292      * only occupies a portion of Task bounds.
293      * @hide
294      */
isActivityEmbedded(@onNull IBinder activityToken)295     public boolean isActivityEmbedded(@NonNull IBinder activityToken) {
296         try {
297             return getController().isActivityEmbedded(activityToken);
298         } catch (RemoteException e) {
299             throw e.rethrowFromSystemServer();
300         }
301     }
302 }
303