153 lines
5.9 KiB
Java
153 lines
5.9 KiB
Java
/*
|
|
* Copyright (C) 2021 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.SdkConstant;
|
|
import android.annotation.SystemApi;
|
|
import android.app.Service;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.hardware.display.DisplayManager;
|
|
import android.os.Handler;
|
|
import android.os.IBinder;
|
|
import android.view.Display;
|
|
import android.view.SurfaceControlViewHost;
|
|
import android.view.WindowManager;
|
|
import android.window.InputTransferToken;
|
|
|
|
import com.android.internal.infra.AndroidFuture;
|
|
import com.android.internal.util.function.pooled.PooledLambda;
|
|
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* Service that hosts active game sessions.
|
|
*
|
|
* This service should be in a separate process from the {@link GameService}. This
|
|
* allows it to perform the heavyweight operations associated with rendering a game
|
|
* session overlay while games are running and release these resources (by allowing
|
|
* the process to be killed) when games are not running.
|
|
*
|
|
* Game Service providers must extend {@link GameSessionService} and declare the service in their
|
|
* Manifest. The service must require the {@link android.Manifest.permission#BIND_GAME_SERVICE} so
|
|
* that other application can not abuse it. This service is used to create instances of
|
|
* {@link GameSession} via {@link #onNewSession(CreateGameSessionRequest)} and will remain bound to
|
|
* so long as at least one {@link GameSession} is running.
|
|
*
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
public abstract class GameSessionService extends Service {
|
|
/**
|
|
* The {@link Intent} action used when binding to the service.
|
|
* To be supported, the service must require the
|
|
* {@link android.Manifest.permission#BIND_GAME_SERVICE} permission so
|
|
* that other applications can not abuse it.
|
|
*/
|
|
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
|
|
public static final String ACTION_GAME_SESSION_SERVICE =
|
|
"android.service.games.action.GAME_SESSION_SERVICE";
|
|
|
|
private final IGameSessionService mInterface = new IGameSessionService.Stub() {
|
|
@Override
|
|
public void create(
|
|
IGameSessionController gameSessionController,
|
|
CreateGameSessionRequest createGameSessionRequest,
|
|
GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
|
|
AndroidFuture gameSessionFuture) {
|
|
Handler.getMain().post(PooledLambda.obtainRunnable(
|
|
GameSessionService::doCreate, GameSessionService.this,
|
|
gameSessionController,
|
|
createGameSessionRequest,
|
|
gameSessionViewHostConfiguration,
|
|
gameSessionFuture));
|
|
}
|
|
};
|
|
|
|
private DisplayManager mDisplayManager;
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
mDisplayManager = this.getSystemService(DisplayManager.class);
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public final IBinder onBind(@Nullable Intent intent) {
|
|
if (intent == null) {
|
|
return null;
|
|
}
|
|
|
|
if (!ACTION_GAME_SESSION_SERVICE.equals(intent.getAction())) {
|
|
return null;
|
|
}
|
|
|
|
return mInterface.asBinder();
|
|
}
|
|
|
|
private void doCreate(
|
|
IGameSessionController gameSessionController,
|
|
CreateGameSessionRequest createGameSessionRequest,
|
|
GameSessionViewHostConfiguration gameSessionViewHostConfiguration,
|
|
AndroidFuture<CreateGameSessionResult> createGameSessionResultFuture) {
|
|
GameSession gameSession = onNewSession(createGameSessionRequest);
|
|
Objects.requireNonNull(gameSession);
|
|
|
|
Display display = mDisplayManager.getDisplay(gameSessionViewHostConfiguration.mDisplayId);
|
|
if (display == null) {
|
|
createGameSessionResultFuture.completeExceptionally(
|
|
new IllegalStateException("No display found for id: "
|
|
+ gameSessionViewHostConfiguration.mDisplayId));
|
|
return;
|
|
}
|
|
|
|
// Use a WindowContext so that views attached to the SurfaceControlViewHost will receive
|
|
// configuration changes (rather than always perceiving the global configuration).
|
|
final Context windowContext = createWindowContext(display,
|
|
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, /*options=*/ null);
|
|
SurfaceControlViewHost surfaceControlViewHost =
|
|
new SurfaceControlViewHost(windowContext, display,
|
|
new InputTransferToken(), "GameSessionService");
|
|
|
|
gameSession.attach(
|
|
gameSessionController,
|
|
createGameSessionRequest.getTaskId(),
|
|
windowContext,
|
|
surfaceControlViewHost,
|
|
gameSessionViewHostConfiguration.mWidthPx,
|
|
gameSessionViewHostConfiguration.mHeightPx);
|
|
|
|
CreateGameSessionResult createGameSessionResult =
|
|
new CreateGameSessionResult(gameSession.mInterface,
|
|
surfaceControlViewHost.getSurfacePackage());
|
|
|
|
createGameSessionResultFuture.complete(createGameSessionResult);
|
|
|
|
gameSession.doCreate();
|
|
}
|
|
|
|
/**
|
|
* Request to create a new {@link GameSession}.
|
|
*/
|
|
@NonNull
|
|
public abstract GameSession onNewSession(
|
|
@NonNull CreateGameSessionRequest createGameSessionRequest);
|
|
}
|