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

Commit 614ba138 authored by shannonchen's avatar shannonchen Committed by Shannon Chen
Browse files

Add a GameSession API for restarting the associated game.

Since restarting a process must be done from the system server side,
we accomplish this by passing a IBinder through to the GameSession
allowing the GameSession to make IPCs to its hosting
GameServiceProviderInstanceImpl. As an additional layer of security,
the implementation takes a taskId and only restarts the given
game if the taskId matches an active game session.

Test: atest FrameworksMockingServicesTests and manual e2e testing
Bug: 210127167
Bug: 202414447
Bug: 202417255
CTS-Coverage-Bug: 206128693
Change-Id: I944da060323ac1779cd2c61d1e411e9868caed91
parent aad39a38
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11182,6 +11182,7 @@ package android.service.games {
    method public void onCreate();
    method public void onDestroy();
    method public void onGameTaskFocusChanged(boolean);
    method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public final boolean restartGame();
    method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
    method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
  }
+20 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.Hide;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.res.Configuration;
@@ -270,6 +271,25 @@ public abstract class GameSession {
        mGameSessionRootView.addView(view, layoutParams);
    }

    /**
     * Attempts to force stop and relaunch the game associated with the current session. This may
     * be useful, for example, after applying settings that will not take effect until the game is
     * restarted.
     *
     * @return {@code true} if the game was successfully restarted; otherwise, {@code false}.
     */
    @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
    public final boolean restartGame() {
        try {
            mGameSessionController.restartGame(mTaskId);
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to restart game", e);
            return false;
        }

        return true;
    }

    /**
     * Root view of the {@link SurfaceControlViewHost} associated with the {@link GameSession}
     * instance. It is responsible for observing changes in the size of the window and resizing
+4 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.service.games;

import android.content.Intent;
import com.android.internal.infra.AndroidFuture;

/**
@@ -23,4 +24,6 @@ import com.android.internal.infra.AndroidFuture;
 */
oneway interface IGameSessionController {
    void takeScreenshot(int taskId, in AndroidFuture gameScreenshotResultFuture);
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)")
    void restartGame(in int taskId);
}
+6 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.app;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.Intent;
@@ -36,17 +37,19 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide
    private final Context mContext;

    GameServiceProviderInstanceFactoryImpl(@NonNull Context context) {
        this.mContext = context;
        mContext = context;
    }

    @NonNull
    @Override
    public GameServiceProviderInstance create(@NonNull
            GameServiceProviderConfiguration gameServiceProviderConfiguration) {
    public GameServiceProviderInstance create(
            @NonNull GameServiceProviderConfiguration gameServiceProviderConfiguration) {
        return new GameServiceProviderInstanceImpl(
                gameServiceProviderConfiguration.getUserHandle(),
                BackgroundThread.getExecutor(),
                mContext,
                new GameClassifierImpl(mContext.getPackageManager()),
                ActivityManager.getService(),
                ActivityTaskManager.getService(),
                (WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE),
                LocalServices.getService(WindowManagerInternal.class),
+44 −0
Original line number Diff line number Diff line
@@ -16,13 +16,18 @@

package com.android.server.app;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -110,12 +115,23 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
                                gameScreenshotResultFuture);
                    });
                }

                @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                public void restartGame(int taskId) {
                    mContext.enforceCallingPermission(Manifest.permission.FORCE_STOP_PACKAGES,
                            "restartGame()");
                    mBackgroundExecutor.execute(() -> {
                        GameServiceProviderInstanceImpl.this.restartGame(taskId);
                    });
                }
            };

    private final Object mLock = new Object();
    private final UserHandle mUserHandle;
    private final Executor mBackgroundExecutor;
    private final Context mContext;
    private final GameClassifier mGameClassifier;
    private final IActivityManager mActivityManager;
    private final IActivityTaskManager mActivityTaskManager;
    private final WindowManagerService mWindowManagerService;
    private final WindowManagerInternal mWindowManagerInternal;
@@ -131,7 +147,9 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
    GameServiceProviderInstanceImpl(
            @NonNull UserHandle userHandle,
            @NonNull Executor backgroundExecutor,
            @NonNull Context context,
            @NonNull GameClassifier gameClassifier,
            @NonNull IActivityManager activityManager,
            @NonNull IActivityTaskManager activityTaskManager,
            @NonNull WindowManagerService windowManagerService,
            @NonNull WindowManagerInternal windowManagerInternal,
@@ -139,7 +157,9 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
            @NonNull ServiceConnector<IGameSessionService> gameSessionServiceConnector) {
        mUserHandle = userHandle;
        mBackgroundExecutor = backgroundExecutor;
        mContext = context;
        mGameClassifier = gameClassifier;
        mActivityManager = activityManager;
        mActivityTaskManager = activityTaskManager;
        mWindowManagerService = windowManagerService;
        mWindowManagerInternal = windowManagerInternal;
@@ -310,6 +330,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
                    + ") is not awaiting game session request. Ignoring.");
            return;
        }
        mGameSessions.put(taskId, existingGameSessionRecord.withGameSessionRequested());

        GameSessionViewHostConfiguration gameSessionViewHostConfiguration =
                createViewHostConfigurationForTask(taskId);
@@ -532,4 +553,27 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
            }
        });
    }

    private void restartGame(int taskId) {
        String packageName;

        synchronized (mLock) {
            boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId);
            if (!isTaskAssociatedWithGameSession) {
                return;
            }

            packageName = mGameSessions.get(taskId).getComponentName().getPackageName();
        }

        try {
            mActivityManager.forceStopPackage(packageName, UserHandle.USER_CURRENT);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }

        Intent launchIntent =
                mContext.getPackageManager().getLaunchIntentForPackage(packageName);
        mContext.startActivity(launchIntent);
    }
}
Loading