blob: 4eeebb442ab5c332bfd265ba0f7e1ee12e90c2a3 [file] [log] [blame]
Sungtak Leebbe37b62018-08-29 15:15:48 -07001/*
2 * Copyright (C) 2018 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 "BufferPoolClient"
18//#define LOG_NDEBUG 0
19
20#include <thread>
21#include <utils/Log.h>
22#include "BufferPoolClient.h"
23#include "Connection.h"
24
25namespace android {
26namespace hardware {
27namespace media {
28namespace bufferpool {
29namespace V2_0 {
30namespace implementation {
31
32static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms
33static constexpr int kPostMaxRetry = 3;
34static constexpr int kCacheTtlUs = 1000000; // TODO: tune
35
36class BufferPoolClient::Impl
37 : public std::enable_shared_from_this<BufferPoolClient::Impl> {
38public:
39 explicit Impl(const sp<Accessor> &accessor);
40
41 explicit Impl(const sp<IAccessor> &accessor);
42
43 bool isValid() {
44 return mValid;
45 }
46
47 bool isLocal() {
48 return mValid && mLocal;
49 }
50
51 ConnectionId getConnectionId() {
52 return mConnectionId;
53 }
54
55 sp<IAccessor> &getAccessor() {
56 return mAccessor;
57 }
58
59 bool isActive(int64_t *lastTransactionUs, bool clearCache);
60
61 ResultStatus allocate(const std::vector<uint8_t> &params,
62 native_handle_t **handle,
63 std::shared_ptr<BufferPoolData> *buffer);
64
65 ResultStatus receive(
66 TransactionId transactionId, BufferId bufferId,
67 int64_t timestampUs,
68 native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer);
69
70 void postBufferRelease(BufferId bufferId);
71
72 bool postSend(
73 BufferId bufferId, ConnectionId receiver,
74 TransactionId *transactionId, int64_t *timestampUs);
75private:
76
77 bool postReceive(
78 BufferId bufferId, TransactionId transactionId,
79 int64_t timestampUs);
80
81 bool postReceiveResult(
82 BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync);
83
84 void trySyncFromRemote();
85
86 bool syncReleased();
87
88 void evictCaches(bool clearCache = false);
89
90 ResultStatus allocateBufferHandle(
91 const std::vector<uint8_t>& params, BufferId *bufferId,
92 native_handle_t **handle);
93
94 ResultStatus fetchBufferHandle(
95 TransactionId transactionId, BufferId bufferId,
96 native_handle_t **handle);
97
98 struct BlockPoolDataDtor;
99 struct ClientBuffer;
100
101 bool mLocal;
102 bool mValid;
103 sp<IAccessor> mAccessor;
104 sp<Connection> mLocalConnection;
105 sp<IConnection> mRemoteConnection;
106 uint32_t mSeqId;
107 ConnectionId mConnectionId;
108 int64_t mLastEvictCacheUs;
109
110 // CachedBuffers
111 struct BufferCache {
112 std::mutex mLock;
113 bool mCreating;
114 std::condition_variable mCreateCv;
115 std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
116 int mActive;
117 int64_t mLastChangeUs;
118
119 BufferCache() : mCreating(false), mActive(0), mLastChangeUs(getTimestampNow()) {}
120
121 void incActive_l() {
122 ++mActive;
123 mLastChangeUs = getTimestampNow();
124 }
125
126 void decActive_l() {
127 --mActive;
128 mLastChangeUs = getTimestampNow();
129 }
130 } mCache;
131
132 // FMQ - release notifier
133 struct {
134 std::mutex mLock;
135 // TODO: use only one list?(using one list may dealy sending messages?)
136 std::list<BufferId> mReleasingIds;
137 std::list<BufferId> mReleasedIds;
138 std::unique_ptr<BufferStatusChannel> mStatusChannel;
139 } mReleasing;
140
141 // This lock is held during synchronization from remote side.
142 // In order to minimize remote calls and locking durtaion, this lock is held
143 // by best effort approach using try_lock().
144 std::mutex mRemoteSyncLock;
145};
146
147struct BufferPoolClient::Impl::BlockPoolDataDtor {
148 BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
149 : mImpl(impl) {}
150
151 void operator()(BufferPoolData *buffer) {
152 BufferId id = buffer->mId;
153 delete buffer;
154
155 auto impl = mImpl.lock();
156 if (impl && impl->isValid()) {
157 impl->postBufferRelease(id);
158 }
159 }
160 const std::weak_ptr<BufferPoolClient::Impl> mImpl;
161};
162
163struct BufferPoolClient::Impl::ClientBuffer {
164private:
165 bool mInvalidated; // TODO: implement
166 int64_t mExpireUs;
167 bool mHasCache;
168 ConnectionId mConnectionId;
169 BufferId mId;
170 native_handle_t *mHandle;
171 std::weak_ptr<BufferPoolData> mCache;
172
173 void updateExpire() {
174 mExpireUs = getTimestampNow() + kCacheTtlUs;
175 }
176
177public:
178 ClientBuffer(
179 ConnectionId connectionId, BufferId id, native_handle_t *handle)
180 : mInvalidated(false), mHasCache(false),
181 mConnectionId(connectionId), mId(id), mHandle(handle) {
182 (void)mInvalidated;
183 mExpireUs = getTimestampNow() + kCacheTtlUs;
184 }
185
186 ~ClientBuffer() {
187 if (mHandle) {
188 native_handle_close(mHandle);
189 native_handle_delete(mHandle);
190 }
191 }
192
193 bool expire() const {
194 int64_t now = getTimestampNow();
195 return now >= mExpireUs;
196 }
197
198 bool hasCache() const {
199 return mHasCache;
200 }
201
202 std::shared_ptr<BufferPoolData> fetchCache(native_handle_t **pHandle) {
203 if (mHasCache) {
204 std::shared_ptr<BufferPoolData> cache = mCache.lock();
205 if (cache) {
206 *pHandle = mHandle;
207 }
208 return cache;
209 }
210 return nullptr;
211 }
212
213 std::shared_ptr<BufferPoolData> createCache(
214 const std::shared_ptr<BufferPoolClient::Impl> &impl,
215 native_handle_t **pHandle) {
216 if (!mHasCache) {
217 // Allocates a raw ptr in order to avoid sending #postBufferRelease
218 // from deleter, in case of native_handle_clone failure.
219 BufferPoolData *ptr = new BufferPoolData(mConnectionId, mId);
220 if (ptr) {
221 std::shared_ptr<BufferPoolData> cache(ptr, BlockPoolDataDtor(impl));
222 if (cache) {
223 mCache = cache;
224 mHasCache = true;
225 *pHandle = mHandle;
226 return cache;
227 }
228 }
229 if (ptr) {
230 delete ptr;
231 }
232 }
233 return nullptr;
234 }
235
236 bool onCacheRelease() {
237 if (mHasCache) {
238 // TODO: verify mCache is not valid;
239 updateExpire();
240 mHasCache = false;
241 return true;
242 }
243 return false;
244 }
245};
246
247BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
248 : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0),
249 mLastEvictCacheUs(getTimestampNow()) {
Sungtak Lee1cb0ccb2018-09-05 14:47:36 -0700250 const StatusDescriptor *fmqDesc;
Sungtak Leebbe37b62018-08-29 15:15:48 -0700251 ResultStatus status = accessor->connect(
252 &mLocalConnection, &mConnectionId, &fmqDesc, true);
253 if (status == ResultStatus::OK) {
254 mReleasing.mStatusChannel =
255 std::make_unique<BufferStatusChannel>(*fmqDesc);
256 mValid = mReleasing.mStatusChannel &&
257 mReleasing.mStatusChannel->isValid();
258 }
259}
260
261BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
262 : mLocal(false), mValid(false), mAccessor(accessor), mSeqId(0),
263 mLastEvictCacheUs(getTimestampNow()) {
264 bool valid = false;
265 sp<IConnection>& outConnection = mRemoteConnection;
266 ConnectionId& id = mConnectionId;
267 std::unique_ptr<BufferStatusChannel>& outChannel =
268 mReleasing.mStatusChannel;
269 Return<void> transResult = accessor->connect(
270 [&valid, &outConnection, &id, &outChannel]
271 (ResultStatus status, sp<IConnection> connection,
Sungtak Lee1cb0ccb2018-09-05 14:47:36 -0700272 ConnectionId connectionId, const StatusDescriptor& desc,
273 const InvalidationDescriptor& invDesc) {
274 (void) invDesc;
Sungtak Leebbe37b62018-08-29 15:15:48 -0700275 if (status == ResultStatus::OK) {
276 outConnection = connection;
277 id = connectionId;
278 outChannel = std::make_unique<BufferStatusChannel>(desc);
279 if (outChannel && outChannel->isValid()) {
280 valid = true;
281 }
282 }
283 });
284 mValid = transResult.isOk() && valid;
285}
286
287bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) {
288 bool active = false;
289 {
290 std::lock_guard<std::mutex> lock(mCache.mLock);
291 syncReleased();
292 evictCaches(clearCache);
293 *lastTransactionUs = mCache.mLastChangeUs;
294 active = mCache.mActive > 0;
295 }
296 if (mValid && mLocal && mLocalConnection) {
297 mLocalConnection->cleanUp(clearCache);
298 return true;
299 }
300 return active;
301}
302
303ResultStatus BufferPoolClient::Impl::allocate(
304 const std::vector<uint8_t> &params,
305 native_handle_t **pHandle,
306 std::shared_ptr<BufferPoolData> *buffer) {
307 if (!mLocal || !mLocalConnection || !mValid) {
308 return ResultStatus::CRITICAL_ERROR;
309 }
310 BufferId bufferId;
311 native_handle_t *handle = nullptr;
312 buffer->reset();
313 ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
314 if (status == ResultStatus::OK) {
315 if (handle) {
316 std::unique_lock<std::mutex> lock(mCache.mLock);
317 syncReleased();
318 evictCaches();
319 auto cacheIt = mCache.mBuffers.find(bufferId);
320 if (cacheIt != mCache.mBuffers.end()) {
321 // TODO: verify it is recycled. (not having active ref)
322 mCache.mBuffers.erase(cacheIt);
323 }
324 auto clientBuffer = std::make_unique<ClientBuffer>(
325 mConnectionId, bufferId, handle);
326 if (clientBuffer) {
327 auto result = mCache.mBuffers.insert(std::make_pair(
328 bufferId, std::move(clientBuffer)));
329 if (result.second) {
330 *buffer = result.first->second->createCache(
331 shared_from_this(), pHandle);
332 if (*buffer) {
333 mCache.incActive_l();
334 }
335 }
336 }
337 }
338 if (!*buffer) {
339 ALOGV("client cache creation failure %d: %lld",
340 handle != nullptr, (long long)mConnectionId);
341 status = ResultStatus::NO_MEMORY;
342 postBufferRelease(bufferId);
343 }
344 }
345 return status;
346}
347
348ResultStatus BufferPoolClient::Impl::receive(
349 TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
350 native_handle_t **pHandle,
351 std::shared_ptr<BufferPoolData> *buffer) {
352 if (!mValid) {
353 return ResultStatus::CRITICAL_ERROR;
354 }
355 if (timestampUs != 0) {
356 timestampUs += kReceiveTimeoutUs;
357 }
358 if (!postReceive(bufferId, transactionId, timestampUs)) {
359 return ResultStatus::CRITICAL_ERROR;
360 }
361 ResultStatus status = ResultStatus::CRITICAL_ERROR;
362 buffer->reset();
363 while(1) {
364 std::unique_lock<std::mutex> lock(mCache.mLock);
365 syncReleased();
366 evictCaches();
367 auto cacheIt = mCache.mBuffers.find(bufferId);
368 if (cacheIt != mCache.mBuffers.end()) {
369 if (cacheIt->second->hasCache()) {
370 *buffer = cacheIt->second->fetchCache(pHandle);
371 if (!*buffer) {
372 // check transfer time_out
373 lock.unlock();
374 std::this_thread::yield();
375 continue;
376 }
377 ALOGV("client receive from reference %lld", (long long)mConnectionId);
378 break;
379 } else {
380 *buffer = cacheIt->second->createCache(shared_from_this(), pHandle);
381 if (*buffer) {
382 mCache.incActive_l();
383 }
384 ALOGV("client receive from cache %lld", (long long)mConnectionId);
385 break;
386 }
387 } else {
388 if (!mCache.mCreating) {
389 mCache.mCreating = true;
390 lock.unlock();
391 native_handle_t* handle = nullptr;
392 status = fetchBufferHandle(transactionId, bufferId, &handle);
393 lock.lock();
394 if (status == ResultStatus::OK) {
395 if (handle) {
396 auto clientBuffer = std::make_unique<ClientBuffer>(
397 mConnectionId, bufferId, handle);
398 if (clientBuffer) {
399 auto result = mCache.mBuffers.insert(
400 std::make_pair(bufferId, std::move(
401 clientBuffer)));
402 if (result.second) {
403 *buffer = result.first->second->createCache(
404 shared_from_this(), pHandle);
405 if (*buffer) {
406 mCache.incActive_l();
407 }
408 }
409 }
410 }
411 if (!*buffer) {
412 status = ResultStatus::NO_MEMORY;
413 }
414 }
415 mCache.mCreating = false;
416 lock.unlock();
417 mCache.mCreateCv.notify_all();
418 break;
419 }
420 mCache.mCreateCv.wait(lock);
421 }
422 }
423 bool needsSync = false;
424 bool posted = postReceiveResult(bufferId, transactionId,
425 *buffer ? true : false, &needsSync);
426 ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId,
427 *buffer ? "ok" : "fail", posted);
428 if (mValid && mLocal && mLocalConnection) {
429 mLocalConnection->cleanUp(false);
430 }
431 if (needsSync && mRemoteConnection) {
432 trySyncFromRemote();
433 }
434 if (*buffer) {
435 if (!posted) {
436 buffer->reset();
437 return ResultStatus::CRITICAL_ERROR;
438 }
439 return ResultStatus::OK;
440 }
441 return status;
442}
443
444
445void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
446 std::lock_guard<std::mutex> lock(mReleasing.mLock);
447 mReleasing.mReleasingIds.push_back(bufferId);
448 mReleasing.mStatusChannel->postBufferRelease(
449 mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
450}
451
452// TODO: revise ad-hoc posting data structure
453bool BufferPoolClient::Impl::postSend(
454 BufferId bufferId, ConnectionId receiver,
455 TransactionId *transactionId, int64_t *timestampUs) {
456 bool ret = false;
457 bool needsSync = false;
458 {
459 std::lock_guard<std::mutex> lock(mReleasing.mLock);
460 *timestampUs = getTimestampNow();
461 *transactionId = (mConnectionId << 32) | mSeqId++;
462 // TODO: retry, add timeout, target?
463 ret = mReleasing.mStatusChannel->postBufferStatusMessage(
464 *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
465 receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
466 needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
467 }
468 if (mValid && mLocal && mLocalConnection) {
469 mLocalConnection->cleanUp(false);
470 }
471 if (needsSync && mRemoteConnection) {
472 trySyncFromRemote();
473 }
474 return ret;
475}
476
477bool BufferPoolClient::Impl::postReceive(
478 BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
479 for (int i = 0; i < kPostMaxRetry; ++i) {
480 std::unique_lock<std::mutex> lock(mReleasing.mLock);
481 int64_t now = getTimestampNow();
482 if (timestampUs == 0 || now < timestampUs) {
483 bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
484 transactionId, bufferId, BufferStatus::TRANSFER_FROM,
485 mConnectionId, -1, mReleasing.mReleasingIds,
486 mReleasing.mReleasedIds);
487 if (result) {
488 return true;
489 }
490 lock.unlock();
491 std::this_thread::yield();
492 } else {
493 mReleasing.mStatusChannel->postBufferStatusMessage(
494 transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
495 mConnectionId, -1, mReleasing.mReleasingIds,
496 mReleasing.mReleasedIds);
497 return false;
498 }
499 }
500 return false;
501}
502
503bool BufferPoolClient::Impl::postReceiveResult(
504 BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) {
505 std::lock_guard<std::mutex> lock(mReleasing.mLock);
506 // TODO: retry, add timeout
507 bool ret = mReleasing.mStatusChannel->postBufferStatusMessage(
508 transactionId, bufferId,
509 result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
510 mConnectionId, -1, mReleasing.mReleasingIds,
511 mReleasing.mReleasedIds);
512 *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync();
513 return ret;
514}
515
516void BufferPoolClient::Impl::trySyncFromRemote() {
517 if (mRemoteSyncLock.try_lock()) {
518 bool needsSync = false;
519 {
520 std::lock_guard<std::mutex> lock(mReleasing.mLock);
521 needsSync = mReleasing.mStatusChannel->needsSync();
522 }
523 if (needsSync) {
524 TransactionId transactionId = (mConnectionId << 32);
525 BufferId bufferId = Connection::SYNC_BUFFERID;
526 Return<void> transResult = mRemoteConnection->fetch(
527 transactionId, bufferId,
528 []
529 (ResultStatus outStatus, Buffer outBuffer) {
530 (void) outStatus;
531 (void) outBuffer;
532 });
533 }
534 mRemoteSyncLock.unlock();
535 }
536}
537
538// should have mCache.mLock
539bool BufferPoolClient::Impl::syncReleased() {
540 std::lock_guard<std::mutex> lock(mReleasing.mLock);
541 if (mReleasing.mReleasingIds.size() > 0) {
542 mReleasing.mStatusChannel->postBufferRelease(
543 mConnectionId, mReleasing.mReleasingIds,
544 mReleasing.mReleasedIds);
545 }
546 if (mReleasing.mReleasedIds.size() > 0) {
547 for (BufferId& id: mReleasing.mReleasedIds) {
548 ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id);
549 auto found = mCache.mBuffers.find(id);
550 if (found != mCache.mBuffers.end()) {
551 if (found->second->onCacheRelease()) {
552 mCache.decActive_l();
553 } else {
554 // should not happen!
555 ALOGW("client %lld cache release status inconsitent!",
556 (long long)mConnectionId);
557 }
558 } else {
559 // should not happen!
560 ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId);
561 }
562 }
563 mReleasing.mReleasedIds.clear();
564 return true;
565 }
566 return false;
567}
568
569// should have mCache.mLock
570void BufferPoolClient::Impl::evictCaches(bool clearCache) {
571 int64_t now = getTimestampNow();
572 if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) {
573 size_t evicted = 0;
574 for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) {
575 if (!it->second->hasCache() && (it->second->expire() || clearCache)) {
576 it = mCache.mBuffers.erase(it);
577 ++evicted;
578 } else {
579 ++it;
580 }
581 }
582 ALOGV("cache count %lld : total %zu, active %d, evicted %zu",
583 (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted);
584 mLastEvictCacheUs = now;
585 }
586}
587
588ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
589 const std::vector<uint8_t>& params, BufferId *bufferId,
590 native_handle_t** handle) {
591 if (mLocalConnection) {
592 const native_handle_t* allocHandle = nullptr;
593 ResultStatus status = mLocalConnection->allocate(
594 params, bufferId, &allocHandle);
595 if (status == ResultStatus::OK) {
596 *handle = native_handle_clone(allocHandle);
597 }
598 ALOGV("client allocate result %lld %d : %u clone %p",
599 (long long)mConnectionId, status == ResultStatus::OK,
600 *handle ? *bufferId : 0 , *handle);
601 return status;
602 }
603 return ResultStatus::CRITICAL_ERROR;
604}
605
606ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
607 TransactionId transactionId, BufferId bufferId,
608 native_handle_t **handle) {
609 sp<IConnection> connection;
610 if (mLocal) {
611 connection = mLocalConnection;
612 } else {
613 connection = mRemoteConnection;
614 }
615 ResultStatus status;
616 Return<void> transResult = connection->fetch(
617 transactionId, bufferId,
618 [&status, &handle]
619 (ResultStatus outStatus, Buffer outBuffer) {
620 status = outStatus;
621 if (status == ResultStatus::OK) {
622 *handle = native_handle_clone(
623 outBuffer.buffer.getNativeHandle());
624 }
625 });
626 return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR;
627}
628
629
630BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
631 mImpl = std::make_shared<Impl>(accessor);
632}
633
634BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
635 mImpl = std::make_shared<Impl>(accessor);
636}
637
638BufferPoolClient::~BufferPoolClient() {
639 // TODO: how to handle orphaned buffers?
640}
641
642bool BufferPoolClient::isValid() {
643 return mImpl && mImpl->isValid();
644}
645
646bool BufferPoolClient::isLocal() {
647 return mImpl && mImpl->isLocal();
648}
649
650bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) {
651 if (!isValid()) {
652 *lastTransactionUs = 0;
653 return false;
654 }
655 return mImpl->isActive(lastTransactionUs, clearCache);
656}
657
658ConnectionId BufferPoolClient::getConnectionId() {
659 if (isValid()) {
660 return mImpl->getConnectionId();
661 }
662 return -1;
663}
664
665ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
666 if (isValid()) {
667 *accessor = mImpl->getAccessor();
668 return ResultStatus::OK;
669 }
670 return ResultStatus::CRITICAL_ERROR;
671}
672
673ResultStatus BufferPoolClient::allocate(
674 const std::vector<uint8_t> &params,
675 native_handle_t **handle,
676 std::shared_ptr<BufferPoolData> *buffer) {
677 if (isValid()) {
678 return mImpl->allocate(params, handle, buffer);
679 }
680 return ResultStatus::CRITICAL_ERROR;
681}
682
683ResultStatus BufferPoolClient::receive(
684 TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
685 native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) {
686 if (isValid()) {
687 return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer);
688 }
689 return ResultStatus::CRITICAL_ERROR;
690}
691
692ResultStatus BufferPoolClient::postSend(
693 ConnectionId receiverId,
694 const std::shared_ptr<BufferPoolData> &buffer,
695 TransactionId *transactionId,
696 int64_t *timestampUs) {
697 if (isValid()) {
698 bool result = mImpl->postSend(
699 buffer->mId, receiverId, transactionId, timestampUs);
700 return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
701 }
702 return ResultStatus::CRITICAL_ERROR;
703}
704
705} // namespace implementation
706} // namespace V2_0
707} // namespace bufferpool
708} // namespace media
709} // namespace hardware
710} // namespace android