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

Commit 8581bc3e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add GameSession APIs for starting activities."

parents dd67e838 ed33816c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -11199,6 +11199,7 @@ package android.service.games {
    method public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean);
    method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame();
    method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
    method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void startActivityFromGameSessionForResult(@NonNull android.content.Intent, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSessionActivityCallback);
    method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
  }
@@ -11208,6 +11209,11 @@ package android.service.games {
    field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0
  }
  public interface GameSessionActivityCallback {
    method public void onActivityResult(int, @Nullable android.content.Intent);
    method public default void onActivityStartFailed(@NonNull Throwable);
  }
  public abstract class GameSessionService extends android.app.Service {
    ctor public GameSessionService();
    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
+14 −3
Original line number Diff line number Diff line
@@ -5503,6 +5503,17 @@ public class Activity extends ContextThemeWrapper
     */
    public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
            IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
        startActivityAsCaller(intent, options, permissionToken, ignoreTargetSecurity, userId, -1);
    }

    /**
     * @see #startActivityAsCaller(Intent, Bundle, IBinder, boolean, int)
     * @param requestCode The request code used for returning a result or -1 if no result should be
     *                    returned.
     * @hide
     */
    public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
            IBinder permissionToken, boolean ignoreTargetSecurity, int userId, int requestCode) {
        if (mParent != null) {
            throw new RuntimeException("Can't be called from a child");
        }
@@ -5510,11 +5521,11 @@ public class Activity extends ContextThemeWrapper
        Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivityAsCaller(
                        this, mMainThread.getApplicationThread(), mToken, this,
                        intent, -1, options, permissionToken, ignoreTargetSecurity, userId);
                        intent, requestCode, options, permissionToken, ignoreTargetSecurity,
                        userId);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, -1, ar.getResultCode(),
                ar.getResultData());
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
        }
        cancelInputsAndStartExitTransition(options);
    }
+3 −0
Original line number Diff line number Diff line
@@ -130,6 +130,9 @@ interface IActivityTaskManager {
            in ProfilerInfo profilerInfo, in Bundle options, int userId);
    int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
            int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
    int startActivityFromGameSession(IApplicationThread caller, in String callingPackage,
            in String callingFeatureId, int callingPid, int callingUid, in Intent intent,
            int taskId, int userId);
    void startRecentsActivity(in Intent intent, in long eventTime,
            in IRecentsAnimationRunner recentsAnimationRunner);
    int startActivityFromRecents(int taskId, in Bundle options);
+74 −0
Original line number Diff line number Diff line
@@ -20,15 +20,23 @@ import android.annotation.Hide;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.ActivityTaskManager;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.view.SurfaceControlViewHost;
import android.view.View;
@@ -41,6 +49,7 @@ import com.android.internal.util.function.pooled.PooledLambda;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import java.util.concurrent.Executor;

/**
@@ -120,6 +129,7 @@ public abstract class GameSession {
    private LifecycleState mLifecycleState = LifecycleState.INITIALIZED;
    private boolean mAreTransientInsetsVisibleDueToGesture = false;
    private IGameSessionController mGameSessionController;
    private Context mContext;
    private int mTaskId;
    private GameSessionRootView mGameSessionRootView;
    private SurfaceControlViewHost mSurfaceControlViewHost;
@@ -137,6 +147,7 @@ public abstract class GameSession {
            int heightPx) {
        mGameSessionController = gameSessionController;
        mTaskId = taskId;
        mContext = context;
        mSurfaceControlViewHost = surfaceControlViewHost;
        mGameSessionRootView = new GameSessionRootView(context, mSurfaceControlViewHost);
        surfaceControlViewHost.setView(mGameSessionRootView, widthPx, heightPx);
@@ -458,4 +469,67 @@ public abstract class GameSession {
                break;
        }
    }

    /**
     * Launches an activity within the same activity stack as the {@link GameSession}. When the
     * target activity exits, {@link GameSessionActivityCallback#onActivityResult(int, Intent)} will
     * be invoked with the result code and result data directly from the target activity (in other
     * words, the result code and data set via the target activity's
     * {@link android.app.Activity#startActivityForResult} call). The caller is expected to handle
     * the results that the target activity returns.
     *
     * <p>Any activity that an app would normally be able to start via {@link
     * android.app.Activity#startActivityForResult} will be startable via this method.
     *
     * <p>Started activities may see a different calling package than the game session's package
     * when calling {@link android.app.Activity#getCallingPackage()}.
     *
     * <p> If an exception is thrown while handling {@code intent},
     * {@link GameSessionActivityCallback#onActivityStartFailed(Throwable)} will be called instead
     * of {@link GameSessionActivityCallback#onActivityResult(int, Intent)}.
     *
     * @param intent   The intent to start.
     * @param options  Additional options for how the Activity should be started. See
     *                 {@link android.app.Activity#startActivityForResult(Intent, int, Bundle)} for
     *                 more details. This value may be null.
     * @param executor Executor on which {@code callback} should be invoked.
     * @param callback Callback to be invoked once the started activity has finished.
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)
    public final void startActivityFromGameSessionForResult(
            @NonNull Intent intent, @Nullable Bundle options, @NonNull Executor executor,
            @NonNull GameSessionActivityCallback callback) {
        Objects.requireNonNull(intent);
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);

        AndroidFuture<GameSessionActivityResult> future =
                new AndroidFuture<GameSessionActivityResult>()
                        .whenCompleteAsync((result, ex) -> {
                            if (ex != null) {
                                callback.onActivityStartFailed(ex);
                                return;
                            }
                            callback.onActivityResult(result.getResultCode(), result.getData());
                        }, executor);

        final Intent trampolineIntent = new Intent();
        trampolineIntent.setComponent(
                new ComponentName(
                        "android", "android.service.games.GameSessionTrampolineActivity"));
        trampolineIntent.putExtra(GameSessionTrampolineActivity.INTENT_KEY, intent);
        trampolineIntent.putExtra(GameSessionTrampolineActivity.OPTIONS_KEY, options);
        trampolineIntent.putExtra(
                GameSessionTrampolineActivity.FUTURE_KEY, future);

        try {
            int result = ActivityTaskManager.getService().startActivityFromGameSession(
                    mContext.getIApplicationThread(), mContext.getPackageName(), "GameSession",
                    Binder.getCallingPid(), Binder.getCallingUid(), trampolineIntent, mTaskId,
                    UserHandle.myUserId());
            Instrumentation.checkStartActivityResult(result, trampolineIntent);
        } catch (Throwable t) {
            executor.execute(() -> callback.onActivityStartFailed(t));
        }
    }
}
+54 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.games;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Intent;
import android.os.Bundle;

import java.util.concurrent.Executor;

/**
 * Callback invoked when an activity launched via
 * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor,
 * GameSessionActivityCallback)}} has returned a result or failed to start.
 *
 * @hide
 */
@SystemApi
public interface GameSessionActivityCallback {
    /**
     * Callback invoked when an activity launched via
     * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor,
     * GameSessionActivityCallback)}} has returned a result.
     *
     * @param resultCode The result code of the launched activity. See {@link
     *                   android.app.Activity#setResult(int)}.
     * @param data       Any data returned by the launched activity. See {@link
     *                   android.app.Activity#setResult(int, Intent)}.
     */
    void onActivityResult(int resultCode, @Nullable Intent data);

    /**
     * Callback invoked when a throwable was thrown when launching the {@link Intent} in
     * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor,
     * GameSessionActivityCallback)}}.
     */
    default void onActivityStartFailed(@NonNull Throwable t) {}
}
Loading