/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.window; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import android.annotation.CallSuper; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.view.RemoteAnimationDefinition; import android.view.WindowManager; import com.android.window.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; /** * Interface for WindowManager to delegate control of {@code TaskFragment}. * @hide */ @TestApi public class TaskFragmentOrganizer extends WindowOrganizer { /** * Key to the {@link Throwable} in {@link TaskFragmentTransaction.Change#getErrorBundle()}. */ public static final String KEY_ERROR_CALLBACK_THROWABLE = "fragment_throwable"; /** * Key to the {@link TaskFragmentInfo} in * {@link TaskFragmentTransaction.Change#getErrorBundle()}. */ public static final String KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO = "task_fragment_info"; /** * Key to the {@link TaskFragmentOperation.OperationType} in * {@link TaskFragmentTransaction.Change#getErrorBundle()}. */ public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type"; /** * No change set. */ @WindowManager.TransitionType @TaskFragmentTransitionType public static final int TASK_FRAGMENT_TRANSIT_NONE = TRANSIT_NONE; /** * A window that didn't exist before has been created and made visible. */ @WindowManager.TransitionType @TaskFragmentTransitionType public static final int TASK_FRAGMENT_TRANSIT_OPEN = TRANSIT_OPEN; /** * A window that was visible no-longer exists (was finished or destroyed). */ @WindowManager.TransitionType @TaskFragmentTransitionType public static final int TASK_FRAGMENT_TRANSIT_CLOSE = TRANSIT_CLOSE; /** * A window is visible before and after but changes in some way (eg. it resizes or changes * windowing-mode). */ @WindowManager.TransitionType @TaskFragmentTransitionType public static final int TASK_FRAGMENT_TRANSIT_CHANGE = TRANSIT_CHANGE; /** * The task fragment drag resize transition used by activity embedding. * * This value is also used in Transitions.TRANSIT_TASK_FRAGMENT_DRAG_RESIZE and must not * conflict with other predefined transition types. * * @hide */ @WindowManager.TransitionType @TaskFragmentTransitionType public static final int TASK_FRAGMENT_TRANSIT_DRAG_RESIZE = TRANSIT_FIRST_CUSTOM + 17; /** * Introduced a sub set of {@link WindowManager.TransitionType} for the types that are used for * TaskFragment transition. * * Doing this instead of exposing {@link WindowManager.TransitionType} because we want to keep * the Shell transition API hidden until it comes fully stable. * @hide */ @IntDef(prefix = { "TASK_FRAGMENT_TRANSIT_" }, value = { TASK_FRAGMENT_TRANSIT_NONE, TASK_FRAGMENT_TRANSIT_OPEN, TASK_FRAGMENT_TRANSIT_CLOSE, TASK_FRAGMENT_TRANSIT_CHANGE, TASK_FRAGMENT_TRANSIT_DRAG_RESIZE, }) @Retention(RetentionPolicy.SOURCE) public @interface TaskFragmentTransitionType {} /** * Creates a {@link Bundle} with an exception, operation type and TaskFragmentInfo (if any) * that can be passed to {@link ITaskFragmentOrganizer#onTaskFragmentError}. * @hide */ public static @NonNull Bundle putErrorInfoInBundle(@NonNull Throwable exception, @Nullable TaskFragmentInfo info, @TaskFragmentOperation.OperationType int opType) { final Bundle errorBundle = new Bundle(); errorBundle.putSerializable(KEY_ERROR_CALLBACK_THROWABLE, exception); if (info != null) { errorBundle.putParcelable(KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, info); } errorBundle.putInt(KEY_ERROR_CALLBACK_OP_TYPE, opType); return errorBundle; } /** * Callbacks from WM Core are posted on this executor. */ private final Executor mExecutor; public TaskFragmentOrganizer(@NonNull Executor executor) { mExecutor = executor; } /** * Gets the executor to run callbacks on. */ @NonNull public Executor getExecutor() { return mExecutor; } /** * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments. */ @CallSuper public void registerOrganizer() { // TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed. try { getController().registerOrganizer(mInterface, false /* isSystemOrganizer */); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments. * * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer * will have additional system capabilities, including: (1) it will receive SurfaceControl for * the organized TaskFragment, and (2) it needs to update the * {@link android.view.SurfaceControl} following the window change accordingly. * * @hide */ @CallSuper @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true) @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG) public void registerOrganizer(boolean isSystemOrganizer) { try { getController().registerOrganizer(mInterface, isSystemOrganizer); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Unregisters a previously registered TaskFragmentOrganizer. */ @CallSuper public void unregisterOrganizer() { try { getController().unregisterOrganizer(mInterface); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Registers remote animations per transition type for the organizer. It will override the * animations if the transition only contains windows that belong to the organized * TaskFragments, and at least one of the transition window is embedded (not filling the Task). * @hide */ @CallSuper public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) { try { getController().registerRemoteAnimations(mInterface, definition); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Unregisters remote animations per transition type for the organizer. * @hide */ @CallSuper public void unregisterRemoteAnimations() { try { getController().unregisterRemoteAnimations(mInterface); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies the server that the organizer has finished handling the given transaction. The * server should apply the given {@link WindowContainerTransaction} for the necessary changes. * * @param transactionToken {@link TaskFragmentTransaction#getTransactionToken()} from * {@link #onTransactionReady(TaskFragmentTransaction)} * @param wct {@link WindowContainerTransaction} that the server should apply for * update of the transaction. * @param transitionType {@link TaskFragmentTransitionType} if it needs to start a * transition. * @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new * transition, which will be queued until the sync engine is * free if there is any other active sync. If {@code false}, * the {@code wct} will be directly applied to the active sync. * @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission * for permission enforcement. */ public void onTransactionHandled(@NonNull IBinder transactionToken, @NonNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) { wct.setTaskFragmentOrganizer(mInterface); try { getController().onTransactionHandled(transactionToken, wct, transitionType, shouldApplyIndependently); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Must use {@link #applyTransaction(WindowContainerTransaction, int, boolean)} instead. * @see #applyTransaction(WindowContainerTransaction, int, boolean) */ @Override public void applyTransaction(@NonNull WindowContainerTransaction wct) { throw new RuntimeException("Not allowed!"); } /** * Requests the server to apply the given {@link WindowContainerTransaction}. * * @param wct {@link WindowContainerTransaction} to apply. * @param transitionType {@link TaskFragmentTransitionType} if it needs to start a * transition. * @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new * transition, which will be queued until the sync engine is * free if there is any other active sync. If {@code false}, * the {@code wct} will be directly applied to the active sync. * @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission * for permission enforcement. */ public void applyTransaction(@NonNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) { if (wct.isEmpty()) { return; } wct.setTaskFragmentOrganizer(mInterface); try { getController().applyTransaction( wct, transitionType, shouldApplyIndependently, null /* remoteTransition */); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Applies a transaction with a {@link RemoteTransition}. Only a system organizer is allowed to * use {@link RemoteTransition}. See {@link TaskFragmentOrganizer#registerOrganizer(boolean)}. * * @hide */ @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG) public void applySystemTransaction(@NonNull WindowContainerTransaction wct, @TaskFragmentTransitionType int transitionType, @Nullable RemoteTransition remoteTransition) { if (wct.isEmpty()) { return; } wct.setTaskFragmentOrganizer(mInterface); try { getController().applyTransaction( wct, transitionType, remoteTransition != null /* shouldApplyIndependently */, remoteTransition); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Called when the transaction is ready so that the organizer can update the TaskFragments based * on the changes in transaction. */ public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) { // Notify the server to finish the transaction. onTransactionHandled(transaction.getTransactionToken(), new WindowContainerTransaction(), TASK_FRAGMENT_TRANSIT_NONE, false /* shouldApplyIndependently */); } private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() { @Override public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) { mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction)); } }; private final TaskFragmentOrganizerToken mToken = new TaskFragmentOrganizerToken(mInterface); @NonNull public TaskFragmentOrganizerToken getOrganizerToken() { return mToken; } private ITaskFragmentOrganizerController getController() { try { return getWindowOrganizerController().getTaskFragmentOrganizerController(); } catch (RemoteException e) { return null; } } /** * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and * only occupies a portion of Task bounds. * @hide */ public boolean isActivityEmbedded(@NonNull IBinder activityToken) { try { return getController().isActivityEmbedded(activityToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } }