Merge "Allow ActivityEmbedding to finish Activity through WCT" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-09-24 02:44:06 +00:00
committed by Android (Google) Code Review
7 changed files with 64 additions and 16 deletions

View File

@@ -3406,6 +3406,7 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams);
method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken);
method public int describeContents();
method @NonNull public android.window.WindowContainerTransaction finishActivity(@NonNull android.os.IBinder);
method @NonNull public android.window.WindowContainerTransaction removeTask(@NonNull android.window.WindowContainerToken);
method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean);

View File

@@ -705,6 +705,23 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
/**
* Finishes the Activity.
* Comparing to directly calling {@link android.app.Activity#finish()}, calling this can make
* sure the finishing happens in the same transaction with other operations.
* @param activityToken activity to be finished.
*/
@NonNull
public WindowContainerTransaction finishActivity(@NonNull IBinder activityToken) {
final HierarchyOp hierarchyOp =
new HierarchyOp.Builder(
HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY)
.setContainer(activityToken)
.build();
mHierarchyOps.add(hierarchyOp);
return this;
}
/**
* Sets/removes the always on top flag for this {@code windowContainer}. See
* {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
@@ -1163,6 +1180,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18;
public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19;
public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20;
public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1484,6 +1502,8 @@ public final class WindowContainerTransaction implements Parcelable {
+ " alwaysOnTop=" + mAlwaysOnTop + "}";
case HIERARCHY_OP_TYPE_REMOVE_TASK:
return "{RemoveTask: task=" + mContainer + "}";
case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
return "{finishActivity: activity=" + mContainer + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop

View File

@@ -432,7 +432,7 @@ class TaskFragmentContainer {
// In case we have requested to reparent the activity to another container (as
// pendingAppeared), we don't want to finish it with this container.
&& mController.getContainerWithActivity(activity) == this) {
activity.finish();
wct.finishActivity(activity.getActivityToken());
}
}
@@ -457,7 +457,7 @@ class TaskFragmentContainer {
|| controller.shouldRetainAssociatedActivity(this, activity)) {
continue;
}
activity.finish();
wct.finishActivity(activity.getActivityToken());
}
mActivitiesToFinishOnExit.clear();
}

View File

@@ -207,7 +207,7 @@ public class SplitControllerTest {
verify(mSplitPresenter, never()).deleteTaskFragment(any(), any());
verify(mSplitController).removeContainer(tf);
verify(mActivity, never()).finish();
verify(mTransaction, never()).finishActivity(any());
}
@Test
@@ -1004,9 +1004,9 @@ public class SplitControllerTest {
assertTrue(primaryContainer.isFinished());
assertTrue(secondaryContainer0.isFinished());
assertTrue(secondaryContainer1.isFinished());
verify(mActivity).finish();
verify(secondaryActivity0).finish();
verify(secondaryActivity1).finish();
verify(mTransaction).finishActivity(mActivity.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken());
assertTrue(taskContainer.mContainers.isEmpty());
assertTrue(taskContainer.mSplitContainers.isEmpty());
}

View File

@@ -107,30 +107,29 @@ public class TaskFragmentContainerTest {
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
doReturn(container).when(mController).getContainerWithActivity(mActivity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Only remove the activity, but not clear the reference until appeared.
container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
verify(mActivity).finish();
verify(mTransaction).finishActivity(mActivity.getActivityToken());
verify(mPresenter, never()).deleteTaskFragment(any(), any());
verify(mController, never()).removeContainer(any());
// Calling twice should not finish activity again.
clearInvocations(mActivity);
container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
clearInvocations(mTransaction);
container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
verify(mActivity, never()).finish();
verify(mTransaction, never()).finishActivity(any());
verify(mPresenter, never()).deleteTaskFragment(any(), any());
verify(mController, never()).removeContainer(any());
// Remove all references after the container has appeared in server.
doReturn(new ArrayList<>()).when(mInfo).getActivities();
container.setInfo(mTransaction, mInfo);
container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
verify(mActivity, never()).finish();
verify(mPresenter).deleteTaskFragment(wct, container.getTaskFragmentToken());
verify(mTransaction, never()).finishActivity(any());
verify(mPresenter).deleteTaskFragment(mTransaction, container.getTaskFragmentToken());
verify(mController).removeContainer(container);
}
@@ -150,7 +149,7 @@ public class TaskFragmentContainerTest {
// The activity is requested to be reparented, so don't finish it.
container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
verify(mActivity, never()).finish();
verify(mTransaction, never()).finishActivity(any());
verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken());
verify(mController).removeContainer(container0);
}

View File

@@ -25,6 +25,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER;
@@ -1007,6 +1008,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
isInLockTaskMode);
break;
}
case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: {
final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
if (activity == null || activity.finishing) {
break;
}
if (activity.isVisible()) {
// Prevent the transition from being executed too early if the activity is
// visible.
activity.finishIfPossible("finish-activity-op", false /* oomAdj */);
} else {
activity.destroyIfPossible("finish-activity-op");
}
break;
}
case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"launchTask HierarchyOp");
@@ -1620,6 +1635,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
organizer);
}
break;
case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
// Allow finish activity if it has the activity token.
break;
default:
// Other types of hierarchy changes are not allowed.
String msg = "Permission Denial: " + func + " from pid="

View File

@@ -729,6 +729,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertEquals(activity0, mDisplayContent.mFocusedApp);
}
@Test
public void testApplyTransaction_finishActivity() {
final ActivityRecord activity = createActivityRecord(mDisplayContent);
mTransaction.finishActivity(activity.token);
assertApplyTransactionAllowed(mTransaction);
assertTrue(activity.finishing);
}
@Test
public void testApplyTransaction_skipTransactionForUnregisterOrganizer() {
mController.unregisterOrganizer(mIOrganizer);