Allow starting activity for result on another TaskFragment

...but still in the same Task. This is only possible when
enabling ActivityEmbedding.

Meanwhile, the result should be sent immediately if the result-to
activity is in the RESUMED state.

Bug: 253387076
Test: atest TaskFragmentTest ActivityRecordTests
Change-Id: I9049e8cb2329836ac2661aa6ead679ffc838c3b8
This commit is contained in:
Louis Chang
2022-11-24 07:29:41 +00:00
parent dad85c36a8
commit 1d00930552
5 changed files with 31 additions and 46 deletions

View File

@@ -3317,9 +3317,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
resultTo.getUriPermissionsLocked());
}
if (mForceSendResultForMediaProjection) {
resultTo.sendResult(this.getUid(), resultWho, requestCode, resultCode,
resultData, resultGrants, true /* forceSendForMediaProjection */);
if (mForceSendResultForMediaProjection || resultTo.isState(RESUMED)) {
// Sending the result to the resultTo activity asynchronously to prevent the
// resultTo activity getting results before this Activity paused.
final ActivityRecord resultToActivity = resultTo;
mAtmService.mH.post(() -> {
synchronized (mAtmService.mGlobalLock) {
resultToActivity.sendResult(this.getUid(), resultWho, requestCode,
resultCode, resultData, resultGrants,
mForceSendResultForMediaProjection);
}
});
} else {
resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
}
@@ -4630,7 +4638,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
false /* forceSendForMediaProjection */);
}
private void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
Intent data, NeededUriGrants dataGrants, boolean forceSendForMediaProjection) {
if (callingUid > 0) {
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(dataGrants,

View File

@@ -78,7 +78,6 @@ import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -2777,11 +2776,6 @@ class ActivityStarter {
errMsg = "The app:" + mCallingUid + "is not trusted to " + mStartActivity;
break;
}
case EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT: {
errMsg = "Cannot embed activity across TaskFragments for result, resultTo: "
+ mStartActivity.resultTo;
break;
}
default:
errMsg = "Unhandled embed result:" + result;
}

View File

@@ -172,13 +172,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
* indicate that an Activity can't be embedded because the Activity is started on a new task.
*/
static final int EMBEDDING_DISALLOWED_NEW_TASK = 3;
/**
* An embedding check result of
* {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
* indicate that an Activity can't be embedded because the Activity is started on a new
* TaskFragment, e.g. start an Activity on a new TaskFragment for result.
*/
static final int EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT = 4;
/**
* Embedding check results of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
@@ -189,7 +182,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
EMBEDDING_DISALLOWED_UNTRUSTED_HOST,
EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION,
EMBEDDING_DISALLOWED_NEW_TASK,
EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT,
})
@interface EmbeddingCheckResult {}
@@ -616,14 +608,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
}
// Cannot embed activity across TaskFragments for activity result.
// If the activity that started for result is finishing, it's likely that this start mode
// is used to place an activity in the same task. Since the finishing activity won't be
// able to get the results, so it's OK to embed in a different TaskFragment.
if (a.resultTo != null && !a.resultTo.finishing && a.resultTo.getTaskFragment() != this) {
return EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
}
return EMBEDDING_ALLOWED;
}

View File

@@ -1206,6 +1206,25 @@ public class ActivityRecordTests extends WindowTestsBase {
}
}
@Test
public void testFinishActivityIfPossible_sendResultImmediatelyIfResumed() {
final Task task = new TaskBuilder(mSupervisor).build();
final TaskFragment taskFragment1 = createTaskFragmentWithActivity(task);
final TaskFragment taskFragment2 = createTaskFragmentWithActivity(task);
final ActivityRecord resultToActivity = taskFragment1.getTopMostActivity();
final ActivityRecord targetActivity = taskFragment2.getTopMostActivity();
resultToActivity.setState(RESUMED, "test");
targetActivity.setState(RESUMED, "test");
targetActivity.resultTo = resultToActivity;
clearInvocations(mAtm.getLifecycleManager());
targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
waitUntilHandlersIdle();
verify(resultToActivity).sendResult(anyInt(), eq(null), anyInt(), anyInt(), any(), eq(null),
anyBoolean());
}
/**
* Verify that complete finish request for non-finishing activity is invalid.
*/

View File

@@ -24,7 +24,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.os.Process.FIRST_APPLICATION_UID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -33,9 +32,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -475,23 +472,6 @@ public class TaskFragmentTest extends WindowTestsBase {
doReturn(true).when(taskFragment).smallerThanMinDimension(any());
assertEquals(EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION,
taskFragment.isAllowedToEmbedActivity(activity));
// Not allow to start activity across TaskFragments for result.
final TaskFragment newTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(taskFragment.getTask())
.build();
final ActivityRecord newActivity = new ActivityBuilder(mAtm)
.setUid(FIRST_APPLICATION_UID)
.build();
doReturn(true).when(newTaskFragment).isAllowedToEmbedActivityInTrustedMode(any(), anyInt());
doReturn(false).when(newTaskFragment).smallerThanMinDimension(any());
newActivity.resultTo = activity;
assertEquals(EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT,
newTaskFragment.isAllowedToEmbedActivity(newActivity));
// Allow embedding if the resultTo activity is finishing.
activity.finishing = true;
assertEquals(EMBEDDING_ALLOWED, newTaskFragment.isAllowedToEmbedActivity(newActivity));
}
@Test