Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75d1558c authored by wilsonshih's avatar wilsonshih
Browse files

Extract StartingSurface interface on WMShellBaseModule(7/N)

Decouple the dependency for the rest CLs.

Bug: 73289295
Bug: 131311659
Test: atest WindowOrganizerTests StartingSurfaceDrawerTests
SplashscreenTests

Change-Id: Ifd323714184fad285b4d65f8c86c9b8feafc2374
parent 8632838c
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -34,6 +34,31 @@ import android.view.WindowManager;
 */
@TestApi
public final class StartingWindowInfo implements Parcelable {
    /**
     * Prefer nothing or not care the type of starting window.
     * @hide
     */
    public static final int STARTING_WINDOW_TYPE_NONE = 0;
    /**
     * Prefer splash screen starting window.
     * @hide
     */
    public static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 1;
    /**
     * Prefer snapshot starting window.
     * @hide
     */
    public static final int STARTING_WINDOW_TYPE_SNAPSHOT = 2;
    /**
     * @hide
     */
    @IntDef(flag = true, prefix = "STARTING_WINDOW_TYPE_", value = {
            STARTING_WINDOW_TYPE_NONE,
            STARTING_WINDOW_TYPE_SPLASH_SCREEN,
            STARTING_WINDOW_TYPE_SNAPSHOT
    })
    public @interface StartingWindowType {}

    /**
     * The {@link TaskInfo} from this task.
     *  @hide
+7 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.Transitions;

import java.util.Optional;
@@ -44,6 +45,7 @@ public class ShellInitImpl {
    private final FullscreenTaskListener mFullscreenTaskListener;
    private final ShellExecutor mMainExecutor;
    private final Transitions mTransitions;
    private final Optional<StartingSurface> mStartingSurfaceOptional;

    private final InitImpl mImpl = new InitImpl();

@@ -53,6 +55,7 @@ public class ShellInitImpl {
            Optional<LegacySplitScreenController> legacySplitScreenOptional,
            Optional<SplitScreenController> splitScreenOptional,
            Optional<AppPairsController> appPairsOptional,
            Optional<StartingSurface> startingSurfaceOptional,
            FullscreenTaskListener fullscreenTaskListener,
            Transitions transitions,
            ShellExecutor mainExecutor) {
@@ -62,6 +65,7 @@ public class ShellInitImpl {
                legacySplitScreenOptional,
                splitScreenOptional,
                appPairsOptional,
                startingSurfaceOptional,
                fullscreenTaskListener,
                transitions,
                mainExecutor).mImpl;
@@ -73,6 +77,7 @@ public class ShellInitImpl {
            Optional<LegacySplitScreenController> legacySplitScreenOptional,
            Optional<SplitScreenController> splitScreenOptional,
            Optional<AppPairsController> appPairsOptional,
            Optional<StartingSurface> startingSurfaceOptional,
            FullscreenTaskListener fullscreenTaskListener,
            Transitions transitions,
            ShellExecutor mainExecutor) {
@@ -85,6 +90,7 @@ public class ShellInitImpl {
        mFullscreenTaskListener = fullscreenTaskListener;
        mTransitions = transitions;
        mMainExecutor = mainExecutor;
        mStartingSurfaceOptional = startingSurfaceOptional;
    }

    private void init() {
@@ -93,6 +99,7 @@ public class ShellInitImpl {

        mShellTaskOrganizer.addListenerForType(
                mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
        mStartingSurfaceOptional.ifPresent(mShellTaskOrganizer::initStartingSurface);
        // Register the shell organizer
        mShellTaskOrganizer.registerOrganizer();

+23 −12
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -110,7 +110,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
    private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>();

    private final Object mLock = new Object();
    private final StartingSurfaceDrawer mStartingSurfaceDrawer;
    private StartingSurface mStartingSurface;

    /**
     * In charge of showing size compat UI. Can be {@code null} if device doesn't support size
@@ -120,23 +120,19 @@ public class ShellTaskOrganizer extends TaskOrganizer {
    private final SizeCompatUIController mSizeCompatUI;

    public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
        this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
                new StartingSurfaceDrawer(context, mainExecutor));
        this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
    }

    public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
            SizeCompatUIController sizeCompatUI) {
        this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
                new StartingSurfaceDrawer(context, mainExecutor));
        this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
    }

    @VisibleForTesting
    ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
            Context context, @Nullable SizeCompatUIController sizeCompatUI,
            StartingSurfaceDrawer startingSurfaceDrawer) {
            Context context, @Nullable SizeCompatUIController sizeCompatUI) {
        super(taskOrganizerController, mainExecutor);
        mSizeCompatUI = sizeCompatUI;
        mStartingSurfaceDrawer = startingSurfaceDrawer;
    }

    @Override
@@ -162,6 +158,15 @@ public class ShellTaskOrganizer extends TaskOrganizer {
        super.createRootTask(displayId, windowingMode, cookie);
    }

    /**
     * @hide
     */
    public void initStartingSurface(StartingSurface startingSurface) {
        synchronized (mLock) {
            mStartingSurface = startingSurface;
        }
    }

