Allows running TaskFragment related hierarchy op in lock task mode
The placeholder activity was removed while device folded, but the TaskFragment was failed to be removed because the hierarchy op was ignored in lock task mode. Most of the TaskFragment related hierarchy ops operate in the same task and it won't change the task order. Therefore, it should still works in lock task mode. However, deleting a TaskFragment may cleanup all activities in the Task, which needed to be handled separately. Bug: 204945438 Test: atest TaskFragmentOrganizerControllerTest Change-Id: If1d35ef81754b53c5f214672000cc0c066628a29
This commit is contained in:
@@ -581,115 +581,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
task.getDisplayArea().setLaunchAdjacentFlagRootTask(clearRoot ? null : task);
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
|
||||
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: {
|
||||
effects |= setAdjacentRootsHierarchyOp(hop);
|
||||
break;
|
||||
}
|
||||
// The following operations may change task order so they are skipped while in lock task
|
||||
// mode. The above operations are still allowed because they don't move tasks. And it may
|
||||
// be necessary such as clearing launch root after entering lock task mode.
|
||||
if (isInLockTaskMode) {
|
||||
Slog.w(TAG, "Skip applying hierarchy operation " + hop + " while in lock task mode");
|
||||
return effects;
|
||||
}
|
||||
|
||||
final WindowContainer wc;
|
||||
final IBinder fragmentToken;
|
||||
switch (type) {
|
||||
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
|
||||
effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_REORDER:
|
||||
case HIERARCHY_OP_TYPE_REPARENT:
|
||||
wc = WindowContainer.fromBinder(hop.getContainer());
|
||||
if (wc == null || !wc.isAttached()) {
|
||||
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
|
||||
break;
|
||||
}
|
||||
if (syncId >= 0) {
|
||||
addToSyncSet(syncId, wc);
|
||||
}
|
||||
if (transition != null) {
|
||||
transition.collect(wc);
|
||||
if (hop.isReparent()) {
|
||||
if (wc.getParent() != null) {
|
||||
// Collect the current parent. It's visibility may change as
|
||||
// a result of this reparenting.
|
||||
transition.collect(wc.getParent());
|
||||
}
|
||||
if (hop.getNewParent() != null) {
|
||||
final WindowContainer parentWc =
|
||||
WindowContainer.fromBinder(hop.getNewParent());
|
||||
if (parentWc == null) {
|
||||
Slog.e(TAG, "Can't resolve parent window from token");
|
||||
break;
|
||||
}
|
||||
transition.collect(parentWc);
|
||||
}
|
||||
}
|
||||
}
|
||||
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
|
||||
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
|
||||
"launchTask HierarchyOp");
|
||||
final Bundle launchOpts = hop.getLaunchOptions();
|
||||
final int taskId = launchOpts.getInt(
|
||||
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
|
||||
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
|
||||
final SafeActivityOptions safeOptions =
|
||||
SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
|
||||
final Integer[] starterResult = { null };
|
||||
// startActivityFromRecents should not be called in lock.
|
||||
mService.mH.post(() -> {
|
||||
try {
|
||||
starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents(
|
||||
caller.mPid, caller.mUid, taskId, safeOptions);
|
||||
} catch (Throwable t) {
|
||||
starterResult[0] = ActivityManager.START_CANCELED;
|
||||
Slog.w(TAG, t);
|
||||
}
|
||||
synchronized (mGlobalLock) {
|
||||
mGlobalLock.notifyAll();
|
||||
}
|
||||
});
|
||||
while (starterResult[0] == null) {
|
||||
try {
|
||||
mGlobalLock.wait();
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_PENDING_INTENT:
|
||||
String resolvedType = hop.getActivityIntent() != null
|
||||
? hop.getActivityIntent().resolveTypeIfNeeded(
|
||||
mService.mContext.getContentResolver())
|
||||
: null;
|
||||
|
||||
Bundle options = null;
|
||||
if (hop.getPendingIntent().isActivity()) {
|
||||
// Set the context display id as preferred for this activity launches, so that
|
||||
// it can land on caller's display. Or just brought the task to front at the
|
||||
// display where it was on since it has higher preference.
|
||||
ActivityOptions activityOptions = hop.getLaunchOptions() != null
|
||||
? new ActivityOptions(hop.getLaunchOptions())
|
||||
: ActivityOptions.makeBasic();
|
||||
activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
|
||||
options = activityOptions.toBundle();
|
||||
}
|
||||
|
||||
mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
|
||||
hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
|
||||
hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
|
||||
null /* requiredPermission */, options);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: {
|
||||
final TaskFragmentCreationParams taskFragmentCreationOptions =
|
||||
hop.getTaskFragmentCreationOptions();
|
||||
createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
|
||||
wc = WindowContainer.fromBinder(hop.getContainer());
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: {
|
||||
final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
|
||||
if (wc == null || !wc.isAttached()) {
|
||||
Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
|
||||
break;
|
||||
@@ -699,10 +602,24 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
throw new IllegalArgumentException(
|
||||
"Can only delete organized TaskFragment, but not Task.");
|
||||
}
|
||||
if (isInLockTaskMode) {
|
||||
final ActivityRecord bottomActivity = taskFragment.getActivity(
|
||||
a -> !a.finishing, false /* traverseTopToBottom */);
|
||||
if (bottomActivity != null
|
||||
&& mService.getLockTaskController().activityBlockedFromFinish(
|
||||
bottomActivity)) {
|
||||
Slog.w(TAG, "Skip removing TaskFragment due in lock task mode.");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
|
||||
new IllegalStateException(
|
||||
"Not allow to delete task fragment in lock task mode."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
|
||||
fragmentToken = hop.getContainer();
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
|
||||
final IBinder fragmentToken = hop.getContainer();
|
||||
if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to operate with invalid fragment token");
|
||||
@@ -721,8 +638,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
convertStartFailureToThrowable(result, activityIntent));
|
||||
}
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
|
||||
fragmentToken = hop.getNewParent();
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
|
||||
final IBinder fragmentToken = hop.getNewParent();
|
||||
final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
|
||||
if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
@@ -733,21 +651,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
|
||||
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
|
||||
final WindowContainer newParent = hop.getNewParent() != null
|
||||
? WindowContainer.fromBinder(hop.getNewParent())
|
||||
: null;
|
||||
if (oldParent == null || !oldParent.isAttached()) {
|
||||
Slog.e(TAG, "Attempt to operate on unknown or detached container: "
|
||||
+ oldParent);
|
||||
break;
|
||||
}
|
||||
reparentTaskFragment(oldParent, newParent, errorCallbackToken);
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
|
||||
fragmentToken = hop.getContainer();
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: {
|
||||
final IBinder fragmentToken = hop.getContainer();
|
||||
final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
|
||||
final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
|
||||
final TaskFragment tf2 = adjacentFragmentToken != null
|
||||
@@ -777,6 +683,126 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
adjacentParams.shouldDelaySecondaryLastActivityRemoval());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// The other operations may change task order so they are skipped while in lock
|
||||
// task mode. The above operations are still allowed because they don't move
|
||||
// tasks. And it may be necessary such as clearing launch root after entering
|
||||
// lock task mode.
|
||||
if (isInLockTaskMode) {
|
||||
Slog.w(TAG, "Skip applying hierarchy operation " + hop
|
||||
+ " while in lock task mode");
|
||||
return effects;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
|
||||
effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_REORDER:
|
||||
case HIERARCHY_OP_TYPE_REPARENT: {
|
||||
final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
|
||||
if (wc == null || !wc.isAttached()) {
|
||||
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
|
||||
break;
|
||||
}
|
||||
if (syncId >= 0) {
|
||||
addToSyncSet(syncId, wc);
|
||||
}
|
||||
if (transition != null) {
|
||||
transition.collect(wc);
|
||||
if (hop.isReparent()) {
|
||||
if (wc.getParent() != null) {
|
||||
// Collect the current parent. It's visibility may change as
|
||||
// a result of this reparenting.
|
||||
transition.collect(wc.getParent());
|
||||
}
|
||||
if (hop.getNewParent() != null) {
|
||||
final WindowContainer parentWc =
|
||||
WindowContainer.fromBinder(hop.getNewParent());
|
||||
if (parentWc == null) {
|
||||
Slog.e(TAG, "Can't resolve parent window from token");
|
||||
break;
|
||||
}
|
||||
transition.collect(parentWc);
|
||||
}
|
||||
}
|
||||
}
|
||||
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
|
||||
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
|
||||
"launchTask HierarchyOp");
|
||||
final Bundle launchOpts = hop.getLaunchOptions();
|
||||
final int taskId = launchOpts.getInt(
|
||||
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
|
||||
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
|
||||
final SafeActivityOptions safeOptions =
|
||||
SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
|
||||
final Integer[] starterResult = {null};
|
||||
// startActivityFromRecents should not be called in lock.
|
||||
mService.mH.post(() -> {
|
||||
try {
|
||||
starterResult[0] = mService.mTaskSupervisor.startActivityFromRecents(
|
||||
caller.mPid, caller.mUid, taskId, safeOptions);
|
||||
} catch (Throwable t) {
|
||||
starterResult[0] = ActivityManager.START_CANCELED;
|
||||
Slog.w(TAG, t);
|
||||
}
|
||||
synchronized (mGlobalLock) {
|
||||
mGlobalLock.notifyAll();
|
||||
}
|
||||
});
|
||||
while (starterResult[0] == null) {
|
||||
try {
|
||||
mGlobalLock.wait();
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_PENDING_INTENT: {
|
||||
String resolvedType = hop.getActivityIntent() != null
|
||||
? hop.getActivityIntent().resolveTypeIfNeeded(
|
||||
mService.mContext.getContentResolver())
|
||||
: null;
|
||||
|
||||
Bundle options = null;
|
||||
if (hop.getPendingIntent().isActivity()) {
|
||||
// Set the context display id as preferred for this activity launches, so that
|
||||
// it can land on caller's display. Or just brought the task to front at the
|
||||
// display where it was on since it has higher preference.
|
||||
ActivityOptions activityOptions = hop.getLaunchOptions() != null
|
||||
? new ActivityOptions(hop.getLaunchOptions())
|
||||
: ActivityOptions.makeBasic();
|
||||
activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
|
||||
options = activityOptions.toBundle();
|
||||
}
|
||||
|
||||
mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
|
||||
hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
|
||||
hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
|
||||
null /* requiredPermission */, options);
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: {
|
||||
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
|
||||
final WindowContainer newParent = hop.getNewParent() != null
|
||||
? WindowContainer.fromBinder(hop.getNewParent())
|
||||
: null;
|
||||
if (oldParent == null || !oldParent.isAttached()) {
|
||||
Slog.e(TAG, "Attempt to operate on unknown or detached container: "
|
||||
+ oldParent);
|
||||
break;
|
||||
}
|
||||
reparentTaskFragment(oldParent, newParent, errorCallbackToken);
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return effects;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user