Merge "Introduce StartingWindowTypeAlgorithm interface" into sc-dev

This commit is contained in:
Sergey Nikolaienkov
2021-04-26 14:42:27 +00:00
committed by Android (Google) Code Review
7 changed files with 228 additions and 106 deletions

View File

@@ -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);
});
}

View File

@@ -0,0 +1,30 @@
/*
* 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);
}

View File

@@ -0,0 +1,123 @@
/*
* 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;
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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;
}
}

View File

@@ -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();
};
}

View File

@@ -72,6 +72,7 @@ import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -381,8 +382,10 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static StartingWindowController provideStartingWindowController(Context context,
@ShellSplashscreenThread ShellExecutor splashScreenExecutor, TransactionPool pool) {
return new StartingWindowController(context, splashScreenExecutor, pool);
@ShellSplashscreenThread ShellExecutor splashScreenExecutor,
StartingWindowTypeAlgorithm startingWindowTypeAlgorithm, TransactionPool pool) {
return new StartingWindowController(context, splashScreenExecutor,
startingWindowTypeAlgorithm, pool);
}
//

View File

@@ -54,6 +54,8 @@ import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -229,4 +231,14 @@ public class WMShellModule {
menuController, pipSnapAlgorithm, pipTransitionController,
floatingContentCoordinator);
}
//
// Starting Windows (Splash Screen)
//
@WMSingleton
@Provides
static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
return new PhoneStartingWindowTypeAlgorithm();
}
}