Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 585e3f87 authored by Lloyd Pique's avatar Lloyd Pique
Browse files

end2end: Implement a fake IComposerClient [4/N]

Implements a minimal but function ComposerClient which responds to all
the initial SurfaceFlinger queries needed to allow fake physical
displays to be connected and used.

The implementation code is split into two parts. First is a series of
special delegating helper implementations, located under the hwc3/delegators
directory. These implement the full interface by delegating to a
templated handler function. This allows the default behavior for the
full interface to be specified by just writing a single function,
whether that behavior is to return an error for all calls, or to do
something else.

This is then used as a base class by the actual implementation which
focuses on overriding only the interface functions which need special
handling.

The existing files are shuffled around a bit, as the delegators will be
reused by a subsequent CL to define a intercepting/forwarding observer
implementation, and this will be distinct from the "fake" implementation
as it will eventually be used to observe the interactions between SF and
the drm_hwcomposer real implementation for use by the test.

This change also introduces a local SharedMutex helper, which wraps
`std::shared_mutex` and enforces the correct locking for the two types
of access (exclusive or shared).

The shared mutex allows the display changes to be made by the test
potentially at the same time as SF is making calls to query the display
properties.

With the fake implementation correctly configured for SurfaceFlinger to
use, SF now boots and enters a usable state. The test can now control
SF through the front-end interfaces, though there are other changes
still needed to make presentation work.

Flag: TEST_ONLY
Bug: 372735083
Test: atest surfaceflinger_end2end_tests

Change-Id: Ic67ed4ed0627649c70910cb20643bfa0c7b75208
parent f110207d
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -32,11 +32,10 @@ cc_test {
    ],
    srcs: [
        "main.cpp",

        "test_framework/core/EdidBuilder.cpp",
        "test_framework/core/TestService.cpp",
        "test_framework/fake_hwc3/Hwc3Composer.cpp",
        "test_framework/fake_hwc3/Hwc3Controller.cpp",
        "test_framework/hwc3/FakeComposer.cpp",
        "test_framework/hwc3/Hwc3Controller.cpp",
        "test_framework/surfaceflinger/SFController.cpp",

        // Internal tests
+55 −0
Original line number Diff line number Diff line
/*
 * 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 <functional>
#include <mutex>
#include <shared_mutex>
#include <type_traits>

namespace android::surfaceflinger::tests::end2end::test_framework::core {

// Ensures all access to a state structure `SharedState` is done while holding an appropriate
// `std::shared_mutex` lock.
//
// This works around limitations with Clang thread safety analysis when using `std::shared_lock`, as
// there will be a compiler diagnostic despite acquiring the lock.
template <typename SharedState>
class GuardedSharedState final {
  public:
    // Allows shared read-only access to the state. The lambda is invoked with a `const
    // SharedState&` first argument referencing the state.
    template <typename F>
    auto withSharedLock(F&& continuation) const -> std::invoke_result_t<F, const SharedState&> {
        const std::shared_lock lock(mMutex);
        return std::invoke(std::forward<F>(continuation), mState);
    }

    // Allows exclusive read/write access to the state. The lambda is invoked with a `SharedState&`
    // first argument referencing the state.
    template <typename F>
    auto withExclusiveLock(F&& continuation) -> std::invoke_result_t<F, SharedState&> {
        const std::lock_guard lock(mMutex);
        return std::invoke(std::forward<F>(continuation), mState);
    }

  private:
    mutable std::shared_mutex mMutex;
    SharedState mState;
};

}  // namespace android::surfaceflinger::tests::end2end::test_framework::core
+3 −3
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@

#include "test_framework/core/DisplayConfiguration.h"
#include "test_framework/core/TestService.h"
#include "test_framework/fake_hwc3/Hwc3Controller.h"
#include "test_framework/hwc3/Hwc3Controller.h"
#include "test_framework/surfaceflinger/SFController.h"

namespace android::surfaceflinger::tests::end2end::test_framework::core {
@@ -56,7 +56,7 @@ 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);
    auto hwcResult = hwc3::Hwc3Controller::make(displays);
    if (!hwcResult) {
        return base::unexpected(std::move(hwcResult).error());
    }
@@ -68,7 +68,7 @@ auto TestService::init(std::span<const DisplayConfiguration> displays)
    }
    auto flinger = *std::move(flingerResult);

    surfaceflinger::SFController::useHwcService(fake_hwc3::Hwc3Controller::getServiceName());
    surfaceflinger::SFController::useHwcService(hwc3::Hwc3Controller::getServiceName());

    if (auto result = flinger->startAndConnect(); !result) {
        return base::unexpected(std::move(result).error());
+4 −4
Original line number Diff line number Diff line
@@ -34,11 +34,11 @@ class SFController;

}  // namespace surfaceflinger

namespace fake_hwc3 {
namespace hwc3 {

class Hwc3Controller;

}  // namespace fake_hwc3
}  // namespace hwc3

namespace core {

@@ -53,7 +53,7 @@ class TestService final {
    explicit TestService(Passkey passkey);

    // Obtains the HWC3 back-end controller
    [[nodiscard]] auto hwc() -> fake_hwc3::Hwc3Controller& {
    [[nodiscard]] auto hwc() -> hwc3::Hwc3Controller& {
        CHECK(mHwc);
        return *mHwc;
    }
@@ -68,7 +68,7 @@ class TestService final {
    [[nodiscard]] auto init(std::span<const DisplayConfiguration> displays)
            -> base::expected<void, std::string>;

    std::shared_ptr<fake_hwc3::Hwc3Controller> mHwc;
    std::shared_ptr<hwc3::Hwc3Controller> mHwc;
    std::shared_ptr<surfaceflinger::SFController> mFlinger;
};

+0 −123
Original line number Diff line number Diff line
/*
 * 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
Loading