summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
l---------services/surfaceflinger/tests/end2end/.clang-format1
-rw-r--r--services/surfaceflinger/tests/end2end/.clang-tidy380
-rw-r--r--services/surfaceflinger/tests/end2end/.clangd20
-rw-r--r--services/surfaceflinger/tests/end2end/Android.bp68
-rw-r--r--services/surfaceflinger/tests/end2end/AndroidTest.xml39
-rw-r--r--services/surfaceflinger/tests/end2end/README.md75
-rw-r--r--services/surfaceflinger/tests/end2end/main.cpp55
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h29
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp82
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/TestService.h76
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp123
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h61
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp106
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h59
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp181
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h70
-rw-r--r--services/surfaceflinger/tests/end2end/tests/.clang-tidy32
-rw-r--r--services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp39
18 files changed, 1496 insertions, 0 deletions
diff --git a/services/surfaceflinger/tests/end2end/.clang-format b/services/surfaceflinger/tests/end2end/.clang-format
new file mode 120000
index 0000000000..5e8e20be26
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clang-format
@@ -0,0 +1 @@
+../../../../../../build/soong/scripts/system-clang-format \ No newline at end of file
diff --git a/services/surfaceflinger/tests/end2end/.clang-tidy b/services/surfaceflinger/tests/end2end/.clang-tidy
new file mode 100644
index 0000000000..29f3b4721c
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clang-tidy
@@ -0,0 +1,380 @@
+# Copyright 2025 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.
+
+FormatStyle: file
+InheritParentConfig: true
+
+# Please add checks explicitly rather than using wildcards like "modernize-*".
+# These check names are current as of LLVM 20.0.0, as reported by "clang-tidy --list-checks --checks=*"
+# For more information on each check, see https://clang.llvm.org/extra/clang-tidy/checks/list.html.
+Checks:
+ # from android-*
+ - android-cloexec-accept
+ - android-cloexec-accept4
+ - android-cloexec-creat
+ - android-cloexec-dup
+ - android-cloexec-epoll-create
+ - android-cloexec-epoll-create1
+ - android-cloexec-fopen
+ - android-cloexec-inotify-init
+ - android-cloexec-inotify-init1
+ - android-cloexec-memfd-create
+ - android-cloexec-open
+ - android-cloexec-pipe
+ - android-cloexec-pipe2
+ - android-cloexec-socket
+ - android-comparison-in-temp-failure-retry
+
+ # from bugprone-*
+ - bugprone-argument-comment
+ - bugprone-assert-side-effect
+ - bugprone-assignment-in-if-condition
+ - bugprone-bad-signal-to-kill-thread
+ - bugprone-bool-pointer-implicit-conversion
+ - bugprone-branch-clone
+ - bugprone-casting-through-void
+ - bugprone-chained-comparison
+ - bugprone-compare-pointer-to-member-virtual-function
+ - bugprone-copy-constructor-init
+ - bugprone-crtp-constructor-accessibility
+ - bugprone-dangling-handle
+ - bugprone-dynamic-static-initializers
+ - bugprone-easily-swappable-parameters
+ - bugprone-empty-catch
+ - bugprone-exception-escape
+ - bugprone-fold-init-type
+ - bugprone-forward-declaration-namespace
+ - bugprone-forwarding-reference-overload
+ - bugprone-implicit-widening-of-multiplication-result
+ - bugprone-inaccurate-erase
+ - bugprone-inc-dec-in-conditions
+ - bugprone-incorrect-enable-if
+ - bugprone-incorrect-roundings
+ - bugprone-infinite-loop
+ - bugprone-integer-division
+ - bugprone-lambda-function-name
+ - bugprone-macro-parentheses
+ - bugprone-macro-repeated-side-effects
+ - bugprone-misplaced-operator-in-strlen-in-alloc
+ - bugprone-misplaced-pointer-arithmetic-in-alloc
+ - bugprone-misplaced-widening-cast
+ - bugprone-move-forwarding-reference
+ - bugprone-multi-level-implicit-pointer-conversion
+ - bugprone-multiple-new-in-one-expression
+ - bugprone-multiple-statement-macro
+ - bugprone-narrowing-conversions
+ - bugprone-no-escape
+ - bugprone-non-zero-enum-to-bool-conversion
+ - bugprone-not-null-terminated-result
+ - bugprone-optional-value-conversion
+ - bugprone-parent-virtual-call
+ - bugprone-pointer-arithmetic-on-polymorphic-object
+ - bugprone-posix-return
+ - bugprone-redundant-branch-condition
+ - bugprone-reserved-identifier
+ - bugprone-return-const-ref-from-parameter
+ - bugprone-shared-ptr-array-mismatch
+ - bugprone-signal-handler
+ - bugprone-signed-char-misuse
+ - bugprone-sizeof-container
+ - bugprone-sizeof-expression
+ - bugprone-spuriously-wake-up-functions
+ - bugprone-standalone-empty
+ - bugprone-string-constructor
+ - bugprone-string-integer-assignment
+ - bugprone-string-literal-with-embedded-nul
+ - bugprone-stringview-nullptr
+ - bugprone-suspicious-enum-usage
+ - bugprone-suspicious-include
+ - bugprone-suspicious-memory-comparison
+ - bugprone-suspicious-memset-usage
+ - bugprone-suspicious-missing-comma
+ - bugprone-suspicious-realloc-usage
+ - bugprone-suspicious-semicolon
+ - bugprone-suspicious-string-compare
+ - bugprone-suspicious-stringview-data-usage
+ - bugprone-swapped-arguments
+ - bugprone-switch-missing-default-case
+ - bugprone-terminating-continue
+ - bugprone-throw-keyword-missing
+ - bugprone-too-small-loop-variable
+ - bugprone-unchecked-optional-access
+ - bugprone-undefined-memory-manipulation
+ - bugprone-undelegated-constructor
+ - bugprone-unhandled-exception-at-new
+ - bugprone-unhandled-self-assignment
+ - bugprone-unique-ptr-array-mismatch
+ - bugprone-unsafe-functions
+ - bugprone-unused-local-non-trivial-variable
+ - bugprone-unused-raii
+ - bugprone-unused-return-value
+ - bugprone-use-after-move
+ - bugprone-virtual-near-miss
+
+ # from cert-*
+ - cert-con36-c
+ - cert-con54-cpp
+ - cert-ctr56-cpp
+ - cert-dcl03-c
+ - cert-dcl16-c
+ - cert-dcl37-c
+ - cert-dcl50-cpp
+ - cert-dcl51-cpp
+ - cert-dcl54-cpp
+ - cert-dcl58-cpp
+ - cert-dcl59-cpp
+ - cert-env33-c
+ - cert-err09-cpp
+ - cert-err33-c
+ - cert-err34-c
+ - cert-err52-cpp
+ - cert-err58-cpp
+ - cert-err60-cpp
+ - cert-err61-cpp
+ - cert-exp42-c
+ - cert-fio38-c
+ - cert-flp30-c
+ - cert-flp37-c
+ - cert-int09-c
+ - cert-mem57-cpp
+ - cert-msc24-c
+ - cert-msc30-c
+ - cert-msc32-c
+ - cert-msc33-c
+ - cert-msc50-cpp
+ - cert-msc51-cpp
+ - cert-msc54-cpp
+ - cert-oop11-cpp
+ - cert-oop54-cpp
+ - cert-oop57-cpp
+ - cert-oop58-cpp
+ - cert-pos44-c
+ - cert-pos47-c
+ - cert-sig30-c
+ - cert-str34-c
+
+ # from concurrency-*
+ - concurrency-mt-unsafe
+ - concurrency-thread-canceltype-asynchronous
+
+ # from cppcoreguidelines-*
+ - cppcoreguidelines-avoid-c-arrays
+ - cppcoreguidelines-avoid-capturing-lambda-coroutines
+ - cppcoreguidelines-avoid-const-or-ref-data-members
+ - cppcoreguidelines-avoid-do-while
+ - cppcoreguidelines-avoid-goto
+ - cppcoreguidelines-avoid-magic-numbers
+ - cppcoreguidelines-avoid-non-const-global-variables
+ - cppcoreguidelines-avoid-reference-coroutine-parameters
+ - cppcoreguidelines-c-copy-assignment-signature
+ - cppcoreguidelines-explicit-virtual-functions
+ - cppcoreguidelines-init-variables
+ - cppcoreguidelines-interfaces-global-init
+ - cppcoreguidelines-macro-to-enum
+ - cppcoreguidelines-macro-usage
+ - cppcoreguidelines-misleading-capture-default-by-value
+ - cppcoreguidelines-missing-std-forward
+ - cppcoreguidelines-narrowing-conversions
+ - cppcoreguidelines-no-malloc
+ - cppcoreguidelines-no-suspend-with-lock
+ - cppcoreguidelines-noexcept-destructor
+ - cppcoreguidelines-noexcept-move-operations
+ - cppcoreguidelines-noexcept-swap
+ - cppcoreguidelines-non-private-member-variables-in-classes
+ - cppcoreguidelines-owning-memory
+ - cppcoreguidelines-prefer-member-initializer
+ - cppcoreguidelines-pro-bounds-array-to-pointer-decay
+ - cppcoreguidelines-pro-bounds-constant-array-index
+ - cppcoreguidelines-pro-bounds-pointer-arithmetic
+ - cppcoreguidelines-pro-type-const-cast
+ - cppcoreguidelines-pro-type-cstyle-cast
+ - cppcoreguidelines-pro-type-member-init
+ - cppcoreguidelines-pro-type-reinterpret-cast
+ - cppcoreguidelines-pro-type-static-cast-downcast
+ - cppcoreguidelines-pro-type-union-access
+ - cppcoreguidelines-pro-type-vararg
+ - cppcoreguidelines-rvalue-reference-param-not-moved
+ - cppcoreguidelines-slicing
+ - cppcoreguidelines-special-member-functions
+ - cppcoreguidelines-use-default-member-init
+ - cppcoreguidelines-virtual-class-destructor
+
+ # from google-*
+ - google-build-explicit-make-pair
+ - google-build-namespaces
+ - google-build-using-namespace
+ - google-default-arguments
+ - google-explicit-constructor
+ - google-global-names-in-headers
+ - google-objc-avoid-nsobject-new
+ - google-objc-avoid-throwing-exception
+ - google-objc-function-naming
+ - google-objc-global-variable-declaration
+ - google-readability-avoid-underscore-in-googletest-name
+ - google-readability-braces-around-statements
+ - google-readability-casting
+ - google-readability-function-size
+ - google-readability-namespace-comments
+ - google-readability-todo
+ - google-runtime-int
+ - google-runtime-operator
+ - google-upgrade-googletest-case
+
+ # from misc-*
+ - misc-confusable-identifiers
+ - misc-const-correctness
+ - misc-coroutine-hostile-raii
+ - misc-definitions-in-headers
+ - misc-header-include-cycle
+ - misc-include-cleaner
+ - misc-misleading-bidirectional
+ - misc-misleading-identifier
+ - misc-misplaced-const
+ - misc-new-delete-overloads
+ - misc-no-recursion
+ - misc-non-copyable-objects
+ - misc-non-private-member-variables-in-classes
+ - misc-redundant-expression
+ - misc-static-assert
+ - misc-throw-by-value-catch-by-reference
+ - misc-unconventional-assign-operator
+ - misc-uniqueptr-reset-release
+ - misc-unused-alias-decls
+ - misc-unused-parameters
+ - misc-unused-using-decls
+ - misc-use-anonymous-namespace
+ - misc-use-internal-linkage
+
+ # from modernize-*
+ - modernize-avoid-bind
+ - modernize-avoid-c-arrays
+ - modernize-concat-nested-namespaces
+ - modernize-deprecated-headers
+ - modernize-deprecated-ios-base-aliases
+ - modernize-loop-convert
+ - modernize-macro-to-enum
+ - modernize-make-shared
+ - modernize-make-unique
+ - modernize-min-max-use-initializer-list
+ - modernize-pass-by-value
+ - modernize-raw-string-literal
+ - modernize-redundant-void-arg
+ - modernize-replace-auto-ptr
+ - modernize-replace-disallow-copy-and-assign-macro
+ - modernize-replace-random-shuffle
+ - modernize-return-braced-init-list
+ - modernize-shrink-to-fit
+ - modernize-type-traits
+ - modernize-unary-static-assert
+ - modernize-use-auto
+ - modernize-use-bool-literals
+ - modernize-use-constraints
+ - modernize-use-default-member-init
+ - modernize-use-designated-initializers
+ - modernize-use-emplace
+ - modernize-use-equals-default
+ - modernize-use-equals-delete
+ - modernize-use-nodiscard
+ - modernize-use-noexcept
+ - modernize-use-nullptr
+ - modernize-use-override
+ - modernize-use-ranges
+ - modernize-use-starts-ends-with
+ - modernize-use-std-format
+ - modernize-use-std-numbers
+ - modernize-use-std-print
+ - modernize-use-trailing-return-type
+ - modernize-use-transparent-functors
+ - modernize-use-uncaught-exceptions
+ - modernize-use-using
+
+ # from performance-*
+ - performance-avoid-endl
+ - performance-enum-size
+ - performance-faster-string-find
+ - performance-for-range-copy
+ - performance-implicit-conversion-in-loop
+ - performance-inefficient-algorithm
+ - performance-inefficient-string-concatenation
+ - performance-inefficient-vector-operation
+ - performance-move-const-arg
+ - performance-move-constructor-init
+ - performance-no-automatic-move
+ - performance-no-int-to-ptr
+ - performance-noexcept-destructor
+ - performance-noexcept-move-constructor
+ - performance-noexcept-swap
+ - performance-trivially-destructible
+ - performance-type-promotion-in-math-fn
+ - performance-unnecessary-copy-initialization
+ - performance-unnecessary-value-param
+
+ # from portability-*
+ - portability-restrict-system-includes
+ - portability-simd-intrinsics
+ - portability-std-allocator-const
+
+ # from readability-*
+ - readability-avoid-const-params-in-decls
+ - readability-avoid-nested-conditional-operator
+ - readability-avoid-return-with-void-value
+ - readability-avoid-unconditional-preprocessor-if
+ - readability-braces-around-statements
+ - readability-const-return-type
+ - readability-container-contains
+ - readability-container-data-pointer
+ - readability-container-size-empty
+ - readability-convert-member-functions-to-static
+ - readability-delete-null-pointer
+ - readability-duplicate-include
+ - readability-else-after-return
+ - readability-enum-initial-value
+ - readability-function-cognitive-complexity
+ - readability-function-size
+ - readability-identifier-length
+ - readability-identifier-naming
+ - readability-implicit-bool-conversion
+ - readability-inconsistent-declaration-parameter-name
+ - readability-isolate-declaration
+ - readability-magic-numbers
+ - readability-make-member-function-const
+ - readability-math-missing-parentheses
+ - readability-misleading-indentation
+ - readability-misplaced-array-index
+ - readability-named-parameter
+ - readability-non-const-parameter
+ - readability-operators-representation
+ - readability-qualified-auto
+ - readability-redundant-access-specifiers
+ - readability-redundant-casting
+ - readability-redundant-control-flow
+ - readability-redundant-declaration
+ - readability-redundant-function-ptr-dereference
+ - readability-redundant-inline-specifier
+ - readability-redundant-member-init
+ - readability-redundant-preprocessor
+ - readability-redundant-smartptr-get
+ - readability-redundant-string-cstr
+ - readability-redundant-string-init
+ - readability-reference-to-constructed-temporary
+ - readability-simplify-boolean-expr
+ - readability-simplify-subscript-expr
+ - readability-static-accessed-through-instance
+ - readability-static-definition-in-anonymous-namespace
+ - readability-string-compare
+ - readability-suspicious-call-argument
+ - readability-uniqueptr-delete-release
+ - readability-uppercase-literal-suffix
+ - readability-use-anyofallof
+ - readability-use-std-min-max
diff --git a/services/surfaceflinger/tests/end2end/.clangd b/services/surfaceflinger/tests/end2end/.clangd
new file mode 100644
index 0000000000..d64d2f8a55
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/.clangd
@@ -0,0 +1,20 @@
+# Copyright 2025 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.
+
+Diagnostics:
+ UnusedIncludes: Strict
+ MissingIncludes: Strict
+ ClangTidy:
+ FastCheckFilter: None
+ # See the .clang-tidy files for additional configuration
diff --git a/services/surfaceflinger/tests/end2end/Android.bp b/services/surfaceflinger/tests/end2end/Android.bp
new file mode 100644
index 0000000000..8810330179
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/Android.bp
@@ -0,0 +1,68 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "surfaceflinger_end2end_tests",
+ test_suites: ["device-tests"],
+ require_root: true,
+
+ cpp_std: "experimental",
+ cflags: [
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-DNODISCARD_EXPECTED",
+ "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
+ "-Wall",
+ "-Wconversion",
+ "-Werror",
+ "-Wextra",
+ "-Wformat",
+ "-Wno-non-virtual-dtor",
+ "-Wno-sign-compare",
+ "-Wno-sign-conversion",
+ "-Wshadow",
+ "-Wthread-safety",
+ "-Wunreachable-code",
+ "-Wunused",
+ ],
+ srcs: [
+ "main.cpp",
+ "test_framework/core/TestService.cpp",
+ "test_framework/fake_hwc3/Hwc3Composer.cpp",
+ "test_framework/fake_hwc3/Hwc3Controller.cpp",
+ "test_framework/surfaceflinger/SFController.cpp",
+ "tests/Placeholder_test.cpp",
+ ],
+ tidy: true,
+ tidy_flags: [
+ "--config=", // Use the .clang-tidy closest to each source file for the configuration
+ ],
+ tidy_checks_as_errors: [
+ "*",
+ ],
+ include_dirs: [
+ "frameworks/native/include",
+ ],
+ local_include_dirs: ["."],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libbinder_ndk",
+ "libcutils",
+ "libgui",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+ static_libs: [
+ "android.hardware.common-V2-ndk",
+ "android.hardware.graphics.common-V6-ndk",
+ "android.hardware.graphics.composer3-V3-ndk",
+ "libgtest",
+ ],
+}
diff --git a/services/surfaceflinger/tests/end2end/AndroidTest.xml b/services/surfaceflinger/tests/end2end/AndroidTest.xml
new file mode 100644
index 0000000000..99cb7b3fee
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 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="Configuration for surfaceflinger_end2end_tests">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Stop everything to run SurfaceFlinger in isolation, with relaxed SELinux permissions -->
+ <option name="teardown-command" value="stop" />
+ <option name="teardown-command" value="setprop debug.sf.nobootanimation 1" />
+
+ <!-- Restart everything with normal settings after the test finishes. -->
+ <option name="teardown-command" value="stop" />
+ <option name="teardown-command" value="setprop debug.sf.nobootanimation 0" />
+ <option name="teardown-command" value="setprop debug.sf.hwc_service_name default" />
+ <option name="teardown-command" value="start" />
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="surfaceflinger_end2end_tests->/data/local/tests/surfaceflinger_end2end_tests" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tests" />
+ <option name="module-name" value="surfaceflinger_end2end_tests" />
+ <option name="native-test-timeout" value="15m"/>
+ </test>
+</configuration>
diff --git a/services/surfaceflinger/tests/end2end/README.md b/services/surfaceflinger/tests/end2end/README.md
new file mode 100644
index 0000000000..2f58cec5c7
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/README.md
@@ -0,0 +1,75 @@
+# `surfaceflinger_end2end_tests`
+
+Tests to cover end to end testing of SurfaceFlinger.
+
+In particular the test framework allows you to simulate various display
+configurations, so the test can confirm displays are handled correctly.
+
+## Quick Useful info
+
+### Running the tests
+
+At present the tests should run on any target, though the typical target would
+be a [Cuttlefish](https://source.android.com/docs/devices/cuttlefish) VM
+target such as `aosp_cf_x86_64_phone`.
+
+At some future time the test may be rewritten to require
+[`vkms`](https://dri.freedesktop.org/docs/drm/gpu/vkms.html) and
+[`drm_hwcomposer`](https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer)
+
+```
+atest surfaceflinger_end2end_tests
+```
+
+You can also run the google test binary directly. However you will also need
+to run a few other set-up and tear-down commands that are part of the
+AndroidTest.xml configuration, so that SurfaceFlinger can be used run isolation
+from the rest of the system.
+
+```
+# Set-up
+adb root
+adb shell stop
+adb shell setenforce 0
+adb shell setprop debug.sf.nobootanimation 1
+
+# Sync and run the test
+adb sync data
+adb shell data/nativetest64/surfaceflinger_end2end_tests/surfaceflinger_end2end_tests
+
+# Tear-down
+adb shell stop
+adb shell setenforce 1
+adb shell setprop debug.sf.nobootanimation 0
+adb shell setprop debug.sf.hwc_service_name default
+```
+
+### Manual clang-tidy checks via Soong
+
+At present Android does not run the clang-tidy checks as part of its
+presubmit checks.
+
+You can run them through the build system by using phony target that are
+automatically created for each source subdirectory.
+
+For the code under `frameworks/native/services/surfaceflinger/tests/end2end`,
+you would build:
+
+```
+m tidy-frameworks-native-services-surfaceflinger-tests-end2end
+```
+
+For more information see the build documentation:
+
+* <https://android.googlesource.com/platform/build/soong/+/main/docs/tidy.md#the-tidy_directory-targets>
+
+### Seeing clang-tidy checks in your editor
+
+If your editor supports using [`clangd`](https://clangd.llvm.org/) as a
+C++ language server, you can build and export a compilation database using
+Soong. With the local `.clangd` configuration file, you should see the same
+checks in editor, along with all the other checks `clangd` runs.
+
+See the build documentation for the compilation database instructions:
+
+https://android.googlesource.com/platform/build/soong/+/main/docs/compdb.md
diff --git a/services/surfaceflinger/tests/end2end/main.cpp b/services/surfaceflinger/tests/end2end/main.cpp
new file mode 100644
index 0000000000..ddf690021d
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/main.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2025 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 <cstdlib>
+#include <string_view>
+
+#include <android-base/logging.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+void init(int argc, char** argv) {
+ using namespace std::string_view_literals;
+
+ ::testing::InitGoogleTest(&argc, argv);
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+
+ auto minimumSeverity = android::base::INFO;
+ for (int i = 1; i < argc; i++) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
+ const std::string_view arg = argv[i];
+
+ if (arg == "-v"sv) {
+ minimumSeverity = android::base::DEBUG;
+ } else if (arg == "-vv"sv) {
+ minimumSeverity = android::base::VERBOSE;
+ }
+ }
+ ::android::base::SetMinimumLogSeverity(minimumSeverity);
+}
+
+} // namespace
+
+auto main(int argc, char** argv) -> int {
+ init(argc, argv);
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+
+ return RUN_ALL_TESTS();
+} \ No newline at end of file
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h
new file mode 100644
index 0000000000..c3a535e10f
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2025 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace android::surfaceflinger::tests::end2end::test_framework::core {
+
+struct DisplayConfiguration final {
+ using Id = int64_t;
+
+ Id id{};
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::core
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp
new file mode 100644
index 0000000000..0531f18527
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2025 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 <memory>
+#include <span>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/expected.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/core/TestService.h"
+#include "test_framework/fake_hwc3/Hwc3Controller.h"
+#include "test_framework/surfaceflinger/SFController.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::core {
+
+struct TestService::Passkey final {};
+
+auto TestService::startWithDisplays(const std::vector<DisplayConfiguration>& displays)
+ -> base::expected<std::unique_ptr<TestService>, std::string> {
+ using namespace std::string_literals;
+
+ auto service = std::make_unique<TestService>(TestService::Passkey{});
+ if (service == nullptr) {
+ return base::unexpected("Failed to construct the TestService instance."s);
+ }
+
+ if (auto result = service->init(displays); !result) {
+ return base::unexpected("Failed to init the TestService instance: "s + result.error());
+ }
+
+ return service;
+}
+
+TestService::TestService(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto TestService::init(std::span<const DisplayConfiguration> displays)
+ -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto hwcResult = fake_hwc3::Hwc3Controller::make(displays);
+ if (!hwcResult) {
+ return base::unexpected(std::move(hwcResult).error());
+ }
+ auto hwc = *std::move(hwcResult);
+
+ auto flingerResult = surfaceflinger::SFController::make();
+ if (!flingerResult) {
+ return base::unexpected(std::move(flingerResult).error());
+ }
+ auto flinger = *std::move(flingerResult);
+
+ surfaceflinger::SFController::useHwcService(fake_hwc3::Hwc3Controller::getServiceName());
+
+ if (auto result = flinger->startAndConnect(); !result) {
+ return base::unexpected(std::move(result).error());
+ }
+
+ mHwc = std::move(hwc);
+ mFlinger = std::move(flinger);
+ return {};
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::core
diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h
new file mode 100644
index 0000000000..21e6426406
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2025 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework {
+
+namespace surfaceflinger {
+
+class SFController;
+
+} // namespace surfaceflinger
+
+namespace fake_hwc3 {
+
+class Hwc3Controller;
+
+} // namespace fake_hwc3
+
+namespace core {
+
+class TestService final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Constructs the test service, and starts it with the given displays as connected at boot.
+ [[nodiscard]] static auto startWithDisplays(const std::vector<DisplayConfiguration>& displays)
+ -> base::expected<std::unique_ptr<TestService>, std::string>;
+
+ explicit TestService(Passkey passkey);
+
+ // Obtains the HWC3 back-end controller
+ [[nodiscard]] auto hwc() -> fake_hwc3::Hwc3Controller& {
+ CHECK(mHwc);
+ return *mHwc;
+ }
+
+ // Obtains the SurfaceFlinger front-end controller
+ [[nodiscard]] auto flinger() -> surfaceflinger::SFController& {
+ CHECK(mFlinger);
+ return *mFlinger;
+ }
+
+ private:
+ [[nodiscard]] auto init(std::span<const DisplayConfiguration> displays)
+ -> base::expected<void, std::string>;
+
+ std::shared_ptr<fake_hwc3::Hwc3Controller> mHwc;
+ std::shared_ptr<surfaceflinger::SFController> mFlinger;
+};
+
+} // namespace core
+} // namespace android::surfaceflinger::tests::end2end::test_framework
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp
new file mode 100644
index 0000000000..5349ef0b58
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2025 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 <cstdint>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/graphics/composer3/BnComposer.h>
+#include <aidl/android/hardware/graphics/composer3/Capability.h>
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <aidl/android/hardware/graphics/composer3/PowerMode.h>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_status.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/fake_hwc3/Hwc3Composer.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer::Hwc3ComposerImpl final
+ : public aidl::android::hardware::graphics::composer3::BnComposer {
+ using Capability = aidl::android::hardware::graphics::composer3::Capability;
+ using IComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
+ using Hwc3PowerMode = aidl::android::hardware::graphics::composer3::PowerMode;
+
+ // begin IComposer overrides
+
+ auto dump(int dumpFd, const char** args, uint32_t num_args) -> binder_status_t override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(dumpFd, args, num_args);
+ return static_cast<binder_status_t>(STATUS_NO_MEMORY);
+ }
+
+ auto createClient(std::shared_ptr<IComposerClient>* out_client) -> ndk::ScopedAStatus override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(out_client);
+ return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+ IComposer::EX_NO_RESOURCES, "Client failed to initialize");
+ }
+
+ auto getCapabilities(std::vector<Capability>* out_capabilities) -> ndk::ScopedAStatus override {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(out_capabilities);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ // end IComposer overrides
+};
+
+struct Hwc3Composer::Passkey final {};
+
+auto Hwc3Composer::getServiceName(std::string_view baseServiceName) -> std::string {
+ return Hwc3ComposerImpl::makeServiceName(baseServiceName);
+}
+
+auto Hwc3Composer::make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string> {
+ using namespace std::string_literals;
+
+ auto composer = std::make_shared<Hwc3Composer>(Passkey{});
+ if (composer == nullptr) {
+ return base::unexpected("Failed to construct the Hwc3Composer instance."s);
+ }
+
+ if (auto result = composer->init(); !result) {
+ return base::unexpected("Failed to init the Hwc3Composer instance: "s + result.error());
+ }
+
+ return composer;
+}
+
+Hwc3Composer::Hwc3Composer(Hwc3Composer::Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto Hwc3Composer::init() -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto impl = ndk::SharedRefBase::make<Hwc3ComposerImpl>();
+ if (!impl) {
+ return base::unexpected("Failed to construct the Hwc3ComposerImpl instance."s);
+ }
+
+ mImpl = std::move(impl);
+
+ return {};
+}
+
+auto Hwc3Composer::getComposer() -> std::shared_ptr<Hwc3IComposer> {
+ return mImpl;
+}
+
+void Hwc3Composer::addDisplay(const core::DisplayConfiguration& display) {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(display, mImpl);
+}
+
+void Hwc3Composer::removeDisplay(core::DisplayConfiguration::Id displayId) {
+ UNIMPLEMENTED(WARNING);
+ ftl::ignore(displayId, mImpl);
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h
new file mode 100644
index 0000000000..6d6b7374c6
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2025 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <android-base/expected.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ class Hwc3ComposerImpl; // An internal class implements the AIDL interface.
+
+ public:
+ using Hwc3IComposer = aidl::android::hardware::graphics::composer3::IComposer;
+
+ // Gets the full qualified service name given a base name for the service.
+ [[nodiscard]] static auto getServiceName(std::string_view baseServiceName) -> std::string;
+
+ // Constructs a Hwc3Composer instance.
+ [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string>;
+
+ explicit Hwc3Composer(Passkey passkey);
+
+ // Obtains the AIDL composer3::IComposer interface for the internal instance.
+ [[nodiscard]] auto getComposer() -> std::shared_ptr<Hwc3IComposer>;
+
+ // Adds a display to the composer. This will sent a hotplug connect event.
+ void addDisplay(const core::DisplayConfiguration& display);
+
+ // Removes a display from the composer. This will sent a hotplug disconnect event.
+ void removeDisplay(core::DisplayConfiguration::Id displayId);
+
+ private:
+ [[nodiscard]] auto init() -> base::expected<void, std::string>;
+
+ std::shared_ptr<Hwc3ComposerImpl> mImpl;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp
new file mode 100644
index 0000000000..ea985c09b4
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2025 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 <memory>
+#include <span>
+#include <string>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_stability.h>
+#include <android/binder_status.h>
+#include <fmt/format.h>
+#include <ftl/ignore.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+#include "test_framework/fake_hwc3/Hwc3Composer.h"
+#include "test_framework/fake_hwc3/Hwc3Controller.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+struct Hwc3Controller::Passkey final {};
+
+auto Hwc3Controller::make(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string> {
+ using namespace std::string_literals;
+
+ auto controller = std::make_unique<Hwc3Controller>(Passkey{});
+ if (controller == nullptr) {
+ return base::unexpected("Failed to construct the Hwc3Controller instance"s);
+ }
+
+ if (auto result = controller->init(displays); !result) {
+ return base::unexpected("Failed to construct the Hwc3Controller instance: "s +
+ result.error());
+ }
+
+ return controller;
+}
+
+Hwc3Controller::Hwc3Controller(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto Hwc3Controller::init(const std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ auto qualifiedServiceName = Hwc3Composer::getServiceName(baseServiceName);
+
+ auto composerResult = Hwc3Composer::make();
+ if (!composerResult) {
+ return base::unexpected(std::move(composerResult).error());
+ }
+ auto composer = *std::move(composerResult);
+
+ for (const auto& display : displays) {
+ composer->addDisplay(display);
+ }
+
+ auto binder = composer->getComposer()->asBinder();
+
+ // This downgrade allows us to use the fake service name without it being defined in the
+ // VINTF manifest.
+ AIBinder_forceDowngradeToLocalStability(binder.get());
+
+ auto status = AServiceManager_addService(binder.get(), qualifiedServiceName.c_str());
+ if (status != STATUS_OK) {
+ return base::unexpected(fmt::format("Failed to register service {}. Error {}.",
+ qualifiedServiceName, status));
+ }
+ LOG(INFO) << "Registered service " << qualifiedServiceName << ". Error: " << status;
+
+ mComposer = std::move(composer);
+ return {};
+}
+
+auto Hwc3Controller::getServiceName() -> std::string {
+ return Hwc3Composer::getServiceName(baseServiceName);
+}
+
+void Hwc3Controller::addDisplay(const core::DisplayConfiguration& config) {
+ CHECK(mComposer);
+ mComposer->addDisplay(config);
+}
+
+void Hwc3Controller::removeDisplay(core::DisplayConfiguration::Id displayId) {
+ CHECK(mComposer);
+ mComposer->removeDisplay(displayId);
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h
new file mode 100644
index 0000000000..e53d2cfc48
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2025 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <span>
+#include <string>
+
+#include <android-base/expected.h>
+
+#include "test_framework/core/DisplayConfiguration.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 {
+
+class Hwc3Composer;
+
+class Hwc3Controller final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Gets the service name for the HWC3 instance that will be created and registered
+ [[nodiscard]] static auto getServiceName() -> std::string;
+
+ // Makes the HWC3 controller instance.
+ [[nodiscard]] static auto make(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string>;
+
+ explicit Hwc3Controller(Passkey passkey);
+
+ // Adds a new display to the HWC3, which will become a hotplug connect event.
+ void addDisplay(const core::DisplayConfiguration& config);
+
+ // Removes a new display from the HWC3, which will become a hotplug disconnect event.
+ void removeDisplay(core::DisplayConfiguration::Id displayId);
+
+ private:
+ static constexpr std::string baseServiceName = "fake";
+
+ [[nodiscard]] auto init(std::span<const core::DisplayConfiguration> displays)
+ -> base::expected<void, std::string>;
+
+ std::shared_ptr<Hwc3Composer> mComposer;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3
diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp
new file mode 100644
index 0000000000..1cf49c5750
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2025 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 <chrono>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <utility>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/gui/ISurfaceComposer.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <ftl/finalizer.h>
+#include <ftl/ignore.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+#include "test_framework/surfaceflinger/SFController.h"
+
+namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger {
+
+namespace {
+
+auto waitForSurfaceFlingerAIDL() -> sp<gui::ISurfaceComposer> {
+ constexpr auto kTimeout = std::chrono::seconds(30);
+ constexpr auto kSurfaceFlingerServiceName = "SurfaceFlingerAIDL";
+ const sp<android::IServiceManager> serviceManager(android::defaultServiceManager());
+ const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout;
+
+ LOG(INFO) << "Waiting " << kTimeout << " for service manager registration....";
+ sp<android::IBinder> flingerService;
+ while (flingerService == nullptr) {
+ if (std::chrono::steady_clock::now() > kTimeoutAfter) {
+ LOG(INFO) << "... Timeout!";
+ return nullptr;
+ }
+
+ constexpr auto sleepTime = std::chrono::milliseconds(10);
+ std::this_thread::sleep_for(sleepTime);
+ flingerService = serviceManager->checkService(String16(kSurfaceFlingerServiceName));
+ }
+ LOG(INFO) << "Obtained surfaceflinger interface from service manager.";
+
+ return interface_cast<gui::ISurfaceComposer>(flingerService);
+}
+
+} // namespace
+
+struct SFController::Passkey final {};
+
+void SFController::useHwcService(std::string_view fqn) {
+ base::SetProperty("debug.sf.hwc_service_name", std::string(fqn));
+}
+
+auto SFController::make() -> base::expected<std::shared_ptr<SFController>, std::string> {
+ using namespace std::string_literals;
+
+ auto controller = std::make_unique<SFController>(Passkey{});
+ if (controller == nullptr) {
+ return base::unexpected("Failed to construct the SFController instance."s);
+ }
+
+ if (auto result = controller->init(); !result) {
+ return base::unexpected("Failed to init the SFController instance: "s + result.error());
+ }
+
+ return controller;
+}
+
+SFController::SFController(Passkey passkey) {
+ ftl::ignore(passkey);
+}
+
+auto SFController::init() -> base::expected<void, std::string> {
+ LOG(INFO) << "Stopping everything to prepare for tests";
+ // NOLINTBEGIN(cert-env33-c)
+ system("stop");
+ // NOLINTEND(cert-env33-c)
+
+ mCleanup = ftl::Finalizer([this]() { stop(); });
+
+ return {};
+}
+
+auto SFController::startAndConnect() -> base::expected<void, std::string> {
+ using namespace std::string_literals;
+
+ start();
+
+ LOG(VERBOSE) << "Getting ISurfaceComposer....";
+ auto surfaceComposerAidl = waitForSurfaceFlingerAIDL();
+ if (surfaceComposerAidl == nullptr) {
+ return base::unexpected("Failed to obtain the surfaceComposerAidl interface."s);
+ }
+ LOG(VERBOSE) << "Getting ISurfaceComposerClient....";
+ sp<gui::ISurfaceComposerClient> surfaceComposerClientAidl;
+ if (!surfaceComposerAidl->createConnection(&surfaceComposerClientAidl).isOk()) {
+ return base::unexpected("Failed to obtain the surfaceComposerClientAidl interface."s);
+ }
+ if (surfaceComposerClientAidl == nullptr) {
+ return base::unexpected("Failed to obtain a valid surfaceComposerClientAidl interface."s);
+ }
+ auto surfaceComposerClient = sp<SurfaceComposerClient>::make(surfaceComposerClientAidl);
+ if (surfaceComposerClient == nullptr) {
+ return base::unexpected(
+ "Failed to construct a surfaceComposerClient around the aidl interface."s);
+ }
+
+ mSurfaceComposerAidl = std::move(surfaceComposerAidl);
+ mSurfaceComposerClientAidl = std::move(surfaceComposerClientAidl);
+ mSurfaceComposerClient = std::move(surfaceComposerClient);
+
+ LOG(INFO) << "Connected to surfaceflinger";
+ return {};
+}
+
+void SFController::start() {
+ LOG(INFO) << "Starting surfaceflinger";
+ // NOLINTBEGIN(cert-env33-c)
+ system("start surfaceflinger");
+ // NOLINTEND(cert-env33-c)
+}
+
+void SFController::stop() {
+ LOG(INFO) << "Stopping surfaceflinger";
+ // NOLINTBEGIN(cert-env33-c)
+ system("stop surfaceflinger");
+ // NOLINTEND(cert-env33-c)
+
+ if (mSurfaceComposerAidl != nullptr) {
+ LOG(INFO) << "Waiting for SF AIDL interface to die";
+
+ constexpr auto kTimeout = std::chrono::seconds(30);
+ const auto binder = android::gui::ISurfaceComposer::asBinder(mSurfaceComposerAidl);
+ const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout;
+
+ while (binder->isBinderAlive()) {
+ if (std::chrono::steady_clock::now() > kTimeoutAfter) {
+ LOG(INFO) << "... Timeout!";
+ break;
+ }
+
+ ftl::ignore = binder->pingBinder();
+
+ constexpr auto kPollInterval = std::chrono::milliseconds(10);
+ std::this_thread::sleep_for(kPollInterval);
+ }
+
+ constexpr auto kShutdownWait = std::chrono::milliseconds(500);
+ std::this_thread::sleep_for(kShutdownWait);
+ }
+
+ mSurfaceComposerClient = nullptr;
+ mSurfaceComposerClientAidl = nullptr;
+ mSurfaceComposerAidl = nullptr;
+}
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger
diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h
new file mode 100644
index 0000000000..58bac9197f
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2025 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include <android-base/expected.h>
+#include <ftl/finalizer.h>
+#include <utils/StrongPointer.h>
+
+namespace android::gui {
+
+class ISurfaceComposer;
+class ISurfaceComposerClient;
+
+} // namespace android::gui
+
+namespace android {
+
+class SurfaceComposerClient;
+
+} // namespace android
+
+namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger {
+
+class SFController final {
+ struct Passkey; // Uses the passkey idiom to restrict construction.
+
+ public:
+ // Sets a property so that SurfaceFlinger uses the named HWC service.
+ static void useHwcService(std::string_view fqn);
+
+ // Makes an instance of the SFController.
+ [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<SFController>, std::string>;
+
+ explicit SFController(Passkey pass);
+
+ // Starts SurfaceFlinger and establishes the AIDL interface connections.
+ [[nodiscard]] auto startAndConnect() -> base::expected<void, std::string>;
+
+ private:
+ [[nodiscard]] auto init() -> base::expected<void, std::string>;
+ static void start();
+ void stop();
+
+ sp<gui::ISurfaceComposer> mSurfaceComposerAidl;
+ sp<gui::ISurfaceComposerClient> mSurfaceComposerClientAidl;
+ sp<SurfaceComposerClient> mSurfaceComposerClient;
+
+ // Finalizers should be last so their destructors are invoked first.
+ ftl::FinalizerFtl mCleanup;
+};
+
+} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger
diff --git a/services/surfaceflinger/tests/end2end/tests/.clang-tidy b/services/surfaceflinger/tests/end2end/tests/.clang-tidy
new file mode 100644
index 0000000000..4924c466c0
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/tests/.clang-tidy
@@ -0,0 +1,32 @@
+# Copyright 2025 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.
+
+FormatStyle: file
+InheritParentConfig: true
+
+# Note: For tests, we are actually turning off certain checks enabled for the
+# non-test code in the parent .clang-tidy file.
+Checks:
+ - -cppcoreguidelines-avoid-magic-numbers # Allow tests to use magic numbers.
+ - -cppcoreguidelines-avoid-goto # Google Test macros use goto.
+ - -cppcoreguidelines-avoid-non-const-global-variables # Google Test macros define global variables.
+ - -cppcoreguidelines-macro-usage # Google Benchmark defines function-like macros.
+ - -cppcoreguidelines-owning-memory # Google Test macros use operator new directly.
+ - -google-runtime-int # Tests might intentionally use the base "short"/"long" types and not want to use "int16"/"int64".
+ - -misc-use-anonymous-namespace # Google Test macros declare some static global variables to not export them.
+ - -modernize-use-trailing-return-type # Google Test macros use non-trailing return types.
+ - -performance-move-const-arg # Tests might std::move() a trivially copyable value as part of testing that moving works.
+ - -readability-function-cognitive-complexity # Assertions turn into extra branches, increasing apparent complexity.
+ - -readability-magic-numbers # Allow tests to use magic numbers
+
diff --git a/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp
new file mode 100644
index 0000000000..3c4277f8a0
--- /dev/null
+++ b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2025 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 <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "test_framework/core/TestService.h"
+
+namespace android::surfaceflinger::tests::end2end {
+namespace {
+
+struct Placeholder : public ::testing::Test {};
+
+TEST_F(Placeholder, Bringup) {
+ auto serviceResult = test_framework::core::TestService::startWithDisplays({
+ {.id = 123},
+ });
+ if (!serviceResult) {
+ LOG(WARNING) << "End2End service not available. " << serviceResult.error();
+ GTEST_SKIP() << "End2End service not available. " << serviceResult.error();
+ }
+}
+
+} // namespace
+} // namespace android::surfaceflinger::tests::end2end