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,
__anon7984c2760102() 74                  StreamBufferRequestError* status) {
75             return this->AllocateBufferFunc(num_buffer, buffers, status);
76           },
77       .return_func =
__anon7984c2760202() 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     hal_buffer_managed_stream_ids_.insert(kDummyCacheRegInfo.stream_id);
104     cache_manager_ =
105         StreamBufferCacheManager::Create(hal_buffer_managed_stream_ids_);
106     ASSERT_NE(cache_manager_, nullptr)
107         << " Creating StreamBufferCacheManager failed";
108   }
109 
110   // Set remaining_number_of_fulfillment_. This should be called before any
111   // other operations if the test needs to control this.
SetRemainingFulfillment(uint32_t remaining_num)112   void SetRemainingFulfillment(uint32_t remaining_num) {
113     remaining_number_of_fulfillment_ = remaining_num;
114   }
115 
116   // StreamBufferCacheManager created by this test fixture
117   std::unique_ptr<StreamBufferCacheManager> cache_manager_;
118 
119   // Counts the total number of buffers acquired by each stream id
120   std::map<int32_t, uint32_t> buffer_allocation_cnt_;
121 
122   // Counts the total number of buffers returned by each stream id
123   std::map<int32_t, uint32_t> buffer_return_cnt_;
124 
125   // Max number of requests that AllocateBufferFunc can fulfill. This is used to
126   // mock the failure of buffer provider from the framework.
127   uint32_t remaining_number_of_fulfillment_ = kDefaultRemainingFulfillment;
128 
129   // Number of times the ReturnBufferFunc is called.
130   int32_t num_return_buffer_func_called = 0;
131 
132   // Set of hal buffer managed stream ids
133   std::set<int32_t> hal_buffer_managed_stream_ids_;
134 };
135 
136 // Test RegisterStream
TEST_F(StreamBufferCacheManagerTests,RegisterStream)137 TEST_F(StreamBufferCacheManagerTests, RegisterStream) {
138   // RegisterStream should succeed
139   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
140   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
141 
142   // RegisterStream should fail when registering the same stream twice
143   res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
144   ASSERT_NE(res, OK) << " RegisterStream succeeded when registering the same "
145                         "stream for more than once!";
146 
147   // RegisterStream should succeed when registering another stream
148   StreamBufferCacheRegInfo another_reg_info = kDummyCacheRegInfo;
149   another_reg_info.stream_id = kDummyCacheRegInfo.stream_id + 1;
150   res = cache_manager_->RegisterStream(another_reg_info);
151   ASSERT_EQ(res, OK) << " RegisterStream another stream failed!"
152                      << strerror(res);
153 }
154 
155 // Test NotifyProviderReadiness
TEST_F(StreamBufferCacheManagerTests,NotifyProviderReadiness)156 TEST_F(StreamBufferCacheManagerTests, NotifyProviderReadiness) {
157   // Need to register stream before notifying provider readiness
158   status_t res =
159       cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
160   ASSERT_NE(res, OK) << " NotifyProviderReadiness succeeded without reigstering"
161                         " the stream.";
162 
163   res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
164   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
165 
166   // Notify ProviderReadiness should succeed after the stream is registered
167   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
168   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
169 }
170 
171 // Test the correct order of calling GetStreamBuffer
TEST_F(StreamBufferCacheManagerTests,BasicGetStreamBuffer)172 TEST_F(StreamBufferCacheManagerTests, BasicGetStreamBuffer) {
173   StreamBufferRequestResult req_result;
174   // GetStreamBuffer should fail before the stream is registered.
175   status_t res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
176                                                  &req_result);
177   ASSERT_NE(res, OK) << " GetStreamBuffer should fail before stream is "
178                         "registered and provider readiness is notified.";
179 
180   res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
181   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
182 
183   // GetStreamBuffer should fail before the stream's provider is notified for
184   // readiness.
185   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
186                                         &req_result);
187   ASSERT_NE(res, OK) << " GetStreamBuffer should fail before stream is "
188                         "registered and provider readiness is notified.";
189 
190   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
191   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
192 
193   // GetStreamBuffer should succeed after the stream is registered and its
194   // provider's readiness is notified.
195   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
196                                         &req_result);
197   ASSERT_EQ(res, OK) << " Getting stream buffer failed!" << strerror(res);
198 }
199 
200 // Test sequence of function call to GetStreamBuffer
TEST_F(StreamBufferCacheManagerTests,SequenceOfGetStreamBuffer)201 TEST_F(StreamBufferCacheManagerTests, SequenceOfGetStreamBuffer) {
202   const uint32_t kValidBufferRequests = 2;
203   SetRemainingFulfillment(kValidBufferRequests);
204   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
205   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
206 
207   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
208   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
209 
210   // Allow enough time for the buffer allocator to refill the cache
211   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
212 
213   // First GetStreamBuffer should succeed immediately with a non-dummy buffer
214   StreamBufferRequestResult req_result;
215   auto t_start = std::chrono::high_resolution_clock::now();
216   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
217                                         &req_result);
218   auto t_end = std::chrono::high_resolution_clock::now();
219   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
220   ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
221       << " First buffer request should be fulfilled immediately.";
222   ASSERT_EQ(req_result.is_dummy_buffer, false)
223       << " First buffer request got dummy buffer.";
224 
225   // Second GetStreamBuffer should succeed with a non-dummy buffer, but should
226   // happen after a gap longer than kBufferAcquireMinLatency.
227   t_start = std::chrono::high_resolution_clock::now();
228   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
229                                         &req_result);
230   t_end = std::chrono::high_resolution_clock::now();
231   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
232   ASSERT_EQ(true, t_end - t_start > kBufferAcquireMinLatency)
233       << " Buffer acquisition gap between two consecutive reqs is too small.";
234   ASSERT_EQ(req_result.is_dummy_buffer, false)
235       << " Second buffer request got dummy buffer.";
236 
237   // Allow enough time for the buffer allocator to refill the cache
238   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
239   // No more remaining fulfilment so StreamBufferCache should be either deactive
240   // or inactive.
241   bool is_active = false;
242   res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
243   ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
244   ASSERT_EQ(is_active, false)
245       << " StreamBufferCache should be either deactive or inactive!";
246 
247   // Third GetStreamBuffer should succeed with a dummy buffer immediately
248   t_start = std::chrono::high_resolution_clock::now();
249   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
250                                         &req_result);
251   t_end = std::chrono::high_resolution_clock::now();
252   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
253   ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
254       << " Buffer acquisition gap for a dummy return should be negligible.";
255   ASSERT_EQ(req_result.is_dummy_buffer, true)
256       << " Third buffer request did not get dummy buffer.";
257 }
258 
259 // Test NotifyFlushingAll
TEST_F(StreamBufferCacheManagerTests,NotifyFlushingAll)260 TEST_F(StreamBufferCacheManagerTests, NotifyFlushingAll) {
261   // One before the first GetStreamBuffer. One after that. One for the
262   // GetStreamBuffer happens after the NotifyFlushingAll.
263   const uint32_t kValidBufferRequests = 3;
264   SetRemainingFulfillment(kValidBufferRequests);
265   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
266   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
267 
268   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
269   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
270 
271   // Allow enough time for the buffer allocator to refill the cache
272   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
273 
274   // First GetStreamBuffer should succeed immediately with a non-dummy buffer
275   StreamBufferRequestResult req_result;
276   auto t_start = std::chrono::high_resolution_clock::now();
277   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
278                                         &req_result);
279   auto t_end = std::chrono::high_resolution_clock::now();
280   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
281   ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
282       << " First buffer request should be fulfilled immediately.";
283   ASSERT_EQ(req_result.is_dummy_buffer, false)
284       << " First buffer request got dummy buffer.";
285 
286   // Allow enough time for the buffer allocator to refill the cache
287   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
288   // NotifyFlushingAll should succeed
289   ASSERT_EQ(num_return_buffer_func_called, 0)
290       << " ReturnBufferFunc should not be called before NotifyFlushingAll!";
291   res = cache_manager_->NotifyFlushingAll();
292   ASSERT_EQ(res, OK) << " NotifyFlushingAll failed!" << strerror(res);
293   std::this_thread::sleep_for(kBufferReturnMaxLatency);
294   ASSERT_EQ(num_return_buffer_func_called, 1)
295       << " ReturnBufferFunc was not called after NotifyFlushingAll is invoked!";
296 
297   // GetStreamBuffer should still be able to re-trigger cache to refill after
298   // NotifyFlushingAll is called.
299   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
300                                         &req_result);
301   ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
302   ASSERT_EQ(req_result.is_dummy_buffer, false)
303       << " Buffer request got dummy buffer.";
304 }
305 
306 // Test IsStreamActive
TEST_F(StreamBufferCacheManagerTests,IsStreamActive)307 TEST_F(StreamBufferCacheManagerTests, IsStreamActive) {
308   const uint32_t kValidBufferRequests = 1;
309   SetRemainingFulfillment(kValidBufferRequests);
310   status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
311   ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
312 
313   res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
314   ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
315 
316   // Allow enough time for the buffer allocator to refill the cache
317   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
318 
319   // StreamBufferCache should be valid before dummy buffer is used.
320   bool is_active = false;
321   res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
322   ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
323   ASSERT_EQ(is_active, true) << " StreamBufferCache should be active!";
324 
325   StreamBufferRequestResult req_result;
326   res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
327                                         &req_result);
328 
329   // Allow enough time for buffer provider to finish its job
330   std::this_thread::sleep_for(kAllocateBufferFuncLatency);
331   // There is only one valid buffer request. So the stream will be deactive
332   // after the GetStreamBuffer(when the cache tries the second buffer request).
333   res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
334   ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
335   ASSERT_EQ(is_active, false) << " StreamBufferCache should be deactived!";
336 }
337 
338 }  // namespace google_camera_hal
339 }  // namespace android
340