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

Commit c02c6d91 authored by Steven Thomas's avatar Steven Thomas Committed by Android (Google) Code Review
Browse files

Merge "Add vr flinger test"

parents 169dd11c 97f1f4cb
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -93,3 +93,7 @@ cc_library_static {
    header_libs: headerLibraries,
    name: "libvrflinger",
}

subdirs = [
    "tests",
]
+37 −0
Original line number Diff line number Diff line
shared_libs = [
    "android.hardware.configstore-utils",
    "android.hardware.configstore@1.0",
    "libbinder",
    "libbufferhubqueue",
    "libcutils",
    "libgui",
    "libhidlbase",
    "liblog",
    "libui",
    "libutils",
    "libnativewindow",
    "libpdx_default_transport",
]

static_libs = [
    "libdisplay",
]

cc_test {
    srcs: ["vrflinger_test.cpp"],
    // See go/apct-presubmit for documentation on how this .filter file is used
    // by Android's automated testing infrastructure for test filtering.
    data: ["vrflinger_test.filter"],
    static_libs: static_libs,
    shared_libs: shared_libs,
    cflags: [
        "-DLOG_TAG=\"VrFlingerTest\"",
        "-DTRACE=0",
        "-O0",
        "-g",
        "-Wall",
        "-Werror",
    ],
    cppflags: ["-std=c++1z"],
    name: "vrflinger_test",
}
+229 −0
Original line number Diff line number Diff line
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
#include <android/hardware_buffer.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <configstore/Utils.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <gui/ISurfaceComposer.h>
#include <log/log.h>
#include <utils/StrongPointer.h>

#include <chrono>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>

#include <private/dvr/display_client.h>

using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
using android::dvr::display::DisplayClient;
using android::dvr::display::Surface;
using android::dvr::display::SurfaceAttribute;
using android::dvr::display::SurfaceAttributeValue;

namespace android {
namespace dvr {

// The transaction code for asking surface flinger if vr flinger is active. This
// is done as a hidden api since it's only used for tests. See the "case 1028"
// block in SurfaceFlinger::onTransact() in SurfaceFlinger.cpp.
constexpr uint32_t kIsVrFlingerActiveTransactionCode = 1028;

// The maximum amount of time to give vr flinger to activate/deactivate. If the
// switch hasn't completed in this amount of time, the test will fail.
constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1);

// How long to wait between each check to see if the vr flinger switch
// completed.
constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);

// A Binder connection to surface flinger.
class SurfaceFlingerConnection {
 public:
  static std::unique_ptr<SurfaceFlingerConnection> Create() {
    sp<ISurfaceComposer> surface_flinger = interface_cast<ISurfaceComposer>(
        defaultServiceManager()->getService(String16("SurfaceFlinger")));
    if (surface_flinger == nullptr) {
      return nullptr;
    }

    return std::unique_ptr<SurfaceFlingerConnection>(
        new SurfaceFlingerConnection(surface_flinger));
  }

  // Returns true if the surface flinger process is still running. We use this
  // to detect if surface flinger has crashed.
  bool IsAlive() {
    IInterface::asBinder(surface_flinger_)->pingBinder();
    return IInterface::asBinder(surface_flinger_)->isBinderAlive();
  }

  // Return true if vr flinger is currently active, false otherwise. If there's
  // an error communicating with surface flinger, std::nullopt is returned.
  std::optional<bool> IsVrFlingerActive() {
    Parcel data, reply;
    status_t result =
        data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
    if (result != NO_ERROR) {
      return std::nullopt;
    }
    result = IInterface::asBinder(surface_flinger_)
                 ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
    if (result != NO_ERROR) {
      return std::nullopt;
    }
    bool vr_flinger_active;
    result = reply.readBool(&vr_flinger_active);
    if (result != NO_ERROR) {
      return std::nullopt;
    }
    return vr_flinger_active;
  }

  enum class VrFlingerSwitchResult : int8_t {
    kSuccess,
    kTimedOut,
    kCommunicationError,
    kSurfaceFlingerDied
  };

  // Wait for vr flinger to become active or inactive.
  VrFlingerSwitchResult WaitForVrFlinger(bool wait_active) {
    auto start_time = std::chrono::steady_clock::now();
    while (1) {
      std::this_thread::sleep_for(kVrFlingerSwitchPollInterval);
      if (!IsAlive()) {
        return VrFlingerSwitchResult::kSurfaceFlingerDied;
      }
      std::optional<bool> vr_flinger_active = IsVrFlingerActive();
      if (!vr_flinger_active.has_value()) {
        return VrFlingerSwitchResult::kCommunicationError;
      }
      if (vr_flinger_active.value() == wait_active) {
        return VrFlingerSwitchResult::kSuccess;
      } else if (std::chrono::steady_clock::now() - start_time >
                 kVrFlingerSwitchMaxTime) {
        return VrFlingerSwitchResult::kTimedOut;
      }
    }
  }

