diff --git a/dev/integration_tests/android_engine_test/lib/hcpp/platform_view_main.dart b/dev/integration_tests/android_engine_test/lib/hcpp/platform_view_main.dart index c941d4427c..1b3c1d32ee 100644 --- a/dev/integration_tests/android_engine_test/lib/hcpp/platform_view_main.dart +++ b/dev/integration_tests/android_engine_test/lib/hcpp/platform_view_main.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:convert'; + import 'package:android_driver_extensions/extension.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -14,7 +16,14 @@ import '../src/allow_list_devices.dart'; void main() async { ensureAndroidDevice(); - enableFlutterDriverExtension(commands: [nativeDriverCommands]); + enableFlutterDriverExtension( + handler: (String? command) async { + return json.encode({ + 'supported': await HybridAndroidViewController.checkIfSupported(), + }); + }, + commands: [nativeDriverCommands], + ); // Run on full screen. await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); diff --git a/dev/integration_tests/android_engine_test/test_driver/hcpp/platform_view_main_test.dart b/dev/integration_tests/android_engine_test/test_driver/hcpp/platform_view_main_test.dart index ec4a6bcfc3..a93856e593 100644 --- a/dev/integration_tests/android_engine_test/test_driver/hcpp/platform_view_main_test.dart +++ b/dev/integration_tests/android_engine_test/test_driver/hcpp/platform_view_main_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:convert'; + import 'package:android_driver_extensions/native_driver.dart'; import 'package:android_driver_extensions/skia_gold.dart'; import 'package:flutter_driver/flutter_driver.dart'; @@ -43,6 +45,13 @@ void main() async { await flutterDriver.close(); }); + test('verify that HCPP is supported and enabled', () async { + final Map response = + json.decode(await flutterDriver.requestData('')) as Map; + + expect(response['supported'], true); + }, timeout: Timeout.none); + test('should screenshot an HCPP platform view', () async { await expectLater( nativeDriver.screenshot(), diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder.cc b/engine/src/flutter/shell/platform/android/android_shell_holder.cc index e86effe09b..6fd4d87046 100644 --- a/engine/src/flutter/shell/platform/android/android_shell_holder.cc +++ b/engine/src/flutter/shell/platform/android/android_shell_holder.cc @@ -16,6 +16,7 @@ #include #include +#include "common/settings.h" #include "flutter/fml/cpu_affinity.h" #include "flutter/fml/logging.h" #include "flutter/fml/make_copyable.h" @@ -261,8 +262,6 @@ std::unique_ptr AndroidShellHolder::Spawn( return std::make_unique(shell); }; - // TODO(xster): could be worth tracing this to investigate whether - // the IsolateConfiguration could be cached somewhere. auto config = BuildRunConfiguration(entrypoint, libraryUrl, entrypoint_args); if (!config) { // If the RunConfiguration was null, the kernel blob wasn't readable. @@ -358,4 +357,8 @@ void AndroidShellHolder::UpdateDisplayMetrics() { shell_->OnDisplayUpdates(std::move(displays)); } +bool AndroidShellHolder::IsSurfaceControlEnabled() { + return GetPlatformView()->IsSurfaceControlEnabled(); +} + } // namespace flutter diff --git a/engine/src/flutter/shell/platform/android/android_shell_holder.h b/engine/src/flutter/shell/platform/android/android_shell_holder.h index 276f46a043..b72ecec586 100644 --- a/engine/src/flutter/shell/platform/android/android_shell_holder.h +++ b/engine/src/flutter/shell/platform/android/android_shell_holder.h @@ -93,6 +93,8 @@ class AndroidShellHolder { fml::WeakPtr GetPlatformView(); + bool IsSurfaceControlEnabled(); + Rasterizer::Screenshot Screenshot(Rasterizer::ScreenshotType type, bool base64_encode); diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index 9e46ada709..7845b1e3e5 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -365,6 +365,7 @@ public class FlutterEngine implements ViewUtils.DisplayUpdater { PlatformViewsController2 platformViewsController2 = new PlatformViewsController2(); platformViewsController2.setRegistry(platformViewsController.getRegistry()); + platformViewsController2.setFlutterJNI(flutterJNI); flutterJNI.addEngineLifecycleListener(engineLifecycleListener); flutterJNI.setPlatformViewsController(platformViewsController); diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 9b216d71b2..bc6403ebde 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -1624,4 +1624,11 @@ public class FlutterJNI { } private native boolean nativeShouldDisableAHB(); + + /** Whether the SurfaceControl swapchain required for hcpp is enabled and active. */ + public boolean IsSurfaceControlEnabled() { + return nativeIsSurfaceControlEnabled(nativeShellHolderId); + } + + private native boolean nativeIsSurfaceControlEnabled(long nativeShellHolderId); } diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel2.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel2.java index 8966e39e4e..a838b9a4c4 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel2.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel2.java @@ -79,6 +79,9 @@ public class PlatformViewsChannel2 { case "clearFocus": clearFocus(call, result); break; + case "isSurfaceControlEnabled": + isSurfaceControlEnabled(call, result); + break; default: result.notImplemented(); } @@ -171,6 +174,11 @@ public class PlatformViewsChannel2 { result.error("error", detailedExceptionString(exception), null); } } + + private void isSurfaceControlEnabled( + @NonNull MethodCall call, @NonNull MethodChannel.Result result) { + result.success(handler.isSurfaceControlEnabled()); + } }; /** @@ -213,6 +221,9 @@ public class PlatformViewsChannel2 { /** Clears the focus from the platform view with a give id if it is currently focused. */ void clearFocus(int viewId); + + /** Whether the SurfaceControl swapchain is enabled. */ + boolean isSurfaceControlEnabled(); } /** Request sent from Flutter to create a new platform view. */ diff --git a/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController2.java b/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController2.java index f42b7949bd..e59211c2fe 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController2.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController2.java @@ -27,6 +27,7 @@ import io.flutter.Log; import io.flutter.embedding.android.AndroidTouchProcessor; import io.flutter.embedding.android.FlutterView; import io.flutter.embedding.android.MotionEventTracker; +import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.engine.FlutterOverlaySurface; import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.mutatorsstack.*; @@ -50,6 +51,7 @@ public class PlatformViewsController2 implements PlatformViewsAccessibilityDeleg private AndroidTouchProcessor androidTouchProcessor; private Context context; private FlutterView flutterView; + private FlutterJNI flutterJNI = null; @Nullable private TextInputPlugin textInputPlugin; @@ -77,6 +79,11 @@ public class PlatformViewsController2 implements PlatformViewsAccessibilityDeleg this.registry = (PlatformViewRegistryImpl) registry; } + /** Whether the SurfaceControl swapchain mode is enabled. */ + public void setFlutterJNI(FlutterJNI flutterJNI) { + this.flutterJNI = flutterJNI; + } + @Override public boolean usesVirtualDisplay(int id) { return false; @@ -679,5 +686,13 @@ public class PlatformViewsController2 implements PlatformViewsAccessibilityDeleg } embeddedView.clearFocus(); } + + @Override + public boolean isSurfaceControlEnabled() { + if (flutterJNI == null) { + return false; + } + return flutterJNI.IsSurfaceControlEnabled(); + } }; } diff --git a/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java b/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java index a49a038265..423071c41a 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java @@ -11,7 +11,6 @@ import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -// TODO(mattcarroll): re-evalute docs in this class and add nullability annotations. /** * Registry of backend textures used with a single {@link io.flutter.embedding.android.FlutterView} * instance. Entries may be embedded into the Flutter view using the FlutterViewGetScaledFontSize(unscaled_font_size, configuration_id); } + +bool PlatformViewAndroid::IsSurfaceControlEnabled() const { + return android_use_new_platform_view_; +} + } // namespace flutter diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.h b/engine/src/flutter/shell/platform/android/platform_view_android.h index 5d0b630a38..c14e98d3be 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.h +++ b/engine/src/flutter/shell/platform/android/platform_view_android.h @@ -119,6 +119,9 @@ class PlatformViewAndroid final : public PlatformView { return platform_message_handler_; } + /// @brief Whether the SurfaceControl based swapchain is enabled and active. + bool IsSurfaceControlEnabled() const; + private: const std::shared_ptr jni_facade_; std::shared_ptr android_context_; diff --git a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc index 8c798812e4..221af37541 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc @@ -383,6 +383,12 @@ static void UpdateDisplayMetrics(JNIEnv* env, ANDROID_SHELL_HOLDER->UpdateDisplayMetrics(); } +static bool IsSurfaceControlEnabled(JNIEnv* env, + jobject jcaller, + jlong shell_holder) { + return ANDROID_SHELL_HOLDER->IsSurfaceControlEnabled(); +} + static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { auto screenshot = ANDROID_SHELL_HOLDER->Screenshot( Rasterizer::ScreenshotType::UncompressedImage, false); @@ -873,6 +879,11 @@ bool RegisterApi(JNIEnv* env) { .signature = "()Z", .fnPtr = reinterpret_cast( &impeller::android::ShadowRealm::ShouldDisableAHB), + }, + { + .name = "nativeIsSurfaceControlEnabled", + .signature = "(J)Z", + .fnPtr = reinterpret_cast(&IsSurfaceControlEnabled), }}; if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, diff --git a/packages/flutter/lib/src/services/platform_views.dart b/packages/flutter/lib/src/services/platform_views.dart index b6ae2328ea..b9b4cdfe1e 100644 --- a/packages/flutter/lib/src/services/platform_views.dart +++ b/packages/flutter/lib/src/services/platform_views.dart @@ -1157,6 +1157,11 @@ class HybridAndroidViewController extends AndroidViewController { final _AndroidViewControllerInternals _internals = _Hybrid2AndroidViewControllerInternals(); + /// Perform a runtime check to determine if HCPP mode is supported on the + /// current device. + static Future checkIfSupported() => + _Hybrid2AndroidViewControllerInternals.checkIfSurfaceControlEnabled(); + @override bool get _createRequiresSize => false; @@ -1440,7 +1445,18 @@ class _HybridAndroidViewControllerInternals extends _AndroidViewControllerIntern } } +// The HCPP platform view controller. +// +// This is only supported via an opt in on Impeller Android. class _Hybrid2AndroidViewControllerInternals extends _AndroidViewControllerInternals { + // Determine if HCPP can be used. + static Future checkIfSurfaceControlEnabled() async { + return (await SystemChannels.platform_views_2.invokeMethod( + 'isSurfaceControlEnabled', + {}, + ))!; + } + @override int get textureId { throw UnimplementedError('Not supported for hybrid composition.');