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

Commit 5a3ebc03 authored by Sergey Nikolaienkov's avatar Sergey Nikolaienkov
Browse files

Introduce StartingWindowTypeAlgorithm interface

Extracting StartingWindowTypeAlgorithm static nested class within
StartingWindowController into StartingWidnowTypeAlgorithm interface and
PhoneStartingWindowTypeAlgorithm class that implemets the interface.

Adding an alternative implementation - TvStartingWindowTypeAlgorithm, for
use on TV. The TV implementation always returns StartingWindowTypeAlgorithm.

Setting up TvWMShellModule and WMShellModule to provide the TV and the
Phone implementations respectively.

Also removing a redundant StartingWindowController constructor.

Bug: 182759603
Test: make SystemUI
Change-Id: I10bab125320a9cdc9e3ddaedad1c74930b681fa6
parent 814ecd72
Loading
Loading
Loading
Loading
+11 −103
Original line number Diff line number Diff line
@@ -15,18 +15,10 @@
 */
package com.android.wm.shell.startingsurface;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
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_SAME_PACKAGE;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;

import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;

@@ -68,27 +60,24 @@ import java.util.function.BiConsumer;
 */
public class StartingWindowController implements RemoteCallable<StartingWindowController> {
    private static final String TAG = StartingWindowController.class.getSimpleName();

    // TODO b/183150443 Keep this flag open for a while, several things might need to adjust.
    static final boolean DEBUG_SPLASH_SCREEN = true;
    static final boolean DEBUG_TASK_SNAPSHOT = false;
    public static final boolean DEBUG_SPLASH_SCREEN = true;
    public static final boolean DEBUG_TASK_SNAPSHOT = false;

    private final StartingSurfaceDrawer mStartingSurfaceDrawer;
    private final StartingTypeChecker mStartingTypeChecker = new StartingTypeChecker();
    private final StartingWindowTypeAlgorithm mStartingWindowTypeAlgorithm;

    private BiConsumer<Integer, Integer> mTaskLaunchingCallback;
    private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl();
    private final Context mContext;
    private final ShellExecutor mSplashScreenExecutor;

    // For Car Launcher
    public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) {
        this(context, splashScreenExecutor, new TransactionPool());
    }

    public StartingWindowController(Context context, ShellExecutor splashScreenExecutor,
            TransactionPool pool) {
            StartingWindowTypeAlgorithm startingWindowTypeAlgorithm, TransactionPool pool) {
        mContext = context;
        mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool);
        mStartingWindowTypeAlgorithm = startingWindowTypeAlgorithm;
        mSplashScreenExecutor = splashScreenExecutor;
    }

@@ -109,90 +98,6 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
        return mSplashScreenExecutor;
    }

    private static class StartingTypeChecker {

        private @StartingWindowInfo.StartingWindowType int
                estimateStartingWindowType(StartingWindowInfo windowInfo) {
            final int parameter = windowInfo.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;
            final boolean samePackage = (parameter & TYPE_PARAMETER_SAME_PACKAGE) != 0;
            return estimateStartingWindowType(windowInfo, newTask, taskSwitch,
                    processRunning, allowTaskSnapshot, activityCreated, samePackage);
        }

        // reference from ActivityRecord#getStartingWindowType
        private int estimateStartingWindowType(StartingWindowInfo windowInfo,
                boolean newTask, boolean taskSwitch, boolean processRunning,
                boolean allowTaskSnapshot, boolean activityCreated, boolean samePackage) {
            if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                Slog.d(TAG, "preferredStartingWindowType newTask " + newTask
                        + " taskSwitch " + taskSwitch
                        + " processRunning " + processRunning
                        + " allowTaskSnapshot " + allowTaskSnapshot
                        + " activityCreated " + activityCreated
                        + " samePackage " + samePackage);
            }
            if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
                if (!processRunning) {
                    return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
                }
                if (newTask) {
                    if (samePackage) {
                        return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
                    } else {
                        return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
                    }
                }
                if (taskSwitch && !activityCreated) {
                    return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
                }
            }
            if (taskSwitch && allowTaskSnapshot) {
                final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
                if (isSnapshotCompatible(windowInfo, snapshot)) {
                    return STARTING_WINDOW_TYPE_SNAPSHOT;
                }
                if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
                    return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
                }
            }
            return STARTING_WINDOW_TYPE_NONE;
        }

        /**
         * 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;
            }
            if (!snapshot.getTopActivityComponent().equals(windowInfo.taskInfo.topActivity)) {
                if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                    Slog.d(TAG, "isSnapshotCompatible obsoleted snapshot "
                            + windowInfo.taskInfo.topActivity);
                }
                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;
        }
    }

    /*
     * Registers the starting window listener.
     *
@@ -212,7 +117,8 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
    public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
        mSplashScreenExecutor.execute(() -> {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow");
            final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(

            final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType(
                    windowInfo);
            final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
            if (mTaskLaunchingCallback != null && shouldSendToListener(suggestionType)) {
@@ -228,8 +134,10 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
                final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
                mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,
                        snapshot);
            } else /* suggestionType == STARTING_WINDOW_TYPE_NONE */ {
                // Don't add a staring window.
            }
            // If prefer don't show, then don't show!

            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        });
    }
