1 /*
2  * Copyright (C) 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 #define LOG_TAG "StreamBufferCacheManagerTests"
18 #include <cutils/properties.h>
19 #include <gtest/gtest.h>
20 #include <log/log.h>
21 
22 #include <chrono>
23 #include <map>
24 #include <string>
25 #include <thread>
26 #include <unordered_set>
27 #include <vector>
28 
29 #include "stream_buffer_cache_manager.h"
30 
31 namespace android {
32 namespace google_camera_hal {
33 
34 using namespace std::chrono_literals;
35 
36 class StreamBufferCacheManagerTests : public ::testing::Test {
37  protected:
38   // This is used to mock the framework callback latency
39   static constexpr auto kAllocateBufferFuncLatency = 10ms;
40   // The minimum interval two successful buffer acquisition must have, this
41   // should be close to kAllocateBufferFuncLatency, but leave a safe gap(1ms)
42   // in case of timing fluctuation.
43   static constexpr auto kBufferAcquireMinLatency = 9ms;
44   // The maximum latency that the cached buffer is returned to the framework
45   static constexpr auto kBufferReturnMaxLatency = 5ms;
46   static const uint32_t kDefaultRemainingFulfillment = 2;
47 
AllocateBufferFunc(uint32_t num_buffer,std::vector<StreamBuffer> * buffers,StreamBufferRequestError * status)48   status_t AllocateBufferFunc(uint32_t num_buffer,
49                               std::vector<StreamBuffer>* buffers,
50                               StreamBufferRequestError* status) {
51     *status = StreamBufferRequestError::kOk;
52     buffers->clear();
53     if (remaining_number_of_fulfillment_ != 0) {
54       buffers->resize(num_buffer);
55     } else {
56       *status = StreamBufferRequestError::kStreamDisconnected;
57       return OK;
58     }
59 
60     // Mocking the framework callback latency
61     std::this_thread::sleep_for(kAllocateBufferFuncLatency);
62     remaining_number_of_fulfillment_--;
63     return OK;
64   }
65 
ReturnBufferFunc(const std::vector<StreamBuffer> &)66   status_t ReturnBufferFunc(const std::vector<StreamBuffer>& /*buffers*/) {
67     num_return_buffer_func_called++;
68     return OK;
69   }
70 
71   const StreamBufferCacheRegInfo kDummyCacheRegInfo{
72       .request_func =
73           [this](uint32_t num_buffer, std::vector<StreamBuffer>* buffers,
__anon64f806620102() 74                  StreamBufferRequestError* status) {
75             return this->AllocateBufferFunc(num_buffer, buffers, status);
76           },
77       .return_func =
__anon64f806620202() 78           [this](const std::vector<StreamBuffer>& buffers) {
79             return this->ReturnBufferFunc(buffers);
80           },
81       .stream_id = 1,
82       .width = 640,
83       .height = 480,
84       .format = HAL_PIXEL_FORMAT_RAW10,
85       .producer_flags = 0,
86       .consumer_flags = 0,
87       .num_buffers_to_cache = 1};
88 
SetUp()89   void SetUp() override {
90     // Skip test if product is unsupported.
91     char product_name[PROPERTY_VALUE_MAX];
92     // TODO(b/142732212): Flacky occurred,
93     // Remove "blueline", "crosshatch", "flame", "coral"
94     // from supported_product_list first.
95     std::unordered_set<std::string> const supported_product_list{""};
96     property_get("ro.build.product", product_name, "");
97     bool product_support_test =
98         supported_product_list.find(std::string{product_name}) !=
99         supported_product_list.end();
100     if (!product_support_test) {
101       GTEST_SKIP();
102     }
103 
104     cache_manager_ = StreamBufferCacheManager::Create();
105     ASSERT_NE(cache_manager_, nullptr)
106         << " Creating StreamBufferCacheManager failed";
107   }
108 
109   // Set remaining_number_of_fulfillment_. This should be called before any
110   // other operations if the test needs to control this.
SetRemainingFulfillment(uint32_t remaining_num)111   void SetRemainingFulfillment(uint32_t remaining_num) {
112     remaining_number_of_fulfillment_ = remaining_num;
113   }
114 
115   // StreamBufferCacheManager created by this test fixture
116   std::unique_ptr<StreamBufferCacheManager> cache_manager_;
117 
118   // Counts the total number of buffers acquired by each stream id
119   std::map<int32_t, uint32_t> buffer_allocation_cnt_;
120 
121   // Counts the total number of buffers returned by each stream id
122   std::map<int32_t, uint32_t> buffer_return_cnt_;
123 
124   // Max number of requests that AllocateBufferFunc can fulfill. This is used to
125   // mock the failure of buffer provider from the framework.
126   uint32_t remaining_number_of_fulfillment_ = kDefaultRemainingFulfillment;
127 
128   // Number of times the ReturnBufferFunc is called.
129   int32_t num_return_buffer_func_called = 0;
130 };
131 
132 // Test RegisterStream
TEST_F(StreamBufferCacheManagerTests,RegisterStream)133 TEST_F(StreamBufferCacheManagerTests, RegisterStream) {
134   // RegisterStream should succeed
135   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
136   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
137 
138   // RegisterStream should fail when registering the same stream twice
139   res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
140   ASSERT_NE(res, OK) << " RegisterStream succeeded when registering the same "
141                         "stream for more than once!";
142 
143   // RegisterStream should succeed when registering another stream
144   StreamBufferCacheRegInfo another_reg_info = kDummyCacheRegInfo;
145   another_reg_info.stream_id = kDummyCacheRegInfo.stream_id + 1;
146   res = cache_manager_->RegisterStream(another_reg_info);
147   ASSERT_EQ(res, OK) << " RegisterStream another stream failed!"
148                      << strerror(res);
149 }
150 
151 // Test NotifyProviderReadiness
TEST_F(StreamBufferCacheManagerTests,NotifyProviderReadiness)152 TEST_F(StreamBufferCacheManagerTests, NotifyProviderReadiness) {
153   // Need to register stream before notifying provider readiness
154   status_t res =
155       cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
156   ASSERT_NE(res, OK) << " NotifyProviderReadiness succeeded without reigstering"
157                         " the stream.";
158 
159   res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
160   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
161 
162   // Notify ProviderReadiness should succeed after the stream is registered
163   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
164   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
165 }
166 
167 // Test the correct order of calling GetStreamBuffer
TEST_F(StreamBufferCacheManagerTests,BasicGetStreamBuffer)168 TEST_F(StreamBufferCacheManagerTests, BasicGetStreamBuffer) {
169   StreamBufferRequestResult req_result;
170   // GetStreamBuffer should fail before the stream is registered.
171   status_t res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
172                                                  &req_result);
173   ASSERT_NE(res, OK) << " GetStreamBuffer should fail before stream is "
174                         "registered and provider readiness is notified.";
175 
176   res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
177   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
178 
179   // GetStreamBuffer should fail before the stream's provider is notified for
180   // readiness.
181   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
182                                         &req_result);
183   ASSERT_NE(res, OK) << " GetStreamBuffer should fail before stream is "
184                         "registered and provider readiness is notified.";
185 
186   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
187   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
188 
189   // GetStreamBuffer should succeed after the stream is registered and its
190   // provider's readiness is notified.
191   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
192                                         &req_result);
193   ASSERT_EQ(res, OK) << " Getting stream buffer failed!" << strerror(res);
194 }
195 
196 // Test sequence of function call to GetStreamBuffer
TEST_F(StreamBufferCacheManagerTests,SequenceOfGetStreamBuffer)197 TEST_F(StreamBufferCacheManagerTests, SequenceOfGetStreamBuffer) {
198   const uint32_t kValidBufferRequests = 2;
199   SetRemainingFulfillment(kValidBufferRequests);
200   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
201   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
202 
203   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
204   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
205 
206   // Allow enough time for the buffer allocator to refill the cache
207   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
208 
209   // First GetStreamBuffer should succeed immediately with a non-dummy buffer
210   StreamBufferRequestResult req_result;
211   auto t_start = std::chrono::high_resolution_clock::now();
212   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
213                                         &req_result);
214   auto t_end = std::chrono::high_resolution_clock::now();
215   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
216   ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
217       << " First buffer request should be fulfilled immediately.";
218   ASSERT_EQ(req_result.is_dummy_buffer, false)
219       << " First buffer request got dummy buffer.";
220 
221   // Second GetStreamBuffer should succeed with a non-dummy buffer, but should
222   // happen after a gap longer than kBufferAcquireMinLatency.
223   t_start = std::chrono::high_resolution_clock::now();
224   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
225                                         &req_result);
226   t_end = std::chrono::high_resolution_clock::now();
227   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
228   ASSERT_EQ(true, t_end - t_start > kBufferAcquireMinLatency)
229       << " Buffer acquisition gap between two consecutive reqs is too small.";
230   ASSERT_EQ(req_result.is_dummy_buffer, false)
231       << " Second buffer request got dummy buffer.";
232 
233   // Allow enough time for the buffer allocator to refill the cache
234   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
235   // No more remaining fulfilment so StreamBufferCache should be either deactive
236   // or inactive.
237   bool is_active = false;
238   res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
239   ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
240   ASSERT_EQ(is_active, false)
241       << " StreamBufferCache should be either deactive or inactive!";
242 
243   // Third GetStreamBuffer should succeed with a dummy buffer immediately
244   t_start = std::chrono::high_resolution_clock::now();
245   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
246                                         &req_result);
247   t_end = std::chrono::high_resolution_clock::now();
248   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
249   ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
250       << " Buffer acquisition gap for a dummy return should be negligible.";
251   ASSERT_EQ(req_result.is_dummy_buffer, true)
252       << " Third buffer request did not get dummy buffer.";
253 }
254 
255 // Test NotifyFlushingAll
TEST_F(StreamBufferCacheManagerTests,NotifyFlushingAll)256 TEST_F(StreamBufferCacheManagerTests, NotifyFlushingAll) {
257   // One before the first GetStreamBuffer. One after that. One for the
258   // GetStreamBuffer happens after the NotifyFlushingAll.
259   const uint32_t kValidBufferRequests = 3;
260   SetRemainingFulfillment(kValidBufferRequests);
261   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
262   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
263 
264   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
265   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
266 
267   // Allow enough time for the buffer allocator to refill the cache
268   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
269 
270   // First GetStreamBuffer should succeed immediately with a non-dummy buffer
271   StreamBufferRequestResult req_result;
272   auto t_start = std::chrono::high_resolution_clock::now();
273   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
274                                         &req_result);
275   auto t_end = std::chrono::high_resolution_clock::now();
276   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
277   ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
278       << " First buffer request should be fulfilled immediately.";
279   ASSERT_EQ(req_result.is_dummy_buffer, false)
280       << " First buffer request got dummy buffer.";
281 
282   // Allow enough time for the buffer allocator to refill the cache
283   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
284   // NotifyFlushingAll should succeed
285   ASSERT_EQ(num_return_buffer_func_called, 0)
286       << " ReturnBufferFunc should not be called before NotifyFlushingAll!";
287   res = cache_manager_->NotifyFlushingAll();
288   ASSERT_EQ(res, OK) << " NotifyFlushingAll failed!" << strerror(res);
289   std::this_thread::sleep_for(kBufferReturnMaxLatency);
290   ASSERT_EQ(num_return_buffer_func_called, 1)
291       << " ReturnBufferFunc was not called after NotifyFlushingAll is invoked!";
292 
293   // GetStreamBuffer should still be able to re-trigger cache to refill after
294   // NotifyFlushingAll is called.
295   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
296                                         &req_result);
297   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
298   ASSERT_EQ(req_result.is_dummy_buffer, false)
299       << " Buffer request got dummy buffer.";
300 }
301 
302 // Test IsStreamActive
TEST_F(StreamBufferCacheManagerTests,IsStreamActive)303 TEST_F(StreamBufferCacheManagerTests, IsStreamActive) {
304   const uint32_t kValidBufferRequests = 1;
305   SetRemainingFulfillment(kValidBufferRequests);
306   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
307   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
308 
309   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
310   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
311 
312   // Allow enough time for the buffer allocator to refill the cache
313   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
314 
315   // StreamBufferCache should be valid before dummy buffer is used.
316   bool is_active = false;
317   res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
318   ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
319   ASSERT_EQ(is_active, true) << " StreamBufferCache should be active!";
320 
321   StreamBufferRequestResult req_result;
322   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
323                                         &req_result);
324 
325   // Allow enough time for buffer provider to finish its job
326   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
327   // There is only one valid buffer request. So the stream will be deactive
328   // after the GetStreamBuffer(when the cache tries the second buffer request).
329   res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
330   ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
331   ASSERT_EQ(is_active, false) << " StreamBufferCache should be deactived!";
332 }
333 
334 }  // namespace google_camera_hal
335 }  // namespace android
336