    /**
     * Adds a listener for a specific task id.
     */
@@ -254,17 +259,23 @@ public class ShellTaskOrganizer extends TaskOrganizer {

    @Override
    public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
        mStartingSurfaceDrawer.addStartingWindow(info, appToken);
        if (mStartingSurface != null) {
            mStartingSurface.addStartingWindow(info, appToken);
        }
    }

    @Override
    public void removeStartingWindow(int taskId) {
        mStartingSurfaceDrawer.removeStartingWindow(taskId);
        if (mStartingSurface != null) {
            mStartingSurface.removeStartingWindow(taskId);
        }
    }

    @Override
    public void copySplashScreenView(int taskId) {
        mStartingSurfaceDrawer.copySplashScreenView(taskId);
        if (mStartingSurface != null) {
            mStartingSurface.copySplashScreenView(taskId);
        }
    }

    @Override
+39 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.wm.shell.startingsurface;

import android.os.IBinder;
import android.window.StartingWindowInfo;

/**
 * Interface to engage starting window feature.
 */
public interface StartingSurface {
    /**
     * Called when a task need a starting window.
     */
    void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken);
    /**
     * Called when the content of a task is ready to show, starting window can be removed.
     */
    void removeStartingWindow(int taskId);
    /**
     * Called when the Task wants to copy the splash screen.
     * @param taskId
     */
    void copySplashScreenView(int taskId);
}
+7 −117
Original line number Diff line number Diff line
@@ -16,15 +16,9 @@

package com.android.wm.shell.startingsurface;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.res.Configuration.EMPTY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;

import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
@@ -37,7 +31,6 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -46,7 +39,6 @@ import android.view.WindowManager;
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.StartingWindowInfo;
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;

import com.android.internal.R;
@@ -56,19 +48,13 @@ import com.android.wm.shell.common.ShellExecutor;
import java.util.function.Consumer;

/**
 * Implementation to draw the starting window to an application, and remove the starting window
 * until the application displays its own window.
 *
 * When receive {@link TaskOrganizer#addStartingWindow} callback, use this class to create a
 * starting window and attached to the Task, then when the Task want to remove the starting window,
 * the TaskOrganizer will receive {@link TaskOrganizer#removeStartingWindow} callback then use this
 * class to remove the starting window of the Task.
 * A class which able to draw splash screen or snapshot as the starting window for a task.
 * @hide
 */
public class StartingSurfaceDrawer {
    static final String TAG = StartingSurfaceDrawer.class.getSimpleName();
    static final boolean DEBUG_SPLASH_SCREEN = false;
    static final boolean DEBUG_TASK_SNAPSHOT = false;
    static final boolean DEBUG_SPLASH_SCREEN = StartingWindowController.DEBUG_SPLASH_SCREEN;
    static final boolean DEBUG_TASK_SNAPSHOT = StartingWindowController.DEBUG_TASK_SNAPSHOT;

