/* * 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 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.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.app.GameServiceProviderInstanceImplTest.FakeGameService.GameServiceState; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager; import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.app.IProcessObserver; import android.app.ITaskStackListener; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Picture; import android.graphics.Rect; import android.net.Uri; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.service.games.CreateGameSessionRequest; import android.service.games.CreateGameSessionResult; import android.service.games.GameScreenshotResult; import android.service.games.GameSessionViewHostConfiguration; import android.service.games.GameStartedEvent; import android.service.games.IGameService; import android.service.games.IGameServiceController; import android.service.games.IGameSession; import android.service.games.IGameSessionController; import android.service.games.IGameSessionService; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost.SurfacePackage; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.FunctionalUtils.ThrowingConsumer; import com.android.internal.util.Preconditions; import com.android.internal.util.ScreenshotHelper; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener; import com.android.server.wm.WindowManagerService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.HashMap; import java.util.function.Consumer; /** * Unit tests for the {@link GameServiceProviderInstanceImpl}. */ @RunWith(AndroidJUnit4.class) @SmallTest @Presubmit public final class GameServiceProviderInstanceImplTest { private static final GameSessionViewHostConfiguration DEFAULT_GAME_SESSION_VIEW_HOST_CONFIGURATION = new GameSessionViewHostConfiguration(1, 500, 800); private static final int USER_ID = 10; private static final String APP_A_PACKAGE = "com.package.app.a"; private static final ComponentName APP_A_MAIN_ACTIVITY = new ComponentName(APP_A_PACKAGE, "com.package.app.a.MainActivity"); private static final String GAME_A_PACKAGE = "com.package.game.a"; private static final ComponentName GAME_A_MAIN_ACTIVITY = new ComponentName(GAME_A_PACKAGE, "com.package.game.a.MainActivity"); private static final String GAME_B_PACKAGE = "com.package.game.b"; private static final ComponentName GAME_B_MAIN_ACTIVITY = new ComponentName(GAME_B_PACKAGE, "com.package.game.b.MainActivity"); private static final Bitmap TEST_BITMAP; static { Picture picture = new Picture(); Canvas canvas = picture.beginRecording(200, 100); Paint p = new Paint(); p.setColor(Color.BLACK); canvas.drawCircle(10, 10, 10, p); picture.endRecording(); TEST_BITMAP = Bitmap.createBitmap(picture); } private MockitoSession mMockingSession; private GameServiceProviderInstance mGameServiceProviderInstance; @Mock private ActivityManagerInternal mMockActivityManagerInternal; @Mock private IActivityTaskManager mMockActivityTaskManager; @Mock private WindowManagerService mMockWindowManagerService; @Mock private WindowManagerInternal mMockWindowManagerInternal; @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal; @Mock private IActivityManager mMockActivityManager; @Mock private ScreenshotHelper mMockScreenshotHelper; private MockContext mMockContext; private FakeGameClassifier mFakeGameClassifier; private FakeGameService mFakeGameService; private FakeServiceConnector mFakeGameServiceConnector; private FakeGameSessionService mFakeGameSessionService; private FakeServiceConnector mFakeGameSessionServiceConnector; private ArrayList mTaskStackListeners; private ArrayList mProcessObservers; private ArrayList mTaskSystemBarsListeners; private ArrayList mRunningTaskInfos; @Mock private PackageManager mMockPackageManager; @Before public void setUp() throws PackageManager.NameNotFoundException, RemoteException { mMockingSession = mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) .startMocking(); mMockContext = new MockContext(InstrumentationRegistry.getInstrumentation().getContext()); mFakeGameClassifier = new FakeGameClassifier(); mFakeGameClassifier.recordGamePackage(GAME_A_PACKAGE); mFakeGameClassifier.recordGamePackage(GAME_B_PACKAGE); mFakeGameService = new FakeGameService(); mFakeGameServiceConnector = new FakeServiceConnector<>(mFakeGameService); mFakeGameSessionService = new FakeGameSessionService(); mFakeGameSessionServiceConnector = new FakeServiceConnector<>(mFakeGameSessionService); mTaskStackListeners = new ArrayList<>(); doAnswer(invocation -> { mTaskStackListeners.add(invocation.getArgument(0)); return null; }).when(mMockActivityTaskManager).registerTaskStackListener(any()); doAnswer(invocation -> { mTaskStackListeners.remove(invocation.getArgument(0)); return null; }).when(mMockActivityTaskManager).unregisterTaskStackListener(any()); mProcessObservers = new ArrayList<>(); doAnswer(invocation -> { mProcessObservers.add(invocation.getArgument(0)); return null; }).when(mMockActivityManager).registerProcessObserver(any()); doAnswer(invocation -> { mProcessObservers.remove(invocation.getArgument(0)); return null; }).when(mMockActivityManager).unregisterProcessObserver(any()); mTaskSystemBarsListeners = new ArrayList<>(); doAnswer(invocation -> { mTaskSystemBarsListeners.add(invocation.getArgument(0)); return null; }).when(mMockWindowManagerInternal).registerTaskSystemBarsListener(any()); doAnswer(invocation -> { mTaskSystemBarsListeners.remove(invocation.getArgument(0)); return null; }).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any()); mRunningTaskInfos = new ArrayList<>(); when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt())) .thenReturn(mRunningTaskInfos); final UserHandle userHandle = new UserHandle(USER_ID); mGameServiceProviderInstance = new GameServiceProviderInstanceImpl( userHandle, ConcurrentUtils.DIRECT_EXECUTOR, mMockContext, new GameTaskInfoProvider(userHandle, mMockActivityTaskManager, mFakeGameClassifier), mMockActivityManager, mMockActivityManagerInternal, mMockActivityTaskManager, mMockWindowManagerService, mMockWindowManagerInternal, mActivityTaskManagerInternal, mFakeGameServiceConnector, mFakeGameSessionServiceConnector, mMockScreenshotHelper); } @After public void tearDown() { mMockingSession.finishMocking(); } @Test public void start_startsGameSession() throws Exception { mGameServiceProviderInstance.start(); assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.CONNECTED); assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue(); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void start_multipleTimes_startsGameSessionOnce() throws Exception { mGameServiceProviderInstance.start(); mGameServiceProviderInstance.start(); assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.CONNECTED); assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue(); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void stop_neverStarted_doesNothing() throws Exception { mGameServiceProviderInstance.stop(); assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void startAndStop_startsAndStopsGameSession() throws Exception { mGameServiceProviderInstance.start(); mGameServiceProviderInstance.stop(); assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED); assertThat(mFakeGameService.getConnectedCount()).isEqualTo(1); assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse(); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void startAndStop_multipleTimes_startsAndStopsGameSessionMultipleTimes() throws Exception { mGameServiceProviderInstance.start(); mGameServiceProviderInstance.stop(); mGameServiceProviderInstance.start(); mGameServiceProviderInstance.stop(); assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED); assertThat(mFakeGameService.getConnectedCount()).isEqualTo(2); assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse(); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(2); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void stop_stopMultipleTimes_stopsGameSessionOnce() throws Exception { mGameServiceProviderInstance.start(); mGameServiceProviderInstance.stop(); mGameServiceProviderInstance.stop(); assertThat(mFakeGameService.getState()).isEqualTo(GameServiceState.DISCONNECTED); assertThat(mFakeGameService.getConnectedCount()).isEqualTo(1); assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse(); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(1); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void gameTaskStarted_neverStarted_doesNothing() throws Exception { dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void gameTaskRemoved_neverStarted_doesNothing() throws Exception { dispatchTaskRemoved(10); assertThat(mFakeGameServiceConnector.getConnectCount()).isEqualTo(0); assertThat(mFakeGameSessionServiceConnector.getConnectCount()).isEqualTo(0); } @Test public void gameTaskStarted_afterStopped_doesNotSendGameStartedEvent() throws Exception { mGameServiceProviderInstance.start(); mGameServiceProviderInstance.stop(); dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY); assertThat(mFakeGameService.getGameStartedEvents()).isEmpty(); } @Test public void appTaskStarted_doesNotSendGameStartedEvent() throws Exception { mGameServiceProviderInstance.start(); dispatchTaskCreated(10, APP_A_MAIN_ACTIVITY); assertThat(mFakeGameService.getGameStartedEvents()).isEmpty(); } @Test public void taskStarted_nullComponentName_ignoresAndDoesNotCrash() throws Exception { mGameServiceProviderInstance.start(); dispatchTaskCreated(10, null); assertThat(mFakeGameService.getGameStartedEvents()).isEmpty(); } @Test public void gameSessionRequested_withoutTaskDispatch_doesNotCrashAndDoesNotCreateGameSession() throws Exception { mGameServiceProviderInstance.start(); mFakeGameService.requestCreateGameSession(10); assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty(); } @Test public void gameTaskStarted_noSessionRequest_callsStartGame() throws Exception { mGameServiceProviderInstance.start(); dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY); GameStartedEvent expectedGameStartedEvent = new GameStartedEvent(10, GAME_A_PACKAGE); assertThat(mFakeGameService.getGameStartedEvents()) .containsExactly(expectedGameStartedEvent).inOrder(); } @Test public void gameTaskStarted_requestToCreateGameSessionIncludesTaskConfiguration() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSessionService.CapturedCreateInvocation capturedCreateInvocation = getOnlyElement(mFakeGameSessionService.getCapturedCreateInvocations()); assertThat(capturedCreateInvocation.mGameSessionViewHostConfiguration) .isEqualTo(DEFAULT_GAME_SESSION_VIEW_HOST_CONFIGURATION); } @Test public void gameTaskStarted_failsToDetermineTaskOverlayConfiguration_gameSessionNotCreated() throws Exception { mGameServiceProviderInstance.start(); dispatchTaskCreated(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty(); } @Test public void gameTaskStartedAndSessionRequested_createsGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsDestroyed).isFalse(); assertThat(gameSession10.mIsFocused).isFalse(); } @Test public void gameTaskStartedAndSessionRequested_secondSessionRequest_ignoredAndDoesNotCrash() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); mFakeGameService.requestCreateGameSession(10); CreateGameSessionRequest expectedCreateGameSessionRequest = new CreateGameSessionRequest(10, GAME_A_PACKAGE); assertThat(getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mCreateGameSessionRequest) .isEqualTo(expectedCreateGameSessionRequest); } @Test public void gameSessionSuccessfullyCreated_createsTaskOverlay() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10)); } @Test public void gameProcessStopped_soleProcess_destroysGameSession() throws Exception { int gameProcessId = 1000; mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); startProcessForPackage(gameProcessId, GAME_A_PACKAGE); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsDestroyed).isFalse(); // Death of the sole game process destroys the game session. dispatchProcessDied(gameProcessId); assertThat(gameSession10.mIsDestroyed).isTrue(); } @Test public void gameProcessStopped_soleProcess_destroysMultipleGameSessionsForSamePackage() throws Exception { int gameProcessId = 1000; mGameServiceProviderInstance.start(); // Multiple tasks exist for the same package. startTask(10, GAME_A_MAIN_ACTIVITY); startTask(11, GAME_A_MAIN_ACTIVITY); startProcessForPackage(gameProcessId, GAME_A_PACKAGE); mFakeGameService.requestCreateGameSession(10); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); assertThat(gameSession10.mIsDestroyed).isFalse(); assertThat(gameSession11.mIsDestroyed).isFalse(); // Death of the sole game process destroys both game sessions. dispatchProcessDied(gameProcessId); assertThat(gameSession10.mIsDestroyed).isTrue(); assertThat(gameSession11.mIsDestroyed).isTrue(); } @Test public void gameProcessStopped_multipleProcesses_gameSessionDestroyedWhenAllDead() throws Exception { int firstGameProcessId = 1000; int secondGameProcessId = 1001; mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); startProcessForPackage(firstGameProcessId, GAME_A_PACKAGE); startProcessForPackage(secondGameProcessId, GAME_A_PACKAGE); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsDestroyed).isFalse(); // Death of the first process (with the second one still alive) does not destroy the game // session. dispatchProcessDied(firstGameProcessId); assertThat(gameSession10.mIsDestroyed).isFalse(); // Death of the second process does destroy the game session. dispatchProcessDied(secondGameProcessId); assertThat(gameSession10.mIsDestroyed).isTrue(); } @Test public void gameProcessCreatedAfterInitialProcessDead_newGameSessionCreated() throws Exception { int firstGameProcessId = 1000; int secondGameProcessId = 1000; mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); startProcessForPackage(firstGameProcessId, GAME_A_PACKAGE); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsDestroyed).isFalse(); // After the first game process dies, the game session should be destroyed. dispatchProcessDied(firstGameProcessId); assertThat(gameSession10.mIsDestroyed).isTrue(); // However, when a new process for the game starts, a new game session should be created. startProcessForPackage(secondGameProcessId, GAME_A_PACKAGE); // Verify that a new pending game session is created for the game's taskId. assertNotNull(mFakeGameSessionService.removePendingFutureForTaskId(10)); } @Test public void gameProcessCreatedAfterInitialProcessDead_multipleGameSessionsCreatedSamePackage() throws Exception { int firstGameProcessId = 1000; int secondGameProcessId = 1000; mGameServiceProviderInstance.start(); // Multiple tasks exist for the same package. startTask(10, GAME_A_MAIN_ACTIVITY); startTask(11, GAME_A_MAIN_ACTIVITY); startProcessForPackage(firstGameProcessId, GAME_A_PACKAGE); mFakeGameService.requestCreateGameSession(10); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); assertThat(gameSession10.mIsDestroyed).isFalse(); assertThat(gameSession11.mIsDestroyed).isFalse(); // After the first game process dies, both game sessions for the package should be // destroyed. dispatchProcessDied(firstGameProcessId); assertThat(gameSession10.mIsDestroyed).isTrue(); assertThat(gameSession11.mIsDestroyed).isTrue(); // However, when a new process for the game starts, new game sessions for the same // package should be created. startProcessForPackage(secondGameProcessId, GAME_A_PACKAGE); // Verify that new pending game sessions were created for each of the game's taskIds. assertNotNull(mFakeGameSessionService.removePendingFutureForTaskId(10)); assertNotNull(mFakeGameSessionService.removePendingFutureForTaskId(11)); } @Test public void gameProcessStarted_gameSessionNotRequested_doesNothing() throws Exception { int gameProcessId = 1000; mGameServiceProviderInstance.start(); // A game task and process are started, but requestCreateGameSession is never called. startTask(10, GAME_A_MAIN_ACTIVITY); startProcessForPackage(gameProcessId, GAME_A_PACKAGE); // No game session should be created. assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).isEmpty(); } @Test public void processActivityAndDeath_notForGame_gameSessionUnaffected() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); // Process activity for a process without a known package is ignored. startProcessForPackage(1000, /*packageName=*/ null); dispatchProcessActivity(1000); dispatchProcessDied(1000); // Process activity for a process with a different package is ignored startProcessForPackage(1001, GAME_B_PACKAGE); dispatchProcessActivity(1001); dispatchProcessDied(1001); // Death of a process for which there was no activity is ignored dispatchProcessDied(1002); // Despite all the process activity and death, the game session is not destroyed. assertThat(gameSession10.mIsDestroyed).isFalse(); } @Test public void taskSystemBarsListenerChanged_noAssociatedGameSession_doesNothing() { mGameServiceProviderInstance.start(); dispatchTaskSystemBarsEvent(taskSystemBarsListener -> { taskSystemBarsListener.onTransientSystemBarsVisibilityChanged( 10, /* areVisible= */ false, /* wereRevealedFromSwipeOnSystemBar= */ false); }); } @Test public void systemBarsTransientShownDueToGesture_hasGameSession_propagatesToGameSession() { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); dispatchTaskSystemBarsEvent(taskSystemBarsListener -> { taskSystemBarsListener.onTransientSystemBarsVisibilityChanged( 10, /* areVisible= */ true, /* wereRevealedFromSwipeOnSystemBar= */ true); }); assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isTrue(); } @Test public void systemBarsTransientShownButNotGesture_hasGameSession_notPropagatedToGameSession() { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); dispatchTaskSystemBarsEvent(taskSystemBarsListener -> { taskSystemBarsListener.onTransientSystemBarsVisibilityChanged( 10, /* areVisible= */ true, /* wereRevealedFromSwipeOnSystemBar= */ false); }); assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isFalse(); } @Test public void gameTaskFocused_propagatedToGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsFocused).isFalse(); dispatchTaskFocused(10, /*focused=*/ true); assertThat(gameSession10.mIsFocused).isTrue(); dispatchTaskFocused(10, /*focused=*/ false); assertThat(gameSession10.mIsFocused).isFalse(); } @Test public void gameTaskAlreadyFocusedWhenGameSessionCreated_propagatedToGameSession() throws Exception { ActivityTaskManager.RootTaskInfo gameATaskInfo = new ActivityTaskManager.RootTaskInfo(); gameATaskInfo.taskId = 10; when(mMockActivityTaskManager.getFocusedRootTaskInfo()).thenReturn(gameATaskInfo); mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsFocused).isTrue(); } @Test public void gameTaskRemoved_whileAwaitingGameSessionAttached_destroysGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); dispatchTaskRemoved(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsDestroyed).isTrue(); } @Test public void gameTaskRemoved_whileGameSessionAttached_destroysGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); dispatchTaskRemoved(10); assertThat(gameSession10.mIsDestroyed).isTrue(); } @Test public void gameTaskFocusedWithCreateAfterRemoved_gameSessionRecreated() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); stopTask(10); assertThat(gameSession10.mIsDestroyed).isTrue(); // If the game task is restored via the Recents UI, the task will be running again but // we would not expect any call to TaskStackListener#onTaskCreated. addRunningTaskInfo(10, GAME_A_MAIN_ACTIVITY); // We now receive a task focused event for the task. This will occur if the game task is // restored via the Recents UI. dispatchTaskFocused(10, /*focused=*/ true); mFakeGameService.requestCreateGameSession(10); // Verify that a new pending game session is created for the game's taskId. assertNotNull(mFakeGameSessionService.removePendingFutureForTaskId(10)); } @Test public void gameTaskRemoved_removesTaskOverlay() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); stopTask(10); verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10)); verify(mMockWindowManagerInternal).removeTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10)); } @Test public void gameTaskStartedAndSessionRequested_multipleTimes_createsMultipleGameSessions() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); startTask(11, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); assertThat(gameSession10.mIsDestroyed).isFalse(); assertThat(gameSession11.mIsDestroyed).isFalse(); } @Test public void gameTaskStartedTwice_sessionRequestedSecondTimeOnly_createsOneGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); startTask(11, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); assertThat(gameSession10.mIsDestroyed).isFalse(); assertThat(mFakeGameSessionService.getCapturedCreateInvocations()).hasSize(1); } @Test public void gameTaskRemoved_multipleSessions_destroysOnlyThatGameSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); startTask(11, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); dispatchTaskRemoved(10); assertThat(gameSession10.mIsDestroyed).isTrue(); assertThat(gameSession11.mIsDestroyed).isFalse(); assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue(); } @Test public void allGameTasksRemoved_destroysAllGameSessionsAndGameSessionServiceIsDisconnected() { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); startTask(11, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); dispatchTaskRemoved(10); dispatchTaskRemoved(11); assertThat(gameSession10.mIsDestroyed).isTrue(); assertThat(gameSession11.mIsDestroyed).isTrue(); assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse(); } @Test public void createSessionRequested_afterAllPreviousSessionsDestroyed_createsSession() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); startTask(11, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); dispatchTaskRemoved(10); dispatchTaskRemoved(11); startTask(12, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(12); FakeGameSession gameSession12 = new FakeGameSession(); SurfacePackage mockSurfacePackage12 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(12) .complete(new CreateGameSessionResult(gameSession12, mockSurfacePackage12)); assertThat(gameSession10.mIsDestroyed).isTrue(); assertThat(gameSession11.mIsDestroyed).isTrue(); assertThat(gameSession12.mIsDestroyed).isFalse(); assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isTrue(); } @Test public void gameSessionServiceDies_severalActiveGameSessions_destroysGameSessions() { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); startTask(11, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); mFakeGameSessionServiceConnector.killServiceProcess(); assertThat(gameSession10.mIsDestroyed).isTrue(); assertThat(gameSession11.mIsDestroyed).isTrue(); assertThat(mFakeGameServiceConnector.getIsConnected()).isTrue(); assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse(); } @Test public void stop_severalActiveGameSessions_destroysGameSessionsAndUnbinds() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); startTask(11, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); mGameServiceProviderInstance.stop(); assertThat(gameSession10.mIsDestroyed).isTrue(); assertThat(gameSession11.mIsDestroyed).isTrue(); assertThat(mFakeGameServiceConnector.getIsConnected()).isFalse(); assertThat(mFakeGameSessionServiceConnector.getIsConnected()).isFalse(); } @Test public void takeScreenshot_failureNoBitmapCaptured() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture resultFuture = new AndroidFuture<>(); gameSessionController.takeScreenshot(10, resultFuture); GameScreenshotResult result = resultFuture.get(); assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR, result.getStatus()); verify(mMockWindowManagerService).captureTaskBitmap(eq(10), any()); } @Test public void takeScreenshot_success() throws Exception { SurfaceControl mockOverlaySurfaceControl = Mockito.mock(SurfaceControl.class); SurfaceControl[] excludeLayers = new SurfaceControl[1]; excludeLayers[0] = mockOverlaySurfaceControl; int taskId = 10; when(mMockWindowManagerService.captureTaskBitmap(eq(10), any())).thenReturn(TEST_BITMAP); doAnswer(invocation -> { Consumer consumer = invocation.getArgument(invocation.getArguments().length - 1); consumer.accept(Uri.parse("a/b.png")); return null; }).when(mMockScreenshotHelper).takeScreenshot(any(), any(), any()); mGameServiceProviderInstance.start(); startTask(taskId, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(taskId); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class); when(mockOverlaySurfacePackage.getSurfaceControl()).thenReturn(mockOverlaySurfaceControl); mFakeGameSessionService.removePendingFutureForTaskId(taskId) .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage)); IGameSessionController gameSessionController = getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController; AndroidFuture resultFuture = new AndroidFuture<>(); gameSessionController.takeScreenshot(taskId, resultFuture); GameScreenshotResult result = resultFuture.get(); assertEquals(GameScreenshotResult.GAME_SCREENSHOT_SUCCESS, result.getStatus()); } @Test public void restartGame_taskIdAssociatedWithGame_restartsTargetGame() throws Exception { Intent launchIntent = new Intent("com.test.ACTION_LAUNCH_GAME_PACKAGE") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); when(mMockPackageManager.getLaunchIntentForPackage(GAME_A_PACKAGE)) .thenReturn(launchIntent); mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); startTask(11, GAME_B_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(11); FakeGameSession gameSession11 = new FakeGameSession(); SurfacePackage mockSurfacePackage11 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(11) .complete(new CreateGameSessionResult(gameSession11, mockSurfacePackage11)); mFakeGameSessionService.getCapturedCreateInvocations().get(0) .mGameSessionController.restartGame(10); verify(mActivityTaskManagerInternal).restartTaskActivityProcessIfVisible( 10, GAME_A_PACKAGE); } @Test public void restartGame_taskIdNotAssociatedWithGame_noOp() throws Exception { mGameServiceProviderInstance.start(); startTask(10, GAME_A_MAIN_ACTIVITY); mFakeGameService.requestCreateGameSession(10); FakeGameSession gameSession10 = new FakeGameSession(); SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); mFakeGameSessionService.removePendingFutureForTaskId(10) .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); getOnlyElement( mFakeGameSessionService.getCapturedCreateInvocations()) .mGameSessionController.restartGame(11); verify(mMockActivityManager).registerProcessObserver(any()); verifyNoMoreInteractions(mMockActivityManager); verify(mActivityTaskManagerInternal, never()) .restartTaskActivityProcessIfVisible(anyInt(), anyString()); } private void startTask(int taskId, ComponentName componentName) { addRunningTaskInfo(taskId, componentName); dispatchTaskCreated(taskId, componentName); } private void addRunningTaskInfo(int taskId, ComponentName componentName) { RunningTaskInfo runningTaskInfo = new RunningTaskInfo(); runningTaskInfo.taskId = taskId; runningTaskInfo.baseActivity = componentName; runningTaskInfo.displayId = 1; runningTaskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 500, 800)); mRunningTaskInfos.add(runningTaskInfo); } private void stopTask(int taskId) { mRunningTaskInfos.removeIf(runningTaskInfo -> runningTaskInfo.taskId == taskId); dispatchTaskRemoved(taskId); } private void dispatchTaskRemoved(int taskId) { dispatchTaskChangeEvent(taskStackListener -> { taskStackListener.onTaskRemoved(taskId); }); } private void dispatchTaskCreated(int taskId, @Nullable ComponentName componentName) { dispatchTaskChangeEvent(taskStackListener -> { taskStackListener.onTaskCreated(taskId, componentName); }); } private void dispatchTaskFocused(int taskId, boolean focused) { dispatchTaskChangeEvent(taskStackListener -> { taskStackListener.onTaskFocusChanged(taskId, focused); }); } private void dispatchTaskChangeEvent( ThrowingConsumer taskStackListenerConsumer) { for (ITaskStackListener taskStackListener : mTaskStackListeners) { taskStackListenerConsumer.accept(taskStackListener); } } private void startProcessForPackage(int processId, @Nullable String packageName) { if (packageName != null) { when(mMockActivityManagerInternal.getPackageNameByPid(processId)).thenReturn( packageName); } dispatchProcessActivity(processId); } private void dispatchProcessActivity(int processId) { dispatchProcessChangedEvent(processObserver -> { // Neither uid nor foregroundActivities are used by the implementation being tested. processObserver.onForegroundActivitiesChanged(processId, /*uid=*/ 0, /*foregroundActivities=*/ false); }); } private void dispatchProcessDied(int processId) { dispatchProcessChangedEvent(processObserver -> { // The uid param is not used by the implementation being tested. processObserver.onProcessDied(processId, /*uid=*/ 0); }); } private void dispatchProcessChangedEvent( ThrowingConsumer processObserverConsumer) { for (IProcessObserver processObserver : mProcessObservers) { processObserverConsumer.accept(processObserver); } } private void dispatchTaskSystemBarsEvent( ThrowingConsumer taskSystemBarsListenerConsumer) { for (TaskSystemBarsListener listener : mTaskSystemBarsListeners) { taskSystemBarsListenerConsumer.accept(listener); } } static final class FakeGameService extends IGameService.Stub { private IGameServiceController mGameServiceController; public enum GameServiceState { DISCONNECTED, CONNECTED, } private ArrayList mGameStartedEvents = new ArrayList<>(); private int mConnectedCount = 0; private GameServiceState mGameServiceState = GameServiceState.DISCONNECTED; public GameServiceState getState() { return mGameServiceState; } public int getConnectedCount() { return mConnectedCount; } public ArrayList getGameStartedEvents() { return mGameStartedEvents; } @Override public void connected(IGameServiceController gameServiceController) { Preconditions.checkState(mGameServiceState == GameServiceState.DISCONNECTED); mGameServiceState = GameServiceState.CONNECTED; mConnectedCount += 1; mGameServiceController = gameServiceController; } @Override public void disconnected() { Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED); mGameServiceState = GameServiceState.DISCONNECTED; mGameServiceController = null; } @Override public void gameStarted(GameStartedEvent gameStartedEvent) { Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED); mGameStartedEvents.add(gameStartedEvent); } public void requestCreateGameSession(int task) { Preconditions.checkState(mGameServiceState == GameServiceState.CONNECTED); try { mGameServiceController.createGameSession(task); } catch (RemoteException ex) { throw new AssertionError(ex); } } } static final class FakeGameSessionService extends IGameSessionService.Stub { private final ArrayList mCapturedCreateInvocations = new ArrayList<>(); private final HashMap> mPendingCreateGameSessionResultFutures = new HashMap<>(); public static final class CapturedCreateInvocation { private final IGameSessionController mGameSessionController; private final CreateGameSessionRequest mCreateGameSessionRequest; private final GameSessionViewHostConfiguration mGameSessionViewHostConfiguration; CapturedCreateInvocation( IGameSessionController gameSessionController, CreateGameSessionRequest createGameSessionRequest, GameSessionViewHostConfiguration gameSessionViewHostConfiguration) { mGameSessionController = gameSessionController; mCreateGameSessionRequest = createGameSessionRequest; mGameSessionViewHostConfiguration = gameSessionViewHostConfiguration; } } public ArrayList getCapturedCreateInvocations() { return mCapturedCreateInvocations; } public AndroidFuture removePendingFutureForTaskId(int taskId) { return mPendingCreateGameSessionResultFutures.remove(taskId); } @Override public void create( IGameSessionController gameSessionController, CreateGameSessionRequest createGameSessionRequest, GameSessionViewHostConfiguration gameSessionViewHostConfiguration, AndroidFuture createGameSessionResultFuture) { mCapturedCreateInvocations.add( new CapturedCreateInvocation( gameSessionController, createGameSessionRequest, gameSessionViewHostConfiguration)); Preconditions.checkState(!mPendingCreateGameSessionResultFutures.containsKey( createGameSessionRequest.getTaskId())); mPendingCreateGameSessionResultFutures.put( createGameSessionRequest.getTaskId(), createGameSessionResultFuture); } } private static class FakeGameSession extends IGameSession.Stub { boolean mIsDestroyed = false; boolean mIsFocused = false; boolean mAreTransientSystemBarsVisibleFromRevealGesture = false; @Override public void onDestroyed() { mIsDestroyed = true; } @Override public void onTaskFocusChanged(boolean focused) { mIsFocused = focused; } @Override public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean areVisible) { mAreTransientSystemBarsVisibleFromRevealGesture = areVisible; } } private final class MockContext extends ContextWrapper { MockContext(Context base) { super(base); } @Override public PackageManager getPackageManager() { return mMockPackageManager; } } }