Merge "Allow non-resizable apps in split-screen (6/n)" into sc-dev am: 0838b40ec8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13438138

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I84230eeb2e6952081de0f74e7efba70305818e12
This commit is contained in:
Chris Li
2021-02-01 18:03:15 +00:00
committed by Automerger Merge Worker
6 changed files with 156 additions and 2 deletions

View File

@@ -36,7 +36,6 @@ import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
@@ -179,6 +178,19 @@ public class TaskInfo {
@Nullable
public ActivityInfo topActivityInfo;
/**
* The top activity in this task.
* @hide
*/
@Nullable
public IBinder topActivityToken;
/**
* Whether the direct top activity is in size compat mode on foreground.
* @hide
*/
public boolean topActivityInSizeCompat;
/**
* Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity
* supports), this is what the system actually uses for resizability based on other policy and
@@ -356,6 +368,8 @@ public class TaskInfo {
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
topActivityToken = source.readStrongBinder();
topActivityInSizeCompat = source.readBoolean();
}
/**
@@ -391,6 +405,8 @@ public class TaskInfo {
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
dest.writeStrongBinder(topActivityToken);
dest.writeBoolean(topActivityInSizeCompat);
}
@Override
@@ -415,6 +431,8 @@ public class TaskInfo {
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ " topActivityToken=" + topActivityToken
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ "}";
}
}

View File

@@ -2180,6 +2180,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/** @return Root task of this activity, null if there is no task. */
@Nullable
Task getRootTask() {
return task != null ? task.getRootTask() : null;
}
@@ -2188,6 +2189,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return task != null ? task.getRootTaskId() : INVALID_TASK_ID;
}
/** @return the first organized parent task. */
@Nullable
Task getOrganizedTask() {
return task != null ? task.getOrganizedTask() : null;
}
@Override
@Nullable
TaskDisplayArea getDisplayArea() {

View File

@@ -5485,14 +5485,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Checks whether the given activity is in size compatibility mode and notifies the change. */
void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
final Task organizedTask = r.getOrganizedTask();
if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
|| organizedTask == null) {
// The callback is only interested in the foreground changes of fullscreen activity.
return;
}
if (!r.inSizeCompatMode()) {
if (mLastCompatModeActivity != null) {
// TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
mAtmService.getTaskChangeNotificationController()
.notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
// This will do nothing until SizeCompatModeActivityController is moved to shell
organizedTask.onSizeCompatActivityChanged();
}
mLastCompatModeActivity = null;
return;
@@ -5501,8 +5506,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return;
}
mLastCompatModeActivity = r;
// TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
mAtmService.getTaskChangeNotificationController()
.notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
// This will do nothing until SizeCompatModeActivityController is moved to shell
organizedTask.onSizeCompatActivityChanged();
}
boolean isUidPresent(int uid) {

View File

@@ -3082,6 +3082,20 @@ class Task extends WindowContainer<WindowContainer> {
return parentTask == null ? this : parentTask.getRootTask();
}
/** @return the first organized task. */
@Nullable
Task getOrganizedTask() {
if (isOrganized()) {
return this;
}
final WindowContainer parent = getParent();
if (parent == null) {
return null;
}
final Task parentTask = parent.asTask();
return parentTask == null ? null : parentTask.getOrganizedTask();
}
// TODO(task-merge): Figure out what's the right thing to do for places that used it.
boolean isRootTask() {
return getRootTask() == this;
@@ -4172,6 +4186,14 @@ class Task extends WindowContainer<WindowContainer> {
info.topActivityInfo = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.info
: null;
info.topActivityToken = mReuseActivitiesReport.top != null
? mReuseActivitiesReport.top.appToken
: null;
// Whether the direct top activity is in size compat mode on foreground.
info.topActivityInSizeCompat = mReuseActivitiesReport.top != null
&& mReuseActivitiesReport.top.getOrganizedTask() == this
&& mReuseActivitiesReport.top.inSizeCompatMode()
&& mReuseActivitiesReport.top.isState(RESUMED);
info.launchCookies.clear();
info.addLaunchCookie(mLaunchCookie);
forAllActivities(r -> {
@@ -5221,6 +5243,12 @@ class Task extends WindowContainer<WindowContainer> {
}
}
/** Called when the top activity in the Root Task enters or exits size compat mode. */
void onSizeCompatActivityChanged() {
// Trigger TaskInfoChanged to update the size compat restart button.
dispatchTaskInfoChangedIfNeeded(true /* force */);
}
/**
* See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this
* transaction will be consumed by the next BASE_APPLICATION window within our hierarchy

View File

@@ -480,10 +480,13 @@ public class SizeCompatTests extends WindowTestsBase {
/**
* Ensures that {@link TaskStackListener} can receive callback about the activity in size
* compatibility mode.
*
* TODO(b/178327644) Remove after update DC#handleActivitySizeCompatModeIfNeeded
*/
@Test
public void testHandleActivitySizeCompatMode() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
ActivityRecord activity = mActivity;
activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -520,6 +523,46 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(null, compatTokens.get(0));
}
/**
* Ensures that {@link TaskOrganizerController} can receive callback about the activity in size
* compatibility mode.
*/
@Test
public void testHandleActivitySizeCompatModeChanged() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
ActivityRecord activity = mActivity;
activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
// Resize the display so that the activity exercises size-compat mode.
resizeDisplay(mTask.mDisplayContent, 1000, 2500);
// Expect the exact token when the activity is in size compatibility mode.
verify(mTask).onSizeCompatActivityChanged();
ActivityManager.RunningTaskInfo taskInfo = mTask.getTaskInfo();
assertEquals(mActivity.appToken, taskInfo.topActivityToken);
assertTrue(taskInfo.topActivityInSizeCompat);
// Make the activity resizable again by restarting it
clearInvocations(mTask);
activity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
activity.mVisibleRequested = true;
activity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
// Expect null token when switching to non-size-compat mode activity.
verify(mTask).onSizeCompatActivityChanged();
taskInfo = mTask.getTaskInfo();
assertEquals(mActivity.appToken, taskInfo.topActivityToken);
assertFalse(taskInfo.topActivityInSizeCompat);
}
@Test
public void testShouldUseSizeCompatModeOnResizableTask() {
setUpDisplaySizeWithApp(1000, 2500);

View File

@@ -42,6 +42,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -55,6 +56,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
@@ -1244,6 +1246,54 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertEquals(splitPrimaryRootTask, activity.getRootTask());
}
@Test
public void testSizeCompatModeChangedOnFirstOrganizedTask() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer();
final Task rootTask = createStack();
final Task task = createTask(rootTask);
final ActivityRecord activity = createActivityRecord(rootTask.mDisplayContent, task);
final ArgumentCaptor<RunningTaskInfo> infoCaptor =
ArgumentCaptor.forClass(RunningTaskInfo.class);
assertTrue(rootTask.isOrganized());
spyOn(activity);
doReturn(true).when(activity).inSizeCompatMode();
doReturn(true).when(activity).isState(RESUMED);
// Ensure task info show top activity in size compat.
rootTask.onSizeCompatActivityChanged();
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskInfoChanged(infoCaptor.capture());
RunningTaskInfo info = infoCaptor.getValue();
assertEquals(rootTask.mTaskId, info.taskId);
assertEquals(activity.appToken, info.topActivityToken);
assertTrue(info.topActivityInSizeCompat);
// Ensure task info show top activity that is not in foreground as not in size compat.
clearInvocations(organizer);
doReturn(false).when(activity).isState(RESUMED);
rootTask.onSizeCompatActivityChanged();
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskInfoChanged(infoCaptor.capture());
info = infoCaptor.getValue();
assertEquals(rootTask.mTaskId, info.taskId);
assertEquals(activity.appToken, info.topActivityToken);
assertFalse(info.topActivityInSizeCompat);
// Ensure task info show non size compat top activity as not in size compat.
clearInvocations(organizer);
doReturn(true).when(activity).isState(RESUMED);
doReturn(false).when(activity).inSizeCompatMode();
rootTask.onSizeCompatActivityChanged();
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer).onTaskInfoChanged(infoCaptor.capture());
info = infoCaptor.getValue();
assertEquals(rootTask.mTaskId, info.taskId);
assertEquals(activity.appToken, info.topActivityToken);
assertFalse(info.topActivityInSizeCompat);
}
/**
* Verifies that task vanished is called for a specific task.
*/