Refactor NDK helpers some more, add methods for SurfaceControl/Transaction, tests (#50540)
Adds more dynamic method lookups in service of https://github.com/flutter/flutter/issues/143105
Moves the TU out to FML so that Impeller can more easily use it.
Adds checking on `AHardwareBuffer_getId` so that it checks the return value before returning what is potentially garbage.
Adds some smoke tests to make sure these things actually work/look up meaningful symbols. Test is in the shell because we have testing infra for this kind of thing there.
diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files
index 396430f..96588c1 100644
--- a/ci/licenses_golden/excluded_files
+++ b/ci/licenses_golden/excluded_files
@@ -100,6 +100,7 @@
../../../flutter/fml/message_loop_task_queues_unittests.cc
../../../flutter/fml/message_loop_unittests.cc
../../../flutter/fml/paths_unittests.cc
+../../../flutter/fml/platform/android/ndk_helpers_unittests.cc
../../../flutter/fml/platform/darwin/cf_utils_unittests.mm
../../../flutter/fml/platform/darwin/scoped_nsobject_arc_unittests.mm
../../../flutter/fml/platform/darwin/scoped_nsobject_unittests.mm
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 3095959..1b17db6 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -4832,6 +4832,8 @@
ORIGIN: ../../../flutter/fml/platform/android/jni_weak_ref.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/fml/platform/android/message_loop_android.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/fml/platform/android/message_loop_android.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/fml/platform/android/ndk_helpers.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/fml/platform/android/ndk_helpers.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/fml/platform/android/paths_android.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/fml/platform/android/paths_android.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/fml/platform/android/scoped_java_ref.cc + ../../../flutter/LICENSE
@@ -6474,8 +6476,6 @@
ORIGIN: ../../../flutter/shell/platform/android/jni/platform_view_android_jni.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/jni/platform_view_android_jni.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/library_loader.cc + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/shell/platform/android/ndk_helpers.cc + ../../../flutter/LICENSE
-ORIGIN: ../../../flutter/shell/platform/android/ndk_helpers.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/platform_message_handler_android.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/platform_message_response_android.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/platform_message_response_android.h + ../../../flutter/LICENSE
@@ -7663,6 +7663,8 @@
FILE: ../../../flutter/fml/platform/android/jni_weak_ref.h
FILE: ../../../flutter/fml/platform/android/message_loop_android.cc
FILE: ../../../flutter/fml/platform/android/message_loop_android.h
+FILE: ../../../flutter/fml/platform/android/ndk_helpers.cc
+FILE: ../../../flutter/fml/platform/android/ndk_helpers.h
FILE: ../../../flutter/fml/platform/android/paths_android.cc
FILE: ../../../flutter/fml/platform/android/paths_android.h
FILE: ../../../flutter/fml/platform/android/scoped_java_ref.cc
@@ -9324,8 +9326,6 @@
FILE: ../../../flutter/shell/platform/android/jni/platform_view_android_jni.cc
FILE: ../../../flutter/shell/platform/android/jni/platform_view_android_jni.h
FILE: ../../../flutter/shell/platform/android/library_loader.cc
-FILE: ../../../flutter/shell/platform/android/ndk_helpers.cc
-FILE: ../../../flutter/shell/platform/android/ndk_helpers.h
FILE: ../../../flutter/shell/platform/android/platform_message_handler_android.cc
FILE: ../../../flutter/shell/platform/android/platform_message_handler_android.h
FILE: ../../../flutter/shell/platform/android/platform_message_response_android.cc
diff --git a/fml/BUILD.gn b/fml/BUILD.gn
index 3267884..088c391 100644
--- a/fml/BUILD.gn
+++ b/fml/BUILD.gn
@@ -184,6 +184,8 @@
"platform/android/jni_weak_ref.h",
"platform/android/message_loop_android.cc",
"platform/android/message_loop_android.h",
+ "platform/android/ndk_helpers.cc",
+ "platform/android/ndk_helpers.h",
"platform/android/paths_android.cc",
"platform/android/paths_android.h",
"platform/android/scoped_java_ref.cc",
diff --git a/fml/platform/android/ndk_helpers.cc b/fml/platform/android/ndk_helpers.cc
new file mode 100644
index 0000000..6726743
--- /dev/null
+++ b/fml/platform/android/ndk_helpers.cc
@@ -0,0 +1,258 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fml/platform/android/ndk_helpers.h"
+
+#include "fml/logging.h"
+#include "fml/native_library.h"
+
+#include <android/hardware_buffer.h>
+#include <dlfcn.h>
+
+namespace flutter {
+
+namespace {
+
+#define DECLARE_TYPES(ret, name, args) \
+ typedef ret(*fp_##name) args; \
+ ret(*_##name) args = nullptr
+
+DECLARE_TYPES(int,
+ AHardwareBuffer_allocate,
+ (const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer));
+DECLARE_TYPES(int,
+ AHardwareBuffer_isSupported,
+ (const AHardwareBuffer_Desc* desc));
+DECLARE_TYPES(AHardwareBuffer*,
+ AHardwareBuffer_fromHardwareBuffer,
+ (JNIEnv * env, jobject hardwareBufferObj));
+DECLARE_TYPES(void, AHardwareBuffer_release, (AHardwareBuffer * buffer));
+DECLARE_TYPES(void,
+ AHardwareBuffer_describe,
+ (AHardwareBuffer * buffer, AHardwareBuffer_Desc* desc));
+DECLARE_TYPES(int,
+ AHardwareBuffer_getId,
+ (AHardwareBuffer * buffer, uint64_t* outId));
+
+DECLARE_TYPES(bool, ATrace_isEnabled, (void));
+
+DECLARE_TYPES(ASurfaceControl*,
+ ASurfaceControl_createFromWindow,
+ (ANativeWindow * parent, const char* debug_name));
+DECLARE_TYPES(void,
+ ASurfaceControl_release,
+ (ASurfaceControl * surface_control));
+DECLARE_TYPES(ASurfaceTransaction*, ASurfaceTransaction_create, (void));
+DECLARE_TYPES(void,
+ ASurfaceTransaction_delete,
+ (ASurfaceTransaction * surface_transaction));
+DECLARE_TYPES(void,
+ ASurfaceTransaction_apply,
+ (ASurfaceTransaction * surface_transaction));
+DECLARE_TYPES(void,
+ ASurfaceTransaction_setBuffer,
+ (ASurfaceTransaction * transaction,
+ ASurfaceControl* surface_control,
+ AHardwareBuffer* buffer,
+ int acquire_fence_fd));
+
+DECLARE_TYPES(AChoreographer*, AChoreographer_getInstance, (void));
+DECLARE_TYPES(void,
+ AChoreographer_postFrameCallback,
+ (AChoreographer * choreographer,
+ AChoreographer_frameCallback callbackk,
+ void* data));
+DECLARE_TYPES(void,
+ AChoreographer_postFrameCallback64,
+ (AChoreographer * choreographer,
+ AChoreographer_frameCallback64 callbackk,
+ void* data));
+
+DECLARE_TYPES(EGLClientBuffer,
+ eglGetNativeClientBufferANDROID,
+ (AHardwareBuffer * buffer));
+
+#undef DECLARE_TYPES
+
+std::once_flag init_once;
+
+void InitOnceCallback() {
+ static fml::RefPtr<fml::NativeLibrary> android =
+ fml::NativeLibrary::Create("libandroid.so");
+ FML_CHECK(android.get() != nullptr);
+ static fml::RefPtr<fml::NativeLibrary> egl =
+ fml::NativeLibrary::Create("libEGL.so");
+ FML_CHECK(egl.get() != nullptr);
+
+#define LOOKUP(lib, func) \
+ _##func = lib->ResolveFunction<fp_##func>(#func).value_or(nullptr)
+
+ LOOKUP(egl, eglGetNativeClientBufferANDROID);
+
+ LOOKUP(android, AHardwareBuffer_fromHardwareBuffer);
+ LOOKUP(android, AHardwareBuffer_release);
+ LOOKUP(android, AHardwareBuffer_getId);
+ LOOKUP(android, AHardwareBuffer_describe);
+ LOOKUP(android, AHardwareBuffer_allocate);
+ LOOKUP(android, AHardwareBuffer_isSupported);
+ LOOKUP(android, ATrace_isEnabled);
+ LOOKUP(android, AChoreographer_getInstance);
+ if (_AChoreographer_getInstance) {
+ LOOKUP(android, AChoreographer_postFrameCallback64);
+ if (!_AChoreographer_postFrameCallback64) {
+ LOOKUP(android, AChoreographer_postFrameCallback);
+ }
+ }
+
+ LOOKUP(android, ASurfaceControl_createFromWindow);
+ LOOKUP(android, ASurfaceControl_release);
+ LOOKUP(android, ASurfaceTransaction_apply);
+ LOOKUP(android, ASurfaceTransaction_create);
+ LOOKUP(android, ASurfaceTransaction_delete);
+ LOOKUP(android, ASurfaceTransaction_setBuffer);
+#undef LOOKUP
+}
+
+} // namespace
+
+void NDKHelpers::Init() {
+ std::call_once(init_once, InitOnceCallback);
+}
+
+bool NDKHelpers::ATrace_isEnabled() {
+ if (_ATrace_isEnabled) {
+ return _ATrace_isEnabled();
+ }
+ return false;
+}
+
+ChoreographerSupportStatus NDKHelpers::ChoreographerSupported() {
+ if (_AChoreographer_postFrameCallback64) {
+ return ChoreographerSupportStatus::kSupported64;
+ }
+ if (_AChoreographer_postFrameCallback) {
+ return ChoreographerSupportStatus::kSupported32;
+ }
+ return ChoreographerSupportStatus::kUnsupported;
+}
+
+AChoreographer* NDKHelpers::AChoreographer_getInstance() {
+ FML_CHECK(_AChoreographer_getInstance);
+ return _AChoreographer_getInstance();
+}
+
+void NDKHelpers::AChoreographer_postFrameCallback(
+ AChoreographer* choreographer,
+ AChoreographer_frameCallback callback,
+ void* data) {
+ FML_CHECK(_AChoreographer_postFrameCallback);
+ return _AChoreographer_postFrameCallback(choreographer, callback, data);
+}
+
+void NDKHelpers::AChoreographer_postFrameCallback64(
+ AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback,
+ void* data) {
+ FML_CHECK(_AChoreographer_postFrameCallback64);
+ return _AChoreographer_postFrameCallback64(choreographer, callback, data);
+}
+
+bool NDKHelpers::HardwareBufferSupported() {
+ const bool r = _AHardwareBuffer_fromHardwareBuffer != nullptr;
+ return r;
+}
+
+AHardwareBuffer* NDKHelpers::AHardwareBuffer_fromHardwareBuffer(
+ JNIEnv* env,
+ jobject hardwareBufferObj) {
+ FML_CHECK(_AHardwareBuffer_fromHardwareBuffer != nullptr);
+ return _AHardwareBuffer_fromHardwareBuffer(env, hardwareBufferObj);
+}
+
+void NDKHelpers::AHardwareBuffer_release(AHardwareBuffer* buffer) {
+ FML_CHECK(_AHardwareBuffer_release != nullptr);
+ _AHardwareBuffer_release(buffer);
+}
+
+void NDKHelpers::AHardwareBuffer_describe(AHardwareBuffer* buffer,
+ AHardwareBuffer_Desc* desc) {
+ FML_CHECK(_AHardwareBuffer_describe != nullptr);
+ _AHardwareBuffer_describe(buffer, desc);
+}
+
+std::optional<HardwareBufferKey> NDKHelpers::AHardwareBuffer_getId(
+ AHardwareBuffer* buffer) {
+ if (_AHardwareBuffer_getId == nullptr) {
+ return std::nullopt;
+ }
+ HardwareBufferKey outId;
+ int result = _AHardwareBuffer_getId(buffer, &outId);
+ if (result == 0) {
+ return outId;
+ }
+ return std::nullopt;
+}
+
+EGLClientBuffer NDKHelpers::eglGetNativeClientBufferANDROID(
+ AHardwareBuffer* buffer) {
+ FML_CHECK(_eglGetNativeClientBufferANDROID != nullptr);
+ return _eglGetNativeClientBufferANDROID(buffer);
+}
+
+bool NDKHelpers::SurfaceControlAndTransactionSupported() {
+ return _ASurfaceControl_createFromWindow && _ASurfaceControl_release &&
+ _ASurfaceTransaction_create && _ASurfaceTransaction_apply &&
+ _ASurfaceTransaction_delete && _ASurfaceTransaction_setBuffer;
+}
+
+ASurfaceControl* NDKHelpers::ASurfaceControl_createFromWindow(
+ ANativeWindow* parent,
+ const char* debug_name) {
+ FML_CHECK(_ASurfaceControl_createFromWindow);
+ return _ASurfaceControl_createFromWindow(parent, debug_name);
+}
+
+void NDKHelpers::ASurfaceControl_release(ASurfaceControl* surface_control) {
+ FML_CHECK(_ASurfaceControl_release);
+ return _ASurfaceControl_release(surface_control);
+}
+
+ASurfaceTransaction* NDKHelpers::ASurfaceTransaction_create() {
+ FML_CHECK(_ASurfaceTransaction_create);
+ return _ASurfaceTransaction_create();
+}
+
+void NDKHelpers::ASurfaceTransaction_delete(
+ ASurfaceTransaction* surface_transaction) {
+ FML_CHECK(_ASurfaceTransaction_delete);
+ _ASurfaceTransaction_delete(surface_transaction);
+}
+
+void NDKHelpers::ASurfaceTransaction_apply(
+ ASurfaceTransaction* surface_transaction) {
+ FML_CHECK(_ASurfaceTransaction_apply);
+ _ASurfaceTransaction_apply(surface_transaction);
+}
+
+void NDKHelpers::ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
+ ASurfaceControl* surface_control,
+ AHardwareBuffer* buffer,
+ int acquire_fence_fd) {
+ FML_CHECK(_ASurfaceTransaction_setBuffer);
+ _ASurfaceTransaction_setBuffer(transaction, surface_control, buffer,
+ acquire_fence_fd);
+}
+
+int NDKHelpers::AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) {
+ FML_CHECK(_AHardwareBuffer_isSupported);
+ return _AHardwareBuffer_isSupported(desc);
+}
+
+int NDKHelpers::AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
+ AHardwareBuffer** outBuffer) {
+ FML_CHECK(_AHardwareBuffer_allocate);
+ return _AHardwareBuffer_allocate(desc, outBuffer);
+}
+
+} // namespace flutter
diff --git a/fml/platform/android/ndk_helpers.h b/fml/platform/android/ndk_helpers.h
new file mode 100644
index 0000000..9299776
--- /dev/null
+++ b/fml/platform/android/ndk_helpers.h
@@ -0,0 +1,100 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_FML_PLATFORM_ANDROID_NDK_HELPERS_H_
+#define FLUTTER_FML_PLATFORM_ANDROID_NDK_HELPERS_H_
+
+#include <EGL/egl.h>
+#include <android/choreographer.h>
+#include <android/hardware_buffer.h>
+#include <android/surface_control.h>
+#include <android/trace.h>
+#include <jni.h>
+#include <optional>
+
+namespace flutter {
+
+using HardwareBufferKey = uint64_t;
+
+enum class ChoreographerSupportStatus {
+ // Unavailable, API level < 24.
+ kUnsupported,
+ // Available, but only with postFrameCallback.
+ kSupported32,
+ // Available, but only with postFrameCallback64.
+ kSupported64,
+};
+
+// A collection of NDK functions that are available depending on the version of
+// the Android SDK we are linked with at runtime.
+class NDKHelpers {
+ public:
+ // Safe to call multiple times.
+ // Normally called from JNI_OnLoad.
+ static void Init();
+
+ // API Version 23
+ static bool ATrace_isEnabled();
+
+ // API Version 24
+ static ChoreographerSupportStatus ChoreographerSupported();
+ static AChoreographer* _Nullable AChoreographer_getInstance();
+ // Deprecated in 29, available since 24.
+ static void AChoreographer_postFrameCallback(
+ AChoreographer* _Nonnull choreographer,
+ AChoreographer_frameCallback _Nonnull callback,
+ void* _Nullable data);
+
+ // API Version 26
+ static bool HardwareBufferSupported();
+ static AHardwareBuffer* _Nonnull AHardwareBuffer_fromHardwareBuffer(
+ JNIEnv* _Nonnull env,
+ jobject _Nonnull hardwareBufferObj);
+ static void AHardwareBuffer_release(AHardwareBuffer* _Nonnull buffer);
+ static void AHardwareBuffer_describe(AHardwareBuffer* _Nonnull buffer,
+ AHardwareBuffer_Desc* _Nullable desc);
+ static int AHardwareBuffer_allocate(
+ const AHardwareBuffer_Desc* _Nonnull desc,
+ AHardwareBuffer* _Nullable* _Nullable outBuffer);
+ static EGLClientBuffer _Nonnull eglGetNativeClientBufferANDROID(
+ AHardwareBuffer* _Nonnull buffer);
+
+ // API Version 29
+ static int AHardwareBuffer_isSupported(
+ const AHardwareBuffer_Desc* _Nonnull desc);
+
+ static void AChoreographer_postFrameCallback64(
+ AChoreographer* _Nonnull choreographer,
+ AChoreographer_frameCallback64 _Nonnull callback,
+ void* _Nullable data);
+
+ static bool SurfaceControlAndTransactionSupported();
+
+ static ASurfaceControl* _Nonnull ASurfaceControl_createFromWindow(
+ ANativeWindow* _Nonnull parent,
+ const char* _Nullable debug_name);
+ static void ASurfaceControl_release(
+ ASurfaceControl* _Nonnull surface_control);
+
+ static ASurfaceTransaction* _Nonnull ASurfaceTransaction_create();
+ static void ASurfaceTransaction_delete(
+ ASurfaceTransaction* _Nonnull surface_transaction);
+ static void ASurfaceTransaction_apply(
+ ASurfaceTransaction* _Nonnull surface_transaction);
+ static void ASurfaceTransaction_setBuffer(
+ ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ AHardwareBuffer* _Nonnull buffer,
+ int acquire_fence_fd);
+
+ // API Version 31
+
+ // Returns std::nullopt on API version 26 - 30.
+ static std::optional<HardwareBufferKey> AHardwareBuffer_getId(
+ AHardwareBuffer* _Nonnull buffer);
+};
+
+} // namespace flutter
+
+#endif // FLUTTER_FML_PLATFORM_ANDROID_NDK_HELPERS_H_
diff --git a/fml/platform/android/ndk_helpers_unittests.cc b/fml/platform/android/ndk_helpers_unittests.cc
new file mode 100644
index 0000000..e6cab42
--- /dev/null
+++ b/fml/platform/android/ndk_helpers_unittests.cc
@@ -0,0 +1,121 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fml/message_loop.h"
+#include "fml/platform/android/ndk_helpers.h"
+
+#include "gtest/gtest.h"
+
+namespace flutter {
+namespace testing {
+namespace android {
+
+class NdkHelpersTest : public ::testing::Test {
+ public:
+ void SetUp() override { NDKHelpers::Init(); }
+
+ static void OnVsync(int64_t frame_nanos, void* data) {}
+ static void OnVsync32(
+ long frame_nanos, // NOLINT - compat for deprecated call
+ void* data) {}
+};
+
+TEST_F(NdkHelpersTest, ATrace) {
+ ASSERT_GT(android_get_device_api_level(), 22);
+ EXPECT_FALSE(NDKHelpers::ATrace_isEnabled());
+}
+
+TEST_F(NdkHelpersTest, AChoreographer32) {
+ if (android_get_device_api_level() >= 29) {
+ GTEST_SKIP() << "This test is for less than API 29.";
+ }
+
+ EXPECT_EQ(NDKHelpers::ChoreographerSupported(),
+ ChoreographerSupportStatus::kSupported32);
+
+ EXPECT_FALSE(NDKHelpers::AChoreographer_getInstance());
+
+ fml::MessageLoop::EnsureInitializedForCurrentThread();
+
+ EXPECT_TRUE(NDKHelpers::AChoreographer_getInstance());
+
+ NDKHelpers::AChoreographer_postFrameCallback(
+ NDKHelpers::AChoreographer_getInstance(), &OnVsync32, nullptr);
+}
+
+TEST_F(NdkHelpersTest, AChoreographer64) {
+ if (android_get_device_api_level() < 29) {
+ GTEST_SKIP() << "This test is for API 29 and above.";
+ }
+
+ EXPECT_EQ(NDKHelpers::ChoreographerSupported(),
+ ChoreographerSupportStatus::kSupported64);
+
+ EXPECT_FALSE(NDKHelpers::AChoreographer_getInstance());
+
+ fml::MessageLoop::EnsureInitializedForCurrentThread();
+
+ EXPECT_TRUE(NDKHelpers::AChoreographer_getInstance());
+
+ NDKHelpers::AChoreographer_postFrameCallback64(
+ NDKHelpers::AChoreographer_getInstance(), &OnVsync, nullptr);
+}
+
+TEST_F(NdkHelpersTest, HardwareBuffer) {
+ if (android_get_device_api_level() < 26) {
+ GTEST_SKIP() << "Test requires at least API 26.";
+ }
+
+ ASSERT_TRUE(NDKHelpers::HardwareBufferSupported());
+
+ AHardwareBuffer_Desc desc{
+ .width = 4,
+ .height = 4,
+ .layers = 1,
+ .format = AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ };
+ if (android_get_device_api_level() >= 29) {
+ EXPECT_TRUE(NDKHelpers::AHardwareBuffer_isSupported(&desc));
+ }
+
+ AHardwareBuffer* buffer = nullptr;
+ // AHardwareBuffer_allocate returns 0 on success.
+ EXPECT_EQ(NDKHelpers::AHardwareBuffer_allocate(&desc, &buffer), 0);
+ EXPECT_TRUE(buffer);
+
+ AHardwareBuffer_Desc out_desc = {};
+ NDKHelpers::AHardwareBuffer_describe(buffer, &out_desc);
+ EXPECT_EQ(desc.width, out_desc.width);
+ EXPECT_EQ(desc.height, out_desc.height);
+ EXPECT_EQ(desc.layers, out_desc.layers);
+ EXPECT_EQ(desc.format, out_desc.format);
+
+ auto id = NDKHelpers::AHardwareBuffer_getId(buffer);
+ if (android_get_device_api_level() >= 31) {
+ EXPECT_TRUE(id.has_value());
+ } else {
+ EXPECT_FALSE(id.has_value());
+ }
+
+ NDKHelpers::AHardwareBuffer_release(buffer);
+}
+
+TEST_F(NdkHelpersTest, SurfaceTransaction) {
+ if (android_get_device_api_level() < 29) {
+ GTEST_SKIP() << "Test requires at least API 29.";
+ }
+ EXPECT_TRUE(NDKHelpers::SurfaceControlAndTransactionSupported());
+
+ // Need ANativeWindow to create ASurfaceControl and set a buffer to the
+ // transaction. Just create/apply/delete as a smoke test.
+
+ ASurfaceTransaction* transaction = NDKHelpers::ASurfaceTransaction_create();
+ EXPECT_TRUE(transaction);
+ NDKHelpers::ASurfaceTransaction_apply(transaction);
+ NDKHelpers::ASurfaceTransaction_delete(transaction);
+}
+
+} // namespace android
+} // namespace testing
+} // namespace flutter
diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn
index 2142960..a6c4cb6 100644
--- a/shell/platform/android/BUILD.gn
+++ b/shell/platform/android/BUILD.gn
@@ -42,6 +42,7 @@
visibility = [ "*" ]
testonly = true
sources = [
+ "//flutter/fml/platform/android/ndk_helpers_unittests.cc",
"android_context_gl_impeller_unittests.cc",
"android_context_gl_unittests.cc",
"android_shell_holder_unittests.cc",
@@ -53,6 +54,7 @@
public_configs = [ "//flutter:config" ]
deps = [
":flutter_shell_native_src",
+ "//flutter/fml",
"//flutter/shell/platform/android/jni:jni_mock",
"//third_party/googletest:gmock",
"//third_party/googletest:gtest",
@@ -113,8 +115,6 @@
"image_lru.cc",
"image_lru.h",
"library_loader.cc",
- "ndk_helpers.cc",
- "ndk_helpers.h",
"platform_message_handler_android.cc",
"platform_message_handler_android.h",
"platform_message_response_android.cc",
diff --git a/shell/platform/android/flutter_main.cc b/shell/platform/android/flutter_main.cc
index 8a2e9ea..6ad57a7 100644
--- a/shell/platform/android/flutter_main.cc
+++ b/shell/platform/android/flutter_main.cc
@@ -18,13 +18,13 @@
#include "flutter/fml/native_library.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/platform/android/jni_util.h"
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "flutter/fml/platform/android/paths_android.h"
#include "flutter/fml/size.h"
#include "flutter/lib/ui/plugins/callback_cache.h"
#include "flutter/runtime/dart_vm.h"
#include "flutter/shell/common/shell.h"
#include "flutter/shell/common/switches.h"
-#include "flutter/shell/platform/android/ndk_helpers.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
#include "txt/platform.h"
diff --git a/shell/platform/android/image_external_texture.cc b/shell/platform/android/image_external_texture.cc
index da6f558..855b56d 100644
--- a/shell/platform/android/image_external_texture.cc
+++ b/shell/platform/android/image_external_texture.cc
@@ -4,8 +4,9 @@
#include <android/hardware_buffer_jni.h>
#include <android/sensor.h>
+#include "flutter/fml/platform/android/jni_util.h"
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
-#include "flutter/shell/platform/android/ndk_helpers.h"
namespace flutter {
diff --git a/shell/platform/android/image_external_texture_gl.cc b/shell/platform/android/image_external_texture_gl.cc
index 1dea2df..6a676e9 100644
--- a/shell/platform/android/image_external_texture_gl.cc
+++ b/shell/platform/android/image_external_texture_gl.cc
@@ -8,11 +8,11 @@
#include <android/sensor.h>
#include "flutter/common/graphics/texture.h"
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "flutter/impeller/core/formats.h"
#include "flutter/impeller/display_list/dl_image_impeller.h"
#include "flutter/impeller/toolkit/egl/image.h"
#include "flutter/impeller/toolkit/gles/texture.h"
-#include "flutter/shell/platform/android/ndk_helpers.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
diff --git a/shell/platform/android/image_external_texture_gl.h b/shell/platform/android/image_external_texture_gl.h
index e3040d5..8aeec74 100644
--- a/shell/platform/android/image_external_texture_gl.h
+++ b/shell/platform/android/image_external_texture_gl.h
@@ -17,8 +17,8 @@
#include "flutter/impeller/toolkit/egl/image.h"
#include "flutter/impeller/toolkit/gles/texture.h"
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "flutter/shell/platform/android/android_context_gl_skia.h"
-#include "flutter/shell/platform/android/ndk_helpers.h"
namespace flutter {
diff --git a/shell/platform/android/image_external_texture_vk.cc b/shell/platform/android/image_external_texture_vk.cc
index 5eaf470..9af0683 100644
--- a/shell/platform/android/image_external_texture_vk.cc
+++ b/shell/platform/android/image_external_texture_vk.cc
@@ -2,6 +2,7 @@
#include "flutter/shell/platform/android/image_external_texture_vk.h"
#include <cstdint>
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "flutter/impeller/core/formats.h"
#include "flutter/impeller/core/texture_descriptor.h"
#include "flutter/impeller/display_list/dl_image_impeller.h"
@@ -9,7 +10,6 @@
#include "flutter/impeller/renderer/backend/vulkan/command_buffer_vk.h"
#include "flutter/impeller/renderer/backend/vulkan/command_encoder_vk.h"
#include "flutter/impeller/renderer/backend/vulkan/texture_vk.h"
-#include "flutter/shell/platform/android/ndk_helpers.h"
namespace flutter {
diff --git a/shell/platform/android/image_lru.h b/shell/platform/android/image_lru.h
index 34fa97c..28a31cd 100644
--- a/shell/platform/android/image_lru.h
+++ b/shell/platform/android/image_lru.h
@@ -9,7 +9,7 @@
#include <cstddef>
#include "display_list/image/dl_image.h"
-#include "shell/platform/android/ndk_helpers.h"
+#include "fml/platform/android/ndk_helpers.h"
namespace flutter {
diff --git a/shell/platform/android/library_loader.cc b/shell/platform/android/library_loader.cc
index 103a3ae..9f3691d 100644
--- a/shell/platform/android/library_loader.cc
+++ b/shell/platform/android/library_loader.cc
@@ -3,9 +3,9 @@
// found in the LICENSE file.
#include "flutter/fml/platform/android/jni_util.h"
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "flutter/shell/platform/android/android_image_generator.h"
#include "flutter/shell/platform/android/flutter_main.h"
-#include "flutter/shell/platform/android/ndk_helpers.h"
#include "flutter/shell/platform/android/platform_view_android.h"
#include "flutter/shell/platform/android/vsync_waiter_android.h"
diff --git a/shell/platform/android/ndk_helpers.cc b/shell/platform/android/ndk_helpers.cc
deleted file mode 100644
index 65fc936..0000000
--- a/shell/platform/android/ndk_helpers.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "flutter/shell/platform/android/ndk_helpers.h"
-
-#include "fml/native_library.h"
-
-#include "flutter/fml/logging.h"
-
-#include <android/hardware_buffer.h>
-#include <dlfcn.h>
-
-namespace flutter {
-
-namespace {
-
-typedef AHardwareBuffer* (*fp_AHardwareBuffer_fromHardwareBuffer)(
- JNIEnv* env,
- jobject hardwareBufferObj);
-typedef void (*fp_AHardwareBuffer_acquire)(AHardwareBuffer* buffer);
-typedef void (*fp_AHardwareBuffer_release)(AHardwareBuffer* buffer);
-typedef void (*fp_AHardwareBuffer_describe)(AHardwareBuffer* buffer,
- AHardwareBuffer_Desc* desc);
-typedef void (*fp_AHardwareBuffer_getId)(AHardwareBuffer* buffer,
- uint64_t* outId);
-
-typedef bool (*fp_ATrace_isEnabled)(void);
-
-typedef AChoreographer* (*fp_AChoreographer_getInstance)(void);
-typedef void (*fp_AChoreographer_postFrameCallback)(
- AChoreographer* choreographer,
- AChoreographer_frameCallback callbackk,
- void* data);
-typedef void (*fp_AChoreographer_postFrameCallback64)(
- AChoreographer* choreographer,
- AChoreographer_frameCallback64 callbackk,
- void* data);
-
-typedef EGLClientBuffer (*fp_eglGetNativeClientBufferANDROID)(
- AHardwareBuffer* buffer);
-
-AHardwareBuffer* (*_AHardwareBuffer_fromHardwareBuffer)(
- JNIEnv* env,
- jobject hardwareBufferObj) = nullptr;
-void (*_AHardwareBuffer_acquire)(AHardwareBuffer* buffer) = nullptr;
-void (*_AHardwareBuffer_release)(AHardwareBuffer* buffer) = nullptr;
-void (*_AHardwareBuffer_describe)(AHardwareBuffer* buffer,
- AHardwareBuffer_Desc* desc) = nullptr;
-void (*_AHardwareBuffer_getId)(AHardwareBuffer* buffer,
- uint64_t* outId) = nullptr;
-bool (*_ATrace_isEnabled)() = nullptr;
-AChoreographer* (*_AChoreographer_getInstance)() = nullptr;
-void (*_AChoreographer_postFrameCallback)(
- AChoreographer* choreographer,
- AChoreographer_frameCallback callbackk,
- void* data) = nullptr;
-void (*_AChoreographer_postFrameCallback64)(
- AChoreographer* choreographer,
- AChoreographer_frameCallback64 callbackk,
- void* data) = nullptr;
-
-EGLClientBuffer (*_eglGetNativeClientBufferANDROID)(AHardwareBuffer* buffer) =
- nullptr;
-
-std::once_flag init_once;
-
-void InitOnceCallback() {
- static fml::RefPtr<fml::NativeLibrary> android =
- fml::NativeLibrary::Create("libandroid.so");
- FML_CHECK(android.get() != nullptr);
- static fml::RefPtr<fml::NativeLibrary> egl =
- fml::NativeLibrary::Create("libEGL.so");
- FML_CHECK(egl.get() != nullptr);
- _eglGetNativeClientBufferANDROID =
- egl->ResolveFunction<fp_eglGetNativeClientBufferANDROID>(
- "eglGetNativeClientBufferANDROID")
- .value_or(nullptr);
- _AHardwareBuffer_fromHardwareBuffer =
- android
- ->ResolveFunction<fp_AHardwareBuffer_fromHardwareBuffer>(
- "AHardwareBuffer_fromHardwareBuffer")
- .value_or(nullptr);
- _AHardwareBuffer_acquire = android
- ->ResolveFunction<fp_AHardwareBuffer_acquire>(
- "AHardwareBuffer_acquire")
- .value_or(nullptr);
- _AHardwareBuffer_release = android
- ->ResolveFunction<fp_AHardwareBuffer_release>(
- "AHardwareBuffer_release")
- .value_or(nullptr);
- _AHardwareBuffer_getId =
- android
- ->ResolveFunction<fp_AHardwareBuffer_getId>("AHardwareBuffer_getId")
- .value_or(nullptr);
- _AHardwareBuffer_describe =
- android
- ->ResolveFunction<fp_AHardwareBuffer_describe>(
- "AHardwareBuffer_describe")
- .value_or(nullptr);
-
- _ATrace_isEnabled =
- android->ResolveFunction<fp_ATrace_isEnabled>("ATrace_isEnabled")
- .value_or(nullptr);
-
- _AChoreographer_getInstance =
- android
- ->ResolveFunction<fp_AChoreographer_getInstance>(
- "AChoreographer_getInstance")
- .value_or(nullptr);
- if (_AChoreographer_getInstance) {
- _AChoreographer_postFrameCallback64 =
- android
- ->ResolveFunction<fp_AChoreographer_postFrameCallback64>(
- "AChoreographer_postFrameCallback64")
- .value_or(nullptr);
-#if FML_ARCH_CPU_64_BITS
- if (!_AChoreographer_postFrameCallback64) {
- _AChoreographer_postFrameCallback =
- android
- ->ResolveFunction<fp_AChoreographer_postFrameCallback>(
- "AChoreographer_postFrameCallback")
- .value_or(nullptr);
- }
-#endif
- }
-}
-
-} // namespace
-
-void NDKHelpers::Init() {
- std::call_once(init_once, InitOnceCallback);
-}
-
-bool NDKHelpers::ATrace_isEnabled() {
- if (_ATrace_isEnabled) {
- return _ATrace_isEnabled();
- }
- return false;
-}
-
-ChoreographerSupportStatus NDKHelpers::ChoreographerSupported() {
- if (_AChoreographer_postFrameCallback64) {
- return ChoreographerSupportStatus::kSupported64;
- }
- if (_AChoreographer_postFrameCallback) {
- return ChoreographerSupportStatus::kSupported32;
- }
- return ChoreographerSupportStatus::kUnsupported;
-}
-
-AChoreographer* NDKHelpers::AChoreographer_getInstance() {
- FML_CHECK(_AChoreographer_getInstance);
- return _AChoreographer_getInstance();
-}
-
-void NDKHelpers::AChoreographer_postFrameCallback(
- AChoreographer* choreographer,
- AChoreographer_frameCallback callback,
- void* data) {
- FML_CHECK(_AChoreographer_postFrameCallback);
- return _AChoreographer_postFrameCallback(choreographer, callback, data);
-}
-
-void NDKHelpers::AChoreographer_postFrameCallback64(
- AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback,
- void* data) {
- FML_CHECK(_AChoreographer_postFrameCallback64);
- return _AChoreographer_postFrameCallback64(choreographer, callback, data);
-}
-
-bool NDKHelpers::HardwareBufferSupported() {
- const bool r = _AHardwareBuffer_fromHardwareBuffer != nullptr;
- return r;
-}
-
-AHardwareBuffer* NDKHelpers::AHardwareBuffer_fromHardwareBuffer(
- JNIEnv* env,
- jobject hardwareBufferObj) {
- FML_CHECK(_AHardwareBuffer_fromHardwareBuffer != nullptr);
- return _AHardwareBuffer_fromHardwareBuffer(env, hardwareBufferObj);
-}
-
-void NDKHelpers::AHardwareBuffer_acquire(AHardwareBuffer* buffer) {
- FML_CHECK(_AHardwareBuffer_acquire != nullptr);
- _AHardwareBuffer_acquire(buffer);
-}
-
-void NDKHelpers::AHardwareBuffer_release(AHardwareBuffer* buffer) {
- FML_CHECK(_AHardwareBuffer_release != nullptr);
- _AHardwareBuffer_release(buffer);
-}
-
-void NDKHelpers::AHardwareBuffer_describe(AHardwareBuffer* buffer,
- AHardwareBuffer_Desc* desc) {
- FML_CHECK(_AHardwareBuffer_describe != nullptr);
- _AHardwareBuffer_describe(buffer, desc);
-}
-
-std::optional<HardwareBufferKey> NDKHelpers::AHardwareBuffer_getId(
- AHardwareBuffer* buffer) {
- if (_AHardwareBuffer_getId == nullptr) {
- return std::nullopt;
- }
- HardwareBufferKey outId;
- _AHardwareBuffer_getId(buffer, &outId);
- return outId;
-}
-
-EGLClientBuffer NDKHelpers::eglGetNativeClientBufferANDROID(
- AHardwareBuffer* buffer) {
- FML_CHECK(_eglGetNativeClientBufferANDROID != nullptr);
- return _eglGetNativeClientBufferANDROID(buffer);
-}
-
-} // namespace flutter
diff --git a/shell/platform/android/ndk_helpers.h b/shell/platform/android/ndk_helpers.h
deleted file mode 100644
index 5b7856a..0000000
--- a/shell/platform/android/ndk_helpers.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_NDK_HELPERS_H_
-#define FLUTTER_SHELL_PLATFORM_ANDROID_NDK_HELPERS_H_
-
-#include "flutter/fml/native_library.h"
-#include "flutter/fml/platform/android/jni_util.h"
-
-#include "flutter/impeller/toolkit/egl/egl.h"
-
-#include <android/choreographer.h>
-#include <android/hardware_buffer.h>
-#include <android/surface_control.h>
-#include <android/trace.h>
-
-namespace flutter {
-
-using HardwareBufferKey = uint64_t;
-
-enum class ChoreographerSupportStatus {
- // Unavailable, API level < 24.
- kUnsupported,
- // Available, but only with postFrameCallback.
- kSupported32,
- // Available, but only with postFrameCallback64.
- kSupported64,
-};
-
-// A collection of NDK functions that are available depending on the version of
-// the Android SDK we are linked with at runtime.
-class NDKHelpers {
- public:
- // Safe to call multiple times.
- // Normally called from JNI_OnLoad.
- static void Init();
-
- // API Version 23
- static bool ATrace_isEnabled();
-
- // API Version 24
- static ChoreographerSupportStatus ChoreographerSupported();
- static AChoreographer* AChoreographer_getInstance();
- // Deprecated in 29, available since 24.
- static void AChoreographer_postFrameCallback(
- AChoreographer* choreographer,
- AChoreographer_frameCallback callback,
- void* data);
-
- // API Version 26
- static bool HardwareBufferSupported();
- static AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(
- JNIEnv* env,
- jobject hardwareBufferObj);
- static void AHardwareBuffer_acquire(AHardwareBuffer* buffer);
- static void AHardwareBuffer_release(AHardwareBuffer* buffer);
- static void AHardwareBuffer_describe(AHardwareBuffer* buffer,
- AHardwareBuffer_Desc* desc);
- static EGLClientBuffer eglGetNativeClientBufferANDROID(
- AHardwareBuffer* buffer);
-
- // API Version 29
- static void AChoreographer_postFrameCallback64(
- AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback,
- void* data);
-
- // API Version 31
-
- // Returns std::nullopt on API version 26 - 30.
- static std::optional<HardwareBufferKey> AHardwareBuffer_getId(
- AHardwareBuffer* buffer);
-};
-
-} // namespace flutter
-
-#endif // FLUTTER_SHELL_PLATFORM_ANDROID_NDK_HELPERS_H_
diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc
index 7d13c10..b6d5dfc 100644
--- a/shell/platform/android/platform_view_android_jni_impl.cc
+++ b/shell/platform/android/platform_view_android_jni_impl.cc
@@ -12,7 +12,7 @@
#include <sstream>
#include <utility>
-#include "flutter/shell/platform/android/ndk_helpers.h"
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "include/android/SkImageAndroid.h"
#include "unicode/uchar.h"
diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc
index 7b7f136..d0a8db3 100644
--- a/shell/platform/android/vsync_waiter_android.cc
+++ b/shell/platform/android/vsync_waiter_android.cc
@@ -10,10 +10,10 @@
#include "flutter/common/task_runners.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/platform/android/jni_util.h"
+#include "flutter/fml/platform/android/ndk_helpers.h"
#include "flutter/fml/platform/android/scoped_java_ref.h"
#include "flutter/fml/size.h"
#include "flutter/fml/trace_event.h"
-#include "flutter/shell/platform/android/ndk_helpers.h"
namespace flutter {