    private final Context mContext;
    private final DisplayManager mDisplayManager;
@@ -107,106 +93,10 @@ public class StartingSurfaceDrawer {
        return context.createDisplayContext(targetDisplay);
    }

    private static class PreferredStartingTypeHelper {
        private static final int STARTING_TYPE_NO = 0;
        private static final int STARTING_TYPE_SPLASH_SCREEN = 1;
        private static final int STARTING_TYPE_SNAPSHOT = 2;

        TaskSnapshot mSnapshot;
        int mPreferredType;

        PreferredStartingTypeHelper(StartingWindowInfo taskInfo) {
            final int parameter = taskInfo.startingWindowTypeParameter;
            final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0;
            final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0;
            final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
            final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
            final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
            mPreferredType = preferredStartingWindowType(taskInfo, newTask, taskSwitch,
                    processRunning, allowTaskSnapshot, activityCreated);
        }

        // reference from ActivityRecord#getStartingWindowType
        private int preferredStartingWindowType(StartingWindowInfo windowInfo,
                boolean newTask, boolean taskSwitch, boolean processRunning,
                boolean allowTaskSnapshot, boolean activityCreated) {
            if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                Slog.d(TAG, "preferredStartingWindowType newTask " + newTask
                        + " taskSwitch " + taskSwitch
                        + " processRunning " + processRunning
                        + " allowTaskSnapshot " + allowTaskSnapshot
                        + " activityCreated " + activityCreated);
            }

            if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
                return STARTING_TYPE_SPLASH_SCREEN;
            } else if (taskSwitch && allowTaskSnapshot) {
                final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId);
                if (isSnapshotCompatible(windowInfo, snapshot)) {
                    return STARTING_TYPE_SNAPSHOT;
                }
                if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
                    return STARTING_TYPE_SPLASH_SCREEN;
                }
                return STARTING_TYPE_NO;
            } else {
                return STARTING_TYPE_NO;
            }
        }

        /**
         * Returns {@code true} if the task snapshot is compatible with this activity (at least the
         * rotation must be the same).
         */
        private boolean isSnapshotCompatible(StartingWindowInfo windowInfo, TaskSnapshot snapshot) {
            if (snapshot == null) {
                if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                    Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
                }
                return false;
            }

            final int taskRotation = windowInfo.taskInfo.configuration
                    .windowConfiguration.getRotation();
            final int snapshotRotation = snapshot.getRotation();
            if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation
                        + " snapshot " + snapshotRotation);
            }
            return taskRotation == snapshotRotation;
        }

        private TaskSnapshot getTaskSnapshot(int taskId) {
            if (mSnapshot != null) {
                return mSnapshot;
            }
            try {
                mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId,
                        false/* isLowResolution */);
            } catch (RemoteException e) {
                Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e);
                return null;
            }
            return mSnapshot;
        }
    }

    /**
     * Called when a task need a starting window.
     * Called when a task need a splash screen starting window.
     */
    public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
        final PreferredStartingTypeHelper helper =
                new PreferredStartingTypeHelper(windowInfo);
        if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SPLASH_SCREEN) {
            addSplashScreenStartingWindow(windowInfo, appToken);
        } else if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SNAPSHOT) {
            final TaskSnapshot snapshot = helper.mSnapshot;
            makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
        }
        // If prefer don't show, then don't show!
    }

    private void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
    public void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
        final RunningTaskInfo taskInfo = windowInfo.taskInfo;
        final ActivityInfo activityInfo = taskInfo.topActivityInfo;
        if (activityInfo == null) {
@@ -378,8 +268,8 @@ public class StartingSurfaceDrawer {
    /**
     * Called when a task need a snapshot starting window.
     */
    private void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo,
            IBinder appToken, TaskSnapshot snapshot) {
    void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken,
            TaskSnapshot snapshot) {
        final int taskId = startingWindowInfo.taskInfo.taskId;
        final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
                snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */);
Loading