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 <binder/ProcessState.h>
21 #include <gui/DisplayEventReceiver.h>
22 #include <gui/IRegionSamplingListener.h>
23 #include <gui/ISurfaceComposer.h>
24 #include <gui/Surface.h>
25 #include <gui/SurfaceComposerClient.h>
26 #include <private/gui/ComposerService.h>
27 #include <utils/Looper.h>
28 
29 using namespace std::chrono_literals;
30 
31 namespace android::test {
32 
33 struct ChoreographerSync {
ChoreographerSyncandroid::test::ChoreographerSync34     ChoreographerSync(DisplayEventReceiver& receiver) : receiver_(receiver) {}
35     ~ChoreographerSync() = default;
36 
notifyandroid::test::ChoreographerSync37     void notify() const {
38         std::unique_lock<decltype(mutex_)> lk(mutex_);
39 
40         auto check_event = [](auto const& ev) -> bool {
41             return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
42         };
43         DisplayEventReceiver::Event ev_;
44         int evs = receiver_.getEvents(&ev_, 1);
45         auto vsync_event_found = check_event(ev_);
46         while (evs) {
47             evs = receiver_.getEvents(&ev_, 1);
48             vsync_event_found |= check_event(ev_);
49         }
50 
51         if (vsync_event_found) {
52             notification_arrived_ = true;
53             cv_.notify_all();
54         }
55     }
56 
wait_vsync_notifyandroid::test::ChoreographerSync57     void wait_vsync_notify() const {
58         std::unique_lock<decltype(mutex_)> lk(mutex_);
59         cv_.wait(lk, [this] { return notification_arrived_; });
60         notification_arrived_ = false;
61     }
62 
63 private:
64     ChoreographerSync(ChoreographerSync const&) = delete;
65     ChoreographerSync& operator=(ChoreographerSync const&) = delete;
66 
67     std::mutex mutable mutex_;
68     std::condition_variable mutable cv_;
69     bool mutable notification_arrived_ = false;
70     DisplayEventReceiver& receiver_;
71 };
72 
73 struct ChoreographerSim {
makeandroid::test::ChoreographerSim74     static std::unique_ptr<ChoreographerSim> make() {
75         auto receiver = std::make_unique<DisplayEventReceiver>();
76         if (!receiver || receiver->initCheck() == NO_INIT) {
77             ALOGE("No display reciever");
78             return nullptr;
79         }
80         return std::unique_ptr<ChoreographerSim>(new ChoreographerSim(std::move(receiver)));
81     }
82 
~ChoreographerSimandroid::test::ChoreographerSim83     ~ChoreographerSim() {
84         poll_ = false;
85         looper->wake();
86         choreographer_thread_.join();
87     }
88 
request_render_waitandroid::test::ChoreographerSim89     void request_render_wait(std::function<void()> const& render_fn) {
90         display_event_receiver_->requestNextVsync();
91         choreographer_.wait_vsync_notify();
92         render_fn();
93 
94         // Purpose is to make sure that the content is latched by the time we sample.
95         // Waiting one vsync after queueing could still race with vsync, so wait for two, after
96         // which the content is pretty reliably on screen.
97         display_event_receiver_->requestNextVsync();
98         choreographer_.wait_vsync_notify();
99         display_event_receiver_->requestNextVsync();
100         choreographer_.wait_vsync_notify();
101     }
102 
103 private:
ChoreographerSimandroid::test::ChoreographerSim104     ChoreographerSim(std::unique_ptr<DisplayEventReceiver> receiver)
105           : display_event_receiver_{std::move(receiver)},
106             choreographer_{*display_event_receiver_},
107             looper{new Looper(false)} {
__anon4d7e458f0302null108         choreographer_thread_ = std::thread([this] {
109             auto vsync_notify_fd = display_event_receiver_->getFd();
110             looper->addFd(vsync_notify_fd, 0, Looper::EVENT_INPUT,
111                           [](int /*fd*/, int /*events*/, void* data) -> int {
112                               if (!data) return 0;
113                               reinterpret_cast<ChoreographerSync*>(data)->notify();
114                               return 1;
115                           },
116                           const_cast<void*>(reinterpret_cast<void const*>(&choreographer_)));
117 
118             while (poll_) {
119                 auto const poll_interval =
120                         std::chrono::duration_cast<std::chrono::milliseconds>(1s).count();
121                 auto rc = looper->pollOnce(poll_interval);
122                 if ((rc != Looper::POLL_CALLBACK) && (rc != Looper::POLL_WAKE))
123                     ALOGW("Vsync Looper returned: %i\n", rc);
124             }
125         });
126     }
127 
128     ChoreographerSim(ChoreographerSim const&) = delete;
129     ChoreographerSim& operator=(ChoreographerSim const&) = delete;
130 
131     std::unique_ptr<DisplayEventReceiver> const display_event_receiver_;
132     ChoreographerSync const choreographer_;
133     sp<Looper> looper;
134     std::thread choreographer_thread_;
135     std::atomic<bool> poll_{true};
136 };
137 
138 struct Listener : BnRegionSamplingListener {
onSampleCollectedandroid::test::Listener139     void onSampleCollected(float medianLuma) override {
140         std::unique_lock<decltype(mutex)> lk(mutex);
141         received = true;
142         mLuma = medianLuma;
143         cv.notify_all();
144     };
wait_eventandroid::test::Listener145     bool wait_event(std::chrono::milliseconds timeout) {
146         std::unique_lock<decltype(mutex)> lk(mutex);
147         return cv.wait_for(lk, timeout, [this] { return received; });
148     }
149 
lumaandroid::test::Listener150     float luma() {
151         std::unique_lock<decltype(mutex)> lk(mutex);
152         return mLuma;
153     }
154 
resetandroid::test::Listener155     void reset() {
156         std::unique_lock<decltype(mutex)> lk(mutex);
157         received = false;
158     }
159 
160 private:
161     std::condition_variable cv;
162     std::mutex mutex;
163     bool received = false;
164     float mLuma = -0.0f;
165 };
166 
167 // Hoisted to TestSuite setup to avoid flake in test (b/124675919)
168 std::unique_ptr<ChoreographerSim> gChoreographerSim = nullptr;
169 
170 struct RegionSamplingTest : ::testing::Test {
171 protected:
RegionSamplingTestandroid::test::RegionSamplingTest172     RegionSamplingTest() { ProcessState::self()->startThreadPool(); }
173 
SetUpTestSuiteandroid::test::RegionSamplingTest174     static void SetUpTestSuite() {
175         gChoreographerSim = ChoreographerSim::make();
176         ASSERT_NE(gChoreographerSim, nullptr);
177     }
178 
SetUpandroid::test::RegionSamplingTest179     void SetUp() override {
180         mSurfaceComposerClient = new SurfaceComposerClient;
181         ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck());
182 
183         mBackgroundLayer =
184                 mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0,
185                                                       0, PIXEL_FORMAT_RGBA_8888,
186                                                       ISurfaceComposerClient::eFXSurfaceEffect);
187         uint32_t layerPositionBottom = 0x7E000000;
188         SurfaceComposerClient::Transaction{}
189                 .setLayer(mBackgroundLayer, layerPositionBottom)
190                 .setPosition(mBackgroundLayer, 100, 100)
191                 .setColor(mBackgroundLayer, half3{0.5, 0.5, 0.5})
192                 .show(mBackgroundLayer)
193                 .apply();
194 
195         mContentLayer = mSurfaceComposerClient->createSurface(String8("Content RegionSamplingTest"),
196                                                               300, 300, PIXEL_FORMAT_RGBA_8888, 0);
197 
198         SurfaceComposerClient::Transaction{}
199                 .setLayer(mContentLayer, layerPositionBottom + 1)
200                 .setPosition(mContentLayer, 100, 100)
201                 .setColor(mContentLayer, half3{0.5, 0.5, 0.5})
202                 .show(mContentLayer)
203                 .apply();
204 
205         mTopLayer = mSurfaceComposerClient->createSurface(String8("TopLayer RegionSamplingTest"), 0,
206                                                           0, PIXEL_FORMAT_RGBA_8888, 0);
207         SurfaceComposerClient::Transaction{}
208                 .setLayer(mTopLayer, layerPositionBottom + 2)
209                 .setPosition(mTopLayer, 0, 0)
210                 .show(mBackgroundLayer)
211                 .apply();
212     }
213 
fill_renderandroid::test::RegionSamplingTest214     void fill_render(uint32_t rgba_value) {
215         auto surface = mContentLayer->getSurface();
216         ANativeWindow_Buffer outBuffer;
217         status_t status = surface->lock(&outBuffer, NULL);
218         ASSERT_EQ(status, android::OK);
219         auto b = reinterpret_cast<uint32_t*>(outBuffer.bits);
220         for (auto i = 0; i < outBuffer.height; i++) {
221             for (auto j = 0; j < outBuffer.width; j++) {
222                 b[j] = rgba_value;
223             }
224             b += outBuffer.stride;
225         }
226 
227         gChoreographerSim->request_render_wait([&surface] { surface->unlockAndPost(); });
228     }
229 
230     sp<SurfaceComposerClient> mSurfaceComposerClient;
231     sp<SurfaceControl> mBackgroundLayer;
232     sp<SurfaceControl> mContentLayer;
233     sp<SurfaceControl> mTopLayer;
234 
235     uint32_t const rgba_green = 0xFF00FF00;
236     float const luma_green = 0.7152;
237     uint32_t const rgba_blue = 0xFFFF0000;
238     float const luma_blue = 0.0722;
239     float const error_margin = 0.01;
240     float const luma_gray = 0.50;
241 };
242 
TEST_F(RegionSamplingTest,invalidLayerHandle_doesNotCrash)243 TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) {
244     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
245     sp<Listener> listener = new Listener();
246     const Rect sampleArea{100, 100, 200, 200};
247     // Passing in composer service as the layer handle should not crash, we'll
248     // treat it as a layer that no longer exists and silently allow sampling to
249     // occur.
250     status_t status = composer->addRegionSamplingListener(sampleArea,
251                                                           IInterface::asBinder(composer), listener);
252     ASSERT_EQ(NO_ERROR, status);
253     composer->removeRegionSamplingListener(listener);
254 }
255 
TEST_F(RegionSamplingTest,DISABLED_CollectsLuma)256 TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
257     fill_render(rgba_green);
258 
259     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
260     sp<Listener> listener = new Listener();
261     const Rect sampleArea{100, 100, 200, 200};
262     composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
263 
264     EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
265     EXPECT_NEAR(listener->luma(), luma_green, error_margin);
266 
267     composer->removeRegionSamplingListener(listener);
268 }
269 
TEST_F(RegionSamplingTest,DISABLED_CollectsChangingLuma)270 TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) {
271     fill_render(rgba_green);
272 
273     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
274     sp<Listener> listener = new Listener();
275     const Rect sampleArea{100, 100, 200, 200};
276     composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
277 
278     EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
279     EXPECT_NEAR(listener->luma(), luma_green, error_margin);
280 
281     listener->reset();
282 
283     fill_render(rgba_blue);
284     EXPECT_TRUE(listener->wait_event(300ms))
285             << "timed out waiting for 2nd luma event to be received";
286     EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
287 
288     composer->removeRegionSamplingListener(listener);
289 }
290 
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromTwoRegions)291 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) {
292     fill_render(rgba_green);
293     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
294     sp<Listener> greenListener = new Listener();
295     const Rect greenSampleArea{100, 100, 200, 200};
296     composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener);
297 
298     sp<Listener> grayListener = new Listener();
299     const Rect graySampleArea{500, 100, 600, 200};
300     composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener);
301 
302     EXPECT_TRUE(grayListener->wait_event(300ms))
303             << "timed out waiting for luma event to be received";
304     EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin);
305     EXPECT_TRUE(greenListener->wait_event(300ms))
306             << "timed out waiting for luma event to be received";
307     EXPECT_NEAR(greenListener->luma(), luma_green, error_margin);
308 
309     composer->removeRegionSamplingListener(greenListener);
310     composer->removeRegionSamplingListener(grayListener);
311 }
312 
TEST_F(RegionSamplingTest,DISABLED_TestIfInvalidInputParameters)313 TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) {
314     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
315     sp<Listener> listener = new Listener();
316     const Rect sampleArea{100, 100, 200, 200};
317     // Invalid input sampleArea
318     EXPECT_EQ(BAD_VALUE,
319               composer->addRegionSamplingListener(Rect::INVALID_RECT, mTopLayer->getHandle(),
320                                                   listener));
321     listener->reset();
322     // Invalid input binder
323     EXPECT_EQ(NO_ERROR, composer->addRegionSamplingListener(sampleArea, NULL, listener));
324     // Invalid input listener
325     EXPECT_EQ(BAD_VALUE,
326               composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), NULL));
327     EXPECT_EQ(BAD_VALUE, composer->removeRegionSamplingListener(NULL));
328     // remove the listener
329     composer->removeRegionSamplingListener(listener);
330 }
331 
TEST_F(RegionSamplingTest,DISABLED_TestCallbackAfterRemoveListener)332 TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) {
333     fill_render(rgba_green);
334     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
335     sp<Listener> listener = new Listener();
336     const Rect sampleArea{100, 100, 200, 200};
337     composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
338     fill_render(rgba_green);
339 
340     EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
341     EXPECT_NEAR(listener->luma(), luma_green, error_margin);
342 
343     listener->reset();
344     composer->removeRegionSamplingListener(listener);
345     fill_render(rgba_green);
346     EXPECT_FALSE(listener->wait_event(100ms))
347             << "callback should stop after remove the region sampling listener";
348 }
349 
TEST_F(RegionSamplingTest,DISABLED_CollectsLumaFromMovingLayer)350 TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) {
351     sp<ISurfaceComposer> composer = ComposerService::getComposerService();
352     sp<Listener> listener = new Listener();
353     Rect sampleArea{100, 100, 200, 200};
354 
355     // Test: listener in (100, 100). See layer before move, no layer after move.
356     fill_render(rgba_blue);
357     composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
358     EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
359     EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
360     listener->reset();
361     SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
362     EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
363     EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
364     composer->removeRegionSamplingListener(listener);
365 
366     // Test: listener offset to (600, 600). No layer before move, see layer after move.
367     fill_render(rgba_green);
368     sampleArea.offsetTo(600, 600);
369     composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
370     EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
371     EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
372     listener->reset();
373     SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
374     EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
375     EXPECT_NEAR(listener->luma(), luma_green, error_margin);
376     composer->removeRegionSamplingListener(listener);
377 }
378 
379 } // namespace android::test
380