Loading core/java/android/app/Activity.java +14 −0 Original line number Diff line number Diff line Loading @@ -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. Loading core/java/android/app/ActivityClient.java +10 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading core/java/android/app/IActivityClientController.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -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); Loading services/core/java/com/android/server/wm/ActivityClientController.java +20 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"); Loading Loading @@ -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) { Loading services/core/java/com/android/server/wm/ActivityRecord.java +75 −2 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -3257,7 +3259,12 @@ 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 */); } else { resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData); } resultTo = null; } else if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "No result destination from " + this); Loading Loading @@ -3451,6 +3458,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } void setForceSendResultForMediaProjection() { mForceSendResultForMediaProjection = true; } private void prepareActivityHideTransitionAnimationIfOvarlay() { if (mTaskOverlay) { prepareActivityHideTransitionAnimation(); Loading Loading @@ -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()); Loading @@ -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>(); Loading @@ -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<>(); Loading Loading
core/java/android/app/Activity.java +14 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
core/java/android/app/ActivityClient.java +10 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading
core/java/android/app/IActivityClientController.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
services/core/java/com/android/server/wm/ActivityClientController.java +20 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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"); Loading Loading @@ -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) { Loading
services/core/java/com/android/server/wm/ActivityRecord.java +75 −2 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -3257,7 +3259,12 @@ 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 */); } else { resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData); } resultTo = null; } else if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "No result destination from " + this); Loading Loading @@ -3451,6 +3458,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } void setForceSendResultForMediaProjection() { mForceSendResultForMediaProjection = true; } private void prepareActivityHideTransitionAnimationIfOvarlay() { if (mTaskOverlay) { prepareActivityHideTransitionAnimation(); Loading Loading @@ -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()); Loading @@ -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>(); Loading @@ -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<>(); Loading