[Android] add runtime flag to determine if HCPP is supported. (#163004)

Developers will need to _conditionally_ use HCPP (or the framework will
need to handle it automatically). This requires the ability to query at
runtime whether HCPP mode is enabled + supported.

Add a message channel to do so, and add the usage of this to the
android_engine_test. Does not yet add any automatic selection.

---------

Co-authored-by: Matan Lurey <matanlurey@users.noreply.github.com>
This commit is contained in:
Jonah Williams 2025-02-10 14:43:25 -08:00 committed by GitHub
parent 5e37c966c0
commit 9438fd4471
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 95 additions and 4 deletions

View File

@ -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: <CommandExtension>[nativeDriverCommands]);
enableFlutterDriverExtension(
handler: (String? command) async {
return json.encode(<String, Object?>{
'supported': await HybridAndroidViewController.checkIfSupported(),
});
},
commands: <CommandExtension>[nativeDriverCommands],
);
// Run on full screen.
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);

View File

@ -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<String, Object?> response =
json.decode(await flutterDriver.requestData('')) as Map<String, Object?>;
expect(response['supported'], true);
}, timeout: Timeout.none);
test('should screenshot an HCPP platform view', () async {
await expectLater(
nativeDriver.screenshot(),

View File

@ -16,6 +16,7 @@
#include <string>
#include <utility>
#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> AndroidShellHolder::Spawn(
return std::make_unique<Rasterizer>(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

View File

@ -93,6 +93,8 @@ class AndroidShellHolder {
fml::WeakPtr<PlatformViewAndroid> GetPlatformView();
bool IsSurfaceControlEnabled();
Rasterizer::Screenshot Screenshot(Rasterizer::ScreenshotType type,
bool base64_encode);

View File

@ -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);

View File

@ -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);
}

View File

@ -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. */

View File

@ -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();
}
};
}

View File

@ -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 <a

View File

@ -498,4 +498,9 @@ double PlatformViewAndroid::GetScaledFontSize(double unscaled_font_size,
return jni_facade_->FlutterViewGetScaledFontSize(unscaled_font_size,
configuration_id);
}
bool PlatformViewAndroid::IsSurfaceControlEnabled() const {
return android_use_new_platform_view_;
}
} // namespace flutter

View File

@ -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<PlatformViewAndroidJNI> jni_facade_;
std::shared_ptr<AndroidContext> android_context_;

View File

@ -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<void*>(
&impeller::android::ShadowRealm::ShouldDisableAHB),
},
{
.name = "nativeIsSurfaceControlEnabled",
.signature = "(J)Z",
.fnPtr = reinterpret_cast<void*>(&IsSurfaceControlEnabled),
}};
if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods,

View File

@ -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<bool> 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<bool> checkIfSurfaceControlEnabled() async {
return (await SystemChannels.platform_views_2.invokeMethod<bool>(
'isSurfaceControlEnabled',
<String, Object?>{},
))!;
}
@override
int get textureId {
throw UnimplementedError('Not supported for hybrid composition.');