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:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user