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