Merge "[Partial Screenshare] introduce a hidden, permission-protected Activity API" into tm-qpr-dev am: 15229543c6

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

Change-Id: I02ce6a579a09e0676ee6f77010e68f48c7a005f9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Naomi Musgrave
2022-06-08 10:14:34 +00:00
committed by Automerger Merge Worker
6 changed files with 164 additions and 4 deletions

View File

@@ -6432,6 +6432,20 @@ public class Activity extends ContextThemeWrapper
}
}
/**
* Ensures the activity's result is immediately returned to the caller when {@link #finish()}
* is invoked
*
* <p>Should be invoked alongside {@link #setResult(int, Intent)}, so the provided results are
* in place before finishing. Must only be invoked during MediaProjection setup.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
public final void setForceSendResultForMediaProjection() {
ActivityClient.getInstance().setForceSendResultForMediaProjection(mToken);
}
/**
* Call this to set the result that your activity will return to its
* caller.

View File

@@ -17,6 +17,7 @@
package android.app;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
@@ -184,6 +185,15 @@ public class ActivityClient {
}
}
@RequiresPermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
void setForceSendResultForMediaProjection(IBinder token) {
try {
getActivityClientController().setForceSendResultForMediaProjection(token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
public boolean isTopOfTask(IBinder token) {
try {
return getActivityClientController().isTopOfTask(token);

View File

@@ -67,6 +67,12 @@ interface IActivityClientController {
boolean finishActivityAffinity(in IBinder token);
/** Finish all activities that were started for result from the specified activity. */
void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
/**
* Indicates that when the activity finsihes, the result should be immediately sent to the
* originating activity. Must only be invoked during MediaProjection setup.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)")
void setForceSendResultForMediaProjection(in IBinder token);
boolean isTopOfTask(in IBinder token);
boolean willActivityBeVisible(in IBinder token);

View File

@@ -43,6 +43,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_N
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
import android.Manifest;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -462,8 +463,8 @@ class ActivityClientController extends IActivityClientController.Stub {
// Explicitly dismissing the activity so reset its relaunch flag.
r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else {
r.finishIfPossible(resultCode, resultData, resultGrants,
"app-request", true /* oomAdj */);
r.finishIfPossible(resultCode, resultData, resultGrants, "app-request",
true /* oomAdj */);
res = r.finishing;
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
@@ -524,6 +525,23 @@ class ActivityClientController extends IActivityClientController.Stub {
}
}
@Override
public void setForceSendResultForMediaProjection(IBinder token) {
// Require that this is invoked only during MediaProjection setup.
mService.mAmInternal.enforceCallingPermission(
Manifest.permission.MANAGE_MEDIA_PROJECTION,
"setForceSendResultForMediaProjection");
final ActivityRecord r;
synchronized (mGlobalLock) {
r = ActivityRecord.isInRootTaskLocked(token);
if (r == null || !r.isInHistory()) {
return;
}
r.setForceSendResultForMediaProjection();
}
}
@Override
public boolean isTopOfTask(IBinder token) {
synchronized (mGlobalLock) {

View File

@@ -595,6 +595,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// pre-NYC apps that don't have a sense of being resized.
int mRelaunchReason = RELAUNCH_REASON_NONE;
private boolean mForceSendResultForMediaProjection = false;
TaskDescription taskDescription; // the recents information for this activity
// The locusId associated with this activity, if set.
@@ -3257,7 +3259,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
resultTo.getUriPermissionsLocked());
}
resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
if (mForceSendResultForMediaProjection) {
resultTo.sendResult(this.getUid(), resultWho, requestCode, resultCode,
resultData, resultGrants, true /* forceSendForMediaProjection */);
} else {
resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
}
resultTo = null;
} else if (DEBUG_RESULTS) {
Slog.v(TAG_RESULTS, "No result destination from " + this);
@@ -3451,6 +3458,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
void setForceSendResultForMediaProjection() {
mForceSendResultForMediaProjection = true;
}
private void prepareActivityHideTransitionAnimationIfOvarlay() {
if (mTaskOverlay) {
prepareActivityHideTransitionAnimation();
@@ -4541,6 +4552,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
Intent data, NeededUriGrants dataGrants) {
sendResult(callingUid, resultWho, requestCode, resultCode, data, dataGrants,
false /* forceSendForMediaProjection */);
}
private void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
Intent data, NeededUriGrants dataGrants, boolean forceSendForMediaProjection) {
if (callingUid > 0) {
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(dataGrants,
getUriPermissionsLocked());
@@ -4549,8 +4566,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (DEBUG_RESULTS) {
Slog.v(TAG, "Send activity result to " + this
+ " : who=" + resultWho + " req=" + requestCode
+ " res=" + resultCode + " data=" + data);
+ " res=" + resultCode + " data=" + data
+ " forceSendForMediaProjection=" + forceSendForMediaProjection);
}
if (isState(RESUMED) && attachedToProcess()) {
try {
final ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
@@ -4563,9 +4582,63 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
// Schedule sending results now for Media Projection setup.
if (forceSendForMediaProjection && attachedToProcess() && isState(STARTED, PAUSING, PAUSED,
STOPPING, STOPPED)) {
final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
// Build result to be returned immediately.
transaction.addCallback(ActivityResultItem.obtain(
List.of(new ResultInfo(resultWho, requestCode, resultCode, data))));
// When the activity result is delivered, the activity will transition to RESUMED.
// Since the activity is only resumed so the result can be immediately delivered,
// return it to its original lifecycle state.
ActivityLifecycleItem lifecycleItem = getLifecycleItemForCurrentStateForResult();
if (lifecycleItem != null) {
transaction.setLifecycleStateRequest(lifecycleItem);
} else {
Slog.w(TAG, "Unable to get the lifecycle item for state " + mState
+ " so couldn't immediately send result");
}
try {
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
} catch (RemoteException e) {
Slog.w(TAG, "Exception thrown sending result to " + this, e);
}
}
addResultLocked(null /* from */, resultWho, requestCode, resultCode, data);
}
/**
* Provides a lifecycle item for the current stat. Only to be used when force sending an
* activity result (as part of MeidaProjection setup). Does not support the following states:
* {@link State#INITIALIZING}, {@link State#RESTARTING_PROCESS},
* {@link State#FINISHING}, {@link State#DESTROYING}, {@link State#DESTROYED}. It does not make
* sense to force send a result to an activity in these states. Does not support
* {@link State#RESUMED} since a resumed activity will end in the resumed state after handling
* the result.
*
* @return an {@link ActivityLifecycleItem} for the current state, or {@code null} if the
* state is not valid.
*/
@Nullable
private ActivityLifecycleItem getLifecycleItemForCurrentStateForResult() {
switch (mState) {
case STARTED:
return StartActivityItem.obtain(null);
case PAUSING:
case PAUSED:
return PauseActivityItem.obtain();
case STOPPING:
case STOPPED:
return StopActivityItem.obtain(configChangeFlags);
default:
// Do not send a result immediately if the activity is in state INITIALIZING,
// RESTARTING_PROCESS, FINISHING, DESTROYING, or DESTROYED.
return null;
}
}
private void addNewIntentLocked(ReferrerIntent intent) {
if (newIntents == null) {
newIntents = new ArrayList<>();

View File

@@ -1160,6 +1160,45 @@ public class ActivityRecordTests extends WindowTestsBase {
assertTrue(lastTransition.allReady());
}
@Test
public void testFinishActivityIfPossible_sendResultImmediately() {
// Create activity representing the source of the activity result.
final ComponentName sourceComponent = ComponentName.createRelative(
DEFAULT_COMPONENT_PACKAGE_NAME, ".SourceActivity");
final ComponentName targetComponent = ComponentName.createRelative(
sourceComponent.getPackageName(), ".TargetActivity");
final ActivityRecord sourceActivity = new ActivityBuilder(mWm.mAtmService)
.setComponent(sourceComponent)
.setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE)
.setCreateTask(true)
.build();
sourceActivity.finishing = false;
sourceActivity.setState(STOPPED, "test");
final ActivityRecord targetActivity = new ActivityBuilder(mWm.mAtmService)
.setComponent(targetComponent)
.setTargetActivity(sourceComponent.getClassName())
.setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE)
.setCreateTask(true)
.setOnTop(true)
.build();
targetActivity.finishing = false;
targetActivity.setState(RESUMED, "test");
targetActivity.resultTo = sourceActivity;
targetActivity.setForceSendResultForMediaProjection();
clearInvocations(mAtm.getLifecycleManager());
targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
try {
verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
any(ClientTransaction.class));
} catch (RemoteException ignored) {
}
}
/**
* Verify that complete finish request for non-finishing activity is invalid.
*/