1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.games; 18 19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assume.assumeTrue; 27 28 import android.app.Activity; 29 import android.app.GameManager; 30 import android.app.Instrumentation; 31 import android.app.PendingIntent; 32 import android.content.ActivityNotFoundException; 33 import android.content.ComponentName; 34 import android.content.ContentResolver; 35 import android.content.ContentUris; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.pm.PackageManager; 39 import android.database.Cursor; 40 import android.graphics.Bitmap; 41 import android.graphics.Color; 42 import android.graphics.ImageDecoder; 43 import android.graphics.Rect; 44 import android.net.Uri; 45 import android.os.IBinder; 46 import android.os.RemoteException; 47 import android.provider.MediaStore; 48 import android.service.games.testing.ActivityResult; 49 import android.service.games.testing.GameSessionEventInfo; 50 import android.service.games.testing.GetResultActivity; 51 import android.service.games.testing.IGameServiceTestService; 52 import android.support.test.uiautomator.By; 53 import android.support.test.uiautomator.UiDevice; 54 import android.support.test.uiautomator.UiObject; 55 import android.support.test.uiautomator.UiObjectNotFoundException; 56 import android.support.test.uiautomator.UiSelector; 57 import android.support.test.uiautomator.Until; 58 import android.util.Log; 59 import android.util.Size; 60 import android.view.WindowManager; 61 import android.view.WindowMetrics; 62 63 import androidx.test.InstrumentationRegistry; 64 import androidx.test.filters.FlakyTest; 65 import androidx.test.runner.AndroidJUnit4; 66 67 import com.android.compatibility.common.util.PollingCheck; 68 import com.android.compatibility.common.util.ShellIdentityUtils; 69 import com.android.compatibility.common.util.ShellUtils; 70 import com.android.compatibility.common.util.UiAutomatorUtils; 71 72 import com.google.common.collect.ImmutableList; 73 74 import org.junit.After; 75 import org.junit.Before; 76 import org.junit.Test; 77 import org.junit.runner.RunWith; 78 import org.junit.runners.Parameterized.Parameter; 79 80 import java.io.ByteArrayOutputStream; 81 import java.io.IOException; 82 import java.time.Instant; 83 import java.util.ArrayList; 84 import java.util.List; 85 import java.util.concurrent.Semaphore; 86 import java.util.concurrent.TimeUnit; 87 import java.util.regex.Matcher; 88 import java.util.regex.Pattern; 89 90 /** 91 * CTS tests for {@link android.service.games.GameService}. 92 */ 93 @FlakyTest(bugId = 263181277) 94 @RunWith(AndroidJUnit4.class) 95 public final class GameServiceTest { 96 static final String TAG = "GameServiceTest"; 97 98 private static final String GAME_PACKAGE_NAME = "android.service.games.cts.game"; 99 private static final String FALSE_POSITIVE_GAME_PACKAGE_NAME = 100 "android.service.games.cts.falsepositive"; 101 private static final String FINISH_ON_BACK_GAME_PACKAGE_NAME = 102 "android.service.games.cts.finishonbackgame"; 103 private static final String NOT_GAME_PACKAGE_NAME = "android.service.games.cts.notgame"; 104 private static final String RESTART_GAME_VERIFIER_PACKAGE_NAME = 105 "android.service.games.cts.restartgameverifier"; 106 private static final String START_ACTIVITY_VERIFIER_PACKAGE_NAME = 107 "android.service.games.cts.startactivityverifier"; 108 private static final String SYSTEM_BAR_VERIFIER_PACKAGE_NAME = 109 "android.service.games.cts.systembarverifier"; 110 private static final String TAKE_SCREENSHOT_VERIFIER_PACKAGE_NAME = 111 "android.service.games.cts.takescreenshotverifier"; 112 private static final String TOUCH_VERIFIER_PACKAGE_NAME = 113 "android.service.games.cts.touchverifier"; 114 115 @Parameter(0) 116 public String mVolumeName; 117 118 private ServiceConnection mServiceConnection; 119 private ContentResolver mContentResolver; 120 121 @Before setUp()122 public void setUp() throws Exception { 123 mServiceConnection = new ServiceConnection(); 124 assertThat( 125 getInstrumentation().getContext().bindService( 126 new Intent("android.service.games.action.TEST_SERVICE").setPackage( 127 getInstrumentation().getContext().getPackageName()), 128 mServiceConnection, 129 Context.BIND_AUTO_CREATE)).isTrue(); 130 mServiceConnection.waitForConnection(10, TimeUnit.SECONDS); 131 132 getTestService().setGamePackageNames( 133 ImmutableList.of( 134 FINISH_ON_BACK_GAME_PACKAGE_NAME, 135 GAME_PACKAGE_NAME, 136 RESTART_GAME_VERIFIER_PACKAGE_NAME, 137 SYSTEM_BAR_VERIFIER_PACKAGE_NAME, 138 TAKE_SCREENSHOT_VERIFIER_PACKAGE_NAME, 139 TOUCH_VERIFIER_PACKAGE_NAME)); 140 141 GameManager gameManager = 142 getInstrumentation().getContext().getSystemService(GameManager.class); 143 144 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(gameManager, 145 manager -> manager.setGameServiceProvider( 146 getInstrumentation().getContext().getPackageName())); 147 mContentResolver = getInstrumentation().getContext().getContentResolver(); 148 149 if (gameServiceFeaturePresent()) { 150 waitForGameServiceConnected(); 151 } 152 } 153 154 @After tearDown()155 public void tearDown() throws Exception { 156 forceStop(GAME_PACKAGE_NAME); 157 forceStop(NOT_GAME_PACKAGE_NAME); 158 forceStop(FALSE_POSITIVE_GAME_PACKAGE_NAME); 159 forceStop(FINISH_ON_BACK_GAME_PACKAGE_NAME); 160 forceStop(RESTART_GAME_VERIFIER_PACKAGE_NAME); 161 forceStop(START_ACTIVITY_VERIFIER_PACKAGE_NAME); 162 forceStop(SYSTEM_BAR_VERIFIER_PACKAGE_NAME); 163 forceStop(TAKE_SCREENSHOT_VERIFIER_PACKAGE_NAME); 164 forceStop(TOUCH_VERIFIER_PACKAGE_NAME); 165 166 GameManager gameManager = 167 getInstrumentation().getContext().getSystemService(GameManager.class); 168 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(gameManager, 169 manager -> manager.setGameServiceProvider("")); 170 171 getTestService().resetState(); 172 } 173 174 @Test gameService_connectsOnStartup()175 public void gameService_connectsOnStartup() throws Exception { 176 assumeGameServiceFeaturePresent(); 177 178 waitForGameServiceConnected(); 179 assertThat(isGameServiceConnected()).isTrue(); 180 } 181 182 @Test gameService_connectsWhenGameServiceComponentIsEnabled()183 public void gameService_connectsWhenGameServiceComponentIsEnabled() throws Exception { 184 assumeGameServiceFeaturePresent(); 185 186 waitForGameServiceConnected(); 187 188 getTestService().setGameServiceComponentEnabled(false); 189 waitForGameServiceDisconnected(); 190 191 getTestService().setGameServiceComponentEnabled(true); 192 waitForGameServiceConnected(); 193 } 194 195 @Test gameService_connectsWhenGameSessionServiceComponentIsEnabled()196 public void gameService_connectsWhenGameSessionServiceComponentIsEnabled() throws Exception { 197 assumeGameServiceFeaturePresent(); 198 199 waitForGameServiceConnected(); 200 201 getTestService().setGameSessionServiceComponentEnabled(false); 202 waitForGameServiceDisconnected(); 203 204 getTestService().setGameSessionServiceComponentEnabled(true); 205 waitForGameServiceConnected(); 206 } 207 208 @Test gameService_startsGameSessionsForGames()209 public void gameService_startsGameSessionsForGames() throws Exception { 210 assumeGameServiceFeaturePresent(); 211 212 launchAndWaitForPackage(NOT_GAME_PACKAGE_NAME); 213 launchAndWaitForPackage(GAME_PACKAGE_NAME); 214 launchAndWaitForPackage(FALSE_POSITIVE_GAME_PACKAGE_NAME); 215 216 assertThat(getTestService().getActiveSessions()).containsExactly( 217 GAME_PACKAGE_NAME); 218 } 219 220 @Test gameService_multipleGames_startsGameSessionsForGames()221 public void gameService_multipleGames_startsGameSessionsForGames() throws Exception { 222 assumeGameServiceFeaturePresent(); 223 224 launchAndWaitForPackage(NOT_GAME_PACKAGE_NAME); 225 launchAndWaitForPackage(GAME_PACKAGE_NAME); 226 int gameTaskId = getActivityTaskId( 227 GAME_PACKAGE_NAME, 228 GAME_PACKAGE_NAME + ".MainActivity"); 229 launchAndWaitForPackage(FALSE_POSITIVE_GAME_PACKAGE_NAME); 230 launchAndWaitForPackage(RESTART_GAME_VERIFIER_PACKAGE_NAME); 231 int restartGameTaskId = getActivityTaskId( 232 RESTART_GAME_VERIFIER_PACKAGE_NAME, 233 RESTART_GAME_VERIFIER_PACKAGE_NAME + ".MainActivity"); 234 235 assertThat(getTestService().getActiveSessions()).containsExactly( 236 GAME_PACKAGE_NAME, RESTART_GAME_VERIFIER_PACKAGE_NAME); 237 238 List<GameSessionEventInfo> gameSessionEventHistory = 239 getTestService().getGameSessionEventHistory(); 240 assertThat(gameSessionEventHistory) 241 .containsExactly( 242 GameSessionEventInfo.create( 243 GAME_PACKAGE_NAME, 244 gameTaskId, 245 GameSessionEventInfo.GAME_SESSION_EVENT_CREATED), 246 GameSessionEventInfo.create( 247 RESTART_GAME_VERIFIER_PACKAGE_NAME, 248 restartGameTaskId, 249 GameSessionEventInfo.GAME_SESSION_EVENT_CREATED)) 250 .inOrder(); 251 } 252 253 @Test gameService_multipleGamesIncludingStops_startsGameSessionsForGames()254 public void gameService_multipleGamesIncludingStops_startsGameSessionsForGames() 255 throws Exception { 256 assumeGameServiceFeaturePresent(); 257 258 launchAndWaitForPackage(NOT_GAME_PACKAGE_NAME); 259 launchAndWaitForPackage(GAME_PACKAGE_NAME); 260 int gameTaskId = getActivityTaskId( 261 GAME_PACKAGE_NAME, 262 GAME_PACKAGE_NAME + ".MainActivity"); 263 launchAndWaitForPackage(FALSE_POSITIVE_GAME_PACKAGE_NAME); 264 launchAndWaitForPackage(RESTART_GAME_VERIFIER_PACKAGE_NAME); 265 int restartGameTaskId = getActivityTaskId( 266 RESTART_GAME_VERIFIER_PACKAGE_NAME, 267 RESTART_GAME_VERIFIER_PACKAGE_NAME + ".MainActivity"); 268 forceStop(GAME_PACKAGE_NAME); 269 270 assertThat(getTestService().getActiveSessions()).containsExactly( 271 RESTART_GAME_VERIFIER_PACKAGE_NAME); 272 273 List<GameSessionEventInfo> gameSessionEventHistory = 274 getTestService().getGameSessionEventHistory(); 275 assertThat(gameSessionEventHistory) 276 .containsExactly( 277 GameSessionEventInfo.create( 278 GAME_PACKAGE_NAME, 279 gameTaskId, 280 GameSessionEventInfo.GAME_SESSION_EVENT_CREATED), 281 GameSessionEventInfo.create( 282 RESTART_GAME_VERIFIER_PACKAGE_NAME, 283 restartGameTaskId, 284 GameSessionEventInfo.GAME_SESSION_EVENT_CREATED), 285 GameSessionEventInfo.create( 286 GAME_PACKAGE_NAME, 287 gameTaskId, 288 GameSessionEventInfo.GAME_SESSION_EVENT_DESTROYED)) 289 .inOrder(); 290 } 291 292 @Test getTaskId_returnsTaskIdOfGame()293 public void getTaskId_returnsTaskIdOfGame() throws Exception { 294 assumeGameServiceFeaturePresent(); 295 296 launchAndWaitForPackage(GAME_PACKAGE_NAME); 297 298 int taskId = getTestService().getFocusedTaskId(); 299 300 assertThat(taskId).isEqualTo( 301 getActivityTaskId(GAME_PACKAGE_NAME, GAME_PACKAGE_NAME + ".MainActivity")); 302 } 303 304 @Test setTaskOverlayView_addsViewsToOverlay()305 public void setTaskOverlayView_addsViewsToOverlay() throws Exception { 306 assumeGameServiceFeaturePresent(); 307 308 launchAndWaitForPackage(GAME_PACKAGE_NAME); 309 310 waitForTouchableOverlayBounds(); 311 312 assertThat(UiAutomatorUtils.getUiDevice().findObject( 313 By.text("Overlay was rendered on: " + GAME_PACKAGE_NAME))).isNotNull(); 314 } 315 316 @Test setTaskOverlayView_passesTouchesOutsideOverlayToGame()317 public void setTaskOverlayView_passesTouchesOutsideOverlayToGame() throws Exception { 318 assumeGameServiceFeaturePresent(); 319 320 launchAndWaitForPackage(TOUCH_VERIFIER_PACKAGE_NAME); 321 322 Rect touchableBounds = waitForTouchableOverlayBounds(); 323 UiAutomatorUtils.getUiDevice().click(touchableBounds.centerX(), touchableBounds.centerY()); 324 325 UiAutomatorUtils.waitFindObject( 326 By.res(TOUCH_VERIFIER_PACKAGE_NAME, "times_clicked").text("0")); 327 328 UiAutomatorUtils.getUiDevice().click(touchableBounds.centerX(), touchableBounds.top - 30); 329 UiAutomatorUtils.waitFindObject( 330 By.res(TOUCH_VERIFIER_PACKAGE_NAME, "times_clicked").text("1")); 331 332 UiAutomatorUtils.getUiDevice() 333 .click(touchableBounds.centerX(), touchableBounds.bottom + 30); 334 UiAutomatorUtils.waitFindObject( 335 By.res(TOUCH_VERIFIER_PACKAGE_NAME, "times_clicked").text("2")); 336 337 UiAutomatorUtils.getUiDevice().click(touchableBounds.left - 30, touchableBounds.centerY()); 338 UiAutomatorUtils.waitFindObject( 339 By.res(TOUCH_VERIFIER_PACKAGE_NAME, "times_clicked").text("3")); 340 341 UiAutomatorUtils.getUiDevice().click(touchableBounds.right + 30, touchableBounds.centerY()); 342 UiAutomatorUtils.waitFindObject( 343 By.res(TOUCH_VERIFIER_PACKAGE_NAME, "times_clicked").text("4")); 344 } 345 346 @Test onTransientSystemBarVisibilityChanged_nonTransient_doesNotDispatchShow()347 public void onTransientSystemBarVisibilityChanged_nonTransient_doesNotDispatchShow() 348 throws Exception { 349 assumeGameServiceFeaturePresent(); 350 351 launchAndWaitForPackage(SYSTEM_BAR_VERIFIER_PACKAGE_NAME); 352 353 UiAutomatorUtils.getUiDevice().findObject( 354 By.text("Show system bars permanently") 355 .pkg(SYSTEM_BAR_VERIFIER_PACKAGE_NAME)) 356 .click(); 357 358 assertThat( 359 getTestService().getOnSystemBarVisibilityChangedInfo().getTimesShown()) 360 .isEqualTo(0); 361 } 362 363 @Test onTransientSystemBarVisibilityFromRevealGestureChanged_dispatchesHideEvent()364 public void onTransientSystemBarVisibilityFromRevealGestureChanged_dispatchesHideEvent() 365 throws Exception { 366 assumeGameServiceFeaturePresent(); 367 368 launchAndWaitForPackage(GAME_PACKAGE_NAME); 369 swipeFromTopEdgeToShowSystemBars(); 370 371 assertThat( 372 getTestService().getOnSystemBarVisibilityChangedInfo().getTimesShown()) 373 .isEqualTo(1); 374 375 PollingCheck.waitFor( 376 () -> { 377 try { 378 return getTestService().getOnSystemBarVisibilityChangedInfo() 379 .getTimesHidden() > 0; 380 } catch (RemoteException e) { 381 return false; 382 } 383 }); 384 assertThat( 385 getTestService().getOnSystemBarVisibilityChangedInfo().getTimesHidden()) 386 .isEqualTo(1); 387 } 388 389 @Test startActivityForResult_startsActivityAndReceivesResultWithData()390 public void startActivityForResult_startsActivityAndReceivesResultWithData() throws Exception { 391 assumeGameServiceFeaturePresent(); 392 393 launchAndWaitForPackage(GAME_PACKAGE_NAME); 394 395 StartActivityVerifierPage.launch(getTestService()); 396 397 StartActivityVerifierPage.setResultCode(10); 398 StartActivityVerifierPage.setResultData("foobar"); 399 StartActivityVerifierPage.clickSendResultButton(); 400 401 ActivityResult result = getTestService().getLastActivityResult(); 402 403 assertThat(result.getGameSessionPackageName()).isEqualTo(GAME_PACKAGE_NAME); 404 assertThat(result.getSuccess().getResultCode()).isEqualTo(10); 405 String resultData = StartActivityVerifierPage.getResultData(result.getSuccess().getData()); 406 assertThat(resultData).isEqualTo("foobar"); 407 } 408 409 @Test startActivityForResult_startsActivityAndReceivesResultWithNoData()410 public void startActivityForResult_startsActivityAndReceivesResultWithNoData() 411 throws Exception { 412 assumeGameServiceFeaturePresent(); 413 414 launchAndWaitForPackage(GAME_PACKAGE_NAME); 415 416 StartActivityVerifierPage.launch(getTestService()); 417 418 StartActivityVerifierPage.setResultCode(10); 419 StartActivityVerifierPage.clickSendResultButton(); 420 421 ActivityResult result = getTestService().getLastActivityResult(); 422 423 assertThat(result.getGameSessionPackageName()).isEqualTo(GAME_PACKAGE_NAME); 424 assertThat(result.getSuccess().getResultCode()).isEqualTo(10); 425 assertThat(result.getSuccess().getData()).isNull(); 426 } 427 428 @Test startActivityForResult_cannotStartBlockedActivities()429 public void startActivityForResult_cannotStartBlockedActivities() throws Exception { 430 assumeGameServiceFeaturePresent(); 431 432 launchAndWaitForPackage(GAME_PACKAGE_NAME); 433 434 getTestService().startGameSessionActivity( 435 new Intent("android.service.games.cts.startactivityverifier.START_BLOCKED"), null); 436 437 ActivityResult result = getTestService().getLastActivityResult(); 438 439 assertThat(result.getGameSessionPackageName()).isEqualTo(GAME_PACKAGE_NAME); 440 assertThat(result.getFailure().getClazz()).isEqualTo(SecurityException.class); 441 } 442 443 @Test startActivityForResult_propagatesActivityNotFoundException()444 public void startActivityForResult_propagatesActivityNotFoundException() throws Exception { 445 assumeGameServiceFeaturePresent(); 446 447 launchAndWaitForPackage(GAME_PACKAGE_NAME); 448 449 getTestService().startGameSessionActivity(new Intent("NO_ACTION"), null); 450 451 ActivityResult result = getTestService().getLastActivityResult(); 452 453 assertThat(result.getGameSessionPackageName()).isEqualTo(GAME_PACKAGE_NAME); 454 assertThat(result.getFailure().getClazz()).isEqualTo(ActivityNotFoundException.class); 455 } 456 @Test restartGame_noPermission()457 public void restartGame_noPermission() throws Exception { 458 assumeGameServiceFeaturePresent(); 459 getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); 460 clearCache(RESTART_GAME_VERIFIER_PACKAGE_NAME); 461 RestartGameVerifierPage.launch(); 462 463 RestartGameVerifierPage.assertTimesStarted(1); 464 RestartGameVerifierPage.assertHasSavedInstanceState(false); 465 466 getTestService().restartFocusedGameSession(); 467 468 RestartGameVerifierPage.assertTimesStarted(1); 469 RestartGameVerifierPage.assertHasSavedInstanceState(false); 470 } 471 472 @Test restartGame_gameAppIsRestarted()473 public void restartGame_gameAppIsRestarted() throws Exception { 474 assumeGameServiceFeaturePresent(); 475 476 clearCache(RESTART_GAME_VERIFIER_PACKAGE_NAME); 477 RestartGameVerifierPage.launch(); 478 479 RestartGameVerifierPage.assertTimesStarted(1); 480 RestartGameVerifierPage.assertHasSavedInstanceState(false); 481 482 getTestService().restartFocusedGameSession(); 483 484 RestartGameVerifierPage.assertTimesStarted(2); 485 RestartGameVerifierPage.assertHasSavedInstanceState(true); 486 487 getTestService().restartFocusedGameSession(); 488 489 RestartGameVerifierPage.assertTimesStarted(3); 490 RestartGameVerifierPage.assertHasSavedInstanceState(true); 491 } 492 493 @Test restartGame_gameSessionIsPersisted()494 public void restartGame_gameSessionIsPersisted() throws Exception { 495 assumeGameServiceFeaturePresent(); 496 497 clearCache(RESTART_GAME_VERIFIER_PACKAGE_NAME); 498 RestartGameVerifierPage.launch(); 499 500 int gameTaskId = getActivityTaskId( 501 RESTART_GAME_VERIFIER_PACKAGE_NAME, 502 RESTART_GAME_VERIFIER_PACKAGE_NAME + ".MainActivity"); 503 504 RestartGameVerifierPage.assertTimesStarted(1); 505 RestartGameVerifierPage.assertHasSavedInstanceState(false); 506 507 getTestService().restartFocusedGameSession(); 508 509 RestartGameVerifierPage.assertTimesStarted(2); 510 RestartGameVerifierPage.assertHasSavedInstanceState(true); 511 512 List<GameSessionEventInfo> gameSessionEventHistory = 513 getTestService().getGameSessionEventHistory(); 514 515 assertThat(gameSessionEventHistory) 516 .containsExactly( 517 GameSessionEventInfo.create( 518 RESTART_GAME_VERIFIER_PACKAGE_NAME, 519 gameTaskId, 520 GameSessionEventInfo.GAME_SESSION_EVENT_CREATED)) 521 .inOrder(); 522 } 523 524 @Test restartGame_withNonGameActivityAbove_gameSessionIsPersisted()525 public void restartGame_withNonGameActivityAbove_gameSessionIsPersisted() throws Exception { 526 assumeGameServiceFeaturePresent(); 527 528 clearCache(RESTART_GAME_VERIFIER_PACKAGE_NAME); 529 RestartGameVerifierPage.launch(); 530 531 int gameTaskId = getActivityTaskId( 532 RESTART_GAME_VERIFIER_PACKAGE_NAME, 533 RESTART_GAME_VERIFIER_PACKAGE_NAME + ".MainActivity"); 534 535 RestartGameVerifierPage.assertTimesStarted(1); 536 RestartGameVerifierPage.assertHasSavedInstanceState(false); 537 538 StartActivityVerifierPage.launch(getTestService()); 539 StartActivityVerifierPage.setResultCode(0x1337); 540 StartActivityVerifierPage.setResultData("hello mom!"); 541 542 getTestService().restartFocusedGameSession(); 543 544 StartActivityVerifierPage.assertLaunched(); 545 StartActivityVerifierPage.clickSendResultButton(); 546 547 RestartGameVerifierPage.assertTimesStarted(2); 548 RestartGameVerifierPage.assertHasSavedInstanceState(true); 549 550 List<GameSessionEventInfo> gameSessionEventHistory = 551 getTestService().getGameSessionEventHistory(); 552 553 assertThat(gameSessionEventHistory) 554 .containsExactly( 555 GameSessionEventInfo.create( 556 RESTART_GAME_VERIFIER_PACKAGE_NAME, 557 gameTaskId, 558 GameSessionEventInfo.GAME_SESSION_EVENT_CREATED)) 559 .inOrder(); 560 561 ActivityResult result = getTestService().getLastActivityResult(); 562 563 assertThat(result.getGameSessionPackageName()).isEqualTo( 564 RESTART_GAME_VERIFIER_PACKAGE_NAME); 565 assertThat(result.getSuccess().getResultCode()).isEqualTo(0x1337); 566 String resultData = StartActivityVerifierPage.getResultData(result.getSuccess().getData()); 567 assertThat(resultData).isEqualTo("hello mom!"); 568 } 569 570 @Test gamePutInBackgroundAndRestoredViaRecentsUi_gameSessionRestarted()571 public void gamePutInBackgroundAndRestoredViaRecentsUi_gameSessionRestarted() throws Exception { 572 assumeGameServiceFeaturePresent(); 573 574 // The test game finishes its activity when it is put in the background by a press of the 575 // back button. 576 launchAndWaitForPackage(FINISH_ON_BACK_GAME_PACKAGE_NAME); 577 578 assertThat(getTestService().getActiveSessions()).containsExactly( 579 FINISH_ON_BACK_GAME_PACKAGE_NAME); 580 581 // Close the game by pressing the back button. 582 UiAutomatorUtils.getUiDevice().pressBack(); 583 UiAutomatorUtils.getUiDevice().waitForIdle(); 584 585 // With the game closed the game session should be destroyed 586 PollingCheck.waitFor(TimeUnit.SECONDS.toMillis(20), 587 () -> { 588 try { 589 return getTestService().getActiveSessions().isEmpty(); 590 } catch (RemoteException e) { 591 throw new RuntimeException(e); 592 } 593 }, 594 "Timed out waiting for game session to be destroyed."); 595 596 // Restore the game via the Recents UI 597 UiAutomatorUtils.getUiDevice().pressRecentApps(); 598 UiAutomatorUtils.getUiDevice().waitForIdle(); 599 600 logWindowHierarchy("after pressing recent apps"); 601 602 // Don't specify the full name of the test game "CtsGameServiceFinishOnBackGame" because it 603 // may be ellipsized in the Recents UI. 604 UiAutomatorUtils.waitFindObject(By.textStartsWith("CtsGameService")).click(); 605 606 logWindowHierarchy("after clicking on recent app"); 607 608 // There should again be an active game session for the restored game. 609 PollingCheck.waitFor(TimeUnit.SECONDS.toMillis(20), 610 () -> { 611 try { 612 return getTestService().getActiveSessions().size() == 1 613 && getTestService().getActiveSessions().get(0).equals( 614 FINISH_ON_BACK_GAME_PACKAGE_NAME); 615 } catch (RemoteException e) { 616 throw new RuntimeException(e); 617 } 618 }, 619 "Timed out waiting for game session to be re-created."); 620 } 621 622 @Test takeScreenshot_noPermission()623 public void takeScreenshot_noPermission() throws Exception { 624 assumeGameServiceFeaturePresent(); 625 launchAndWaitForPackage(TAKE_SCREENSHOT_VERIFIER_PACKAGE_NAME); 626 waitForTouchableOverlayBounds(); 627 getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); 628 final boolean ret = getTestService().takeScreenshotForFocusedGameSession(); 629 assertFalse(ret); 630 } 631 632 @Test takeScreenshot_expectedScreenshotSaved()633 public void takeScreenshot_expectedScreenshotSaved() throws Exception { 634 assumeGameServiceFeaturePresent(); 635 636 launchAndWaitForPackage(TAKE_SCREENSHOT_VERIFIER_PACKAGE_NAME); 637 638 // Make sure that the overlay is shown so that assertions can be made to check that 639 // the overlay is excluded from the game screenshot. 640 final Rect overlayBounds = waitForTouchableOverlayBounds(); 641 642 long startTimeSecs = Instant.now().getEpochSecond(); 643 final boolean ret = getTestService().takeScreenshotForFocusedGameSession(); 644 645 // Make sure a screenshot was taken, saved in media, and has the same dimensions as the 646 // device screen. 647 assertTrue(ret); 648 649 List<Uri> screenshotUris = PollingCheck.waitFor(TimeUnit.SECONDS.toMillis(5), 650 () -> getScreenshotsFromLastFiveSeconds(startTimeSecs), uris -> !uris.isEmpty()); 651 for (Uri screenshotUri : screenshotUris) { 652 final ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver, 653 screenshotUri); 654 // convert the hardware bitmap to a mutable 4-byte bitmap to get/compare pixel 655 final Bitmap gameScreenshot = ImageDecoder.decodeBitmap(source).copy( 656 Bitmap.Config.ARGB_8888, true); 657 658 final Size screenSize = getScreenSize(); 659 assertThat(gameScreenshot.getWidth()).isEqualTo(screenSize.getWidth()); 660 assertThat(gameScreenshot.getHeight()).isEqualTo(screenSize.getHeight()); 661 662 // The test game is always fullscreen red. It is too expensive to verify that 663 // the entire bitmap is red, so spot check certain areas. 664 665 // 1. Make sure that the overlay is excluded from the screenshot by checking 666 // pixels within the overlay bounds: 667 668 // top-left of overlay bounds: 669 assertThat( 670 gameScreenshot.getPixel(overlayBounds.left + 1, 671 overlayBounds.top + 1)).isEqualTo( 672 Color.RED); 673 // bottom-left corner of overlay bounds: 674 assertThat(gameScreenshot.getPixel(overlayBounds.left + 1, 675 overlayBounds.bottom - 1)).isEqualTo(Color.RED); 676 // top-right corner of overlay bounds: 677 assertThat( 678 gameScreenshot.getPixel(overlayBounds.right - 1, 679 overlayBounds.top + 1)).isEqualTo( 680 Color.RED); 681 // bottom-right corner of overlay bounds: 682 assertThat(gameScreenshot.getPixel(overlayBounds.right - 1, 683 overlayBounds.bottom - 1)).isEqualTo(Color.RED); 684 // middle corner of overlay bounds: 685 assertThat( 686 gameScreenshot.getPixel((overlayBounds.left + overlayBounds.right) / 2, 687 (overlayBounds.top + overlayBounds.bottom) / 2)).isEqualTo( 688 Color.RED); 689 690 // 2. Also check some pixels between the edge of the screen and the overlay 691 // bounds: 692 693 // above and to the left of the overlay 694 assertThat( 695 gameScreenshot.getPixel(overlayBounds.left / 2, 696 overlayBounds.top / 2)).isEqualTo( 697 Color.RED); 698 // below and to the left of the overlay 699 assertThat(gameScreenshot.getPixel(overlayBounds.left / 2, 700 (overlayBounds.bottom + gameScreenshot.getHeight()) / 2)).isEqualTo( 701 Color.RED); 702 // above and to the right of the overlay 703 assertThat(gameScreenshot.getPixel( 704 (overlayBounds.left + gameScreenshot.getWidth()) / 2, 705 overlayBounds.top / 2)).isEqualTo(Color.RED); 706 // below and to the right of the overlay 707 assertThat(gameScreenshot.getPixel( 708 (overlayBounds.left + gameScreenshot.getWidth()) / 2, 709 (overlayBounds.bottom + gameScreenshot.getHeight()) / 2)).isEqualTo( 710 Color.RED); 711 712 // 3. Finally check some pixels at the corners of the screen: 713 714 // top-left corner of screen 715 assertThat(gameScreenshot.getPixel(0, 0)).isEqualTo(Color.RED); 716 // bottom-left corner of screen 717 assertThat( 718 gameScreenshot.getPixel(0, gameScreenshot.getHeight() - 1)).isEqualTo( 719 Color.RED); 720 // top-right corner of screen 721 assertThat(gameScreenshot.getPixel(gameScreenshot.getWidth() - 1, 0)).isEqualTo( 722 Color.RED); 723 // bottom-right corner of screen 724 assertThat(gameScreenshot.getPixel(gameScreenshot.getWidth() - 1, 725 gameScreenshot.getHeight() - 1)).isEqualTo(Color.RED); 726 final PendingIntent pi = MediaStore.createDeleteRequest(mContentResolver, 727 ImmutableList.of(screenshotUri)); 728 final GetResultActivity.Result result = startIntentWithGrant(pi); 729 assertEquals(Activity.RESULT_OK, result.resultCode); 730 } 731 } 732 getScreenshotsFromLastFiveSeconds(long startTimeSecs)733 private List<Uri> getScreenshotsFromLastFiveSeconds(long startTimeSecs) { 734 final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 735 final List<Uri> screenshotUris = new ArrayList<>(); 736 try (Cursor cursor = mContentResolver.query(contentUri, 737 new String[]{MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME, 738 MediaStore.MediaColumns.DATE_ADDED}, null, null, 739 MediaStore.MediaColumns.DATE_ADDED + " DESC")) { 740 while (cursor.moveToNext()) { 741 final long addedTimeSecs = cursor.getLong(2); 742 // try to find the latest screenshot file created within 5s 743 if (addedTimeSecs >= startTimeSecs && addedTimeSecs - startTimeSecs < 5) { 744 final long id = cursor.getLong(0); 745 final Uri screenshotUri = ContentUris.withAppendedId(contentUri, id); 746 final String name = cursor.getString(1); 747 Log.d(TAG, "Found screenshot with name " + name); 748 screenshotUris.add(screenshotUri); 749 } 750 } 751 } 752 return screenshotUris; 753 } 754 startIntentWithGrant(PendingIntent pi)755 private GetResultActivity.Result startIntentWithGrant(PendingIntent pi) throws Exception { 756 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 757 final Intent intent = new Intent(inst.getContext(), GetResultActivity.class); 758 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 759 760 final UiDevice device = UiDevice.getInstance(inst); 761 final GetResultActivity activity = (GetResultActivity) inst.startActivitySync(intent); 762 inst.waitForIdleSync(); 763 activity.mResult.clear(); 764 device.waitForIdle(); 765 activity.startIntentSenderForResult(pi.getIntentSender(), 42, null, 0, 0, 0); 766 device.waitForIdle(); 767 final UiSelector grant = new UiSelector().textMatches("(?i)Allow"); 768 final boolean grantExists = new UiObject(grant).waitForExists(5000); 769 if (grantExists) { 770 device.findObject(grant).click(); 771 } 772 return activity.getResult(); 773 } 774 getTestService()775 private IGameServiceTestService getTestService() { 776 return mServiceConnection.mService; 777 } 778 assumeGameServiceFeaturePresent()779 private static void assumeGameServiceFeaturePresent() { 780 assumeTrue(gameServiceFeaturePresent()); 781 } 782 gameServiceFeaturePresent()783 private static boolean gameServiceFeaturePresent() { 784 return getInstrumentation().getContext().getPackageManager().hasSystemFeature( 785 PackageManager.FEATURE_GAME_SERVICE); 786 } 787 launchAndWaitForPackage(String packageName)788 private static void launchAndWaitForPackage(String packageName) throws Exception { 789 PackageManager packageManager = getInstrumentation().getContext().getPackageManager(); 790 getInstrumentation().getContext().startActivity( 791 packageManager.getLaunchIntentForPackage(packageName)); 792 UiAutomatorUtils.waitFindObject(By.pkg(packageName).depth(0)); 793 } 794 setText(String resourcePackage, String resourceId, String text)795 private static void setText(String resourcePackage, String resourceId, String text) 796 throws Exception { 797 UiAutomatorUtils.waitFindObject(By.res(resourcePackage, resourceId)) 798 .setText(text); 799 } 800 click(String resourcePackage, String resourceId)801 private static void click(String resourcePackage, String resourceId) throws Exception { 802 UiAutomatorUtils.waitFindObject(By.res(resourcePackage, resourceId)) 803 .click(); 804 } 805 swipeFromTopEdgeToShowSystemBars()806 private static void swipeFromTopEdgeToShowSystemBars() { 807 UiDevice uiDevice = UiAutomatorUtils.getUiDevice(); 808 uiDevice.swipe( 809 uiDevice.getDisplayWidth() / 2, 20, 810 uiDevice.getDisplayWidth() / 2, uiDevice.getDisplayHeight() / 2, 811 10); 812 } 813 waitForGameServiceConnected()814 private void waitForGameServiceConnected() { 815 PollingCheck.waitFor(TimeUnit.SECONDS.toMillis(40), () -> isGameServiceConnected(), 816 "Timed out waiting for game service to connect"); 817 } 818 waitForGameServiceDisconnected()819 private void waitForGameServiceDisconnected() { 820 PollingCheck.waitFor(TimeUnit.SECONDS.toMillis(5), () -> !isGameServiceConnected(), 821 "Timed out waiting for game service to disconnect"); 822 } 823 isGameServiceConnected()824 private boolean isGameServiceConnected() { 825 try { 826 return getTestService().isGameServiceConnected(); 827 } catch (RemoteException e) { 828 throw new RuntimeException(e); 829 } 830 } 831 waitForTouchableOverlayBounds()832 private Rect waitForTouchableOverlayBounds() { 833 return PollingCheck.waitFor( 834 TimeUnit.SECONDS.toMillis(5), 835 () -> { 836 try { 837 return getTestService().getTouchableOverlayBounds(); 838 } catch (RemoteException e) { 839 throw new RuntimeException(e); 840 } 841 }, 842 bounds -> bounds != null && !bounds.isEmpty()); 843 } 844 845 private void assertOverlayDoesNotAppear() { 846 assertThat(waitForTouchableOverlayBounds().isEmpty()).isTrue(); 847 } 848 849 private Rect waitForOverlayToDisappear() { 850 return PollingCheck.waitFor( 851 TimeUnit.SECONDS.toMillis(20), 852 () -> { 853 try { 854 return getTestService().getTouchableOverlayBounds(); 855 } catch (RemoteException e) { 856 throw new RuntimeException(e); 857 } 858 }, 859 Rect::isEmpty); 860 } 861 862 private static void forceStop(String packageName) { 863 ShellUtils.runShellCommand("am force-stop %s", packageName); 864 UiAutomatorUtils.getUiDevice().wait(Until.gone(By.pkg(packageName).depth(0)), 865 TimeUnit.SECONDS.toMillis(20)); 866 } 867 868 private static void clearCache(String packageName) { 869 ShellUtils.runShellCommand("pm clear %s", packageName); 870 } 871 872 private static int getActivityTaskId(String packageName, String componentName) { 873 final String output = ShellUtils.runShellCommand("am stack list"); 874 final Pattern pattern = Pattern.compile( 875 String.format(".*taskId=([0-9]+): %s/%s.*", packageName, componentName)); 876 877 for (String line : output.split("\\n")) { 878 Matcher matcher = pattern.matcher(line); 879 if (matcher.matches()) { 880 String taskId = matcher.group(1); 881 return Integer.parseInt(taskId); 882 } 883 } 884 885 return -1; 886 } 887 888 private static class RestartGameVerifierPage { 889 private RestartGameVerifierPage() { 890 } 891 892 public static void launch() throws Exception { 893 launchAndWaitForPackage(RESTART_GAME_VERIFIER_PACKAGE_NAME); 894 } 895 896 public static void assertTimesStarted(int times) throws UiObjectNotFoundException { 897 UiAutomatorUtils.waitFindObject( 898 By.res(RESTART_GAME_VERIFIER_PACKAGE_NAME, "times_started").text( 899 String.valueOf(times))); 900 } 901 902 public static void assertHasSavedInstanceState(boolean hasSavedInstanceState) 903 throws UiObjectNotFoundException { 904 UiAutomatorUtils.waitFindObject( 905 By.res(RESTART_GAME_VERIFIER_PACKAGE_NAME, "has_saved_instance_state").text( 906 String.valueOf(hasSavedInstanceState))); 907 } 908 } 909 910 private static class StartActivityVerifierPage { 911 912 public static void launch(IGameServiceTestService testService) throws Exception { 913 testService.startGameSessionActivity( 914 new Intent("android.service.games.cts.startactivityverifier.START"), null); 915 } 916 917 public static void assertLaunched() throws UiObjectNotFoundException { 918 UiAutomatorUtils.waitFindObject(By.pkg(START_ACTIVITY_VERIFIER_PACKAGE_NAME).depth(0)); 919 } 920 921 public static void setResultCode(int resultCode) throws Exception { 922 setText(START_ACTIVITY_VERIFIER_PACKAGE_NAME, "result_code_edit_text", 923 String.valueOf(resultCode)); 924 } 925 926 public static void setResultData(String resultData) throws Exception { 927 setText(START_ACTIVITY_VERIFIER_PACKAGE_NAME, "result_data_edit_text", resultData); 928 } 929 930 public static void clickSendResultButton() throws Exception { 931 click(START_ACTIVITY_VERIFIER_PACKAGE_NAME, "send_result_button"); 932 } 933 934 public static String getResultData(Intent data) { 935 return data.getStringExtra("data"); 936 } 937 } 938 939 private static Size getScreenSize() { 940 WindowManager wm = 941 (WindowManager) 942 InstrumentationRegistry.getInstrumentation() 943 .getContext() 944 .getSystemService(Context.WINDOW_SERVICE); 945 WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); 946 Rect windowBounds = windowMetrics.getBounds(); 947 return new Size(windowBounds.width(), windowBounds.height()); 948 } 949 950 private void logWindowHierarchy(String state) { 951 try { 952 ByteArrayOutputStream os = new ByteArrayOutputStream(); 953 UiAutomatorUtils.getUiDevice().dumpWindowHierarchy(os); 954 955 Log.d(TAG, "Window hierarchy: " + state); 956 for (String line : os.toString("UTF-8").split("\n")) { 957 Log.d(TAG, line); 958 } 959 } catch (IOException e) { 960 throw new RuntimeException(e); 961 } 962 } 963 964 private static final class ServiceConnection implements android.content.ServiceConnection { 965 private final Semaphore mSemaphore = new Semaphore(0); 966 private IGameServiceTestService mService; 967 968 @Override 969 public void onServiceConnected(ComponentName name, IBinder service) { 970 mService = IGameServiceTestService.Stub.asInterface(service); 971 mSemaphore.release(); 972 } 973 974 @Override 975 public void onServiceDisconnected(ComponentName name) { 976 mService = null; 977 } 978 979 public void waitForConnection(int timeout, TimeUnit timeUnit) throws Exception { 980 assertThat(mSemaphore.tryAcquire(timeout, timeUnit)).isTrue(); 981 } 982 } 983 } 984