summaryrefslogtreecommitdiff
path: root/libs/vr/libvrflinger/tests/vrflinger_test.cpp
blob: ac44f74151bfb5cf2c8afd3c4af45a674d6753c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#include <SurfaceFlingerProperties.h>
#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 != OK) {
      return std::nullopt;
    }
    result = IInterface::asBinder(surface_flinger_)
                 ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
    if (result != OK) {
      return std::nullopt;
    }
    bool vr_flinger_active;
    result = reply.readBool(&vr_flinger_active);
    if (result != OK) {
      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) {
    return WaitForVrFlingerTimed(wait_active, kVrFlingerSwitchPollInterval,
        kVrFlingerSwitchMaxTime);
  }

  // Wait for vr flinger to become active or inactive, specifying custom timeouts.
  VrFlingerSwitchResult WaitForVrFlingerTimed(bool wait_active,
      std::chrono::milliseconds pollInterval, std::chrono::seconds timeout) {
    auto start_time = std::chrono::steady_clock::now();
    while (1) {
      std::this_thread::sleep_for(pollInterval);
      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 > timeout) {
        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 = android::sysprop::use_vr_flinger(false);
  if (!vr_flinger_enabled) {
    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()->buffer()->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()->buffer()->Unlock(), 0);

    ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 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