1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <gtest/gtest.h>
18 #include <thread>
19
20 #include <android/gui/BnRegionSamplingListener.h>
21 #include <binder/ProcessState.h>
22 #include <gui/AidlStatusUtil.h>
23 #include <gui/DisplayEventReceiver.h>
24 #include <gui/ISurfaceComposer.h>
25 #include <gui/Surface.h>
26 #include <gui/SurfaceComposerClient.h>
27 #include <private/gui/ComposerServiceAIDL.h>
28 #include <utils/Looper.h>
29
30 using namespace std::chrono_literals;
31 using android::gui::aidl_utils::statusTFromBinderStatus;
32
33 namespace android::test {
34
35 struct ChoreographerSync {
ChoreographerSyncandroid::test::ChoreographerSync36 ChoreographerSync(DisplayEventReceiver& receiver) : receiver_(receiver) {}
37 ~ChoreographerSync() = default;
38
notifyandroid::test::ChoreographerSync39 void notify() const {
40 std::unique_lock<decltype(mutex_)> lk(mutex_);
41
42 auto check_event = [](auto const& ev) -> bool {
43 return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
44 };
45 DisplayEventReceiver::Event ev_;
46 int evs = receiver_.getEvents(&ev_, 1);
47 auto vsync_event_found = check_event(ev_);
48 while (evs) {
49 evs = receiver_.getEvents(&ev_, 1);
50 vsync_event_found |= check_event(ev_);
51 }
52
53 if (vsync_event_found) {
54 notification_arrived_ = true;
55 cv_.notify_all();
56 }
57 }
58
wait_vsync_notifyandroid::test::ChoreographerSync59 void wait_vsync_notify() const {
60 std::unique_lock<decltype(mutex_)> lk(mutex_);
61 cv_.wait(lk, [this] { return notification_arrived_; });
62 notification_arrived_ = false;
63 }
64
65 private:
66 ChoreographerSync(ChoreographerSync const&) = delete;
67 ChoreographerSync& operator=(ChoreographerSync const&) = delete;
68
69 std::mutex mutable mutex_;
70 std::condition_variable mutable cv_;
71 bool mutable notification_arrived_ = false;
72 DisplayEventReceiver& receiver_;
73 };
74
75 struct ChoreographerSim {
makeandroid::test::ChoreographerSim76 static std::unique_ptr<ChoreographerSim> make() {
77 auto receiver = std::make_unique<DisplayEventReceiver>();
78 if (!receiver || receiver->initCheck() == NO_INIT) {
79 ALOGE("No display reciever");
80 return nullptr;
81 }
82 return std::unique_ptr<ChoreographerSim>(new ChoreographerSim(std::move(receiver)));
83 }
84
~ChoreographerSimandroid::test::ChoreographerSim85 ~ChoreographerSim() {
86 poll_ = false;
87 looper->wake();
88 choreographer_thread_.join();
89 }
90
request_render_waitandroid::test::ChoreographerSim91 void request_render_wait(std::function<void()> const& render_fn) {
92 display_event_receiver_->requestNextVsync();
93 choreographer_.wait_vsync_notify();
94 render_fn();
95
96 // Purpose is to make sure that the content is latched by the time we sample.
97 // Waiting one vsync after queueing could still race with vsync, so wait for two, after
98 // which the content is pretty reliably on screen.
99 display_event_receiver_->requestNextVsync();
100 choreographer_.wait_vsync_notify();
101 display_event_receiver_->requestNextVsync();
102 choreographer_.wait_vsync_notify();
103 }
104
105 private:
ChoreographerSimandroid::test::ChoreographerSim106 ChoreographerSim(std::unique_ptr<DisplayEventReceiver> receiver)
107 : display_event_receiver_{std::move(receiver)},
108 choreographer_{*display_event_receiver_},
109 looper{new Looper(false)} {
__anon3a7db5280302null110 choreographer_thread_ = std::thread([this] {
111 auto vsync_notify_fd = display_event_receiver_->getFd();
112 looper->addFd(vsync_notify_fd, 0, Looper::EVENT_INPUT,
113 [](int /*fd*/, int /*events*/, void* data) -> int {
114 if (!data) return 0;
115 reinterpret_cast<ChoreographerSync*>(data)->notify();
116 return 1;
117 },
118 const_cast<void*>(reinterpret_cast<void const*>(&choreographer_)));
119
120 while (poll_) {
121 auto const poll_interval =
122 std::chrono::duration_cast<std::chrono::milliseconds>(1s).count();
123 auto rc = looper->pollOnce(poll_interval);
124 if ((rc != Looper::POLL_CALLBACK) && (rc != Looper::POLL_WAKE))
125 ALOGW("Vsync Looper returned: %i\n", rc);
126 }
127 });
128 }
129
130 ChoreographerSim(ChoreographerSim const&) = delete;
131 ChoreographerSim& operator=(ChoreographerSim const&) = delete;
132
133 std::unique_ptr<DisplayEventReceiver> const display_event_receiver_;
134 ChoreographerSync const choreographer_;
135 sp<Looper> looper;
136 std::thread choreographer_thread_;
137 std::atomic<bool> poll_{true};
138 };
139
140 struct Listener : android::gui::BnRegionSamplingListener {
onSampleCollectedandroid::test::Listener141 binder::Status onSampleCollected(float medianLuma) override {
142 std::unique_lock<decltype(mutex)> lk(mutex);
143 received = true;
144 mLuma = medianLuma;
145 cv.notify_all();
146 return binder::Status::ok();
147 };
wait_eventandroid::test::Listener148 bool wait_event(std::chrono::milliseconds timeout) {
149 std::unique_lock<decltype(mutex)> lk(mutex);
150 return cv.wait_for(lk, timeout, [this] { return received; });
151 }
152
lumaandroid::test::Listener153 float luma() {
154 std::unique_lock<decltype(mutex)> lk(mutex);
155 return mLuma;
156 }
157
resetandroid::test::Listener158 void reset() {
159 std::unique_lock<decltype(mutex)> lk(mutex);
160 received = false;
161 }
162
163 private:
164 std::condition_variable cv;
165 std::mutex mutex;
166 bool received = false;
167 float mLuma = -0.0f;
168 };
169
170 // Hoisted to TestSuite setup to avoid flake in test (b/124675919)
171 std::unique_ptr<ChoreographerSim> gChoreographerSim = nullptr;
172
173 struct RegionSamplingTest : ::testing::Test {
174 protected:
RegionSamplingTestandroid::test::RegionSamplingTest175 RegionSamplingTest() { ProcessState::self()->startThreadPool(); }
176
SetUpTestSuiteandroid::test::RegionSamplingTest177 static void SetUpTestSuite() {
178 gChoreographerSim = ChoreographerSim::make();
179 ASSERT_NE(gChoreographerSim, nullptr);
180 }
181
SetUpandroid::test::RegionSamplingTest182 void SetUp() override {
183 mSurfaceComposerClient = new SurfaceComposerClient;
184 ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
185
186 mBackgroundLayer =
187 mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0,
188 0, PIXEL_FORMAT_RGBA_8888,
189 ISurfaceComposerClient::eFXSurfaceEffect);
190 uint32_t layerPositionBottom = 0x7E000000;
191 SurfaceComposerClient::Transaction{}
192 .setLayer(mBackgroundLayer, layerPositionBottom)
193 .setPosition(mBackgroundLayer, 100, 100)
194 .setColor(mBackgroundLayer, half3{0.5, 0.5, 0.5})
195 .show(mBackgroundLayer)
196 .apply();
197
198 mContentLayer = mSurfaceComposerClient->createSurface(String8("Content RegionSamplingTest"),
199 300, 300, PIXEL_FORMAT_RGBA_8888, 0);
200
201 SurfaceComposerClient::Transaction{}
202 .setLayer(mContentLayer, layerPositionBottom + 1)
203 .setPosition(mContentLayer, 100, 100)
204 .setColor(mContentLayer, half3{0.5, 0.5, 0.5})
205 .show(mContentLayer)
206 .apply();
207
208 mTopLayer = mSurfaceComposerClient->createSurface(String8("TopLayer RegionSamplingTest"), 0,
209 0, PIXEL_FORMAT_RGBA_8888, 0);
210 SurfaceComposerClient::Transaction{}
211 .setLayer(mTopLayer, layerPositionBottom + 2)
212 .setPosition(mTopLayer, 0, 0)
213 .show(mBackgroundLayer)
214 .apply();
215 }
216
fill_renderandroid::test::RegionSamplingTest217 void fill_render(uint32_t rgba_value) {
218 auto surface = mContentLayer->getSurface();
219 ANativeWindow_Buffer outBuffer;
220 status_t status = surface->lock(&outBuffer, NULL);
221 ASSERT_EQ(status, android::OK);
222 auto b = reinterpret_cast<uint32_t*>(outBuffer.bits);
223 for (auto i = 0; i < outBuffer.height; i++) {
224 for (auto j = 0; j < outBuffer.width; j++) {
225 b[j] = rgba_value;
226 }
227 b += outBuffer.stride;
228 }
229
230 gChoreographerSim->request_render_wait([&surface] { surface->unlockAndPost(); });
231 }
232
233 sp<SurfaceComposerClient> mSurfaceComposerClient;
234 sp<SurfaceControl> mBackgroundLayer;
235 sp<SurfaceControl> mContentLayer;
236 sp<SurfaceControl> mTopLayer;
237
238 uint32_t const rgba_green = 0xFF00FF00;
239 float const luma_green = 0.7152;
240 uint32_t const rgba_blue = 0xFFFF0000;
241 float const luma_blue = 0.0722;
242 float const error_margin = 0.01;
243 float const luma_gray = 0.50;
244 };
245
TEST_F(RegionSamplingTest,invalidLayerHandle_doesNotCrash)246 TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) {
247 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
248 sp<Listener> listener = new Listener();
249 gui::ARect sampleArea;
250 sampleArea.left = 100;
251 sampleArea.top = 100;
252 sampleArea.right = 200;
253 sampleArea.bottom = 200;
254 // Passing in composer service as the layer handle should not crash, we'll
255 // treat it as a layer that no longer exists and silently allow sampling to
256 // occur.
257 binder::Status status =
258 composer->addRegionSamplingListener(sampleArea, IInterface::asBinder(composer),
259 listener);
260 ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
261 composer->removeRegionSamplingListener(listener);
262 }
263
TEST_F(RegionSamplingTest,DISABLED_CollectsLuma)264 TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
265 fill_render(rgba_green);
266
267 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
268 sp<Listener> listener = new Listener();
269 gui::ARect sampleArea;
270 sampleArea.left = 100;
271 sampleArea.top = 100;
272 sampleArea.right = 200;
273 sampleArea.bottom = 200;
274 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
275
276 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
277 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
278
279 composer->removeRegionSamplingListener(listener);
280 }
281
TEST_F(RegionSamplingTest,DISABLED_CollectsChangingLuma)282 TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) {
283 fill_render(rgba_green);
284
285 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
286 sp<Listener> listener = new Listener();
287 gui::ARect sampleArea;
288 sampleArea.left = 100;
289 sampleArea.top = 100;
290 sampleArea.right = 200;
291 sampleArea.bottom = 200;
292 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
293
294 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
295 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
296
297 listener->reset();
298
299 fill_render(rgba_blue);
300 EXPECT_TRUE(listener->wait_event(300ms))
301 << "timed out waiting for 2nd luma event to be received";
302 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
303
304 composer->removeRegionSamplingListener(listener);
305 }
306
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromTwoRegions)307 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) {
308 fill_render(rgba_green);
309 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
310 sp<Listener> greenListener = new Listener();
311 gui::ARect greenSampleArea;
312 greenSampleArea.left = 100;
313 greenSampleArea.top = 100;
314 greenSampleArea.right = 200;
315 greenSampleArea.bottom = 200;
316 composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener);
317
318 sp<Listener> grayListener = new Listener();
319 gui::ARect graySampleArea;
320 graySampleArea.left = 500;
321 graySampleArea.top = 100;
322 graySampleArea.right = 600;
323 graySampleArea.bottom = 200;
324 composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener);
325
326 EXPECT_TRUE(grayListener->wait_event(300ms))
327 << "timed out waiting for luma event to be received";
328 EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin);
329 EXPECT_TRUE(greenListener->wait_event(300ms))
330 << "timed out waiting for luma event to be received";
331 EXPECT_NEAR(greenListener->luma(), luma_green, error_margin);
332
333 composer->removeRegionSamplingListener(greenListener);
334 composer->removeRegionSamplingListener(grayListener);
335 }
336
TEST_F(RegionSamplingTest,DISABLED_TestIfInvalidInputParameters)337 TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) {
338 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
339 sp<Listener> listener = new Listener();
340
341 gui::ARect invalidRect;
342 invalidRect.left = Rect::INVALID_RECT.left;
343 invalidRect.top = Rect::INVALID_RECT.top;
344 invalidRect.right = Rect::INVALID_RECT.right;
345 invalidRect.bottom = Rect::INVALID_RECT.bottom;
346
347 gui::ARect sampleArea;
348 sampleArea.left = 100;
349 sampleArea.top = 100;
350 sampleArea.right = 200;
351 sampleArea.bottom = 200;
352 // Invalid input sampleArea
353 EXPECT_EQ(BAD_VALUE,
354 statusTFromBinderStatus(composer->addRegionSamplingListener(invalidRect,
355 mTopLayer->getHandle(),
356 listener)));
357 listener->reset();
358 // Invalid input binder
359 EXPECT_EQ(NO_ERROR,
360 statusTFromBinderStatus(
361 composer->addRegionSamplingListener(sampleArea, NULL, listener)));
362 // Invalid input listener
363 EXPECT_EQ(BAD_VALUE,
364 statusTFromBinderStatus(composer->addRegionSamplingListener(sampleArea,
365 mTopLayer->getHandle(),
366 NULL)));
367 EXPECT_EQ(BAD_VALUE, statusTFromBinderStatus(composer->removeRegionSamplingListener(NULL)));
368 // remove the listener
369 composer->removeRegionSamplingListener(listener);
370 }
371
TEST_F(RegionSamplingTest,DISABLED_TestCallbackAfterRemoveListener)372 TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) {
373 fill_render(rgba_green);
374 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
375 sp<Listener> listener = new Listener();
376 gui::ARect sampleArea;
377 sampleArea.left = 100;
378 sampleArea.top = 100;
379 sampleArea.right = 200;
380 sampleArea.bottom = 200;
381 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
382 fill_render(rgba_green);
383
384 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
385 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
386
387 listener->reset();
388 composer->removeRegionSamplingListener(listener);
389 fill_render(rgba_green);
390 EXPECT_FALSE(listener->wait_event(100ms))
391 << "callback should stop after remove the region sampling listener";
392 }
393
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromMovingLayer)394 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) {
395 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
396 sp<Listener> listener = new Listener();
397 Rect sampleArea{100, 100, 200, 200};
398 gui::ARect sampleAreaA;
399 sampleAreaA.left = sampleArea.left;
400 sampleAreaA.top = sampleArea.top;
401 sampleAreaA.right = sampleArea.right;
402 sampleAreaA.bottom = sampleArea.bottom;
403
404 // Test: listener in (100, 100). See layer before move, no layer after move.
405 fill_render(rgba_blue);
406 composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
407 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
408 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
409 listener->reset();
410 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
411 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
412 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
413 composer->removeRegionSamplingListener(listener);
414
415 // Test: listener offset to (600, 600). No layer before move, see layer after move.
416 fill_render(rgba_green);
417 sampleArea.offsetTo(600, 600);
418 sampleAreaA.left = sampleArea.left;
419 sampleAreaA.top = sampleArea.top;
420 sampleAreaA.right = sampleArea.right;
421 sampleAreaA.bottom = sampleArea.bottom;
422 composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
423 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
424 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
425 listener->reset();
426 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
427 EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
428 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
429 composer->removeRegionSamplingListener(listener);
430 }
431
432 } // namespace android::test
433