+30 −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.window.StartingWindowInfo;

/**
 * Used by {@link StartingWindowController} for determining the type of a new starting window.
 */
public interface StartingWindowTypeAlgorithm {
    /**
     * @return suggested type for the given window.
     */
    @StartingWindowInfo.StartingWindowType
    int getSuggestedWindowType(StartingWindowInfo windowInfo);
}
+123 −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.phone;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
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_SAME_PACKAGE;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;

import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_SPLASH_SCREEN;
import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_TASK_SNAPSHOT;

import android.util.Slog;
import android.window.StartingWindowInfo;
import android.window.TaskSnapshot;

import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;

/**
 * Algorithm for determining the type of a new starting window on handheld devices.
 * At the moment also used on Android Auto.
 */
public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm {
    private static final String TAG = PhoneStartingWindowTypeAlgorithm.class.getSimpleName();

    @Override
    public int getSuggestedWindowType(StartingWindowInfo windowInfo) {
        final int parameter = windowInfo.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;
        final boolean samePackage = (parameter & TYPE_PARAMETER_SAME_PACKAGE) != 0;
        final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME;

        if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
            Slog.d(TAG, "preferredStartingWindowType newTask " + newTask
                    + " taskSwitch " + taskSwitch
                    + " processRunning " + processRunning
                    + " allowTaskSnapshot " + allowTaskSnapshot
                    + " activityCreated " + activityCreated
                    + " samePackage " + samePackage
                    + " topIsHome " + topIsHome);
        }
        if (!topIsHome) {
            if (!processRunning) {
                return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
            }
            if (newTask) {
                if (samePackage) {
                    return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
                } else {
                    return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
                }
            }
            if (taskSwitch && !activityCreated) {
                return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
            }
        }
        if (taskSwitch && allowTaskSnapshot) {
            if (isSnapshotCompatible(windowInfo)) {
                return STARTING_WINDOW_TYPE_SNAPSHOT;
            }
            if (!topIsHome) {
                return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
            }
        }
        return STARTING_WINDOW_TYPE_NONE;
    }


    /**
     * 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) {
        final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
        if (snapshot == null) {
            if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
            }
            return false;
        }
        if (!snapshot.getTopActivityComponent().equals(windowInfo.taskInfo.topActivity)) {
            if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                Slog.d(TAG, "isSnapshotCompatible obsoleted snapshot "
                        + windowInfo.taskInfo.topActivity);
            }
            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;
    }
}
+35 −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.tv;

import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;

import android.window.StartingWindowInfo;

import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;

/**
 * Algorithm for determining the type of a new starting window on Android TV.
 * For now we always show empty splash screens on Android TV.
 */
public class TvStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm {
    @Override
    public int getSuggestedWindowType(StartingWindowInfo windowInfo) {
        // For now we want to always show empty splash screens on TV.
        return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
    }
}
+12 −1
Original line number Diff line number Diff line
@@ -32,8 +32,9 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions;

import dagger.Module;
@@ -80,4 +81,14 @@ public class TvWMShellModule {
                displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
                taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
    }

    //
    // Starting Windows (Splash Screen)
    //

    @WMSingleton
    @Provides
    static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
        return new TvStartingWindowTypeAlgorithm();
    };
}
Loading