blob: 6e9d910d7e036b8293e412cad636e2263abe2c83 [file] [log] [blame]
/*
* 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.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.WindowManagerState.STATE_STOPPED;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.Task;
import android.server.wm.WindowManagerState.TaskFragment;
import android.view.SurfaceControl;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import org.junit.Test;
/**
* Tests that verify the behavior of {@link TaskFragmentOrganizer}.
*
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:TaskFragmentOrganizerTest
*/
@Presubmit
public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase {
private final ComponentName mLaunchingActivity = new ComponentName(mContext,
WindowMetricsActivityTests.MetricsActivity.class);
/**
* Verifies the behavior of
* {@link WindowContainerTransaction#createTaskFragment(TaskFragmentCreationParams)} to create
* TaskFragment.
*/
@Test
public void testCreateTaskFragment() {
assumeTrue("MultiWindow is not supported.", supportsMultiWindow());
mWmState.computeState(mOwnerActivityName);
Task parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId());
final int originalTaskFragCount = parentTask.getTaskFragments().size();
final IBinder taskFragToken = new Binder();
final Rect bounds = new Rect(0, 0, 1000, 1000);
final int windowingMode = WINDOWING_MODE_MULTI_WINDOW;
final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
mTaskFragmentOrganizer.getOrganizerToken(), taskFragToken, mOwnerToken)
.setInitialBounds(bounds)
.setWindowingMode(windowingMode)
.build();
final WindowContainerTransaction wct = new WindowContainerTransaction()
.createTaskFragment(params);
mTaskFragmentOrganizer.applyTransaction(wct);
mTaskFragmentOrganizer.waitForTaskFragmentCreated();
final TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken);
assertEmptyTaskFragment(info, taskFragToken);
assertThat(info.getConfiguration().windowConfiguration.getBounds()).isEqualTo(bounds);
assertThat(info.getWindowingMode()).isEqualTo(windowingMode);
mWmState.computeState(mOwnerActivityName);
parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId());
final int curTaskFragCount = parentTask.getTaskFragments().size();
assertWithMessage("There must be a TaskFragment created under Task#"
+ mOwnerTaskId).that(curTaskFragCount - originalTaskFragCount)
.isEqualTo(1);
}
/**
* Verifies the behavior of
* {@link WindowContainerTransaction#reparentActivityToTaskFragment(IBinder, IBinder)} to
* reparent {@link Activity} to TaskFragment.
*/
@Test
public void testReparentActivity() {
mWmState.computeState(mOwnerActivityName);
final TaskFragmentCreationParams params = generateTaskFragCreationParams();
final IBinder taskFragToken = params.getFragmentToken();
final WindowContainerTransaction wct = new WindowContainerTransaction()
.createTaskFragment(params)
.reparentActivityToTaskFragment(taskFragToken, mOwnerToken);
mTaskFragmentOrganizer.applyTransaction(wct);
mTaskFragmentOrganizer.waitForTaskFragmentCreated();
assertNotEmptyTaskFragment(mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken),
taskFragToken, mOwnerToken);
mWmState.waitForActivityState(mOwnerActivityName, STATE_RESUMED);
final Task parentTask = mWmState.getTaskByActivity(mOwnerActivityName);
final TaskFragment taskFragment = mWmState.getTaskFragmentByActivity(mOwnerActivityName);
// Assert window hierarchy must be as follows
// - owner Activity's Task (parentTask)
// - taskFragment
// - owner Activity
assertWindowHierarchy(parentTask, taskFragment, mWmState.getActivity(mOwnerActivityName));
}
/**
* Verifies the behavior of
* {@link WindowContainerTransaction#startActivityInTaskFragment(IBinder, IBinder, Intent,
* Bundle)} to start Activity in TaskFragment without creating new Task.
*/
@Test
public void testStartActivityInTaskFragment_reuseTask() {
final TaskFragmentCreationParams params = generateTaskFragCreationParams();
final IBinder taskFragToken = params.getFragmentToken();
final WindowContainerTransaction wct = new WindowContainerTransaction()
.createTaskFragment(params)
.startActivityInTaskFragment(taskFragToken, mOwnerToken,
new Intent().setComponent(mLaunchingActivity), null /* activityOptions */);
mTaskFragmentOrganizer.applyTransaction(wct);
mTaskFragmentOrganizer.waitForTaskFragmentCreated();
TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken);
assertNotEmptyTaskFragment(info, taskFragToken);
mWmState.waitForActivityState(mLaunchingActivity, STATE_RESUMED);
Task parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId());
TaskFragment taskFragment = mWmState.getTaskFragmentByActivity(mLaunchingActivity);
// Assert window hierarchy must be as follows
// - owner Activity's Task (parentTask)
// - taskFragment
// - LAUNCHING_ACTIVITY
// - owner Activity
assertWindowHierarchy(parentTask, taskFragment, mWmState.getActivity(mLaunchingActivity));
assertWindowHierarchy(parentTask, mWmState.getActivity(mOwnerActivityName));
assertWithMessage("The owner Activity's Task must be reused as"
+ " the launching Activity's Task.").that(parentTask)
.isEqualTo(mWmState.getTaskByActivity(mLaunchingActivity));
}
/**
* Verifies the behavior of
* {@link WindowContainerTransaction#deleteTaskFragment(WindowContainerToken)} to remove
* the organized TaskFragment.
*/
@Test
public void testDeleteTaskFragment() {
final TaskFragmentInfo taskFragmentInfo = createTaskFragment(null);
final IBinder taskFragToken = taskFragmentInfo.getFragmentToken();
assertEmptyTaskFragment(taskFragmentInfo, taskFragmentInfo.getFragmentToken());
mWmState.computeState(mOwnerActivityName);
final int originalTaskFragCount = mWmState.getRootTask(mOwnerTaskId).getTaskFragments()
.size();
WindowContainerTransaction wct = new WindowContainerTransaction()
.deleteTaskFragment(taskFragmentInfo.getToken());
mTaskFragmentOrganizer.applyTransaction(wct);
mTaskFragmentOrganizer.waitForTaskFragmentRemoved();
assertEmptyTaskFragment(mTaskFragmentOrganizer.getRemovedTaskFragmentInfo(taskFragToken),
taskFragToken);
mWmState.computeState(mOwnerActivityName);
final int currTaskFragCount = mWmState.getRootTask(mOwnerTaskId).getTaskFragments().size();
assertWithMessage("TaskFragment with token " + taskFragToken + " must be"
+ " removed.").that(originalTaskFragCount - currTaskFragCount).isEqualTo(1);
}
/**
* Verifies the visibility of an activity behind a TaskFragment that has the same
* bounds of the host Task.
*/
@Test
public void testActivityVisibilityBehindTaskFragment() {
// Start an activity and reparent it to a TaskFragment.
final Activity embeddedActivity =
startActivity(WindowMetricsActivityTests.MetricsActivity.class);
final IBinder embeddedActivityToken = getActivityToken(embeddedActivity);
final TaskFragmentCreationParams params = generateTaskFragCreationParams();
final IBinder taskFragToken = params.getFragmentToken();
final WindowContainerTransaction wct = new WindowContainerTransaction()
.createTaskFragment(params)
.reparentActivityToTaskFragment(taskFragToken, embeddedActivityToken);
mTaskFragmentOrganizer.applyTransaction(wct);
mTaskFragmentOrganizer.waitForTaskFragmentCreated();
// The activity below must be occluded and stopped.
waitAndAssertActivityState(mOwnerActivityName, STATE_STOPPED,
"Activity must be stopped");
// Finishing the top activity and remain the TaskFragment on top. The next top activity
// must be resumed.
embeddedActivity.finish();
waitAndAssertResumedActivity(mOwnerActivityName, "Activity must be resumed");
}
/**
* Verifies that config changes with {@link WindowContainerTransaction.Change#getChangeMask()}
* are disallowed for embedded TaskFragments.
*/
@Test
public void testTaskFragmentConfigChange_disallowChangeMaskChanges() {
final TaskFragmentInfo taskFragmentInfo = createTaskFragment(mLaunchingActivity);
final WindowContainerToken token = taskFragmentInfo.getToken();
final WindowContainerTransaction wct0 = new WindowContainerTransaction()
.scheduleFinishEnterPip(token, new Rect(0, 0, 100, 100));
assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct0));
final WindowContainerTransaction wct1 = new WindowContainerTransaction()
.setBoundsChangeTransaction(token, new SurfaceControl.Transaction());
assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct1));
final WindowContainerTransaction wct3 = new WindowContainerTransaction()
.setFocusable(token, false /* focusable */);
assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct3));
final WindowContainerTransaction wct4 = new WindowContainerTransaction()
.setHidden(token, false /* hidden */);
assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct4));
}
}