/* * Copyright (C) 2020 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 android.annotation.BinderThread; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.app.ActivityManager; import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import java.util.List; import java.util.concurrent.Executor; /** * Interface for ActivityTaskManager/WindowManager to delegate control of tasks. * @hide */ @TestApi public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizerController mTaskOrganizerController; // Callbacks WM Core are posted on this executor if it isn't null, otherwise direct calls are // made on the incoming binder call. private final Executor mExecutor; public TaskOrganizer() { this(null /*taskOrganizerController*/, null /*executor*/); } /** @hide */ @VisibleForTesting public TaskOrganizer(ITaskOrganizerController taskOrganizerController, Executor executor) { mExecutor = executor != null ? executor : command -> command.run(); mTaskOrganizerController = taskOrganizerController != null ? taskOrganizerController : getController(); } /** * Register a TaskOrganizer to manage tasks as they enter a supported windowing mode. * * @return a list of the tasks that should be managed by the organizer, not including tasks * created via {@link #createRootTask}. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @CallSuper @NonNull public List registerOrganizer() { try { return mTaskOrganizerController.registerTaskOrganizer(mInterface).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Unregisters a previously registered task organizer. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @CallSuper public void unregisterOrganizer() { try { mTaskOrganizerController.unregisterTaskOrganizer(mInterface); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Called when a task with the registered windowing mode can be controlled by this task * organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer * to show this task. */ @BinderThread public void onTaskAppeared(@NonNull ActivityManager.RunningTaskInfo taskInfo, @NonNull SurfaceControl leash) {} @BinderThread public void onTaskVanished(@NonNull ActivityManager.RunningTaskInfo taskInfo) {} @BinderThread public void onTaskInfoChanged(@NonNull ActivityManager.RunningTaskInfo taskInfo) {} @BinderThread public void onBackPressedOnTaskRoot(@NonNull ActivityManager.RunningTaskInfo taskInfo) {} /** * Creates a persistent root task in WM for a particular windowing-mode. * @param displayId The display to create the root task on. * @param windowingMode Windowing mode to put the root task in. * @param launchCookie Launch cookie to associate with the task so that is can be identified * when the {@link ITaskOrganizer#onTaskAppeared} callback is called. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @Nullable public void createRootTask(int displayId, int windowingMode, @Nullable IBinder launchCookie) { try { mTaskOrganizerController.createRootTask(displayId, windowingMode, launchCookie); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Deletes a persistent root task in WM */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean deleteRootTask(@NonNull WindowContainerToken task) { try { return mTaskOrganizerController.deleteRootTask(task); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Gets direct child tasks (ordered from top-to-bottom) */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @Nullable public List getChildTasks( @NonNull WindowContainerToken parent, @NonNull int[] activityTypes) { try { return mTaskOrganizerController.getChildTasks(parent, activityTypes); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Gets all root tasks on a display (ordered from top-to-bottom) */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @Nullable public List getRootTasks( int displayId, @NonNull int[] activityTypes) { try { return mTaskOrganizerController.getRootTasks(displayId, activityTypes); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** Get the root task which contains the current ime target */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @Nullable public WindowContainerToken getImeTarget(int display) { try { return mTaskOrganizerController.getImeTarget(display); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Set's the root task to launch new tasks into on a display. {@code null} means no launch * root and thus new tasks just end up directly on the display. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setLaunchRoot(int displayId, @NonNull WindowContainerToken root) { try { mTaskOrganizerController.setLaunchRoot(displayId, root); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Requests that the given task organizer is notified when back is pressed on the root activity * of one of its controlled tasks. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull WindowContainerToken task, boolean interceptBackPressed) { try { mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(task, interceptBackPressed); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash)); } @Override public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { mExecutor.execute(() -> TaskOrganizer.this.onTaskVanished(taskInfo)); } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { mExecutor.execute(() -> TaskOrganizer.this.onTaskInfoChanged(info)); } @Override public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo info) { mExecutor.execute(() -> TaskOrganizer.this.onBackPressedOnTaskRoot(info)); } }; private ITaskOrganizerController getController() { try { return getWindowOrganizerController().getTaskOrganizerController(); } catch (RemoteException e) { return null; } } }