1 #include "producer_channel.h"
2
3 #include <log/log.h>
4 #include <sync/sync.h>
5 #include <sys/epoll.h>
6 #include <sys/eventfd.h>
7 #include <sys/poll.h>
8 #include <utils/Trace.h>
9
10 #include <algorithm>
11 #include <atomic>
12 #include <thread>
13
14 #include <private/dvr/bufferhub_rpc.h>
15 #include "consumer_channel.h"
16
17 using android::pdx::BorrowedHandle;
18 using android::pdx::ErrorStatus;
19 using android::pdx::Message;
20 using android::pdx::RemoteChannelHandle;
21 using android::pdx::Status;
22 using android::pdx::rpc::BufferWrapper;
23 using android::pdx::rpc::DispatchRemoteMethod;
24 using android::pdx::rpc::WrapBuffer;
25
26 namespace android {
27 namespace dvr {
28
29 namespace {
30
FindNextClearedBit(uint64_t bits)31 static inline uint64_t FindNextClearedBit(uint64_t bits) {
32 return ~bits - (~bits & (~bits - 1));
33 }
34
35 } // namespace
36
ProducerChannel(BufferHubService * service,int channel_id,uint32_t width,uint32_t height,uint32_t layer_count,uint32_t format,uint64_t usage,size_t user_metadata_size,int * error)37 ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id,
38 uint32_t width, uint32_t height,
39 uint32_t layer_count, uint32_t format,
40 uint64_t usage, size_t user_metadata_size,
41 int* error)
42 : BufferHubChannel(service, channel_id, channel_id, kProducerType),
43 pending_consumers_(0),
44 producer_owns_(true),
45 user_metadata_size_(user_metadata_size),
46 metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize +
47 user_metadata_size) {
48 if (int ret = buffer_.Alloc(width, height, layer_count, format, usage)) {
49 ALOGE("ProducerChannel::ProducerChannel: Failed to allocate buffer: %s",
50 strerror(-ret));
51 *error = ret;
52 return;
53 }
54
55 if (int ret = metadata_buffer_.Alloc(metadata_buf_size_, /*height=*/1,
56 /*layer_count=*/1,
57 BufferHubDefs::kMetadataFormat,
58 BufferHubDefs::kMetadataUsage)) {
59 ALOGE("ProducerChannel::ProducerChannel: Failed to allocate metadata: %s",
60 strerror(-ret));
61 *error = ret;
62 return;
63 }
64
65 void* metadata_ptr = nullptr;
66 if (int ret = metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0,
67 /*y=*/0, metadata_buf_size_,
68 /*height=*/1, &metadata_ptr)) {
69 ALOGE("ProducerChannel::ProducerChannel: Failed to lock metadata.");
70 *error = -ret;
71 return;
72 }
73 metadata_header_ =
74 reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr);
75
76 // Using placement new here to reuse shared memory instead of new allocation
77 // and also initialize the value to zero.
78 buffer_state_ =
79 new (&metadata_header_->buffer_state) std::atomic<uint64_t>(0);
80 fence_state_ =
81 new (&metadata_header_->fence_state) std::atomic<uint64_t>(0);
82
83 acquire_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
84 release_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
85 if (!acquire_fence_fd_ || !release_fence_fd_) {
86 ALOGE("ProducerChannel::ProducerChannel: Failed to create shared fences.");
87 *error = -EIO;
88 return;
89 }
90
91 dummy_fence_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
92 if (!dummy_fence_fd_) {
93 ALOGE("ProducerChannel::ProducerChannel: Failed to create dummy fences.");
94 *error = -EIO;
95 return;
96 }
97
98 epoll_event event;
99 event.events = 0;
100 event.data.u64 = 0ULL;
101 if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_ADD, dummy_fence_fd_.Get(),
102 &event) < 0) {
103 ALOGE(
104 "ProducerChannel::ProducerChannel: Failed to modify the shared "
105 "release fence to include the dummy fence: %s",
106 strerror(errno));
107 *error = -EIO;
108 return;
109 }
110
111 // Success.
112 *error = 0;
113 }
114
Create(BufferHubService * service,int channel_id,uint32_t width,uint32_t height,uint32_t layer_count,uint32_t format,uint64_t usage,size_t user_metadata_size)115 Status<std::shared_ptr<ProducerChannel>> ProducerChannel::Create(
116 BufferHubService* service, int channel_id, uint32_t width, uint32_t height,
117 uint32_t layer_count, uint32_t format, uint64_t usage,
118 size_t user_metadata_size) {
119 int error;
120 std::shared_ptr<ProducerChannel> producer(
121 new ProducerChannel(service, channel_id, width, height, layer_count,
122 format, usage, user_metadata_size, &error));
123 if (error < 0)
124 return ErrorStatus(-error);
125 else
126 return {std::move(producer)};
127 }
128
~ProducerChannel()129 ProducerChannel::~ProducerChannel() {
130 ALOGD_IF(TRACE,
131 "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d "
132 "state=%" PRIx64 ".",
133 channel_id(), buffer_id(), buffer_state_->load());
134 for (auto consumer : consumer_channels_)
135 consumer->OnProducerClosed();
136 }
137
GetBufferInfo() const138 BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const {
139 // Derive the mask of signaled buffers in this producer / consumer set.
140 uint64_t signaled_mask = signaled() ? BufferHubDefs::kProducerStateBit : 0;
141 for (const ConsumerChannel* consumer : consumer_channels_) {
142 signaled_mask |= consumer->signaled() ? consumer->consumer_state_bit() : 0;
143 }
144
145 return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
146 buffer_.height(), buffer_.layer_count(), buffer_.format(),
147 buffer_.usage(), pending_consumers_, buffer_state_->load(),
148 signaled_mask, metadata_header_->queue_index, name_);
149 }
150
HandleImpulse(Message & message)151 void ProducerChannel::HandleImpulse(Message& message) {
152 ATRACE_NAME("ProducerChannel::HandleImpulse");
153 switch (message.GetOp()) {
154 case BufferHubRPC::ProducerGain::Opcode:
155 OnProducerGain(message);
156 break;
157 case BufferHubRPC::ProducerPost::Opcode:
158 OnProducerPost(message, {});
159 break;
160 }
161 }
162
HandleMessage(Message & message)163 bool ProducerChannel::HandleMessage(Message& message) {
164 ATRACE_NAME("ProducerChannel::HandleMessage");
165 switch (message.GetOp()) {
166 case BufferHubRPC::GetBuffer::Opcode:
167 DispatchRemoteMethod<BufferHubRPC::GetBuffer>(
168 *this, &ProducerChannel::OnGetBuffer, message);
169 return true;
170
171 case BufferHubRPC::NewConsumer::Opcode:
172 DispatchRemoteMethod<BufferHubRPC::NewConsumer>(
173 *this, &ProducerChannel::OnNewConsumer, message);
174 return true;
175
176 case BufferHubRPC::ProducerPost::Opcode:
177 DispatchRemoteMethod<BufferHubRPC::ProducerPost>(
178 *this, &ProducerChannel::OnProducerPost, message);
179 return true;
180
181 case BufferHubRPC::ProducerGain::Opcode:
182 DispatchRemoteMethod<BufferHubRPC::ProducerGain>(
183 *this, &ProducerChannel::OnProducerGain, message);
184 return true;
185
186 case BufferHubRPC::ProducerMakePersistent::Opcode:
187 DispatchRemoteMethod<BufferHubRPC::ProducerMakePersistent>(
188 *this, &ProducerChannel::OnProducerMakePersistent, message);
189 return true;
190
191 case BufferHubRPC::ProducerRemovePersistence::Opcode:
192 DispatchRemoteMethod<BufferHubRPC::ProducerRemovePersistence>(
193 *this, &ProducerChannel::OnRemovePersistence, message);
194 return true;
195
196 default:
197 return false;
198 }
199 }
200
GetBuffer(uint64_t buffer_state_bit)201 BufferDescription<BorrowedHandle> ProducerChannel::GetBuffer(
202 uint64_t buffer_state_bit) {
203 return {
204 buffer_, metadata_buffer_, buffer_id(),
205 buffer_state_bit, acquire_fence_fd_.Borrow(), release_fence_fd_.Borrow()};
206 }
207
OnGetBuffer(Message &)208 Status<BufferDescription<BorrowedHandle>> ProducerChannel::OnGetBuffer(
209 Message& /*message*/) {
210 ATRACE_NAME("ProducerChannel::OnGetBuffer");
211 ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx64 ".",
212 buffer_id(), buffer_state_->load());
213 return {GetBuffer(BufferHubDefs::kProducerStateBit)};
214 }
215
CreateConsumer(Message & message)216 Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) {
217 ATRACE_NAME("ProducerChannel::CreateConsumer");
218 ALOGD_IF(TRACE,
219 "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
220 buffer_id(), producer_owns_);
221
222 int channel_id;
223 auto status = message.PushChannel(0, nullptr, &channel_id);
224 if (!status) {
225 ALOGE(
226 "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
227 status.GetErrorMessage().c_str());
228 return ErrorStatus(ENOMEM);
229 }
230
231 // Try find the next consumer state bit which has not been claimed by any
232 // consumer yet.
233 uint64_t consumer_state_bit = FindNextClearedBit(
234 active_consumer_bit_mask_ | orphaned_consumer_bit_mask_ |
235 BufferHubDefs::kProducerStateBit);
236 if (consumer_state_bit == 0ULL) {
237 ALOGE(
238 "ProducerChannel::CreateConsumer: reached the maximum mumber of "
239 "consumers per producer: 63.");
240 return ErrorStatus(E2BIG);
241 }
242
243 auto consumer =
244 std::make_shared<ConsumerChannel>(service(), buffer_id(), channel_id,
245 consumer_state_bit, shared_from_this());
246 const auto channel_status = service()->SetChannel(channel_id, consumer);
247 if (!channel_status) {
248 ALOGE(
249 "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
250 "%s",
251 channel_status.GetErrorMessage().c_str());
252 return ErrorStatus(ENOMEM);
253 }
254
255 if (!producer_owns_ &&
256 !BufferHubDefs::IsBufferReleased(buffer_state_->load())) {
257 // Signal the new consumer when adding it to a posted producer.
258 if (consumer->OnProducerPosted())
259 pending_consumers_++;
260 }
261
262 active_consumer_bit_mask_ |= consumer_state_bit;
263 return {status.take()};
264 }
265
OnNewConsumer(Message & message)266 Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) {
267 ATRACE_NAME("ProducerChannel::OnNewConsumer");
268 ALOGD_IF(TRACE, "ProducerChannel::OnNewConsumer: buffer_id=%d", buffer_id());
269 return CreateConsumer(message);
270 }
271
OnProducerPost(Message &,LocalFence acquire_fence)272 Status<void> ProducerChannel::OnProducerPost(
273 Message&, LocalFence acquire_fence) {
274 ATRACE_NAME("ProducerChannel::OnProducerPost");
275 ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
276 if (!producer_owns_) {
277 ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
278 return ErrorStatus(EBUSY);
279 }
280
281 epoll_event event;
282 event.events = 0;
283 event.data.u64 = 0ULL;
284 int ret = epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
285 dummy_fence_fd_.Get(), &event);
286 ALOGE_IF(ret < 0,
287 "ProducerChannel::OnProducerPost: Failed to modify the shared "
288 "release fence to include the dummy fence: %s",
289 strerror(errno));
290
291 eventfd_t dummy_fence_count = 0ULL;
292 if (eventfd_read(dummy_fence_fd_.Get(), &dummy_fence_count) < 0) {
293 const int error = errno;
294 if (error != EAGAIN) {
295 ALOGE(
296 "ProducerChannel::ProducerChannel: Failed to read dummy fence, "
297 "error: %s",
298 strerror(error));
299 return ErrorStatus(error);
300 }
301 }
302
303 ALOGW_IF(dummy_fence_count > 0,
304 "ProducerChannel::ProducerChannel: %" PRIu64
305 " dummy fence(s) was signaled during last release/gain cycle "
306 "buffer_id=%d.",
307 dummy_fence_count, buffer_id());
308
309 post_fence_ = std::move(acquire_fence);
310 producer_owns_ = false;
311
312 // Signal any interested consumers. If there are none, the buffer will stay
313 // in posted state until a consumer comes online. This behavior guarantees
314 // that no frame is silently dropped.
315 pending_consumers_ = 0;
316 for (auto consumer : consumer_channels_) {
317 if (consumer->OnProducerPosted())
318 pending_consumers_++;
319 }
320 ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
321 pending_consumers_);
322
323 return {};
324 }
325
OnProducerGain(Message &)326 Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) {
327 ATRACE_NAME("ProducerChannel::OnGain");
328 ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
329 if (producer_owns_) {
330 ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
331 channel_id());
332 return ErrorStatus(EALREADY);
333 }
334
335 // There are still pending consumers, return busy.
336 if (pending_consumers_ > 0) {
337 ALOGE(
338 "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
339 "still has %d pending consumer(s).",
340 buffer_id(), pending_consumers_);
341 return ErrorStatus(EBUSY);
342 }
343
344 ClearAvailable();
345 producer_owns_ = true;
346 post_fence_.close();
347 return {std::move(returned_fence_)};
348 }
349
OnConsumerAcquire(Message &)350 Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) {
351 ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
352 ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
353 buffer_id());
354 if (producer_owns_) {
355 ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
356 return ErrorStatus(EBUSY);
357 }
358
359 // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
360 // Serialization just needs to read the handle.
361 return {std::move(post_fence_)};
362 }
363
OnConsumerRelease(Message &,LocalFence release_fence)364 Status<void> ProducerChannel::OnConsumerRelease(Message&,
365 LocalFence release_fence) {
366 ATRACE_NAME("ProducerChannel::OnConsumerRelease");
367 ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
368 buffer_id());
369 if (producer_owns_) {
370 ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
371 return ErrorStatus(EBUSY);
372 }
373
374 // Attempt to merge the fences if necessary.
375 if (release_fence) {
376 if (returned_fence_) {
377 LocalFence merged_fence(sync_merge("bufferhub_merged",
378 returned_fence_.get_fd(),
379 release_fence.get_fd()));
380 const int error = errno;
381 if (!merged_fence) {
382 ALOGE("ProducerChannel::OnConsumerRelease: Failed to merge fences: %s",
383 strerror(error));
384 return ErrorStatus(error);
385 }
386 returned_fence_ = std::move(merged_fence);
387 } else {
388 returned_fence_ = std::move(release_fence);
389 }
390 }
391
392 OnConsumerIgnored();
393 if (pending_consumers_ == 0) {
394 // Clear the producer bit atomically to transit into released state. This
395 // has to done by BufferHub as it requries synchronization among all
396 // consumers.
397 BufferHubDefs::ModifyBufferState(buffer_state_,
398 BufferHubDefs::kProducerStateBit, 0ULL);
399 ALOGD_IF(TRACE,
400 "ProducerChannel::OnConsumerRelease: releasing last consumer: "
401 "buffer_id=%d state=%" PRIx64 ".",
402 buffer_id(), buffer_state_->load());
403
404 if (orphaned_consumer_bit_mask_) {
405 ALOGW(
406 "ProducerChannel::OnConsumerRelease: orphaned buffer detected "
407 "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64
408 " queue_index=%" PRIu64 ".",
409 buffer_id(), orphaned_consumer_bit_mask_,
410 metadata_header_->queue_index);
411 orphaned_consumer_bit_mask_ = 0;
412 }
413
414 SignalAvailable();
415 }
416
417 ALOGE_IF(pending_consumers_ &&
418 BufferHubDefs::IsBufferReleased(buffer_state_->load()),
419 "ProducerChannel::OnConsumerRelease: buffer state inconsistent: "
420 "pending_consumers=%d, buffer buffer is in releaed state.",
421 pending_consumers_);
422 return {};
423 }
424
OnConsumerIgnored()425 void ProducerChannel::OnConsumerIgnored() {
426 if (pending_consumers_ == 0) {
427 ALOGE("ProducerChannel::OnConsumerIgnored: no pending consumer.");
428 return;
429 }
430
431 --pending_consumers_;
432 ALOGD_IF(TRACE,
433 "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left",
434 buffer_id(), pending_consumers_);
435 }
436
OnConsumerOrphaned(ConsumerChannel * channel)437 void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
438 // Ignore the orphaned consumer.
439 OnConsumerIgnored();
440
441 const uint64_t consumer_state_bit = channel->consumer_state_bit();
442 ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_bit,
443 "ProducerChannel::OnConsumerOrphaned: Consumer "
444 "(consumer_state_bit=%" PRIx64 ") is already orphaned.",
445 consumer_state_bit);
446 orphaned_consumer_bit_mask_ |= consumer_state_bit;
447
448 // Atomically clear the fence state bit as an orphaned consumer will never
449 // signal a release fence. Also clear the buffer state as it won't be released
450 // as well.
451 fence_state_->fetch_and(~consumer_state_bit);
452 BufferHubDefs::ModifyBufferState(buffer_state_, consumer_state_bit, 0ULL);
453
454 ALOGW(
455 "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer "
456 "buffer_id=%d consumer_state_bit=%" PRIx64 " queue_index=%" PRIu64
457 " buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".",
458 buffer_id(), consumer_state_bit, metadata_header_->queue_index,
459 buffer_state_->load(), fence_state_->load());
460 }
461
OnProducerMakePersistent(Message & message,const std::string & name,int user_id,int group_id)462 Status<void> ProducerChannel::OnProducerMakePersistent(Message& message,
463 const std::string& name,
464 int user_id,
465 int group_id) {
466 ATRACE_NAME("ProducerChannel::OnProducerMakePersistent");
467 ALOGD_IF(TRACE,
468 "ProducerChannel::OnProducerMakePersistent: buffer_id=%d name=%s "
469 "user_id=%d group_id=%d",
470 buffer_id(), name.c_str(), user_id, group_id);
471
472 if (name.empty() || (user_id < 0 && user_id != kNoCheckId) ||
473 (group_id < 0 && group_id != kNoCheckId)) {
474 return ErrorStatus(EINVAL);
475 }
476
477 // Try to add this buffer with the requested name.
478 if (service()->AddNamedBuffer(name, std::static_pointer_cast<ProducerChannel>(
479 shared_from_this()))) {
480 // If successful, set the requested permissions.
481
482 // A value of zero indicates that the ids from the sending process should be
483 // used.
484 if (user_id == kUseCallerId)
485 user_id = message.GetEffectiveUserId();
486 if (group_id == kUseCallerId)
487 group_id = message.GetEffectiveGroupId();
488
489 owner_user_id_ = user_id;
490 owner_group_id_ = group_id;
491 name_ = name;
492 return {};
493 } else {
494 // Otherwise a buffer with that name already exists.
495 return ErrorStatus(EALREADY);
496 }
497 }
498
OnRemovePersistence(Message &)499 Status<void> ProducerChannel::OnRemovePersistence(Message&) {
500 if (service()->RemoveNamedBuffer(*this))
501 return {};
502 else
503 return ErrorStatus(ENOENT);
504 }
505
AddConsumer(ConsumerChannel * channel)506 void ProducerChannel::AddConsumer(ConsumerChannel* channel) {
507 consumer_channels_.push_back(channel);
508 }
509
RemoveConsumer(ConsumerChannel * channel)510 void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) {
511 consumer_channels_.erase(
512 std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
513 active_consumer_bit_mask_ &= ~channel->consumer_state_bit();
514
515 const uint64_t buffer_state = buffer_state_->load();
516 if (BufferHubDefs::IsBufferPosted(buffer_state) ||
517 BufferHubDefs::IsBufferAcquired(buffer_state)) {
518 // The consumer client is being destoryed without releasing. This could
519 // happen in corner cases when the consumer crashes. Here we mark it
520 // orphaned before remove it from producer.
521 OnConsumerOrphaned(channel);
522 }
523
524 if (BufferHubDefs::IsBufferReleased(buffer_state) ||
525 BufferHubDefs::IsBufferGained(buffer_state)) {
526 // The consumer is being close while it is suppose to signal a release
527 // fence. Signal the dummy fence here.
528 if (fence_state_->load() & channel->consumer_state_bit()) {
529 epoll_event event;
530 event.events = EPOLLIN;
531 event.data.u64 = channel->consumer_state_bit();
532 if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
533 dummy_fence_fd_.Get(), &event) < 0) {
534 ALOGE(
535 "ProducerChannel::RemoveConsumer: Failed to modify the shared "
536 "release fence to include the dummy fence: %s",
537 strerror(errno));
538 return;
539 }
540 ALOGW(
541 "ProducerChannel::RemoveConsumer: signal dummy release fence "
542 "buffer_id=%d",
543 buffer_id());
544 eventfd_write(dummy_fence_fd_.Get(), 1);
545 }
546 }
547 }
548
549 // Returns true if either the user or group ids match the owning ids or both
550 // owning ids are not set, in which case access control does not apply.
CheckAccess(int euid,int egid)551 bool ProducerChannel::CheckAccess(int euid, int egid) {
552 const bool no_check =
553 owner_user_id_ == kNoCheckId && owner_group_id_ == kNoCheckId;
554 const bool euid_check = euid == owner_user_id_ || euid == kRootId;
555 const bool egid_check = egid == owner_group_id_ || egid == kRootId;
556 return no_check || euid_check || egid_check;
557 }
558
559 // Returns true if the given parameters match the underlying buffer parameters.
CheckParameters(uint32_t width,uint32_t height,uint32_t layer_count,uint32_t format,uint64_t usage,size_t user_metadata_size)560 bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
561 uint32_t layer_count, uint32_t format,
562 uint64_t usage,
563 size_t user_metadata_size) {
564 return user_metadata_size == user_metadata_size_ &&
565 buffer_.width() == width && buffer_.height() == height &&
566 buffer_.layer_count() == layer_count && buffer_.format() == format &&
567 buffer_.usage() == usage;
568 }
569
570 } // namespace dvr
571 } // namespace android
572