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_FIRST_CUSTOM; 22 import static android.view.WindowManager.TRANSIT_NONE; 23 import static android.view.WindowManager.TRANSIT_OPEN; 24 import static android.window.ActivityWindowInfo.getActivityWindowInfo; 25 26 import android.annotation.CallSuper; 27 import android.annotation.FlaggedApi; 28 import android.annotation.IntDef; 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.annotation.RequiresPermission; 32 import android.annotation.TestApi; 33 import android.app.Activity; 34 import android.os.Bundle; 35 import android.os.IBinder; 36 import android.os.RemoteException; 37 import android.view.RemoteAnimationDefinition; 38 import android.view.WindowManager; 39 40 import com.android.window.flags.Flags; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.Objects; 45 import java.util.concurrent.Executor; 46 47 /** 48 * Interface for WindowManager to delegate control of {@code TaskFragment}. 49 * @hide 50 */ 51 @TestApi 52 public class TaskFragmentOrganizer extends WindowOrganizer { 53 54 /** 55 * Key to the {@link Throwable} in {@link TaskFragmentTransaction.Change#getErrorBundle()}. 56 */ 57 public static final String KEY_ERROR_CALLBACK_THROWABLE = "fragment_throwable"; 58 59 /** 60 * Key to the {@link TaskFragmentInfo} in 61 * {@link TaskFragmentTransaction.Change#getErrorBundle()}. 62 */ 63 public static final String KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO = "task_fragment_info"; 64 65 /** 66 * Key to the {@link TaskFragmentOperation.OperationType} in 67 * {@link TaskFragmentTransaction.Change#getErrorBundle()}. 68 */ 69 public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type"; 70 71 /** 72 * Key to bundle {@link TaskFragmentInfo}s from the system in 73 * {@link #registerOrganizer(boolean, Bundle)} 74 * 75 * @hide 76 */ 77 public static final String KEY_RESTORE_TASK_FRAGMENTS_INFO = "key_restore_task_fragments_info"; 78 79 /** 80 * Key to bundle {@link TaskFragmentParentInfo} from the system in 81 * {@link #registerOrganizer(boolean, Bundle)} 82 * 83 * @hide 84 */ 85 public static final String KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO = 86 "key_restore_task_fragment_parent_info"; 87 88 /** 89 * No change set. 90 */ 91 @WindowManager.TransitionType 92 @TaskFragmentTransitionType 93 public static final int TASK_FRAGMENT_TRANSIT_NONE = TRANSIT_NONE; 94 95 /** 96 * A window that didn't exist before has been created and made visible. 97 */ 98 @WindowManager.TransitionType 99 @TaskFragmentTransitionType 100 public static final int TASK_FRAGMENT_TRANSIT_OPEN = TRANSIT_OPEN; 101 102 /** 103 * A window that was visible no-longer exists (was finished or destroyed). 104 */ 105 @WindowManager.TransitionType 106 @TaskFragmentTransitionType 107 public static final int TASK_FRAGMENT_TRANSIT_CLOSE = TRANSIT_CLOSE; 108 109 /** 110 * A window is visible before and after but changes in some way (eg. it resizes or changes 111 * windowing-mode). 112 */ 113 @WindowManager.TransitionType 114 @TaskFragmentTransitionType 115 public static final int TASK_FRAGMENT_TRANSIT_CHANGE = TRANSIT_CHANGE; 116 117 118 /** 119 * The task fragment drag resize transition used by activity embedding. 120 * 121 * This value is also used in Transitions.TRANSIT_TASK_FRAGMENT_DRAG_RESIZE and must not 122 * conflict with other predefined transition types. 123 * 124 * @hide 125 */ 126 @WindowManager.TransitionType 127 @TaskFragmentTransitionType 128 public static final int TASK_FRAGMENT_TRANSIT_DRAG_RESIZE = TRANSIT_FIRST_CUSTOM + 17; 129 130 /** 131 * Introduced a sub set of {@link WindowManager.TransitionType} for the types that are used for 132 * TaskFragment transition. 133 * 134 * Doing this instead of exposing {@link WindowManager.TransitionType} because we want to keep 135 * the Shell transition API hidden until it comes fully stable. 136 * @hide 137 */ 138 @IntDef(prefix = { "TASK_FRAGMENT_TRANSIT_" }, value = { 139 TASK_FRAGMENT_TRANSIT_NONE, 140 TASK_FRAGMENT_TRANSIT_OPEN, 141 TASK_FRAGMENT_TRANSIT_CLOSE, 142 TASK_FRAGMENT_TRANSIT_CHANGE, 143 TASK_FRAGMENT_TRANSIT_DRAG_RESIZE, 144 }) 145 @Retention(RetentionPolicy.SOURCE) 146 public @interface TaskFragmentTransitionType {} 147 148 /** 149 * Creates a {@link Bundle} with an exception, operation type and TaskFragmentInfo (if any) 150 * that can be passed to {@link ITaskFragmentOrganizer#onTaskFragmentError}. 151 * @hide 152 */ putErrorInfoInBundle(@onNull Throwable exception, @Nullable TaskFragmentInfo info, @TaskFragmentOperation.OperationType int opType)153 public static @NonNull Bundle putErrorInfoInBundle(@NonNull Throwable exception, 154 @Nullable TaskFragmentInfo info, @TaskFragmentOperation.OperationType int opType) { 155 final Bundle errorBundle = new Bundle(); 156 errorBundle.putSerializable(KEY_ERROR_CALLBACK_THROWABLE, exception); 157 if (info != null) { 158 errorBundle.putParcelable(KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, info); 159 } 160 errorBundle.putInt(KEY_ERROR_CALLBACK_OP_TYPE, opType); 161 return errorBundle; 162 } 163 164 /** 165 * Callbacks from WM Core are posted on this executor. 166 */ 167 private final Executor mExecutor; 168 TaskFragmentOrganizer(@onNull Executor executor)169 public TaskFragmentOrganizer(@NonNull Executor executor) { 170 mExecutor = executor; 171 } 172 173 /** 174 * Gets the executor to run callbacks on. 175 */ 176 @NonNull getExecutor()177 public Executor getExecutor() { 178 return mExecutor; 179 } 180 181 /** 182 * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments. 183 */ 184 @CallSuper registerOrganizer()185 public void registerOrganizer() { 186 registerOrganizer(false /* isSystemOrganizer */, null /* outSavedState */); 187 } 188 189 /** 190 * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments. 191 * <p> 192 * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer 193 * will have additional system capabilities, including: (1) it will receive SurfaceControl for 194 * the organized TaskFragment, and (2) it needs to update the 195 * {@link android.view.SurfaceControl} following the window change accordingly. 196 * 197 * @hide 198 */ 199 @CallSuper 200 @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true) 201 @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG) registerOrganizer(boolean isSystemOrganizer)202 public void registerOrganizer(boolean isSystemOrganizer) { 203 registerOrganizer(isSystemOrganizer, null /* outSavedState */); 204 } 205 206 /** 207 * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments. 208 * <p> 209 * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer 210 * will have additional system capabilities, including: (1) it will receive SurfaceControl for 211 * the organized TaskFragment, and (2) it needs to update the 212 * {@link android.view.SurfaceControl} following the window change accordingly. 213 * 214 * @param isSystemOrganizer If it is a system organizer 215 * @param outSavedState Returning the saved state (if any) that previously saved. This is 216 * useful when retrieve the state from the same TaskFragmentOrganizer 217 * that was killed by the system (e.g. to reclaim memory). Note that 218 * the save state is dropped and unable to retrieve once the system 219 * restarts or the organizer is unregistered. 220 * @hide 221 */ 222 @CallSuper 223 @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true) registerOrganizer(boolean isSystemOrganizer, @Nullable Bundle outSavedState)224 public void registerOrganizer(boolean isSystemOrganizer, @Nullable Bundle outSavedState) { 225 try { 226 getController().registerOrganizer(mInterface, isSystemOrganizer, 227 outSavedState != null ? outSavedState : new Bundle()); 228 } catch (RemoteException e) { 229 throw e.rethrowFromSystemServer(); 230 } 231 } 232 233 /** 234 * Unregisters a previously registered TaskFragmentOrganizer. 235 */ 236 @CallSuper unregisterOrganizer()237 public void unregisterOrganizer() { 238 try { 239 getController().unregisterOrganizer(mInterface); 240 } catch (RemoteException e) { 241 throw e.rethrowFromSystemServer(); 242 } 243 } 244 245 /** 246 * Registers remote animations per transition type for the organizer. It will override the 247 * animations if the transition only contains windows that belong to the organized 248 * TaskFragments, and at least one of the transition window is embedded (not filling the Task). 249 * @hide 250 */ 251 @CallSuper registerRemoteAnimations(@onNull RemoteAnimationDefinition definition)252 public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) { 253 try { 254 getController().registerRemoteAnimations(mInterface, definition); 255 } catch (RemoteException e) { 256 throw e.rethrowFromSystemServer(); 257 } 258 } 259 260 /** 261 * Unregisters remote animations per transition type for the organizer. 262 * @hide 263 */ 264 @CallSuper unregisterRemoteAnimations()265 public void unregisterRemoteAnimations() { 266 try { 267 getController().unregisterRemoteAnimations(mInterface); 268 } catch (RemoteException e) { 269 throw e.rethrowFromSystemServer(); 270 } 271 } 272 273 /** 274 * Saves the state in the system, where the state can be restored if the process of 275 * the TaskFragmentOrganizer is restarted. 276 * 277 * @hide 278 * 279 * @param state the state to save. 280 */ setSavedState(@onNull Bundle state)281 public void setSavedState(@NonNull Bundle state) { 282 if (state.getSize() > 200000) { 283 throw new IllegalArgumentException("Saved state too large, " + state.getSize()); 284 } 285 286 try { 287 getController().setSavedState(mInterface, state); 288 } catch (RemoteException e) { 289 throw e.rethrowFromSystemServer(); 290 } 291 } 292 293 /** 294 * Notifies the server that the organizer has finished handling the given transaction. The 295 * server should apply the given {@link WindowContainerTransaction} for the necessary changes. 296 * 297 * @param transactionToken {@link TaskFragmentTransaction#getTransactionToken()} from 298 * {@link #onTransactionReady(TaskFragmentTransaction)} 299 * @param wct {@link WindowContainerTransaction} that the server should apply for 300 * update of the transaction. 301 * @param transitionType {@link TaskFragmentTransitionType} if it needs to start a 302 * transition. 303 * @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new 304 * transition, which will be queued until the sync engine is 305 * free if there is any other active sync. If {@code false}, 306 * the {@code wct} will be directly applied to the active sync. 307 * @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission 308 * for permission enforcement. 309 */ onTransactionHandled(@onNull IBinder transactionToken, @NonNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently)310 public void onTransactionHandled(@NonNull IBinder transactionToken, 311 @NonNull WindowContainerTransaction wct, 312 @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) { 313 wct.setTaskFragmentOrganizer(mInterface); 314 try { 315 getController().onTransactionHandled(transactionToken, wct, transitionType, 316 shouldApplyIndependently); 317 } catch (RemoteException e) { 318 throw e.rethrowFromSystemServer(); 319 } 320 } 321 322 /** 323 * Must use {@link #applyTransaction(WindowContainerTransaction, int, boolean)} instead. 324 * @see #applyTransaction(WindowContainerTransaction, int, boolean) 325 */ 326 @Override applyTransaction(@onNull WindowContainerTransaction wct)327 public void applyTransaction(@NonNull WindowContainerTransaction wct) { 328 throw new RuntimeException("Not allowed!"); 329 } 330 331 /** 332 * Requests the server to apply the given {@link WindowContainerTransaction}. 333 * 334 * @param wct {@link WindowContainerTransaction} to apply. 335 * @param transitionType {@link TaskFragmentTransitionType} if it needs to start a 336 * transition. 337 * @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new 338 * transition, which will be queued until the sync engine is 339 * free if there is any other active sync. If {@code false}, 340 * the {@code wct} will be directly applied to the active sync. 341 * @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission 342 * for permission enforcement. 343 */ applyTransaction(@onNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently)344 public void applyTransaction(@NonNull WindowContainerTransaction wct, 345 @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) { 346 if (wct.isEmpty()) { 347 return; 348 } 349 wct.setTaskFragmentOrganizer(mInterface); 350 try { 351 getController().applyTransaction( 352 wct, transitionType, shouldApplyIndependently, null /* remoteTransition */); 353 } catch (RemoteException e) { 354 throw e.rethrowFromSystemServer(); 355 } 356 } 357 358 /** 359 * Applies a transaction with a {@link RemoteTransition}. Only a system organizer is allowed to 360 * use {@link RemoteTransition}. See {@link TaskFragmentOrganizer#registerOrganizer(boolean)}. 361 * 362 * @hide 363 */ 364 @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG) applySystemTransaction(@onNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, @Nullable RemoteTransition remoteTransition)365 public void applySystemTransaction(@NonNull WindowContainerTransaction wct, 366 @TaskFragmentTransitionType int transitionType, 367 @Nullable RemoteTransition remoteTransition) { 368 if (wct.isEmpty()) { 369 return; 370 } 371 wct.setTaskFragmentOrganizer(mInterface); 372 try { 373 getController().applyTransaction( 374 wct, transitionType, remoteTransition != null /* shouldApplyIndependently */, 375 remoteTransition); 376 } catch (RemoteException e) { 377 throw e.rethrowFromSystemServer(); 378 } 379 } 380 381 /** 382 * Called when the transaction is ready so that the organizer can update the TaskFragments based 383 * on the changes in transaction. 384 */ onTransactionReady(@onNull TaskFragmentTransaction transaction)385 public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) { 386 // Notify the server to finish the transaction. 387 onTransactionHandled(transaction.getTransactionToken(), new WindowContainerTransaction(), 388 TASK_FRAGMENT_TRANSIT_NONE, false /* shouldApplyIndependently */); 389 } 390 391 private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() { 392 @Override 393 public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) { 394 mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction)); 395 } 396 }; 397 398 private final TaskFragmentOrganizerToken mToken = new TaskFragmentOrganizerToken(mInterface); 399 400 @NonNull getOrganizerToken()401 public TaskFragmentOrganizerToken getOrganizerToken() { 402 return mToken; 403 } 404 getController()405 private ITaskFragmentOrganizerController getController() { 406 try { 407 return getWindowOrganizerController().getTaskFragmentOrganizerController(); 408 } catch (RemoteException e) { 409 return null; 410 } 411 } 412 413 /** 414 * Checks if an activity is organized by a {@link android.window.TaskFragmentOrganizer} and 415 * only occupies a portion of Task bounds. 416 * 417 * @see ActivityWindowInfo for additional window info. 418 * @hide 419 */ isActivityEmbedded(@onNull Activity activity)420 public static boolean isActivityEmbedded(@NonNull Activity activity) { 421 Objects.requireNonNull(activity); 422 final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity); 423 return activityWindowInfo != null && activityWindowInfo.isEmbedded(); 424 } 425 } 426