summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tyler Lacey <tlacey@google.com> 2022-01-19 14:45:33 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-01-19 14:45:33 +0000
commit607e78a56f76514edd5b40b7a06c19532e472806 (patch)
treefbe2c19e300e446511e6b761a6dbbd7c7ba7e1df
parent9c74d734719393e3604213dd5f6590c712af0216 (diff)
parent3d7dc6d0a0014a50d358671c5625ff02e06e9cf5 (diff)
Merge "Allow Game Service to trigger the Game Session creation"
-rw-r--r--core/api/system-current.txt11
-rw-r--r--core/java/android/service/games/GameService.java52
-rw-r--r--core/java/android/service/games/GameStartedEvent.aidl23
-rw-r--r--core/java/android/service/games/GameStartedEvent.java119
-rw-r--r--core/java/android/service/games/IGameService.aidl6
-rw-r--r--core/java/android/service/games/IGameServiceController.aidl24
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java87
-rw-r--r--services/core/java/com/android/server/app/GameSessionRecord.java50
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java240
9 files changed, 555 insertions, 57 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 313fc75ed668..949dee3d6942 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10877,9 +10877,11 @@ package android.service.games {
public class GameService extends android.app.Service {
ctor public GameService();
+ method public final void createGameSession(@IntRange(from=0) int);
method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
method public void onConnected();
method public void onDisconnected();
+ method public void onGameStarted(@NonNull android.service.games.GameStartedEvent);
field public static final String ACTION_GAME_SERVICE = "android.service.games.action.GAME_SERVICE";
field public static final String SERVICE_META_DATA = "android.game_service";
}
@@ -10897,6 +10899,15 @@ package android.service.games {
field public static final String ACTION_GAME_SESSION_SERVICE = "android.service.games.action.GAME_SESSION_SERVICE";
}
+ public final class GameStartedEvent implements android.os.Parcelable {
+ ctor public GameStartedEvent(@IntRange(from=0) int, @NonNull String);
+ method public int describeContents();
+ method @NonNull public String getPackageName();
+ method @IntRange(from=0) public int getTaskId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.games.GameStartedEvent> CREATOR;
+ }
+
}
package android.service.notification {
diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java
index 105c2aa53374..870a7e3f2646 100644
--- a/core/java/android/service/games/GameService.java
+++ b/core/java/android/service/games/GameService.java
@@ -16,6 +16,8 @@
package android.service.games;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -38,6 +40,12 @@ import java.util.Objects;
* when a game session should begin. It is always kept running by the system.
* Because of this it should be kept as lightweight as possible.
*
+ * <p> Instead of requiring permissions for sensitive actions (e.g., starting a new game session),
+ * this class is provided with an {@link IGameServiceController} instance which exposes the
+ * sensitive functionality. This controller is provided by the system server when calling the
+ * {@link IGameService#connected(IGameServiceController)} method exposed by this class. The system
+ * server does so only when creating the bound game service.
+ *
* <p>Heavyweight operations (such as showing UI) should be implemented in the
* associated {@link GameSessionService} when a game session is taking place. Its
* implementation should run in a separate process from the {@link GameService}.
@@ -79,12 +87,13 @@ public class GameService extends Service {
*/
public static final String SERVICE_META_DATA = "android.game_service";
+ private IGameServiceController mGameServiceController;
private IGameManagerService mGameManagerService;
private final IGameService mInterface = new IGameService.Stub() {
@Override
- public void connected() {
+ public void connected(IGameServiceController gameServiceController) {
Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
- GameService::doOnConnected, GameService.this));
+ GameService::doOnConnected, GameService.this, gameServiceController));
}
@Override
@@ -92,6 +101,12 @@ public class GameService extends Service {
Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
GameService::onDisconnected, GameService.this));
}
+
+ @Override
+ public void gameStarted(GameStartedEvent gameStartedEvent) {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ GameService::onGameStarted, GameService.this, gameStartedEvent));
+ }
};
private final IBinder.DeathRecipient mGameManagerServiceDeathRecipient = () -> {
@@ -111,7 +126,7 @@ public class GameService extends Service {
return null;
}
- private void doOnConnected() {
+ private void doOnConnected(@NonNull IGameServiceController gameServiceController) {
mGameManagerService =
IGameManagerService.Stub.asInterface(
ServiceManager.getService(Context.GAME_SERVICE));
@@ -122,6 +137,7 @@ public class GameService extends Service {
Log.w(TAG, "Unable to link to death with system service");
}
+ mGameServiceController = gameServiceController;
onConnected();
}
@@ -138,4 +154,34 @@ public class GameService extends Service {
* The service should clean up any resources that it holds at this point.
*/
public void onDisconnected() {}
+
+ /**
+ * Called when a game task is started. It is the responsibility of the service to determine what
+ * action to take (e.g., request that a game session be created).
+ *
+ * @param gameStartedEvent Contains information about the game being started.
+ */
+ public void onGameStarted(@NonNull GameStartedEvent gameStartedEvent) {}
+
+ /**
+ * Call to create a new game session be created for a game. This method may be called
+ * by a game service following {@link #onGameStarted}, using the task ID provided by the
+ * provided {@link GameStartedEvent} (using {@link GameStartedEvent#getTaskId}).
+ *
+ * If a game session already exists for the game task, this call will be ignored and the
+ * existing session will continue.
+ *
+ * @param taskId The taskId of the game.
+ */
+ public final void createGameSession(@IntRange(from = 0) int taskId) {
+ if (mGameServiceController == null) {
+ throw new IllegalStateException("Can not call before connected()");
+ }
+
+ try {
+ mGameServiceController.createGameSession(taskId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Request for game session failed", e);
+ }
+ }
}
diff --git a/core/java/android/service/games/GameStartedEvent.aidl b/core/java/android/service/games/GameStartedEvent.aidl
new file mode 100644
index 000000000000..8a5a4a16abab
--- /dev/null
+++ b/core/java/android/service/games/GameStartedEvent.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+
+/**
+ * @hide
+ */
+parcelable GameStartedEvent; \ No newline at end of file
diff --git a/core/java/android/service/games/GameStartedEvent.java b/core/java/android/service/games/GameStartedEvent.java
new file mode 100644
index 000000000000..bf292606fbf6
--- /dev/null
+++ b/core/java/android/service/games/GameStartedEvent.java
@@ -0,0 +1,119 @@
+/*
+ * 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.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Event object provided when a game task is started.
+ *
+ * This is provided to the Game Service via
+ * {@link GameService#onGameStarted(GameStartedEvent)}. It includes the game's taskId
+ * (see {@link #getTaskId}) that the game's package name (see {@link #getPackageName}).
+ *
+ * @hide
+ */
+@SystemApi
+public final class GameStartedEvent implements Parcelable {
+
+ @NonNull
+ public static final Parcelable.Creator<GameStartedEvent> CREATOR =
+ new Parcelable.Creator<GameStartedEvent>() {
+ @Override
+ public GameStartedEvent createFromParcel(Parcel source) {
+ return new GameStartedEvent(
+ source.readInt(),
+ source.readString());
+ }
+
+ @Override
+ public GameStartedEvent[] newArray(int size) {
+ return new GameStartedEvent[0];
+ }
+ };
+
+ private final int mTaskId;
+ private final String mPackageName;
+
+ public GameStartedEvent(@IntRange(from = 0) int taskId, @NonNull String packageName) {
+ this.mTaskId = taskId;
+ this.mPackageName = packageName;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mTaskId);
+ dest.writeString(mPackageName);
+ }
+
+ /**
+ * Unique identifier for the task associated with the game.
+ */
+ @IntRange(from = 0)
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ /**
+ * The package name for the game.
+ */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public String toString() {
+ return "GameStartedEvent{"
+ + "mTaskId="
+ + mTaskId
+ + ", mPackageName='"
+ + mPackageName
+ + "\'}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof GameStartedEvent)) {
+ return false;
+ }
+
+ GameStartedEvent that = (GameStartedEvent) o;
+ return mTaskId == that.mTaskId
+ && Objects.equals(mPackageName, that.mPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTaskId, mPackageName);
+ }
+}
diff --git a/core/java/android/service/games/IGameService.aidl b/core/java/android/service/games/IGameService.aidl
index 8a0d6365977b..38c8416117e0 100644
--- a/core/java/android/service/games/IGameService.aidl
+++ b/core/java/android/service/games/IGameService.aidl
@@ -16,10 +16,14 @@
package android.service.games;
+import android.service.games.GameStartedEvent;
+import android.service.games.IGameServiceController;
+
/**
* @hide
*/
oneway interface IGameService {
- void connected();
+ void connected(in IGameServiceController gameServiceController);
void disconnected();
+ void gameStarted(in GameStartedEvent gameStartedEvent);
}
diff --git a/core/java/android/service/games/IGameServiceController.aidl b/core/java/android/service/games/IGameServiceController.aidl
new file mode 100644
index 000000000000..886f519b6605
--- /dev/null
+++ b/core/java/android/service/games/IGameServiceController.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * @hide
+ */
+oneway interface IGameServiceController {
+ void createGameSession(int taskId);
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 3f3f257aedc5..cc060e94a52a 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -24,7 +24,9 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.games.CreateGameSessionRequest;
+import android.service.games.GameStartedEvent;
import android.service.games.IGameService;
+import android.service.games.IGameServiceController;
import android.service.games.IGameSession;
import android.service.games.IGameSessionService;
import android.util.Slog;
@@ -61,6 +63,17 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
});
}
};
+
+ private final IGameServiceController mGameServiceController =
+ new IGameServiceController.Stub() {
+ @Override
+ public void createGameSession(int taskId) {
+ mBackgroundExecutor.execute(() -> {
+ GameServiceProviderInstanceImpl.this.createGameSession(taskId);
+ });
+ }
+ };
+
private final Object mLock = new Object();
private final UserHandle mUserHandle;
private final Executor mBackgroundExecutor;
@@ -114,7 +127,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
// TODO(b/204503192): In cases where the connection to the game service fails retry with
// back off mechanism.
AndroidFuture<Void> unusedPostConnectedFuture = mGameServiceConnector.post(gameService -> {
- gameService.connected();
+ gameService.connected(mGameServiceController);
});
try {
@@ -168,10 +181,38 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
synchronized (mLock) {
- createGameSessionLocked(taskId, componentName);
+ gameTaskStartedLocked(taskId, componentName);
}
}
+ @GuardedBy("mLock")
+ private void gameTaskStartedLocked(int sessionId, @NonNull ComponentName componentName) {
+ if (DEBUG) {
+ Slog.i(TAG, "gameStartedLocked() id: " + sessionId + " component: " + componentName);
+ }
+
+ if (!mIsRunning) {
+ return;
+ }
+
+ GameSessionRecord existingGameSessionRecord = mGameSessions.get(sessionId);
+ if (existingGameSessionRecord != null) {
+ Slog.w(TAG, "Existing game session found for task (id: " + sessionId
+ + ") creation. Ignoring.");
+ return;
+ }
+
+ GameSessionRecord gameSessionRecord = GameSessionRecord.awaitingGameSessionRequest(
+ sessionId, componentName);
+ mGameSessions.put(sessionId, gameSessionRecord);
+
+ AndroidFuture<Void> unusedPostGameStartedFuture = mGameServiceConnector.post(
+ gameService -> {
+ gameService.gameStarted(
+ new GameStartedEvent(sessionId, componentName.getPackageName()));
+ });
+ }
+
private void onTaskRemoved(int taskId) {
synchronized (mLock) {
boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId);
@@ -179,14 +220,20 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
return;
}
- destroyGameSessionLocked(taskId);
+ destroyGameSessionIfNecessaryLocked(taskId);
+ }
+ }
+
+ private void createGameSession(int taskId) {
+ synchronized (mLock) {
+ createGameSessionLocked(taskId);
}
}
@GuardedBy("mLock")
- private void createGameSessionLocked(int sessionId, @NonNull ComponentName componentName) {
+ private void createGameSessionLocked(int sessionId) {
if (DEBUG) {
- Slog.i(TAG, "createGameSession() id: " + sessionId + " component: " + componentName);
+ Slog.i(TAG, "createGameSessionLocked() id: " + sessionId);
}
if (!mIsRunning) {
@@ -194,15 +241,19 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
GameSessionRecord existingGameSessionRecord = mGameSessions.get(sessionId);
- if (existingGameSessionRecord != null) {
- Slog.w(TAG, "Existing game session found for task (id: " + sessionId
+ if (existingGameSessionRecord == null) {
+ Slog.w(TAG, "No existing game session record found for task (id: " + sessionId
+ ") creation. Ignoring.");
return;
}
+ if (!existingGameSessionRecord.isAwaitingGameSessionRequest()) {
+ Slog.w(TAG, "Existing game session for task (id: " + sessionId
+ + ") is not awaiting game session request. Ignoring.");
+ return;
+ }
+ mGameSessions.put(sessionId, existingGameSessionRecord.withGameSessionRequested());
- GameSessionRecord gameSessionRecord = GameSessionRecord.pendingGameSession(sessionId,
- componentName);
- mGameSessions.put(sessionId, gameSessionRecord);
+ ComponentName componentName = existingGameSessionRecord.getComponentName();
// TODO(b/207035150): Allow the game service provider to determine if a game session
// should be created. For now we will assume all games should have a session.
@@ -211,10 +262,10 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
.whenCompleteAsync((gameSessionIBinder, exception) -> {
IGameSession gameSession = IGameSession.Stub.asInterface(gameSessionIBinder);
if (exception != null || gameSession == null) {
- Slog.w(TAG, "Failed to create GameSession: " + gameSessionRecord,
+ Slog.w(TAG, "Failed to create GameSession: " + existingGameSessionRecord,
exception);
synchronized (mLock) {
- destroyGameSessionLocked(sessionId);
+ destroyGameSessionIfNecessaryLocked(sessionId);
}
return;
}
@@ -239,9 +290,19 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
GameSessionRecord gameSessionRecord = mGameSessions.get(sessionId);
+ boolean isValidAttachRequest = true;
if (gameSessionRecord == null) {
Slog.w(TAG, "No associated game session record. Destroying id: " + sessionId);
+ isValidAttachRequest = false;
+ }
+ if (gameSessionRecord != null && !gameSessionRecord.isGameSessionRequested()) {
+ Slog.w(TAG,
+ "Game session not requested for existing game session record. Destroying id: "
+ + sessionId);
+ isValidAttachRequest = false;
+ }
+ if (!isValidAttachRequest) {
try {
gameSession.destroy();
} catch (RemoteException ex) {
@@ -254,7 +315,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
}
@GuardedBy("mLock")
- private void destroyGameSessionLocked(int sessionId) {
+ private void destroyGameSessionIfNecessaryLocked(int sessionId) {
// TODO(b/204503192): Limit the lifespan of the game session in the Game Service provider
// to only when the associated task is running. Right now it is possible for a task to
// move into the background and for all associated processes to die and for the Game Session
diff --git a/services/core/java/com/android/server/app/GameSessionRecord.java b/services/core/java/com/android/server/app/GameSessionRecord.java
index 329e9e8144e0..e9daceb0cd39 100644
--- a/services/core/java/com/android/server/app/GameSessionRecord.java
+++ b/services/core/java/com/android/server/app/GameSessionRecord.java
@@ -25,28 +25,59 @@ import java.util.Objects;
final class GameSessionRecord {
+ private enum State {
+ // Game task is running, but GameSession not created.
+ NO_GAME_SESSION_REQUESTED,
+ // Game Service provider requested a Game Session and we are in the
+ // process of creating it. GameSessionRecord.getGameSession() == null;
+ GAME_SESSION_REQUESTED,
+ // A Game Session is created and attached.
+ // GameSessionRecord.getGameSession() != null.
+ GAME_SESSION_ATTACHED,
+ }
+
private final int mTaskId;
private final ComponentName mRootComponentName;
@Nullable
private final IGameSession mIGameSession;
+ private final State mState;
- static GameSessionRecord pendingGameSession(int taskId, ComponentName rootComponentName) {
- return new GameSessionRecord(taskId, rootComponentName, /* gameSession= */ null);
+ static GameSessionRecord awaitingGameSessionRequest(int taskId,
+ ComponentName rootComponentName) {
+ return new GameSessionRecord(taskId, rootComponentName, /* gameSession= */ null,
+ State.NO_GAME_SESSION_REQUESTED);
}
private GameSessionRecord(
int taskId,
@NonNull ComponentName rootComponentName,
- @Nullable IGameSession gameSession) {
+ @Nullable IGameSession gameSession,
+ @NonNull State state) {
this.mTaskId = taskId;
this.mRootComponentName = rootComponentName;
this.mIGameSession = gameSession;
+ this.mState = state;
+ }
+
+ public boolean isAwaitingGameSessionRequest() {
+ return mState == State.NO_GAME_SESSION_REQUESTED;
+ }
+
+ @NonNull
+ public GameSessionRecord withGameSessionRequested() {
+ return new GameSessionRecord(mTaskId, mRootComponentName, /* gameSession=*/ null,
+ State.GAME_SESSION_REQUESTED);
+ }
+
+ public boolean isGameSessionRequested() {
+ return mState == State.GAME_SESSION_REQUESTED;
}
@NonNull
public GameSessionRecord withGameSession(@NonNull IGameSession gameSession) {
Objects.requireNonNull(gameSession);
- return new GameSessionRecord(mTaskId, mRootComponentName, gameSession);
+ return new GameSessionRecord(mTaskId, mRootComponentName, gameSession,
+ State.GAME_SESSION_ATTACHED);
}
@Nullable
@@ -54,6 +85,11 @@ final class GameSessionRecord {
return mIGameSession;
}
+ @NonNull
+ public ComponentName getComponentName() {
+ return mRootComponentName;
+ }
+
@Override
public String toString() {
return "GameSessionRecord{"
@@ -63,6 +99,8 @@ final class GameSessionRecord {
+ mRootComponentName
+ ", mIGameSession="
+ mIGameSession
+ + ", mState="
+ + mState
+ '}';
}
@@ -78,11 +116,11 @@ final class GameSessionRecord {
GameSessionRecord that = (GameSessionRecord) o;
return mTaskId == that.mTaskId && mRootComponentName.equals(that.mRootComponentName)
- && Objects.equals(mIGameSession, that.mIGameSession);
+ && Objects.equals(mIGameSession, that.mIGameSession) && mState == that.mState;
}
@Override
public int hashCode() {
- return Objects.hash(mTaskId, mRootComponentName, mIGameSession);
+ return Objects.hash(mTaskId, mRootComponentName, mIGameSession, mState);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index b6c706ed2730..0d513bb2d68c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -18,6 +18,7 @@ package com.android.server.app;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
@@ -35,7 +36,9 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.service.games.CreateGameSessionRequest;
+import android.service.games.GameStartedEvent;
import android.service.games.IGameService;
+import android.service.games.IGameServiceController;
import android.service.games.IGameSession;
import android.service.games.IGameSessionService;
@@ -50,6 +53,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
@@ -135,7 +139,7 @@ public final class GameServiceProviderInstanceImplTest {
public void start_startsGameSession() throws Exception {
mGameServiceProviderInstance.start();
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
@@ -146,7 +150,7 @@ public final class GameServiceProviderInstanceImplTest {
public void start_multipleTimes_startsGameSessionOnce() throws Exception {
mGameServiceProviderInstance.start();
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
@@ -167,7 +171,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verify(mMockGameService).disconnected();
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
@@ -183,9 +187,9 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verify(mMockGameService).disconnected();
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verify(mMockGameService).disconnected();
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
@@ -199,7 +203,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.stop();
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verify(mMockGameService).disconnected();
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
@@ -231,7 +235,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.stop();
dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verify(mMockGameService).disconnected();
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse();
@@ -244,7 +248,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, APP_A_MAIN_ACTIVITY);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
@@ -256,7 +260,7 @@ public final class GameServiceProviderInstanceImplTest {
mGameServiceProviderInstance.start();
dispatchTaskCreated(10, null);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
mInOrder.verifyNoMoreInteractions();
assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
@@ -264,18 +268,84 @@ public final class GameServiceProviderInstanceImplTest {
}
@Test
- public void gameTaskStarted_createsGameSession() throws Exception {
+ public void gameSessionRequested_withoutTaskDispatch_ignoredAndDoesNotCrash() throws Exception {
+ mGameServiceProviderInstance.start();
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ controllerArgumentCaptor.getValue().createGameSession(10);
+
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void gameTaskStarted_noSessionRequest_callsStartGame() throws Exception {
+ mGameServiceProviderInstance.start();
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception {
CreateGameSessionRequest createGameSessionRequest =
new CreateGameSessionRequest(10, GAME_A_PACKAGE);
Supplier<AndroidFuture<IBinder>> gameSession10Future =
captureCreateGameSessionFuture(createGameSessionRequest);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
+ IGameSessionStub gameSession10 = new IGameSessionStub();
+ gameSession10Future.get().complete(gameSession10);
+
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession10.mIsDestroyed).isFalse();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void gameTaskStartedAndSessionRequested_secondSessionRequest_ignoredAndDoesNotCrash()
+ throws Exception {
+ CreateGameSessionRequest createGameSessionRequest =
+ new CreateGameSessionRequest(10, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession10Future =
+ captureCreateGameSessionFuture(createGameSessionRequest);
+
+ mGameServiceProviderInstance.start();
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
- mInOrder.verify(mMockGameService).connected();
+ controllerArgumentCaptor.getValue().createGameSession(10);
+
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isFalse();
@@ -294,12 +364,18 @@ public final class GameServiceProviderInstanceImplTest {
captureCreateGameSessionFuture(createGameSessionRequest);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
dispatchTaskRemoved(10);
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
@@ -317,12 +393,18 @@ public final class GameServiceProviderInstanceImplTest {
captureCreateGameSessionFuture(createGameSessionRequest);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
dispatchTaskRemoved(10);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest), any());
mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
@@ -333,7 +415,8 @@ public final class GameServiceProviderInstanceImplTest {
}
@Test
- public void gameTaskStarted_multipleTimes_createsMultipleGameSessions() throws Exception {
+ public void gameTaskStartedAndSessionRequested_multipleTimes_createsMultipleGameSessions()
+ throws Exception {
CreateGameSessionRequest createGameSessionRequest10 =
new CreateGameSessionRequest(10, GAME_A_PACKAGE);
Supplier<AndroidFuture<IBinder>> gameSession10Future =
@@ -345,16 +428,25 @@ public final class GameServiceProviderInstanceImplTest {
captureCreateGameSessionFuture(createGameSessionRequest11);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
- dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession11 = new IGameSessionStub();
gameSession11Future.get().complete(gameSession11);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isFalse();
@@ -366,6 +458,40 @@ public final class GameServiceProviderInstanceImplTest {
}
@Test
+ public void gameTaskStartedTwice_sessionRequestedSecondTimeOnly_createsOneGameSessions()
+ throws Exception {
+ CreateGameSessionRequest createGameSessionRequest11 =
+ new CreateGameSessionRequest(11, GAME_A_PACKAGE);
+ Supplier<AndroidFuture<IBinder>> gameSession11Future =
+ captureCreateGameSessionFuture(createGameSessionRequest11);
+
+ // The game task is started twice, but a session is requested only for the second one.
+ mGameServiceProviderInstance.start();
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+
+ dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
+ IGameSessionStub gameSession11 = new IGameSessionStub();
+ gameSession11Future.get().complete(gameSession11);
+
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
+ mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
+ mInOrder.verifyNoMoreInteractions();
+ assertThat(gameSession11.mIsDestroyed).isFalse();
+ assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1);
+ assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue();
+ assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(1);
+ }
+
+ @Test
public void gameTaskRemoved_afterMultipleCreated_destroysOnlyThatGameSession()
throws Exception {
CreateGameSessionRequest createGameSessionRequest10 =
@@ -379,18 +505,27 @@ public final class GameServiceProviderInstanceImplTest {
captureCreateGameSessionFuture(createGameSessionRequest11);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
- dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession11 = new IGameSessionStub();
gameSession11Future.get().complete(gameSession11);
dispatchTaskRemoved(10);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
@@ -414,19 +549,28 @@ public final class GameServiceProviderInstanceImplTest {
captureCreateGameSessionFuture(createGameSessionRequest11);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
- dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession11 = new IGameSessionStub();
gameSession11Future.get().complete(gameSession11);
dispatchTaskRemoved(10);
dispatchTaskRemoved(11);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
@@ -438,7 +582,7 @@ public final class GameServiceProviderInstanceImplTest {
}
@Test
- public void gameTasksCreated_afterAllPreviousSessionsDestroyed_createsSession()
+ public void gameTasksCreatedAndSessionsReq_afterAllPreviousSessionsDestroyed_createsSession()
throws Exception {
CreateGameSessionRequest createGameSessionRequest10 =
new CreateGameSessionRequest(10, GAME_A_PACKAGE);
@@ -456,24 +600,36 @@ public final class GameServiceProviderInstanceImplTest {
captureCreateGameSessionFuture(createGameSessionRequest12);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
- dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession11 = new IGameSessionStub();
gameSession11Future.get().complete(gameSession11);
dispatchTaskRemoved(10);
dispatchTaskRemoved(11);
- dispatchTaskCreated(12, GAME_A_MAIN_ACTIVITY);
+ dispatchTaskCreatedAndTriggerSessionRequest(12, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession12 = new IGameSessionStub();
gameSession11Future.get().complete(gameSession12);
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(12, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest12), any());
mInOrder.verifyNoMoreInteractions();
assertThat(gameSession10.mIsDestroyed).isTrue();
@@ -498,16 +654,25 @@ public final class GameServiceProviderInstanceImplTest {
captureCreateGameSessionFuture(createGameSessionRequest11);
mGameServiceProviderInstance.start();
- dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY);
+ ArgumentCaptor<IGameServiceController> controllerArgumentCaptor = ArgumentCaptor.forClass(
+ IGameServiceController.class);
+ verify(mMockGameService).connected(controllerArgumentCaptor.capture());
+ dispatchTaskCreatedAndTriggerSessionRequest(10, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession10 = new IGameSessionStub();
gameSession10Future.get().complete(gameSession10);
- dispatchTaskCreated(11, GAME_A_MAIN_ACTIVITY);
+ dispatchTaskCreatedAndTriggerSessionRequest(11, GAME_A_MAIN_ACTIVITY,
+ controllerArgumentCaptor.getValue());
IGameSessionStub gameSession11 = new IGameSessionStub();
gameSession11Future.get().complete(gameSession11);
mGameServiceProviderInstance.stop();
- mInOrder.verify(mMockGameService).connected();
+ mInOrder.verify(mMockGameService).connected(any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(10, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest10), any());
+ mInOrder.verify(mMockGameService).gameStarted(
+ eq(new GameStartedEvent(11, GAME_A_PACKAGE)));
mInOrder.verify(mMockGameSessionService).create(eq(createGameSessionRequest11), any());
mInOrder.verify(mMockGameService).disconnected();
mInOrder.verifyNoMoreInteractions();
@@ -536,6 +701,13 @@ public final class GameServiceProviderInstanceImplTest {
});
}
+ private void dispatchTaskCreatedAndTriggerSessionRequest(int taskId,
+ @Nullable ComponentName componentName, IGameServiceController gameServiceController)
+ throws Exception {
+ dispatchTaskCreated(taskId, componentName);
+ gameServiceController.createGameSession(taskId);
+ }
+
private void dispatchTaskCreated(int taskId, @Nullable ComponentName componentName) {
dispatchTaskChangeEvent(taskStackListener -> {
taskStackListener.onTaskCreated(taskId, componentName);