 private:
  SurfaceFlingerConnection(sp<ISurfaceComposer> surface_flinger)
      : surface_flinger_(surface_flinger) {}

  sp<ISurfaceComposer> surface_flinger_ = nullptr;
};

// This test activates vr flinger by creating a vr flinger surface, then
// deactivates vr flinger by destroying the surface. We verify that vr flinger
// is activated and deactivated as expected, and that surface flinger doesn't
// crash.
//
// If the device doesn't support vr flinger (as repoted by ConfigStore), the
// test does nothing.
//
// If the device is a standalone vr device, the test also does nothing, since
// this test verifies the behavior of display handoff from surface flinger to vr
// flinger and back, and standalone devices never hand control of the display
// back to surface flinger.
TEST(VrFlingerTest, ActivateDeactivate) {
  android::ProcessState::self()->startThreadPool();

  // Exit immediately if the device doesn't support vr flinger. This ConfigStore
  // check is the same mechanism used by surface flinger to decide if it should
  // initialize vr flinger.
  bool vr_flinger_enabled =
      getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(
          false);
  if (!vr_flinger_enabled) {
    return;
  }

  // This test doesn't apply to standalone vr devices.
  if (property_get_bool("ro.boot.vr", false)) {
    return;
  }

  auto surface_flinger_connection = SurfaceFlingerConnection::Create();
  ASSERT_NE(surface_flinger_connection, nullptr);

  // Verify we start off with vr flinger disabled.
  ASSERT_TRUE(surface_flinger_connection->IsAlive());
  auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
  ASSERT_TRUE(vr_flinger_active.has_value());
  ASSERT_FALSE(vr_flinger_active.value());

  // Create a vr flinger surface, and verify vr flinger becomes active.
  // Introduce a scope so that, at the end of the scope, the vr flinger surface
  // is destroyed, and vr flinger deactivates.
  {
    auto display_client = DisplayClient::Create();
    ASSERT_NE(display_client, nullptr);
    auto metrics = display_client->GetDisplayMetrics();
    ASSERT_TRUE(metrics.ok());

    auto surface = Surface::CreateSurface({
        {SurfaceAttribute::Direct, SurfaceAttributeValue(true)},
        {SurfaceAttribute::Visible, SurfaceAttributeValue(true)},
    });
    ASSERT_TRUE(surface.ok());
    ASSERT_TRUE(surface.get() != nullptr);

    auto queue = surface.get()->CreateQueue(
        metrics.get().display_width, metrics.get().display_height,
        /*layer_count=*/1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
        AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
            AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
            AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
        /*capacity=*/1,
        /*metadata_size=*/0);
    ASSERT_TRUE(queue.ok());
    ASSERT_TRUE(queue.get() != nullptr);

    size_t slot;
    pdx::LocalHandle release_fence;
    auto buffer = queue.get()->Dequeue(/*timeout=*/0, &slot, &release_fence);
    ASSERT_TRUE(buffer.ok());
    ASSERT_TRUE(buffer.get() != nullptr);

    ASSERT_EQ(buffer.get()->width(), metrics.get().display_width);
    ASSERT_EQ(buffer.get()->height(), metrics.get().display_height);

    void* raw_buf = nullptr;
    ASSERT_GE(buffer.get()->Lock(AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
                                 /*x=*/0, /*y=*/0, buffer.get()->width(),
                                 buffer.get()->height(), &raw_buf),
              0);
    ASSERT_NE(raw_buf, nullptr);
    uint32_t* pixels = static_cast<uint32_t*>(raw_buf);

    for (int i = 0; i < buffer.get()->stride() * buffer.get()->height(); ++i) {
      pixels[i] = 0x0000ff00;
    }

    ASSERT_GE(buffer.get()->Unlock(), 0);

    ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle(),
                                 /*meta=*/nullptr,
                                 /*user_metadata_size=*/0),
              0);

    ASSERT_EQ(
        surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/true),
        SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
  }

  // Now that the vr flinger surface is destroyed, vr flinger should deactivate.
  ASSERT_EQ(
      surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/false),
      SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
}

}  // namespace dvr
}  // namespace android
+5 −0
Original line number Diff line number Diff line
{
        "presubmit": {
            "filter": "VrFlingerTest.*"
        }
}
+6 −0
Original line number Diff line number Diff line
@@ -4619,6 +4619,12 @@ status_t SurfaceFlinger::onTransact(
                }
                return NO_ERROR;
            }
            // Is VrFlinger active?
            case 1028: {
                Mutex::Autolock _l(mStateLock);
                reply->writeBool(getBE().mHwc->isUsingVrComposer());
                return NO_ERROR;
            }
        }
    }
    return err;