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