Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15229543 authored by Naomi Musgrave's avatar Naomi Musgrave Committed by Android (Google) Code Review
Browse files

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

parents bd91d26b 12f5992e
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -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.
+10 −0
Original line number Diff line number Diff line
@@ -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);
+6 −0
Original line number Diff line number Diff line
@@ -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);
+20 −2
Original line number Diff line number Diff line
@@ -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) {
+75 −2
Original line number Diff line number Diff line
@@ -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());
            }
            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<>();
Loading