Add more tests to verify blast buffer queue behaviour
Validate shared buffer mode & auto refresh, max buffer
counts, buffer rejection & geometry.
Test: atest SurfaceViewBufferTests
Bug: 169849887
Change-Id: I322b62f1e0a8f13f68f4e70c8ef1e33a8d6217f5
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index 647da2a..48031de 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -33,6 +33,8 @@
"kotlinx-coroutines-android",
"flickerlib",
"truth-prebuilt",
+ "cts-wm-util",
+ "CtsSurfaceValidatorLib",
],
}
@@ -43,6 +45,7 @@
],
shared_libs: [
"libutils",
+ "libui",
"libgui",
"liblog",
"libandroid",
diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml
index 95885c1..c910ecd 100644
--- a/tests/SurfaceViewBufferTests/AndroidManifest.xml
+++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml
@@ -23,12 +23,19 @@
<uses-permission android:name="android.permission.DUMP" />
<!-- Enable / Disable sv blast adapter !-->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Readback virtual display output !-->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <!-- Save failed test bitmap images !-->
+ <uses-permission android:name="android.Manifest.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:allowBackup="false"
android:supportsRtl="true">
<activity android:name=".MainActivity"
android:taskAffinity="com.android.test.MainActivity"
android:theme="@style/AppTheme"
+ android:configChanges="orientation|screenSize"
android:label="SurfaceViewBufferTestApp"
android:exported="true">
<intent-filter>
@@ -36,6 +43,10 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
+ android:foregroundServiceType="mediaProjection"
+ android:enabled="true">
+ </service>
<uses-library android:name="android.test.runner"/>
</application>
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
index 0c86524..ce226fd 100644
--- a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -32,6 +32,7 @@
extern "C" {
int i = 0;
static ANativeWindow* sAnw;
+static std::map<uint32_t /* slot */, ANativeWindowBuffer*> sBuffers;
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env, jclass,
jobject surfaceObject) {
@@ -39,11 +40,14 @@
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
surface->enableFrameTimestamps(true);
+ surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false);
+ native_window_set_usage(sAnw, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ native_window_set_buffers_format(sAnw, HAL_PIXEL_FORMAT_RGBA_8888);
return 0;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplayed(
- JNIEnv*, jclass, jint jFrameNumber, jint timeoutSec) {
+ JNIEnv*, jclass, jlong jFrameNumber, jint timeoutMs) {
using namespace std::chrono_literals;
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
@@ -63,8 +67,8 @@
&outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime);
if (outDisplayPresentTime < 0) {
auto end = std::chrono::steady_clock::now();
- if (std::chrono::duration_cast<std::chrono::seconds>(end - start).count() >
- timeoutSec) {
+ if (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() >
+ timeoutMs) {
return -1;
}
}
@@ -99,4 +103,121 @@
assert(sAnw);
return ANativeWindow_setBuffersGeometry(sAnw, w, h, format);
}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffersTransform(
+ JNIEnv* /* env */, jclass /* clazz */, jint transform) {
+ assert(sAnw);
+ return native_window_set_buffers_transform(sAnw, transform);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetScalingMode(JNIEnv* /* env */,
+ jclass /* clazz */,
+ jint scalingMode) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ return surface->setScalingMode(scalingMode);
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceDequeueBuffer(JNIEnv* /* env */,
+ jclass /* clazz */,
+ jint slot,
+ jint timeoutMs) {
+ assert(sAnw);
+ ANativeWindowBuffer* anb;
+ int fenceFd;
+ int result = sAnw->dequeueBuffer(sAnw, &anb, &fenceFd);
+ if (result != android::OK) {
+ return result;
+ }
+ sBuffers[slot] = anb;
+ android::sp<android::Fence> fence(new android::Fence(fenceFd));
+ int waitResult = fence->wait(timeoutMs);
+ if (waitResult != android::OK) {
+ sAnw->cancelBuffer(sAnw, sBuffers[slot], -1);
+ sBuffers[slot] = nullptr;
+ return waitResult;
+ }
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceCancelBuffer(JNIEnv* /* env */,
+ jclass /* clazz */,
+ jint slot) {
+ assert(sAnw);
+ assert(sBuffers[slot]);
+ int result = sAnw->cancelBuffer(sAnw, sBuffers[slot], -1);
+ sBuffers[slot] = nullptr;
+ return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_drawBuffer(JNIEnv* env,
+ jclass /* clazz */, jint slot,
+ jintArray jintArrayColor) {
+ assert(sAnw);
+ assert(sBuffers[slot]);
+
+ int* color = env->GetIntArrayElements(jintArrayColor, nullptr);
+
+ ANativeWindowBuffer* buffer = sBuffers[slot];
+ android::sp<android::GraphicBuffer> graphicBuffer(static_cast<android::GraphicBuffer*>(buffer));
+ const android::Rect bounds(buffer->width, buffer->height);
+ android::Region newDirtyRegion;
+ newDirtyRegion.set(bounds);
+
+ void* vaddr;
+ int fenceFd = -1;
+ graphicBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ newDirtyRegion.bounds(), &vaddr, fenceFd);
+
+ for (int32_t row = 0; row < buffer->height; row++) {
+ uint8_t* dst = static_cast<uint8_t*>(vaddr) + (buffer->stride * row) * 4;
+ for (int32_t column = 0; column < buffer->width; column++) {
+ dst[0] = color[0];
+ dst[1] = color[1];
+ dst[2] = color[2];
+ dst[3] = color[3];
+ dst += 4;
+ }
+ }
+ graphicBuffer->unlockAsync(&fenceFd);
+ env->ReleaseIntArrayElements(jintArrayColor, color, JNI_ABORT);
+ return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceQueueBuffer(JNIEnv* /* env */,
+ jclass /* clazz */,
+ jint slot,
+ jboolean freeSlot) {
+ assert(sAnw);
+ assert(sBuffers[slot]);
+ int result = sAnw->queueBuffer(sAnw, sBuffers[slot], -1);
+ if (freeSlot) {
+ sBuffers[slot] = nullptr;
+ }
+ return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount(
+ JNIEnv* /* env */, jclass /* clazz */, jint count) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ int result = native_window_set_buffer_count(sAnw, count);
+ return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetSharedBufferMode(
+ JNIEnv* /* env */, jclass /* clazz */, jboolean shared) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ int result = native_window_set_shared_buffer_mode(sAnw, shared);
+ return result;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetAutoRefresh(
+ JNIEnv* /* env */, jclass /* clazz */, jboolean autoRefresh) {
+ assert(sAnw);
+ android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+ int result = native_window_set_auto_refresh(sAnw, autoRefresh);
+ return result;
+}
}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
new file mode 100644
index 0000000..eb16bad
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+ /** Submit buffers as fast as possible and make sure they are presented on display */
+ @Test
+ fun testQueueBuffers() {
+ val numFrames = 100L
+ val trace = withTrace {
+ for (i in 1..numFrames) {
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ }
+ assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 1000 /* ms */))
+ }
+
+ assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ }
+
+ @Test
+ fun testSetBufferScalingMode_outOfOrderQueueBuffer() {
+ val trace = withTrace {
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+
+ it.mSurfaceProxy.SurfaceQueueBuffer(1)
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(2, 5000 /* ms */))
+ }
+
+ assertThat(trace).hasFrameSequence("SurfaceView", 1..2L)
+ }
+
+ @Test
+ fun testSetBufferScalingMode_multipleDequeueBuffer() {
+ val numFrames = 20L
+ val trace = withTrace {
+ for (count in 1..(numFrames / 2)) {
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ it.mSurfaceProxy.SurfaceQueueBuffer(1)
+ }
+ assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
+ }
+
+ assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ }
+
+ @Test
+ fun testSetBufferCount_queueMaxBufferCountMinusOne() {
+ val numBufferCount = 8
+ val numFrames = numBufferCount * 5L
+ val trace = withTrace {
+ assertEquals(0, it.mSurfaceProxy.NativeWindowSetBufferCount(numBufferCount + 1))
+ for (i in 1..numFrames / numBufferCount) {
+ for (bufferSlot in 0..numBufferCount - 1) {
+ assertEquals(0,
+ it.mSurfaceProxy.SurfaceDequeueBuffer(bufferSlot, 1000 /* ms */))
+ }
+
+ for (bufferSlot in 0..numBufferCount - 1) {
+ it.mSurfaceProxy.SurfaceQueueBuffer(bufferSlot)
+ }
+ }
+ assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
+ }
+
+ assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
new file mode 100644
index 0000000..95a7fd5
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.graphics.Point
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+ @Test
+ fun testSetBuffersGeometry_0x0_rejectsBuffer() {
+ val trace = withTrace {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
+ R8G8B8A8_UNORM)
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
+ // Submit buffer one with a different size which should be rejected
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+
+ // submit a buffer with the default buffer size
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */)
+ }
+ // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
+ assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+
+ // Verify the next buffer is submitted with the correct size
+ assertThat(trace).layer("SurfaceView", 3).also {
+ it.hasBufferSize(defaultBufferSize)
+ // scaling mode is not passed down to the layer for blast
+ if (useBlastAdapter) {
+ it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
+ } else {
+ it.hasScalingMode(ScalingMode.FREEZE.ordinal)
+ }
+ }
+ }
+
+ @Test
+ fun testSetBufferScalingMode_freeze() {
+ val bufferSize = Point(300, 200)
+ val trace = withTrace {
+ it.drawFrame()
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+ R8G8B8A8_UNORM)
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+ // Change buffer size and set scaling mode to freeze
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+ R8G8B8A8_UNORM)
+
+ // first dequeued buffer does not have the new size so it should be rejected.
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
+ it.mSurfaceProxy.SurfaceQueueBuffer(1)
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+ }
+
+ // verify buffer size is reset to default buffer size
+ assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+ assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
+ }
+
+ @Test
+ fun testSetBufferScalingMode_freeze_withBufferRotation() {
+ val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x)
+ val trace = withTrace {
+ it.drawFrame()
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize,
+ R8G8B8A8_UNORM)
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+ // Change buffer size and set scaling mode to freeze
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+ R8G8B8A8_UNORM)
+
+ // first dequeued buffer does not have the new size so it should be rejected.
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ // add a buffer transform so the buffer size is correct.
+ it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90)
+ it.mSurfaceProxy.SurfaceQueueBuffer(1)
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+ }
+
+ // verify buffer size is reset to default buffer size
+ assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+ assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
+ assertThat(trace).layer("SurfaceView", 3).hasBufferOrientation(Transform.ROT_90.value)
+ }
+
+ @Test
+ fun testRejectedBuffersAreReleased() {
+ val bufferSize = Point(300, 200)
+ val trace = withTrace {
+ for (count in 0 until 5) {
+ it.drawFrame()
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 1L,
+ 500 /* ms */), 0)
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+ R8G8B8A8_UNORM)
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+ // Change buffer size and set scaling mode to freeze
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+ R8G8B8A8_UNORM)
+
+ // first dequeued buffer does not have the new size so it should be rejected.
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
+ it.mSurfaceProxy.SurfaceQueueBuffer(1)
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 3L,
+ 500 /* ms */), 0)
+ }
+ }
+
+ for (count in 0 until 5) {
+ assertThat(trace).layer("SurfaceView", (count * 3) + 1L)
+ .hasBufferSize(defaultBufferSize)
+ assertThat(trace).layer("SurfaceView", (count * 3) + 2L)
+ .doesNotExist()
+ assertThat(trace).layer("SurfaceView", (count * 3) + 3L)
+ .hasBufferSize(bufferSize)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
new file mode 100644
index 0000000..03f8c05
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.graphics.Point
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+ @Test
+ fun testSetBuffersGeometry_0x0_resetsBufferSize() {
+ val trace = withTrace {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
+ R8G8B8A8_UNORM)
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
+ }
+
+ // verify buffer size is reset to default buffer size
+ assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ }
+
+ @Test
+ fun testSetBuffersGeometry_smallerThanBuffer() {
+ val bufferSize = Point(300, 200)
+ val trace = withTrace {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+ R8G8B8A8_UNORM)
+ it.drawFrame()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
+ }
+
+ assertThat(trace).layer("SurfaceView", 1).also {
+ it.hasBufferSize(bufferSize)
+ it.hasLayerSize(defaultBufferSize)
+ it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
+ }
+ }
+
+ @Test
+ fun testSetBuffersGeometry_largerThanBuffer() {
+ val bufferSize = Point(3000, 2000)
+ val trace = withTrace {
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+ R8G8B8A8_UNORM)
+ it.drawFrame()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
+ }
+
+ assertThat(trace).layer("SurfaceView", 1).also {
+ it.hasBufferSize(bufferSize)
+ it.hasLayerSize(defaultBufferSize)
+ it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
+ }
+ }
+
+ @Test
+ fun testSetBufferScalingMode_freeze() {
+ val bufferSize = Point(300, 200)
+ val trace = withTrace {
+ it.drawFrame()
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
+ R8G8B8A8_UNORM)
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+ // Change buffer size and set scaling mode to freeze
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+ R8G8B8A8_UNORM)
+
+ // first dequeued buffer does not have the new size so it should be rejected.
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
+ it.mSurfaceProxy.SurfaceQueueBuffer(1)
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+ }
+
+ // verify buffer size is reset to default buffer size
+ assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+ assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
+ }
+
+ @Test
+ fun testSetBuffersTransform_FLIP() {
+ val transforms = arrayOf(Transform.FLIP_H, Transform.FLIP_V, Transform.ROT_180).withIndex()
+ for ((index, transform) in transforms) {
+ val trace = withTrace {
+ it.mSurfaceProxy.ANativeWindowSetBuffersTransform(transform)
+ it.mSurfaceProxy.ANativeWindowLock()
+ it.mSurfaceProxy.ANativeWindowUnlockAndPost()
+ it.mSurfaceProxy.waitUntilBufferDisplayed(index + 1L, 500 /* ms */)
+ }
+
+ assertThat(trace).layer("SurfaceView", index + 1L).also {
+ it.hasBufferSize(defaultBufferSize)
+ it.hasLayerSize(defaultBufferSize)
+ it.hasBufferOrientation(transform.value)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
new file mode 100644
index 0000000..eac3041
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.graphics.Point
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+import junit.framework.Assert.assertEquals
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class InverseDisplayTransformTests(useBlastAdapter: Boolean) :
+ SurfaceTracingTestBase(useBlastAdapter) {
+ @Before
+ override fun setup() {
+ scenarioRule.getScenario().onActivity {
+ it.rotate90()
+ }
+ instrumentation.waitForIdleSync()
+ super.setup()
+ }
+
+ @Test
+ fun testSetBufferScalingMode_freeze_withInvDisplayTransform() {
+ assumeFalse("Blast does not support buffer rejection with Inv display " +
+ "transform since the only user for this hidden api is camera which does not use" +
+ "fixed scaling mode.", useBlastAdapter)
+
+ val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x)
+ val trace = withTrace {
+ // Inverse display transforms are sticky AND they are only consumed by the sf after
+ // a valid buffer has been acquired.
+ it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.INVERSE_DISPLAY.value)
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize,
+ R8G8B8A8_UNORM)
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
+ // Change buffer size and set scaling mode to freeze
+ it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
+ R8G8B8A8_UNORM)
+
+ // first dequeued buffer does not have the new size so it should be rejected.
+ it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90.value)
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ it.mSurfaceProxy.ANativeWindowSetBuffersTransform(0)
+ it.mSurfaceProxy.SurfaceQueueBuffer(1)
+ assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
+ }
+
+ // verify buffer size is reset to default buffer size
+ assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ assertThat(trace).layer("SurfaceView", 2).doesNotExist()
+ assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
index b1e1336..ed79054 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
@@ -15,51 +15,80 @@
*/
package com.android.test
-import android.app.Activity
import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.graphics.Color
import android.graphics.Paint
+import android.graphics.Point
import android.graphics.Rect
import android.os.Bundle
import android.view.Gravity
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
+import android.view.View
+import android.view.WindowManager
+import android.view.cts.surfacevalidator.CapturedActivity
import android.widget.FrameLayout
import java.util.concurrent.CountDownLatch
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
-class MainActivity : Activity() {
+class MainActivity : CapturedActivity() {
val mSurfaceProxy = SurfaceProxy()
private var mSurfaceHolder: SurfaceHolder? = null
private val mDrawLock = ReentrantLock()
+ var mSurfaceView: SurfaceView? = null
val surface: Surface? get() = mSurfaceHolder?.surface
- public override fun onCreate(savedInstanceState: Bundle?) {
+ override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- addSurfaceView(Rect(0, 0, 500, 200))
+ addSurfaceView(Point(500, 200))
+ window.decorView.apply {
+ systemUiVisibility =
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN
+ }
}
- fun addSurfaceView(size: Rect): CountDownLatch {
+ override fun getCaptureDurationMs(): Long {
+ return 30000
+ }
+
+ fun addSurfaceView(size: Point): CountDownLatch {
val layout = findViewById<FrameLayout>(android.R.id.content)
val surfaceReadyLatch = CountDownLatch(1)
- val surfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
- layout.addView(surfaceView,
- FrameLayout.LayoutParams(size.width(), size.height(), Gravity.TOP or Gravity.LEFT)
+ mSurfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
+ layout.addView(mSurfaceView!!,
+ FrameLayout.LayoutParams(size.x, size.y, Gravity.TOP or Gravity.LEFT)
.also { it.setMargins(100, 100, 0, 0) })
+
return surfaceReadyLatch
}
+ fun enableSeamlessRotation() {
+ val p: WindowManager.LayoutParams = window.attributes
+ p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ window.attributes = p
+ }
+
+ fun rotate90() {
+ if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+ }
+ }
+
private fun createSurfaceView(
context: Context,
- size: Rect,
+ size: Point,
surfaceReadyLatch: CountDownLatch
): SurfaceView {
val surfaceView = SurfaceView(context)
surfaceView.setWillNotDraw(false)
- surfaceView.holder.setFixedSize(size.width(), size.height())
+ surfaceView.holder.setFixedSize(size.x, size.y)
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
mDrawLock.withLock {
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt
new file mode 100644
index 0000000..df3d30e
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.annotation.ColorInt
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.server.wm.WindowManagerState.getLogicalDisplaySize
+import android.view.cts.surfacevalidator.CapturedActivity
+import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase
+import android.view.cts.surfacevalidator.PixelChecker
+import android.view.cts.surfacevalidator.RectChecker
+import android.widget.FrameLayout
+import androidx.test.rule.ActivityTestRule
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import java.util.concurrent.CountDownLatch
+
+open class ScreenRecordTestBase(useBlastAdapter: Boolean) :
+ SurfaceViewBufferTestBase(useBlastAdapter) {
+ @get:Rule
+ var mActivityRule = ActivityTestRule(MainActivity::class.java)
+
+ private lateinit var mActivity: MainActivity
+
+ @Before
+ override fun setup() {
+ super.setup()
+ mActivity = mActivityRule.launchActivity(Intent())
+ lateinit var surfaceReadyLatch: CountDownLatch
+ runOnUiThread {
+ it.dismissPermissionDialog()
+ it.setLogicalDisplaySize(getLogicalDisplaySize())
+ surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
+ }
+ surfaceReadyLatch.await()
+ // sleep to finish animations
+ instrumentation.waitForIdleSync()
+ }
+
+ @After
+ override fun teardown() {
+ super.teardown()
+ mActivityRule.finishActivity()
+ }
+
+ fun runOnUiThread(predicate: (it: MainActivity) -> Unit) {
+ mActivityRule.runOnUiThread {
+ predicate(mActivity)
+ }
+ }
+
+ fun withScreenRecording(
+ boundsToCheck: Rect,
+ @ColorInt color: Int,
+ predicate: (it: MainActivity) -> Unit
+ ): CapturedActivity.TestResult {
+ val testCase = object : ISurfaceValidatorTestCase {
+ override fun getChecker(): PixelChecker = RectChecker(boundsToCheck, color)
+ override fun start(context: Context, parent: FrameLayout) {
+ predicate(mActivity)
+ }
+ override fun end() { /* do nothing */ }
+ }
+
+ return mActivity.runTest(testCase)
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt
new file mode 100644
index 0000000..996a1d3
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.graphics.Color
+import android.graphics.Rect
+import android.os.SystemClock
+import android.view.cts.surfacevalidator.PixelColor
+import junit.framework.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class SharedBufferModeScreenRecordTests(useBlastAdapter: Boolean) :
+ ScreenRecordTestBase(useBlastAdapter) {
+
+ /** When auto refresh is set, surface flinger will wake up and refresh the display presenting
+ * the latest content in the buffer.
+ */
+ @Test
+ fun testAutoRefresh() {
+ var svBounds = Rect()
+ runOnUiThread {
+ assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+ assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+ it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */)
+ assertEquals(0,
+ it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */))
+
+ svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height)
+ val position = Rect()
+ it.mSurfaceView!!.getBoundsOnScreen(position)
+ svBounds.offsetTo(position.left, position.top)
+
+ // wait for buffers from other layers to be latched and transactions to be processed before
+ // updating the buffer
+ SystemClock.sleep(4000)
+ }
+
+ val result = withScreenRecording(svBounds, PixelColor.RED) {
+ it.mSurfaceProxy.drawBuffer(0, Color.RED)
+ }
+ val failRatio = 1.0f * result.failFrames / (result.failFrames + result.passFrames)
+
+ assertTrue("Error: " + result.failFrames +
+ " incorrect frames observed (out of " + (result.failFrames + result.passFrames) +
+ " frames)", failRatio < 0.05)
+ assertTrue("Error: Did not receive sufficient frame updates expected: >1000 actual:" +
+ result.passFrames, result.passFrames > 1000)
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
new file mode 100644
index 0000000..ae662506
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.graphics.Color
+import android.graphics.Rect
+import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
+ /** Sanity test to check each buffer is presented if its submitted with enough delay
+ * for SF to present the buffers. */
+ @Test
+ fun testCanPresentBuffers() {
+ val numFrames = 15L
+ val trace = withTrace {
+ assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+ for (i in 1..numFrames) {
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(i, 5000 /* ms */))
+ }
+ }
+
+ assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ }
+
+ /** Submit buffers as fast as possible testing that we are not blocked when dequeuing the buffer
+ * by setting the dequeue timeout to 1ms and checking that we present the newest buffer. */
+ @Test
+ fun testFastQueueBuffers() {
+ val numFrames = 15L
+ val trace = withTrace {
+ assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+ for (i in 1..numFrames) {
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+ it.mSurfaceProxy.SurfaceQueueBuffer(0)
+ }
+ assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
+ }
+
+ assertThat(trace).hasFrameSequence("SurfaceView", numFrames..numFrames)
+ }
+
+ /** Keep overwriting the buffer without queuing buffers and check that we present the latest
+ * buffer content. */
+ @Test
+ fun testAutoRefresh() {
+ var svBounds = Rect()
+ runOnUiThread {
+ assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
+ assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true))
+ assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
+ it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */)
+ assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */))
+
+ svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height)
+ val position = Rect()
+ it.mSurfaceView!!.getBoundsOnScreen(position)
+ svBounds.offsetTo(position.left, position.top)
+ }
+
+ runOnUiThread {
+ it.mSurfaceProxy.drawBuffer(0, Color.RED)
+ checkPixels(svBounds, Color.RED)
+ it.mSurfaceProxy.drawBuffer(0, Color.GREEN)
+ checkPixels(svBounds, Color.GREEN)
+ it.mSurfaceProxy.drawBuffer(0, Color.BLUE)
+ checkPixels(svBounds, Color.BLUE)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
index 884aae41..cfbd3ac 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -16,17 +16,46 @@
package com.android.test
+import android.annotation.ColorInt
+import android.graphics.Color
+import android.graphics.Point
+import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
+import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
+
class SurfaceProxy {
init {
System.loadLibrary("surface_jni")
}
external fun setSurface(surface: Any)
- external fun waitUntilBufferDisplayed(frameNumber: Int, timeoutSec: Int)
+ external fun waitUntilBufferDisplayed(frameNumber: Long, timeoutMs: Int): Int
external fun draw()
+ fun drawBuffer(slot: Int, @ColorInt c: Int) {
+ drawBuffer(slot, intArrayOf(Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)))
+ }
+ external fun drawBuffer(slot: Int, color: IntArray)
// android/native_window.h functions
external fun ANativeWindowLock()
external fun ANativeWindowUnlockAndPost()
+ fun ANativeWindowSetBuffersGeometry(surface: Any, size: Point, format: Int) {
+ ANativeWindowSetBuffersGeometry(surface, size.x, size.y, format)
+ }
external fun ANativeWindowSetBuffersGeometry(surface: Any, width: Int, height: Int, format: Int)
+ fun ANativeWindowSetBuffersTransform(transform: Transform) {
+ ANativeWindowSetBuffersTransform(transform.value)
+ }
+ external fun ANativeWindowSetBuffersTransform(transform: Int)
+
+ // gui/Surface.h functions
+ fun SurfaceSetScalingMode(scalingMode: ScalingMode) {
+ SurfaceSetScalingMode(scalingMode.ordinal)
+ }
+ external fun SurfaceSetScalingMode(scalingMode: Int)
+ external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int
+ external fun SurfaceCancelBuffer(slot: Int)
+ external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true)
+ external fun NativeWindowSetBufferCount(count: Int): Int
+ external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int
+ external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int
}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
new file mode 100644
index 0000000..cd4b385
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.annotation.ColorInt
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.Log
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor
+import com.android.server.wm.flicker.monitor.withSFTracing
+import com.android.server.wm.flicker.traces.layers.LayersTrace
+import junit.framework.Assert
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import java.io.FileOutputStream
+import java.io.IOException
+import java.util.concurrent.CountDownLatch
+
+open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
+ SurfaceViewBufferTestBase(useBlastAdapter) {
+ @get:Rule
+ var scenarioRule: ActivityScenarioRule<MainActivity> =
+ ActivityScenarioRule<MainActivity>(MainActivity::class.java)
+
+ @Before
+ override fun setup() {
+ super.setup()
+ stopLayerTrace()
+ addSurfaceView()
+ }
+
+ @After
+ override fun teardown() {
+ super.teardown()
+ scenarioRule.getScenario().close()
+ }
+
+ fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace {
+ return withSFTracing(instrumentation, TRACE_FLAGS) {
+ scenarioRule.getScenario().onActivity {
+ predicate(it)
+ }
+ }
+ }
+
+ fun runOnUiThread(predicate: (it: MainActivity) -> Unit) {
+ scenarioRule.getScenario().onActivity {
+ predicate(it)
+ }
+ }
+
+ private fun addSurfaceView() {
+ lateinit var surfaceReadyLatch: CountDownLatch
+ scenarioRule.getScenario().onActivity {
+ surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
+ }
+ surfaceReadyLatch.await()
+ // sleep to finish animations
+ instrumentation.waitForIdleSync()
+ }
+
+ private fun stopLayerTrace() {
+ val tmpDir = instrumentation.targetContext.dataDir.toPath()
+ LayersTraceMonitor(tmpDir).stop()
+ }
+
+ fun checkPixels(bounds: Rect, @ColorInt color: Int) {
+ val screenshot = instrumentation.getUiAutomation().takeScreenshot()
+ val pixels = IntArray(screenshot.width * screenshot.height)
+ screenshot.getPixels(pixels, 0, screenshot.width, 0, 0, screenshot.width, screenshot.height)
+ for (i in bounds.left + 10..bounds.right - 10) {
+ for (j in bounds.top + 10..bounds.bottom - 10) {
+ val actualColor = pixels[j * screenshot.width + i]
+ if (actualColor != color) {
+ val screenshotPath = instrumentation.targetContext
+ .getExternalFilesDir(null)?.resolve("screenshot.png")
+ try {
+ FileOutputStream(screenshotPath).use { out ->
+ screenshot.compress(Bitmap.CompressFormat.PNG, 100, out)
+ }
+ Log.e("SurfaceViewBufferTests", "Bitmap written to $screenshotPath")
+ } catch (e: IOException) {
+ Log.e("SurfaceViewBufferTests", "Error writing bitmap to file", e)
+ }
+ }
+ Assert.assertEquals("Checking $bounds found mismatch $i,$j",
+ Color.valueOf(color), Color.valueOf(actualColor))
+ }
+ }
+ }
+
+ private companion object {
+ private const val TRACE_FLAGS =
+ (1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
+ }
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
deleted file mode 100644
index b48a91d..0000000
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2020 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.test
-
-import android.app.Instrumentation
-import android.graphics.Rect
-import android.provider.Settings
-import androidx.test.ext.junit.rules.ActivityScenarioRule
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor
-import com.android.server.wm.flicker.monitor.withSFTracing
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import java.util.concurrent.CountDownLatch
-import kotlin.properties.Delegates
-
-@RunWith(Parameterized::class)
-class SurfaceViewBufferTest(val useBlastAdapter: Boolean) {
- private var mInitialUseBlastConfig by Delegates.notNull<Int>()
-
- @get:Rule
- var scenarioRule: ActivityScenarioRule<MainActivity> =
- ActivityScenarioRule<MainActivity>(MainActivity::class.java)
-
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- val defaultBufferSize = Rect(0, 0, 640, 480)
-
- @Before
- fun setup() {
- mInitialUseBlastConfig = Settings.Global.getInt(instrumentation.context.contentResolver,
- "use_blast_adapter_sv", 0)
- val enable = if (useBlastAdapter) 1 else 0
- Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
- enable)
- val tmpDir = instrumentation.targetContext.dataDir.toPath()
- LayersTraceMonitor(tmpDir).stop()
-
- lateinit var surfaceReadyLatch: CountDownLatch
- scenarioRule.getScenario().onActivity {
- surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
- }
- surfaceReadyLatch.await()
- }
-
- @After
- fun teardown() {
- scenarioRule.getScenario().close()
- Settings.Global.putInt(instrumentation.context.contentResolver,
- "use_blast_adapter_sv", mInitialUseBlastConfig)
- }
-
- @Test
- fun testSetBuffersGeometry_0x0_resetsBufferSize() {
- val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
- scenarioRule.getScenario().onActivity {
- it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
- R8G8B8A8_UNORM)
- it.mSurfaceProxy.ANativeWindowLock()
- it.mSurfaceProxy.ANativeWindowUnlockAndPost()
- it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
- }
- }
-
- // verify buffer size is reset to default buffer size
- assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- }
-
- @Test
- fun testSetBuffersGeometry_0x0_rejectsBuffer() {
- val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
- scenarioRule.getScenario().onActivity {
- it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
- R8G8B8A8_UNORM)
- it.mSurfaceProxy.ANativeWindowLock()
- it.mSurfaceProxy.ANativeWindowUnlockAndPost()
- it.mSurfaceProxy.ANativeWindowLock()
- it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
- // Submit buffer one with a different size which should be rejected
- it.mSurfaceProxy.ANativeWindowUnlockAndPost()
-
- // submit a buffer with the default buffer size
- it.mSurfaceProxy.ANativeWindowLock()
- it.mSurfaceProxy.ANativeWindowUnlockAndPost()
- it.mSurfaceProxy.waitUntilBufferDisplayed(3, 1 /* sec */)
- }
- }
- // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
- assertThat(trace).layer("SurfaceView", 2).doesNotExist()
-
- // Verify the next buffer is submitted with the correct size
- assertThat(trace).layer("SurfaceView", 3).also {
- it.hasBufferSize(defaultBufferSize)
- it.hasScalingMode(0 /* NATIVE_WINDOW_SCALING_MODE_FREEZE */)
- }
- }
-
- @Test
- fun testSetBuffersGeometry_smallerThanBuffer() {
- val bufferSize = Rect(0, 0, 300, 200)
- val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
- scenarioRule.getScenario().onActivity {
- it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
- bufferSize.height(), R8G8B8A8_UNORM)
- it.drawFrame()
- it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
- }
- }
-
- assertThat(trace).layer("SurfaceView", 1).also {
- it.hasBufferSize(bufferSize)
- it.hasLayerSize(defaultBufferSize)
- it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
- }
- }
-
- @Test
- fun testSetBuffersGeometry_largerThanBuffer() {
- val bufferSize = Rect(0, 0, 3000, 2000)
- val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
- scenarioRule.getScenario().onActivity {
- it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
- bufferSize.height(), R8G8B8A8_UNORM)
- it.drawFrame()
- it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
- }
- }
-
- assertThat(trace).layer("SurfaceView", 1).also {
- it.hasBufferSize(bufferSize)
- it.hasLayerSize(defaultBufferSize)
- it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
- }
- }
-
- /** Submit buffers as fast as possible and make sure they are queued */
- @Test
- fun testQueueBuffers() {
- val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
- scenarioRule.getScenario().onActivity {
- it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
- R8G8B8A8_UNORM)
- for (i in 0..100) {
- it.mSurfaceProxy.ANativeWindowLock()
- it.mSurfaceProxy.ANativeWindowUnlockAndPost()
- }
- it.mSurfaceProxy.waitUntilBufferDisplayed(100, 1 /* sec */)
- }
- }
- for (frameNumber in 1..100) {
- assertThat(trace).layer("SurfaceView", frameNumber.toLong())
- }
- }
-
- companion object {
- private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL
- private const val R8G8B8A8_UNORM = 1
-
- @JvmStatic
- @Parameterized.Parameters(name = "blast={0}")
- fun data(): Collection<Array<Any>> {
- return listOf(
- arrayOf(false), // First test: submit buffers via bufferqueue
- arrayOf(true) // Second test: submit buffers via blast adapter
- )
- }
- }
-}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
new file mode 100644
index 0000000..093c312
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 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.test
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.provider.Settings
+import androidx.test.InstrumentationRegistry
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.rules.TestName
+import org.junit.runners.Parameterized
+import kotlin.properties.Delegates
+
+open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
+ private var mInitialBlastConfig by Delegates.notNull<Boolean>()
+
+ val instrumentation: Instrumentation
+ get() = InstrumentationRegistry.getInstrumentation()
+
+ @get:Rule
+ var mName = TestName()
+
+ @Before
+ open fun setup() {
+ mInitialBlastConfig = getBlastAdapterSvEnabled()
+ setBlastAdapterSvEnabled(useBlastAdapter)
+ }
+
+ @After
+ open fun teardown() {
+ setBlastAdapterSvEnabled(mInitialBlastConfig)
+ }
+
+ private fun getBlastAdapterSvEnabled(): Boolean {
+ return Settings.Global.getInt(instrumentation.context.contentResolver,
+ "use_blast_adapter_sv", 0) != 0
+ }
+
+ private fun setBlastAdapterSvEnabled(enable: Boolean) {
+ Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
+ if (enable) 1 else 0)
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "blast={0}")
+ fun data(): Collection<Array<Any>> {
+ return listOf(
+ arrayOf(false), // First test: submit buffers via bufferqueue
+ arrayOf(true) // Second test: submit buffers via blast adapter
+ )
+ }
+
+ const val R8G8B8A8_UNORM = 1
+ val defaultBufferSize = Point(640, 480)
+
+ // system/window.h definitions
+ enum class ScalingMode() {
+ FREEZE, // = 0
+ SCALE_TO_WINDOW, // =1
+ SCALE_CROP, // = 2
+ NO_SCALE_CROP // = 3
+ }
+
+ // system/window.h definitions
+ enum class Transform(val value: Int) {
+ /* flip source image horizontally */
+ FLIP_H(1),
+ /* flip source image vertically */
+ FLIP_V(2),
+ /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
+ ROT_90(4),
+ /* rotate source image 180 degrees */
+ ROT_180(3),
+ /* rotate source image 270 degrees clock-wise */
+ ROT_270(7),
+ /* transforms source by the inverse transform of the screen it is displayed onto. This
+ * transform is applied last */
+ INVERSE_DISPLAY(0x08)
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
index abccd6cf..fe9deae 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
@@ -16,6 +16,7 @@
package com.android.test.taskembed
import android.app.Instrumentation
+import android.graphics.Point
import android.graphics.Rect
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.platform.app.InstrumentationRegistry
@@ -93,13 +94,15 @@
// verify buffer size should be changed to expected values.
assertThat(trace).layer(FIRST_ACTIVITY, frame).also {
- it.hasLayerSize(firstBounds)
- it.hasBufferSize(firstBounds)
+ val firstTaskSize = Point(firstBounds.width(), firstBounds.height())
+ it.hasLayerSize(firstTaskSize)
+ it.hasBufferSize(firstTaskSize)
}
assertThat(trace).layer(SECOND_ACTIVITY, frame).also {
- it.hasLayerSize(secondBounds)
- it.hasBufferSize(secondBounds)
+ val secondTaskSize = Point(secondBounds.width(), secondBounds.height())
+ it.hasLayerSize(secondTaskSize)
+ it.hasBufferSize(secondTaskSize)
}
}