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)} {
__anon53ee6fa30302null110 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.1;
243 float const luma_gray = 0.50;
244 static constexpr std::chrono::milliseconds EVENT_WAIT_TIME_MS = 5000ms;
245 };
246
TEST_F(RegionSamplingTest,invalidLayerHandle_doesNotCrash)247 TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) {
248 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
249 sp<Listener> listener = new Listener();
250 gui::ARect sampleArea;
251 sampleArea.left = 100;
252 sampleArea.top = 100;
253 sampleArea.right = 200;
254 sampleArea.bottom = 200;
255 // Passing in composer service as the layer handle should not crash, we'll
256 // treat it as a layer that no longer exists and silently allow sampling to
257 // occur.
258 binder::Status status =
259 composer->addRegionSamplingListener(sampleArea, IInterface::asBinder(composer),
260 listener);
261 ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
262 composer->removeRegionSamplingListener(listener);
263 }
264
TEST_F(RegionSamplingTest,CollectsLuma)265 TEST_F(RegionSamplingTest, CollectsLuma) {
266 fill_render(rgba_green);
267
268 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
269 sp<Listener> listener = new Listener();
270 gui::ARect sampleArea;
271 sampleArea.left = 100;
272 sampleArea.top = 100;
273 sampleArea.right = 200;
274 sampleArea.bottom = 200;
275 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
276
277 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
278 << "timed out waiting for luma event to be received";
279 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
280
281 composer->removeRegionSamplingListener(listener);
282 }
283
TEST_F(RegionSamplingTest,CollectsLumaForSecureLayer)284 TEST_F(RegionSamplingTest, CollectsLumaForSecureLayer) {
285 fill_render(rgba_green);
286 SurfaceComposerClient::Transaction()
287 .setFlags(mContentLayer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
288 .apply(/*synchronous=*/true);
289
290 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
291 sp<Listener> listener = new Listener();
292 gui::ARect sampleArea;
293 sampleArea.left = 100;
294 sampleArea.top = 100;
295 sampleArea.right = 200;
296 sampleArea.bottom = 200;
297 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
298
299 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
300 << "timed out waiting for luma event to be received";
301 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
302
303 composer->removeRegionSamplingListener(listener);
304 }
305
TEST_F(RegionSamplingTest,DISABLED_CollectsChangingLuma)306 TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) {
307 fill_render(rgba_green);
308
309 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
310 sp<Listener> listener = new Listener();
311 gui::ARect sampleArea;
312 sampleArea.left = 100;
313 sampleArea.top = 100;
314 sampleArea.right = 200;
315 sampleArea.bottom = 200;
316 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
317
318 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
319 << "timed out waiting for luma event to be received";
320 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
321
322 listener->reset();
323
324 fill_render(rgba_blue);
325 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
326 << "timed out waiting for 2nd luma event to be received";
327 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
328
329 composer->removeRegionSamplingListener(listener);
330 }
331
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromTwoRegions)332 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) {
333 fill_render(rgba_green);
334 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
335 sp<Listener> greenListener = new Listener();
336 gui::ARect greenSampleArea;
337 greenSampleArea.left = 100;
338 greenSampleArea.top = 100;
339 greenSampleArea.right = 200;
340 greenSampleArea.bottom = 200;
341 composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener);
342
343 sp<Listener> grayListener = new Listener();
344 gui::ARect graySampleArea;
345 graySampleArea.left = 500;
346 graySampleArea.top = 100;
347 graySampleArea.right = 600;
348 graySampleArea.bottom = 200;
349 composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener);
350
351 EXPECT_TRUE(grayListener->wait_event(EVENT_WAIT_TIME_MS))
352 << "timed out waiting for luma event to be received";
353 EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin);
354 EXPECT_TRUE(greenListener->wait_event(EVENT_WAIT_TIME_MS))
355 << "timed out waiting for luma event to be received";
356 EXPECT_NEAR(greenListener->luma(), luma_green, error_margin);
357
358 composer->removeRegionSamplingListener(greenListener);
359 composer->removeRegionSamplingListener(grayListener);
360 }
361
TEST_F(RegionSamplingTest,TestIfInvalidInputParameters)362 TEST_F(RegionSamplingTest, TestIfInvalidInputParameters) {
363 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
364 sp<Listener> listener = new Listener();
365
366 gui::ARect invalidRect;
367 invalidRect.left = Rect::INVALID_RECT.left;
368 invalidRect.top = Rect::INVALID_RECT.top;
369 invalidRect.right = Rect::INVALID_RECT.right;
370 invalidRect.bottom = Rect::INVALID_RECT.bottom;
371
372 gui::ARect sampleArea;
373 sampleArea.left = 100;
374 sampleArea.top = 100;
375 sampleArea.right = 200;
376 sampleArea.bottom = 200;
377 // Invalid input sampleArea
378 EXPECT_EQ(BAD_VALUE,
379 statusTFromBinderStatus(composer->addRegionSamplingListener(invalidRect,
380 mTopLayer->getHandle(),
381 listener)));
382 listener->reset();
383 // Invalid input binder
384 EXPECT_EQ(NO_ERROR,
385 statusTFromBinderStatus(
386 composer->addRegionSamplingListener(sampleArea, NULL, listener)));
387 // Invalid input listener
388 EXPECT_EQ(BAD_VALUE,
389 statusTFromBinderStatus(composer->addRegionSamplingListener(sampleArea,
390 mTopLayer->getHandle(),
391 NULL)));
392 EXPECT_EQ(BAD_VALUE, statusTFromBinderStatus(composer->removeRegionSamplingListener(NULL)));
393 // remove the listener
394 composer->removeRegionSamplingListener(listener);
395 }
396
TEST_F(RegionSamplingTest,TestCallbackAfterRemoveListener)397 TEST_F(RegionSamplingTest, TestCallbackAfterRemoveListener) {
398 fill_render(rgba_green);
399 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
400 sp<Listener> listener = new Listener();
401 gui::ARect sampleArea;
402 sampleArea.left = 100;
403 sampleArea.top = 100;
404 sampleArea.right = 200;
405 sampleArea.bottom = 200;
406 composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
407 fill_render(rgba_green);
408
409 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
410 << "timed out waiting for luma event to be received";
411 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
412
413 listener->reset();
414 composer->removeRegionSamplingListener(listener);
415 fill_render(rgba_green);
416 EXPECT_FALSE(listener->wait_event(100ms))
417 << "callback should stop after remove the region sampling listener";
418 }
419
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromMovingLayer)420 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) {
421 sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
422 sp<Listener> listener = new Listener();
423 Rect sampleArea{100, 100, 200, 200};
424 gui::ARect sampleAreaA;
425 sampleAreaA.left = sampleArea.left;
426 sampleAreaA.top = sampleArea.top;
427 sampleAreaA.right = sampleArea.right;
428 sampleAreaA.bottom = sampleArea.bottom;
429
430 // Test: listener in (100, 100). See layer before move, no layer after move.
431 fill_render(rgba_blue);
432 composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
433 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
434 << "timed out waiting for luma event to be received";
435 EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
436 listener->reset();
437 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
438 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
439 << "timed out waiting for luma event to be received";
440 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
441 composer->removeRegionSamplingListener(listener);
442
443 // Test: listener offset to (600, 600). No layer before move, see layer after move.
444 fill_render(rgba_green);
445 sampleArea.offsetTo(600, 600);
446 sampleAreaA.left = sampleArea.left;
447 sampleAreaA.top = sampleArea.top;
448 sampleAreaA.right = sampleArea.right;
449 sampleAreaA.bottom = sampleArea.bottom;
450 composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
451 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
452 << "timed out waiting for luma event to be received";
453 EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
454 listener->reset();
455 SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
456 EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
457 << "timed out waiting for luma event to be received";
458 EXPECT_NEAR(listener->luma(), luma_green, error_margin);
459 composer->removeRegionSamplingListener(listener);
460 }
461
462 } // namespace android::test
463