From 57928e6a37e66d9c7405e5a6ed8e0904a14975cd Mon Sep 17 00:00:00 2001
From: Vishnu Nair <vishnun@google.com>
Date: Wed, 16 Sep 2020 19:01:04 -0700
Subject: [PATCH] Verify SurfaceView BlastBufferQueue behavior

Initial code to easily configure buffer producers to generate buffers
with different properties inorder exercise and validate
BlastBufferQueue adapter.

The test captures surface flinger traces and verifies properties of a
single buffer. This will allow us to verify buffer presentation order,
buffer rejection, buffer properties and so on.

Test: atest SurfaceViewBufferTests
Bug: 168504870
Change-Id: I9714d7b6f5ffbe5fecca5d93e8184f0e6ac2b4c1
---
 PREUPLOAD.cfg                                 |   1 +
 tests/SurfaceViewBufferTests/Android.bp       |  58 ++++++
 .../AndroidManifest.xml                       |  46 +++++
 tests/SurfaceViewBufferTests/AndroidTest.xml  |  35 ++++
 .../cpp/SurfaceProxy.cpp                      | 102 ++++++++++
 .../res/values/styles.xml                     |  25 +++
 .../src/com/android/test/MainActivity.kt      | 102 ++++++++++
 .../src/com/android/test/SurfaceProxy.kt      |  32 +++
 .../com/android/test/SurfaceViewBufferTest.kt | 185 ++++++++++++++++++
 9 files changed, 586 insertions(+)
 create mode 100644 tests/SurfaceViewBufferTests/Android.bp
 create mode 100644 tests/SurfaceViewBufferTests/AndroidManifest.xml
 create mode 100644 tests/SurfaceViewBufferTests/AndroidTest.xml
 create mode 100644 tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
 create mode 100644 tests/SurfaceViewBufferTests/res/values/styles.xml
 create mode 100644 tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
 create mode 100644 tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
 create mode 100644 tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt

diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 7a8d1a1caf25..f66d12a69594 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,6 +11,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                libs/input/
                services/core/jni/
                services/incremental/
