Merge "Update GameServiceTest to verify screenshot saved" into tm-dev
diff --git a/tests/tests/gameservice/AndroidManifest.xml b/tests/tests/gameservice/AndroidManifest.xml
index a81e64d..936184b 100644
--- a/tests/tests/gameservice/AndroidManifest.xml
+++ b/tests/tests/gameservice/AndroidManifest.xml
@@ -20,6 +20,9 @@
<uses-permission android:name="android.permission.MANAGE_GAME_ACTIVITY"/>
<uses-permission android:name="android.service.games.cts.TEST_START_ACTIVITY"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.MANAGE_MEDIA"/>
<application android:label="CtsGameServiceTestApp">
@@ -47,6 +50,7 @@
</service>
<uses-library android:name="android.test.runner" />
+ <activity android:name="android.service.games.testing.GetResultActivity"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/gameservice/src/android/service/games/GameServiceTest.java b/tests/tests/gameservice/src/android/service/games/GameServiceTest.java
index 6d458ae..d95cabe 100644
--- a/tests/tests/gameservice/src/android/service/games/GameServiceTest.java
+++ b/tests/tests/gameservice/src/android/service/games/GameServiceTest.java
@@ -20,30 +20,44 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import android.app.Activity;
import android.app.GameManager;
+import android.app.Instrumentation;
+import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.ImageDecoder;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
+import android.provider.MediaStore;
import android.service.games.testing.ActivityResult;
+import android.service.games.testing.GetResultActivity;
import android.service.games.testing.IGameServiceTestService;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
+import android.util.Log;
import android.util.Size;
import android.view.WindowManager;
import android.view.WindowMetrics;
-import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.PollingCheck;
@@ -57,7 +71,11 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized.Parameter;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
@@ -68,6 +86,8 @@
*/
@RunWith(AndroidJUnit4.class)
public final class GameServiceTest {
+ static final String TAG = "GameServiceTest";
+
private static final String GAME_PACKAGE_NAME = "android.service.games.cts.game";
private static final String FALSE_POSITIVE_GAME_PACKAGE_NAME =
"android.service.games.cts.falsepositive";
@@ -83,7 +103,11 @@
private static final String TOUCH_VERIFIER_PACKAGE_NAME =
"android.service.games.cts.touchverifier";
+ @Parameter(0)
+ public String mVolumeName;
+
private ServiceConnection mServiceConnection;
+ private ContentResolver mContentResolver;
@Before
public void setUp() throws Exception {
@@ -106,9 +130,11 @@
GameManager gameManager =
getInstrumentation().getContext().getSystemService(GameManager.class);
+
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(gameManager,
manager -> manager.setGameServiceProvider(
getInstrumentation().getContext().getPackageName()));
+ mContentResolver = getInstrumentation().getContext().getContentResolver();
}
@After
@@ -378,7 +404,7 @@
}
@Test
- public void takeScreenshot_expectedBitmapReturned() throws Exception {
+ public void takeScreenshot_expectedScreenshotSaved() throws Exception {
assumeGameServiceFeaturePresent();
launchAndWaitForPackage(TAKE_SCREENSHOT_VERIFIER_PACKAGE_NAME);
@@ -386,68 +412,131 @@
// Make sure that the overlay is shown so that assertions can be made to check that
// the overlay is excluded from the game screenshot.
getTestService().showOverlayForFocusedGameSession();
- Rect overlayBounds = waitForTouchableOverlayBounds();
+ final Rect overlayBounds = waitForTouchableOverlayBounds();
- Bitmap gameScreenshot = getTestService().getBitmapScreenshotForFocusedGameSession();
+ long startTimeSecs = Instant.now().getEpochSecond();
+ final boolean ret = getTestService().takeScreenshotForFocusedGameSession();
- // Make sure a screenshot was taken and has the same dimensions as the device screen.
- assertNotNull(gameScreenshot);
+ // Make sure a screenshot was taken, saved in media, and has the same dimensions as the
+ // device screen.
+ assertTrue(ret);
+ final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ final List<Uri> list = new ArrayList<>();
+ try (Cursor cursor = mContentResolver.query(contentUri,
+ new String[]{MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME,
+ MediaStore.MediaColumns.DATE_ADDED}, null, null,
+ MediaStore.MediaColumns.DATE_ADDED + " DESC")) {
+ while (cursor.moveToNext()) {
+ final long addedTimeSecs = cursor.getLong(2);
+ // try to find the latest screenshot file created within 5s
+ if (addedTimeSecs >= startTimeSecs && addedTimeSecs - startTimeSecs < 5) {
+ final long id = cursor.getLong(0);
+ final Uri screenshotUri = ContentUris.withAppendedId(contentUri, id);
+ final String name = cursor.getString(1);
+ Log.d(TAG, "Found screenshot with name " + name);
+ list.add(screenshotUri);
+ final ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver,
+ screenshotUri);
+ // convert the hardware bitmap to a mutable 4-byte bitmap to get/compare pixel
+ final Bitmap gameScreenshot = ImageDecoder.decodeBitmap(source).copy(
+ Bitmap.Config.ARGB_8888, true);
- Size screenSize = getScreenSize();
- assertThat(gameScreenshot.getWidth()).isEqualTo(screenSize.getWidth());
- assertThat(gameScreenshot.getHeight()).isEqualTo(screenSize.getHeight());
+ final Size screenSize = getScreenSize();
+ assertThat(gameScreenshot.getWidth()).isEqualTo(screenSize.getWidth());
+ assertThat(gameScreenshot.getHeight()).isEqualTo(screenSize.getHeight());
- // The test game is always fullscreen red. It is too expensive to verify that the entire
- // bitmap is red, so spot check certain areas.
+ // The test game is always fullscreen red. It is too expensive to verify that
+ // the entire bitmap is red, so spot check certain areas.
- // 1. Make sure that the overlay is excluded from the screenshot by checking pixels within
- // the overlay bounds:
+ // 1. Make sure that the overlay is excluded from the screenshot by checking
+ // pixels within the overlay bounds:
- // top-left of overlay bounds:
- assertThat(
- gameScreenshot.getPixel(overlayBounds.left + 1, overlayBounds.top + 1)).isEqualTo(
- Color.RED);
- // bottom-left corner of overlay bounds:
- assertThat(gameScreenshot.getPixel(overlayBounds.left + 1,
- overlayBounds.bottom - 1)).isEqualTo(Color.RED);
- // top-right corner of overlay bounds:
- assertThat(
- gameScreenshot.getPixel(overlayBounds.right - 1, overlayBounds.top + 1)).isEqualTo(
- Color.RED);
- // bottom-right corner of overlay bounds:
- assertThat(gameScreenshot.getPixel(overlayBounds.right - 1,
- overlayBounds.bottom - 1)).isEqualTo(Color.RED);
- // middle corner of overlay bounds:
- assertThat(gameScreenshot.getPixel((overlayBounds.left + overlayBounds.right) / 2,
- (overlayBounds.top + overlayBounds.bottom) / 2)).isEqualTo(Color.RED);
+ // top-left of overlay bounds:
+ assertThat(
+ gameScreenshot.getPixel(overlayBounds.left + 1,
+ overlayBounds.top + 1)).isEqualTo(
+ Color.RED);
+ // bottom-left corner of overlay bounds:
+ assertThat(gameScreenshot.getPixel(overlayBounds.left + 1,
+ overlayBounds.bottom - 1)).isEqualTo(Color.RED);
+ // top-right corner of overlay bounds:
+ assertThat(
+ gameScreenshot.getPixel(overlayBounds.right - 1,
+ overlayBounds.top + 1)).isEqualTo(
+ Color.RED);
+ // bottom-right corner of overlay bounds:
+ assertThat(gameScreenshot.getPixel(overlayBounds.right - 1,
+ overlayBounds.bottom - 1)).isEqualTo(Color.RED);
+ // middle corner of overlay bounds:
+ assertThat(
+ gameScreenshot.getPixel((overlayBounds.left + overlayBounds.right) / 2,
+ (overlayBounds.top + overlayBounds.bottom) / 2)).isEqualTo(
+ Color.RED);
- // 2. Also check some pixels between the edge of the screen and the overlay bounds:
+ // 2. Also check some pixels between the edge of the screen and the overlay
+ // bounds:
- // above and to the left of the overlay
- assertThat(
- gameScreenshot.getPixel(overlayBounds.left / 2, overlayBounds.top / 2)).isEqualTo(
- Color.RED);
- // below and to the left of the overlay
- assertThat(gameScreenshot.getPixel(overlayBounds.left / 2,
- (overlayBounds.bottom + gameScreenshot.getHeight()) / 2)).isEqualTo(Color.RED);
- // above and to the right of the overlay
- assertThat(gameScreenshot.getPixel((overlayBounds.left + gameScreenshot.getWidth()) / 2,
- overlayBounds.top / 2)).isEqualTo(Color.RED);
- // below and to the right of the overlay
- assertThat(gameScreenshot.getPixel((overlayBounds.left + gameScreenshot.getWidth()) / 2,
- (overlayBounds.bottom + gameScreenshot.getHeight()) / 2)).isEqualTo(Color.RED);
+ // above and to the left of the overlay
+ assertThat(
+ gameScreenshot.getPixel(overlayBounds.left / 2,
+ overlayBounds.top / 2)).isEqualTo(
+ Color.RED);
+ // below and to the left of the overlay
+ assertThat(gameScreenshot.getPixel(overlayBounds.left / 2,
+ (overlayBounds.bottom + gameScreenshot.getHeight()) / 2)).isEqualTo(
+ Color.RED);
+ // above and to the right of the overlay
+ assertThat(gameScreenshot.getPixel(
+ (overlayBounds.left + gameScreenshot.getWidth()) / 2,
+ overlayBounds.top / 2)).isEqualTo(Color.RED);
+ // below and to the right of the overlay
+ assertThat(gameScreenshot.getPixel(
+ (overlayBounds.left + gameScreenshot.getWidth()) / 2,
+ (overlayBounds.bottom + gameScreenshot.getHeight()) / 2)).isEqualTo(
+ Color.RED);
- // 3. Finally check some pixels at the corners of the screen:
+ // 3. Finally check some pixels at the corners of the screen:
- // top-left corner of screen
- assertThat(gameScreenshot.getPixel(0, 0)).isEqualTo(Color.RED);
- // bottom-left corner of screen
- assertThat(gameScreenshot.getPixel(0, gameScreenshot.getHeight() - 1)).isEqualTo(Color.RED);
- // top-right corner of screen
- assertThat(gameScreenshot.getPixel(gameScreenshot.getWidth() - 1, 0)).isEqualTo(Color.RED);
- // bottom-right corner of screen
- assertThat(gameScreenshot.getPixel(gameScreenshot.getWidth() - 1,
- gameScreenshot.getHeight() - 1)).isEqualTo(Color.RED);
+ // top-left corner of screen
+ assertThat(gameScreenshot.getPixel(0, 0)).isEqualTo(Color.RED);
+ // bottom-left corner of screen
+ assertThat(
+ gameScreenshot.getPixel(0, gameScreenshot.getHeight() - 1)).isEqualTo(
+ Color.RED);
+ // top-right corner of screen
+ assertThat(gameScreenshot.getPixel(gameScreenshot.getWidth() - 1, 0)).isEqualTo(
+ Color.RED);
+ // bottom-right corner of screen
+ assertThat(gameScreenshot.getPixel(gameScreenshot.getWidth() - 1,
+ gameScreenshot.getHeight() - 1)).isEqualTo(Color.RED);
+ final PendingIntent pi = MediaStore.createDeleteRequest(mContentResolver,
+ ImmutableList.of(screenshotUri));
+ final GetResultActivity.Result result = startIntentWithGrant(pi);
+ assertEquals(Activity.RESULT_OK, result.resultCode);
+ }
+ }
+ }
+ assertThat(list.size()).isGreaterThan(0);
+ }
+
+ private GetResultActivity.Result startIntentWithGrant(PendingIntent pi) throws Exception {
+ final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ final Intent intent = new Intent(inst.getContext(), GetResultActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ final UiDevice device = UiDevice.getInstance(inst);
+ final GetResultActivity activity = (GetResultActivity) inst.startActivitySync(intent);
+ inst.waitForIdleSync();
+ activity.mResult.clear();
+ device.waitForIdle();
+ activity.startIntentSenderForResult(pi.getIntentSender(), 42, null, 0, 0, 0);
+ device.waitForIdle();
+ final UiSelector grant = new UiSelector().textMatches("(?i)Allow");
+ final boolean grantExists = new UiObject(grant).waitForExists(5000);
+ if (grantExists) {
+ device.findObject(grant).click();
+ }
+ return activity.getResult();
}
private IGameServiceTestService getTestService() {
diff --git a/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java b/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
index bd0198f..31a4efd 100644
--- a/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
+++ b/tests/tests/gameservice/src/android/service/games/GameServiceTestService.java
@@ -20,7 +20,6 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -154,37 +153,36 @@
}
@Override
- public Bitmap getBitmapScreenshotForFocusedGameSession() {
+ public boolean takeScreenshotForFocusedGameSession() {
+ boolean result = false;
TestGameSession focusedGameSession = TestGameSessionService.getFocusedSession();
- if (focusedGameSession == null) {
- return null;
+ if (focusedGameSession != null) {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ final boolean[] ret = new boolean[1];
+ ScreenshotCallback callback =
+ new ScreenshotCallback() {
+ @Override
+ public void onFailure(int statusCode) {
+ ret[0] = false;
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onSuccess() {
+ ret[0] = true;
+ countDownLatch.countDown();
+ }
+ };
+ focusedGameSession.takeScreenshot(Runnable::run, callback);
+ try {
+ countDownLatch.await(
+ SCREENSHOT_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ result = ret[0];
}
-
- CountDownLatch countDownLatch = new CountDownLatch(1);
- Bitmap[] ret = new Bitmap[1];
- ScreenshotCallback callback =
- new ScreenshotCallback() {
- @Override
- public void onFailure(int statusCode) {
- countDownLatch.countDown();
- }
-
- @Override
- public void onSuccess(Bitmap bitmap) {
- ret[0] = bitmap;
- countDownLatch.countDown();
- }
- };
- focusedGameSession.takeScreenshot(Runnable::run, callback);
-
- try {
- countDownLatch.await(
- SCREENSHOT_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- return null;
- }
-
- return ret[0];
+ return result;
}
public OnSystemBarVisibilityChangedInfo getOnSystemBarVisibilityChangedInfo() {
diff --git a/tests/tests/gameservice/src/android/service/games/testing/GetResultActivity.java b/tests/tests/gameservice/src/android/service/games/testing/GetResultActivity.java
new file mode 100644
index 0000000..87f42dd
--- /dev/null
+++ b/tests/tests/gameservice/src/android/service/games/testing/GetResultActivity.java
@@ -0,0 +1,57 @@
+/*
+ * 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.testing;
+
+import android.app.Activity;
+import android.content.Intent;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class GetResultActivity extends Activity {
+ public static class Result {
+ public final int requestCode;
+ public final int resultCode;
+ public final Intent data;
+
+ public Result(int requestCode, int resultCode, Intent data) {
+ this.requestCode = requestCode;
+ this.resultCode = resultCode;
+ this.data = data;
+ }
+ }
+ public LinkedBlockingQueue<Result> mResult = new LinkedBlockingQueue<>();
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mResult.offer(new Result(requestCode, resultCode, data));
+ finish();
+ }
+
+ public Result getResult() {
+ final Result result;
+ try {
+ result = mResult.poll(20, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (result == null) {
+ throw new IllegalStateException("Activity didn't receive a Result in 20 seconds");
+ }
+ return result;
+ }
+}
diff --git a/tests/tests/gameservice/src/android/service/games/testing/IGameServiceTestService.aidl b/tests/tests/gameservice/src/android/service/games/testing/IGameServiceTestService.aidl
index cd227bb..90ed0fe 100644
--- a/tests/tests/gameservice/src/android/service/games/testing/IGameServiceTestService.aidl
+++ b/tests/tests/gameservice/src/android/service/games/testing/IGameServiceTestService.aidl
@@ -16,7 +16,6 @@
package android.service.games.testing;
import android.content.Intent;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Bundle;
import android.service.games.testing.ActivityResult;
@@ -43,7 +42,7 @@
void showOverlayForFocusedGameSession();
- Bitmap getBitmapScreenshotForFocusedGameSession();
+ boolean takeScreenshotForFocusedGameSession();
OnSystemBarVisibilityChangedInfo getOnSystemBarVisibilityChangedInfo();