Merge "Allow ActivityEmbedding to finish Activity through WCT" into tm-qpr-dev
This commit is contained in:
@@ -3406,6 +3406,7 @@ package android.window {
|
|||||||
method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams);
|
method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams);
|
||||||
method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken);
|
method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken);
|
||||||
method public int describeContents();
|
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 removeTask(@NonNull android.window.WindowContainerToken);
|
||||||
method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean);
|
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);
|
method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean);
|
||||||
|
|||||||
@@ -705,6 +705,23 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
return this;
|
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
|
* Sets/removes the always on top flag for this {@code windowContainer}. See
|
||||||
* {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
|
* {@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_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_SET_ALWAYS_ON_TOP = 19;
|
||||||
public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20;
|
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:
|
// The following key(s) are for use with mLaunchOptions:
|
||||||
// When launching a task (eg. from recents), this is the taskId to be launched.
|
// 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 + "}";
|
+ " alwaysOnTop=" + mAlwaysOnTop + "}";
|
||||||
case HIERARCHY_OP_TYPE_REMOVE_TASK:
|
case HIERARCHY_OP_TYPE_REMOVE_TASK:
|
||||||
return "{RemoveTask: task=" + mContainer + "}";
|
return "{RemoveTask: task=" + mContainer + "}";
|
||||||
|
case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
|
||||||
|
return "{finishActivity: activity=" + mContainer + "}";
|
||||||
default:
|
default:
|
||||||
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
|
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
|
||||||
+ " mToTop=" + mToTop
|
+ " mToTop=" + mToTop
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ class TaskFragmentContainer {
|
|||||||
// In case we have requested to reparent the activity to another container (as
|
// In case we have requested to reparent the activity to another container (as
|
||||||
// pendingAppeared), we don't want to finish it with this container.
|
// pendingAppeared), we don't want to finish it with this container.
|
||||||
&& mController.getContainerWithActivity(activity) == this) {
|
&& mController.getContainerWithActivity(activity) == this) {
|
||||||
activity.finish();
|
wct.finishActivity(activity.getActivityToken());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ class TaskFragmentContainer {
|
|||||||
|| controller.shouldRetainAssociatedActivity(this, activity)) {
|
|| controller.shouldRetainAssociatedActivity(this, activity)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
activity.finish();
|
wct.finishActivity(activity.getActivityToken());
|
||||||
}
|
}
|
||||||
mActivitiesToFinishOnExit.clear();
|
mActivitiesToFinishOnExit.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ public class SplitControllerTest {
|
|||||||
|
|
||||||
verify(mSplitPresenter, never()).deleteTaskFragment(any(), any());
|
verify(mSplitPresenter, never()).deleteTaskFragment(any(), any());
|
||||||
verify(mSplitController).removeContainer(tf);
|
verify(mSplitController).removeContainer(tf);
|
||||||
verify(mActivity, never()).finish();
|
verify(mTransaction, never()).finishActivity(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1004,9 +1004,9 @@ public class SplitControllerTest {
|
|||||||
assertTrue(primaryContainer.isFinished());
|
assertTrue(primaryContainer.isFinished());
|
||||||
assertTrue(secondaryContainer0.isFinished());
|
assertTrue(secondaryContainer0.isFinished());
|
||||||
assertTrue(secondaryContainer1.isFinished());
|
assertTrue(secondaryContainer1.isFinished());
|
||||||
verify(mActivity).finish();
|
verify(mTransaction).finishActivity(mActivity.getActivityToken());
|
||||||
verify(secondaryActivity0).finish();
|
verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken());
|
||||||
verify(secondaryActivity1).finish();
|
verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken());
|
||||||
assertTrue(taskContainer.mContainers.isEmpty());
|
assertTrue(taskContainer.mContainers.isEmpty());
|
||||||
assertTrue(taskContainer.mSplitContainers.isEmpty());
|
assertTrue(taskContainer.mSplitContainers.isEmpty());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,30 +107,29 @@ public class TaskFragmentContainerTest {
|
|||||||
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
|
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
|
||||||
null /* pendingAppearedIntent */, taskContainer, mController);
|
null /* pendingAppearedIntent */, taskContainer, mController);
|
||||||
doReturn(container).when(mController).getContainerWithActivity(mActivity);
|
doReturn(container).when(mController).getContainerWithActivity(mActivity);
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
|
||||||
|
|
||||||
// Only remove the activity, but not clear the reference until appeared.
|
// 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(mPresenter, never()).deleteTaskFragment(any(), any());
|
||||||
verify(mController, never()).removeContainer(any());
|
verify(mController, never()).removeContainer(any());
|
||||||
|
|
||||||
// Calling twice should not finish activity again.
|
// Calling twice should not finish activity again.
|
||||||
clearInvocations(mActivity);
|
clearInvocations(mTransaction);
|
||||||
container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
|
container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
|
||||||
|
|
||||||
verify(mActivity, never()).finish();
|
verify(mTransaction, never()).finishActivity(any());
|
||||||
verify(mPresenter, never()).deleteTaskFragment(any(), any());
|
verify(mPresenter, never()).deleteTaskFragment(any(), any());
|
||||||
verify(mController, never()).removeContainer(any());
|
verify(mController, never()).removeContainer(any());
|
||||||
|
|
||||||
// Remove all references after the container has appeared in server.
|
// Remove all references after the container has appeared in server.
|
||||||
doReturn(new ArrayList<>()).when(mInfo).getActivities();
|
doReturn(new ArrayList<>()).when(mInfo).getActivities();
|
||||||
container.setInfo(mTransaction, mInfo);
|
container.setInfo(mTransaction, mInfo);
|
||||||
container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
|
container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
|
||||||
|
|
||||||
verify(mActivity, never()).finish();
|
verify(mTransaction, never()).finishActivity(any());
|
||||||
verify(mPresenter).deleteTaskFragment(wct, container.getTaskFragmentToken());
|
verify(mPresenter).deleteTaskFragment(mTransaction, container.getTaskFragmentToken());
|
||||||
verify(mController).removeContainer(container);
|
verify(mController).removeContainer(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +149,7 @@ public class TaskFragmentContainerTest {
|
|||||||
// The activity is requested to be reparented, so don't finish it.
|
// The activity is requested to be reparented, so don't finish it.
|
||||||
container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
|
container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
|
||||||
|
|
||||||
verify(mActivity, never()).finish();
|
verify(mTransaction, never()).finishActivity(any());
|
||||||
verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken());
|
verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken());
|
||||||
verify(mController).removeContainer(container0);
|
verify(mController).removeContainer(container0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_CHILDREN_TASKS_REPARENT;
|
||||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
|
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_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_LAUNCH_TASK;
|
||||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
|
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
|
||||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER;
|
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER;
|
||||||
@@ -1007,6 +1008,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
isInLockTaskMode);
|
isInLockTaskMode);
|
||||||
break;
|
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: {
|
case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
|
||||||
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
|
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
|
||||||
"launchTask HierarchyOp");
|
"launchTask HierarchyOp");
|
||||||
@@ -1620,6 +1635,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
organizer);
|
organizer);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
|
||||||
|
// Allow finish activity if it has the activity token.
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Other types of hierarchy changes are not allowed.
|
// Other types of hierarchy changes are not allowed.
|
||||||
String msg = "Permission Denial: " + func + " from pid="
|
String msg = "Permission Denial: " + func + " from pid="
|
||||||
|
|||||||
@@ -729,6 +729,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
|||||||
assertEquals(activity0, mDisplayContent.mFocusedApp);
|
assertEquals(activity0, mDisplayContent.mFocusedApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApplyTransaction_finishActivity() {
|
||||||
|
final ActivityRecord activity = createActivityRecord(mDisplayContent);
|
||||||
|
|
||||||
|
mTransaction.finishActivity(activity.token);
|
||||||
|
assertApplyTransactionAllowed(mTransaction);
|
||||||
|
|
||||||
|
assertTrue(activity.finishing);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testApplyTransaction_skipTransactionForUnregisterOrganizer() {
|
public void testApplyTransaction_skipTransactionForUnregisterOrganizer() {
|
||||||
mController.unregisterOrganizer(mIOrganizer);
|
mController.unregisterOrganizer(mIOrganizer);
|
||||||
|
|||||||
Reference in New Issue
Block a user