+               tests/
                tools/
 
 [Hook Scripts]
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
new file mode 100644
index 000000000000..647da2abd213
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -0,0 +1,58 @@
+// 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.
+
+android_test {
+    name: "SurfaceViewBufferTests",
+    srcs: ["**/*.java","**/*.kt"],
+    manifest: "AndroidManifest.xml",
+    test_config: "AndroidTest.xml",
+    platform_apis: true,
+    certificate: "platform",
+    use_embedded_native_libs: true,
+    jni_libs: [
+        "libsurface_jni",
+    ],
+
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.test.rules",
+        "androidx.test.runner",
+        "androidx.test.ext.junit",
+        "kotlin-stdlib",
+        "kotlinx-coroutines-android",
+        "flickerlib",
+        "truth-prebuilt",
+    ],
+}
+
+cc_library_shared {
+    name: "libsurface_jni",
+    srcs: [
+        "cpp/SurfaceProxy.cpp",
+    ],
+    shared_libs: [
+        "libutils",
+        "libgui",
+        "liblog",
+        "libandroid",
+    ],
+    include_dirs: [
+        "system/core/include"
+    ],
+    stl: "libc++_static",
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+}
diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml
new file mode 100644
index 000000000000..95885c1ca635
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="com.android.test">
+
+    <uses-sdk android:minSdkVersion="29"
+         android:targetSdkVersion="29"/>
+    <!-- Enable / Disable tracing !-->
+    <uses-permission android:name="android.permission.DUMP" />
+    <!-- Enable / Disable sv blast adapter !-->
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+    <application android:allowBackup="false"
+         android:supportsRtl="true">
+        <activity android:name=".MainActivity"
+                  android:taskAffinity="com.android.test.MainActivity"
+                  android:theme="@style/AppTheme"
+                  android:label="SurfaceViewBufferTestApp"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.test"
+                     android:label="SurfaceViewBufferTests">
+    </instrumentation>
+</manifest>
diff --git a/tests/SurfaceViewBufferTests/AndroidTest.xml b/tests/SurfaceViewBufferTests/AndroidTest.xml
new file mode 100644
index 000000000000..b73fe4853ecf
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs SurfaceView Buffer Tests">
+    <option name="test-tag" value="SurfaceViewBufferTests" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="false"/>
+        <option name="test-file-name" value="SurfaceViewBufferTests.apk"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.test"/>
+        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+        <option name="shell-timeout" value="6600s" />
+        <option name="test-timeout" value="6000s" />
+        <option name="hidden-api-checks" value="false" />
+    </test>
+</configuration>
diff --git a/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
new file mode 100644
index 000000000000..0c86524293e7
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/cpp/SurfaceProxy.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#include <android/log.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/window.h>
+#include <gui/Surface.h>
+#include <jni.h>
+#include <system/window.h>
+#include <utils/RefBase.h>
+#include <cassert>
+#include <chrono>
+#include <thread>
+
+#define TAG "SurfaceViewBufferTests"
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+
+extern "C" {
+int i = 0;
+static ANativeWindow* sAnw;
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env, jclass,
+                                                                     jobject surfaceObject) {
+    sAnw = ANativeWindow_fromSurface(env, surfaceObject);
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+    surface->enableFrameTimestamps(true);
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplayed(
+        JNIEnv*, jclass, jint jFrameNumber, jint timeoutSec) {
+    using namespace std::chrono_literals;
+    assert(sAnw);
+    android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
+
+    uint64_t frameNumber = static_cast<uint64_t>(jFrameNumber);
+    nsecs_t outRequestedPresentTime, outAcquireTime, outLatchTime, outFirstRefreshStartTime;
+    nsecs_t outLastRefreshStartTime, outGlCompositionDoneTime, outDequeueReadyTime;
+    nsecs_t outDisplayPresentTime = -1;
+    nsecs_t outReleaseTime;
+
+    auto start = std::chrono::steady_clock::now();
+    while (outDisplayPresentTime < 0) {
+        std::this_thread::sleep_for(8ms);
+        surface->getFrameTimestamps(frameNumber, &outRequestedPresentTime, &outAcquireTime,
+                                    &outLatchTime, &outFirstRefreshStartTime,
+                                    &outLastRefreshStartTime, &outGlCompositionDoneTime,
+                                    &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) {
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_draw(JNIEnv*, jclass) {
+    assert(sAnw);
+    ANativeWindow_Buffer outBuffer;
+    ANativeWindow_lock(sAnw, &outBuffer, nullptr);
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowLock(JNIEnv*, jclass) {
+    assert(sAnw);
+    ANativeWindow_Buffer outBuffer;
+    ANativeWindow_lock(sAnw, &outBuffer, nullptr);
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowUnlockAndPost(JNIEnv*,
+                                                                                     jclass) {
+    assert(sAnw);
+    ANativeWindow_unlockAndPost(sAnw);
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffersGeometry(
+        JNIEnv* /* env */, jclass /* clazz */, jobject /* surfaceObject */, jint w, jint h,
+        jint format) {
+    assert(sAnw);
+    return ANativeWindow_setBuffersGeometry(sAnw, w, h, format);
+}
+}
\ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/res/values/styles.xml b/tests/SurfaceViewBufferTests/res/values/styles.xml
new file mode 100644
index 000000000000..8b50738a06de
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<resources>
+<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+    <item name="windowNoTitle">true</item>
+    <item name="windowActionBar">false</item>
+    <item name="android:windowFullscreen">true</item>
+    <item name="android:windowContentOverlay">@null</item>
+    <item name="android:windowDisablePreview">true</item>
+</style>
+</resources>
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
new file mode 100644
index 000000000000..b1e1336c4f6d
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/MainActivity.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.Activity
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Paint
+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.widget.FrameLayout
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.locks.ReentrantLock
+import kotlin.concurrent.withLock
+
+class MainActivity : Activity() {
+    val mSurfaceProxy = SurfaceProxy()
+    private var mSurfaceHolder: SurfaceHolder? = null
+    private val mDrawLock = ReentrantLock()
+
+    val surface: Surface? get() = mSurfaceHolder?.surface
+
+    public override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        addSurfaceView(Rect(0, 0, 500, 200))
+    }
+
+    fun addSurfaceView(size: Rect): 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)
+                        .also { it.setMargins(100, 100, 0, 0) })
+        return surfaceReadyLatch
+    }
+
+    private fun createSurfaceView(
+        context: Context,
+        size: Rect,
+        surfaceReadyLatch: CountDownLatch
+    ): SurfaceView {
+        val surfaceView = SurfaceView(context)
+        surfaceView.setWillNotDraw(false)
+        surfaceView.holder.setFixedSize(size.width(), size.height())
+        surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
+            override fun surfaceCreated(holder: SurfaceHolder) {
+                mDrawLock.withLock {
+                    mSurfaceHolder = holder
+                    mSurfaceProxy.setSurface(holder.surface)
+                }
+                surfaceReadyLatch.countDown()
+            }
+
+            override fun surfaceChanged(
+                holder: SurfaceHolder,
+                format: Int,
+                width: Int,
+                height: Int
+            ) {
+            }
+
+            override fun surfaceDestroyed(holder: SurfaceHolder) {
+                mDrawLock.withLock {
+                    mSurfaceHolder = null
+                }
+            }
+        })
+        return surfaceView
+    }
+
+    fun drawFrame(): Rect {
+        mDrawLock.withLock {
+            val holder = mSurfaceHolder ?: return Rect()
+            val canvas = holder.lockCanvas()
+            val canvasSize = Rect(0, 0, canvas.width, canvas.height)
+            canvas.drawColor(Color.GREEN)
+            val p = Paint()
+            p.color = Color.RED
+            canvas.drawRect(canvasSize, p)
+            holder.unlockCanvasAndPost(canvas)
+            return canvasSize
+        }
+    }
+}
\ 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
new file mode 100644
index 000000000000..884aae41446c
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceProxy.kt
@@ -0,0 +1,32 @@
+/*
+ * 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
+
+class SurfaceProxy {
+    init {
+        System.loadLibrary("surface_jni")
+    }
+
+    external fun setSurface(surface: Any)
+    external fun waitUntilBufferDisplayed(frameNumber: Int, timeoutSec: Int)
+    external fun draw()
+
+    // android/native_window.h functions
+    external fun ANativeWindowLock()
+    external fun ANativeWindowUnlockAndPost()
+    external fun ANativeWindowSetBuffersGeometry(surface: Any, width: Int, height: Int, format: Int)
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
new file mode 100644
index 000000000000..b48a91d49b91
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTest.kt
@@ -0,0 +1,185 @@
+/*
+ * 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
-- 
GitLab