1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.games; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SdkConstant; 22 import android.annotation.SystemApi; 23 import android.app.Service; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.hardware.display.DisplayManager; 27 import android.os.Binder; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.view.Display; 31 import android.view.SurfaceControlViewHost; 32 import android.view.WindowManager; 33 34 import com.android.internal.infra.AndroidFuture; 35 import com.android.internal.util.function.pooled.PooledLambda; 36 37 import java.util.Objects; 38 39 /** 40 * Service that hosts active game sessions. 41 * 42 * This service should be in a separate process from the {@link GameService}. This 43 * allows it to perform the heavyweight operations associated with rendering a game 44 * session overlay while games are running and release these resources (by allowing 45 * the process to be killed) when games are not running. 46 * 47 * Game Service providers must extend {@link GameSessionService} and declare the service in their 48 * Manifest. The service must require the {@link android.Manifest.permission#BIND_GAME_SERVICE} so 49 * that other application can not abuse it. This service is used to create instances of 50 * {@link GameSession} via {@link #onNewSession(CreateGameSessionRequest)} and will remain bound to 51 * so long as at least one {@link GameSession} is running. 52 * 53 * @hide 54 */ 55 @SystemApi 56 public abstract class GameSessionService extends Service { 57 /** 58 * The {@link Intent} action used when binding to the service. 59 * To be supported, the service must require the 60 * {@link android.Manifest.permission#BIND_GAME_SERVICE} permission so 61 * that other applications can not abuse it. 62 */ 63 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 64 public static final String ACTION_GAME_SESSION_SERVICE = 65 "android.service.games.action.GAME_SESSION_SERVICE"; 66 67 private final IGameSessionService mInterface = new IGameSessionService.Stub() { 68 @Override 69 public void create( 70 IGameSessionController gameSessionController, 71 CreateGameSessionRequest createGameSessionRequest, 72 GameSessionViewHostConfiguration gameSessionViewHostConfiguration, 73 AndroidFuture gameSessionFuture) { 74 Handler.getMain().post(PooledLambda.obtainRunnable( 75 GameSessionService::doCreate, GameSessionService.this, 76 gameSessionController, 77 createGameSessionRequest, 78 gameSessionViewHostConfiguration, 79 gameSessionFuture)); 80 } 81 }; 82 83 private DisplayManager mDisplayManager; 84 85 @Override onCreate()86 public void onCreate() { 87 super.onCreate(); 88 mDisplayManager = this.getSystemService(DisplayManager.class); 89 } 90 91 @Override 92 @Nullable onBind(@ullable Intent intent)93 public final IBinder onBind(@Nullable Intent intent) { 94 if (intent == null) { 95 return null; 96 } 97 98 if (!ACTION_GAME_SESSION_SERVICE.equals(intent.getAction())) { 99 return null; 100 } 101 102 return mInterface.asBinder(); 103 } 104 doCreate( IGameSessionController gameSessionController, CreateGameSessionRequest createGameSessionRequest, GameSessionViewHostConfiguration gameSessionViewHostConfiguration, AndroidFuture<CreateGameSessionResult> createGameSessionResultFuture)105 private void doCreate( 106 IGameSessionController gameSessionController, 107 CreateGameSessionRequest createGameSessionRequest, 108 GameSessionViewHostConfiguration gameSessionViewHostConfiguration, 109 AndroidFuture<CreateGameSessionResult> createGameSessionResultFuture) { 110 GameSession gameSession = onNewSession(createGameSessionRequest); 111 Objects.requireNonNull(gameSession); 112 113 Display display = mDisplayManager.getDisplay(gameSessionViewHostConfiguration.mDisplayId); 114 if (display == null) { 115 createGameSessionResultFuture.completeExceptionally( 116 new IllegalStateException("No display found for id: " 117 + gameSessionViewHostConfiguration.mDisplayId)); 118 return; 119 } 120 121 IBinder hostToken = new Binder(); 122 123 // Use a WindowContext so that views attached to the SurfaceControlViewHost will receive 124 // configuration changes (rather than always perceiving the global configuration). 125 final Context windowContext = createWindowContext(display, 126 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, /*options=*/ null); 127 SurfaceControlViewHost surfaceControlViewHost = 128 new SurfaceControlViewHost(windowContext, display, hostToken, "GameSessionService"); 129 130 gameSession.attach( 131 gameSessionController, 132 createGameSessionRequest.getTaskId(), 133 windowContext, 134 surfaceControlViewHost, 135 gameSessionViewHostConfiguration.mWidthPx, 136 gameSessionViewHostConfiguration.mHeightPx); 137 138 CreateGameSessionResult createGameSessionResult = 139 new CreateGameSessionResult(gameSession.mInterface, 140 surfaceControlViewHost.getSurfacePackage()); 141 142 createGameSessionResultFuture.complete(createGameSessionResult); 143 144 gameSession.doCreate(); 145 } 146 147 /** 148 * Request to create a new {@link GameSession}. 149 */ 150 @NonNull onNewSession( @onNull CreateGameSessionRequest createGameSessionRequest)151 public abstract GameSession onNewSession( 152 @NonNull CreateGameSessionRequest createGameSessionRequest); 153 } 154