1 /* 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 25 namespace android { 26 namespace hardware { 27 namespace media { 28 namespace bufferpool { 29 namespace V1_0 { 30 namespace implementation { 31 32 static constexpr int64_t kReceiveTimeoutUs = 1000000; // 100ms 33 static constexpr int kPostMaxRetry = 3; 34 static constexpr int kCacheTtlUs = 1000000; // TODO: tune 35 36 class BufferPoolClient::Impl 37 : public std::enable_shared_from_this<BufferPoolClient::Impl> { 38 public: 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> ¶ms, 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); 75 private: 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 147 struct 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 163 struct BufferPoolClient::Impl::ClientBuffer { 164 private: 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 177 public: 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 247 BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor) 248 : mLocal(true), mValid(false), mAccessor(accessor), mSeqId(0), 249 mLastEvictCacheUs(getTimestampNow()) { 250 const QueueDescriptor *fmqDesc; 251 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 261 BufferPoolClient::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, 272 ConnectionId connectionId, const QueueDescriptor& desc) { 273 if (status == ResultStatus::OK) { 274 outConnection = connection; 275 id = connectionId; 276 outChannel = std::make_unique<BufferStatusChannel>(desc); 277 if (outChannel && outChannel->isValid()) { 278 valid = true; 279 } 280 } 281 }); 282 mValid = transResult.isOk() && valid; 283 } 284 285 bool BufferPoolClient::Impl::isActive(int64_t *lastTransactionUs, bool clearCache) { 286 bool active = false; 287 { 288 std::lock_guard<std::mutex> lock(mCache.mLock); 289 syncReleased(); 290 evictCaches(clearCache); 291 *lastTransactionUs = mCache.mLastChangeUs; 292 active = mCache.mActive > 0; 293 } 294 if (mValid && mLocal && mLocalConnection) { 295 mLocalConnection->cleanUp(clearCache); 296 return true; 297 } 298 return active; 299 } 300 301 ResultStatus BufferPoolClient::Impl::allocate( 302 const std::vector<uint8_t> ¶ms, 303 native_handle_t **pHandle, 304 std::shared_ptr<BufferPoolData> *buffer) { 305 if (!mLocal || !mLocalConnection || !mValid) { 306 return ResultStatus::CRITICAL_ERROR; 307 } 308 BufferId bufferId; 309 native_handle_t *handle = nullptr; 310 buffer->reset(); 311 ResultStatus status = allocateBufferHandle(params, &bufferId, &handle); 312 if (status == ResultStatus::OK) { 313 if (handle) { 314 std::unique_lock<std::mutex> lock(mCache.mLock); 315 syncReleased(); 316 evictCaches(); 317 auto cacheIt = mCache.mBuffers.find(bufferId); 318 if (cacheIt != mCache.mBuffers.end()) { 319 // TODO: verify it is recycled. (not having active ref) 320 mCache.mBuffers.erase(cacheIt); 321 } 322 auto clientBuffer = std::make_unique<ClientBuffer>( 323 mConnectionId, bufferId, handle); 324 if (clientBuffer) { 325 auto result = mCache.mBuffers.insert(std::make_pair( 326 bufferId, std::move(clientBuffer))); 327 if (result.second) { 328 *buffer = result.first->second->createCache( 329 shared_from_this(), pHandle); 330 if (*buffer) { 331 mCache.incActive_l(); 332 } 333 } 334 } 335 } 336 if (!*buffer) { 337 ALOGV("client cache creation failure %d: %lld", 338 handle != nullptr, (long long)mConnectionId); 339 status = ResultStatus::NO_MEMORY; 340 postBufferRelease(bufferId); 341 } 342 } 343 return status; 344 } 345 346 ResultStatus BufferPoolClient::Impl::receive( 347 TransactionId transactionId, BufferId bufferId, int64_t timestampUs, 348 native_handle_t **pHandle, 349 std::shared_ptr<BufferPoolData> *buffer) { 350 if (!mValid) { 351 return ResultStatus::CRITICAL_ERROR; 352 } 353 if (timestampUs != 0) { 354 timestampUs += kReceiveTimeoutUs; 355 } 356 if (!postReceive(bufferId, transactionId, timestampUs)) { 357 return ResultStatus::CRITICAL_ERROR; 358 } 359 ResultStatus status = ResultStatus::CRITICAL_ERROR; 360 buffer->reset(); 361 while(1) { 362 std::unique_lock<std::mutex> lock(mCache.mLock); 363 syncReleased(); 364 evictCaches(); 365 auto cacheIt = mCache.mBuffers.find(bufferId); 366 if (cacheIt != mCache.mBuffers.end()) { 367 if (cacheIt->second->hasCache()) { 368 *buffer = cacheIt->second->fetchCache(pHandle); 369 if (!*buffer) { 370 // check transfer time_out 371 lock.unlock(); 372 std::this_thread::yield(); 373 continue; 374 } 375 ALOGV("client receive from reference %lld", (long long)mConnectionId); 376 break; 377 } else { 378 *buffer = cacheIt->second->createCache(shared_from_this(), pHandle); 379 if (*buffer) { 380 mCache.incActive_l(); 381 } 382 ALOGV("client receive from cache %lld", (long long)mConnectionId); 383 break; 384 } 385 } else { 386 if (!mCache.mCreating) { 387 mCache.mCreating = true; 388 lock.unlock(); 389 native_handle_t* handle = nullptr; 390 status = fetchBufferHandle(transactionId, bufferId, &handle); 391 lock.lock(); 392 if (status == ResultStatus::OK) { 393 if (handle) { 394 auto clientBuffer = std::make_unique<ClientBuffer>( 395 mConnectionId, bufferId, handle); 396 if (clientBuffer) { 397 auto result = mCache.mBuffers.insert( 398 std::make_pair(bufferId, std::move( 399 clientBuffer))); 400 if (result.second) { 401 *buffer = result.first->second->createCache( 402 shared_from_this(), pHandle); 403 if (*buffer) { 404 mCache.incActive_l(); 405 } 406 } 407 } 408 } 409 if (!*buffer) { 410 status = ResultStatus::NO_MEMORY; 411 } 412 } 413 mCache.mCreating = false; 414 lock.unlock(); 415 mCache.mCreateCv.notify_all(); 416 break; 417 } 418 mCache.mCreateCv.wait(lock); 419 } 420 } 421 bool needsSync = false; 422 bool posted = postReceiveResult(bufferId, transactionId, 423 *buffer ? true : false, &needsSync); 424 ALOGV("client receive %lld - %u : %s (%d)", (long long)mConnectionId, bufferId, 425 *buffer ? "ok" : "fail", posted); 426 if (mValid && mLocal && mLocalConnection) { 427 mLocalConnection->cleanUp(false); 428 } 429 if (needsSync && mRemoteConnection) { 430 trySyncFromRemote(); 431 } 432 if (*buffer) { 433 if (!posted) { 434 buffer->reset(); 435 return ResultStatus::CRITICAL_ERROR; 436 } 437 return ResultStatus::OK; 438 } 439 return status; 440 } 441 442 443 void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) { 444 std::lock_guard<std::mutex> lock(mReleasing.mLock); 445 mReleasing.mReleasingIds.push_back(bufferId); 446 mReleasing.mStatusChannel->postBufferRelease( 447 mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds); 448 } 449 450 // TODO: revise ad-hoc posting data structure 451 bool BufferPoolClient::Impl::postSend( 452 BufferId bufferId, ConnectionId receiver, 453 TransactionId *transactionId, int64_t *timestampUs) { 454 bool ret = false; 455 bool needsSync = false; 456 { 457 std::lock_guard<std::mutex> lock(mReleasing.mLock); 458 *timestampUs = getTimestampNow(); 459 *transactionId = (mConnectionId << 32) | mSeqId++; 460 // TODO: retry, add timeout, target? 461 ret = mReleasing.mStatusChannel->postBufferStatusMessage( 462 *transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId, 463 receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds); 464 needsSync = !mLocal && mReleasing.mStatusChannel->needsSync(); 465 } 466 if (mValid && mLocal && mLocalConnection) { 467 mLocalConnection->cleanUp(false); 468 } 469 if (needsSync && mRemoteConnection) { 470 trySyncFromRemote(); 471 } 472 return ret; 473 } 474 475 bool BufferPoolClient::Impl::postReceive( 476 BufferId bufferId, TransactionId transactionId, int64_t timestampUs) { 477 for (int i = 0; i < kPostMaxRetry; ++i) { 478 std::unique_lock<std::mutex> lock(mReleasing.mLock); 479 int64_t now = getTimestampNow(); 480 if (timestampUs == 0 || now < timestampUs) { 481 bool result = mReleasing.mStatusChannel->postBufferStatusMessage( 482 transactionId, bufferId, BufferStatus::TRANSFER_FROM, 483 mConnectionId, -1, mReleasing.mReleasingIds, 484 mReleasing.mReleasedIds); 485 if (result) { 486 return true; 487 } 488 lock.unlock(); 489 std::this_thread::yield(); 490 } else { 491 mReleasing.mStatusChannel->postBufferStatusMessage( 492 transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT, 493 mConnectionId, -1, mReleasing.mReleasingIds, 494 mReleasing.mReleasedIds); 495 return false; 496 } 497 } 498 return false; 499 } 500 501 bool BufferPoolClient::Impl::postReceiveResult( 502 BufferId bufferId, TransactionId transactionId, bool result, bool *needsSync) { 503 std::lock_guard<std::mutex> lock(mReleasing.mLock); 504 // TODO: retry, add timeout 505 bool ret = mReleasing.mStatusChannel->postBufferStatusMessage( 506 transactionId, bufferId, 507 result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR, 508 mConnectionId, -1, mReleasing.mReleasingIds, 509 mReleasing.mReleasedIds); 510 *needsSync = !mLocal && mReleasing.mStatusChannel->needsSync(); 511 return ret; 512 } 513 514 void BufferPoolClient::Impl::trySyncFromRemote() { 515 if (mRemoteSyncLock.try_lock()) { 516 bool needsSync = false; 517 { 518 std::lock_guard<std::mutex> lock(mReleasing.mLock); 519 needsSync = mReleasing.mStatusChannel->needsSync(); 520 } 521 if (needsSync) { 522 TransactionId transactionId = (mConnectionId << 32); 523 BufferId bufferId = Connection::SYNC_BUFFERID; 524 Return<void> transResult = mRemoteConnection->fetch( 525 transactionId, bufferId, 526 [] 527 (ResultStatus outStatus, Buffer outBuffer) { 528 (void) outStatus; 529 (void) outBuffer; 530 }); 531 if(!transResult.isOk()) { 532 ALOGD("sync from client %lld failed: bufferpool process died.", 533 (long long)mConnectionId); 534 } 535 } 536 mRemoteSyncLock.unlock(); 537 } 538 } 539 540 // should have mCache.mLock 541 bool BufferPoolClient::Impl::syncReleased() { 542 std::lock_guard<std::mutex> lock(mReleasing.mLock); 543 if (mReleasing.mReleasingIds.size() > 0) { 544 mReleasing.mStatusChannel->postBufferRelease( 545 mConnectionId, mReleasing.mReleasingIds, 546 mReleasing.mReleasedIds); 547 } 548 if (mReleasing.mReleasedIds.size() > 0) { 549 for (BufferId& id: mReleasing.mReleasedIds) { 550 ALOGV("client release buffer %lld - %u", (long long)mConnectionId, id); 551 auto found = mCache.mBuffers.find(id); 552 if (found != mCache.mBuffers.end()) { 553 if (found->second->onCacheRelease()) { 554 mCache.decActive_l(); 555 } else { 556 // should not happen! 557 ALOGW("client %lld cache release status inconsitent!", 558 (long long)mConnectionId); 559 } 560 } else { 561 // should not happen! 562 ALOGW("client %lld cache status inconsitent!", (long long)mConnectionId); 563 } 564 } 565 mReleasing.mReleasedIds.clear(); 566 return true; 567 } 568 return false; 569 } 570 571 // should have mCache.mLock 572 void BufferPoolClient::Impl::evictCaches(bool clearCache) { 573 int64_t now = getTimestampNow(); 574 if (now >= mLastEvictCacheUs + kCacheTtlUs || clearCache) { 575 size_t evicted = 0; 576 for (auto it = mCache.mBuffers.begin(); it != mCache.mBuffers.end();) { 577 if (!it->second->hasCache() && (it->second->expire() || clearCache)) { 578 it = mCache.mBuffers.erase(it); 579 ++evicted; 580 } else { 581 ++it; 582 } 583 } 584 ALOGV("cache count %lld : total %zu, active %d, evicted %zu", 585 (long long)mConnectionId, mCache.mBuffers.size(), mCache.mActive, evicted); 586 mLastEvictCacheUs = now; 587 } 588 } 589 590 ResultStatus BufferPoolClient::Impl::allocateBufferHandle( 591 const std::vector<uint8_t>& params, BufferId *bufferId, 592 native_handle_t** handle) { 593 if (mLocalConnection) { 594 const native_handle_t* allocHandle = nullptr; 595 ResultStatus status = mLocalConnection->allocate( 596 params, bufferId, &allocHandle); 597 if (status == ResultStatus::OK) { 598 *handle = native_handle_clone(allocHandle); 599 } 600 ALOGV("client allocate result %lld %d : %u clone %p", 601 (long long)mConnectionId, status == ResultStatus::OK, 602 *handle ? *bufferId : 0 , *handle); 603 return status; 604 } 605 return ResultStatus::CRITICAL_ERROR; 606 } 607 608 ResultStatus BufferPoolClient::Impl::fetchBufferHandle( 609 TransactionId transactionId, BufferId bufferId, 610 native_handle_t **handle) { 611 sp<IConnection> connection; 612 if (mLocal) { 613 connection = mLocalConnection; 614 } else { 615 connection = mRemoteConnection; 616 } 617 ResultStatus status; 618 Return<void> transResult = connection->fetch( 619 transactionId, bufferId, 620 [&status, &handle] 621 (ResultStatus outStatus, Buffer outBuffer) { 622 status = outStatus; 623 if (status == ResultStatus::OK) { 624 *handle = native_handle_clone( 625 outBuffer.buffer.getNativeHandle()); 626 } 627 }); 628 return transResult.isOk() ? status : ResultStatus::CRITICAL_ERROR; 629 } 630 631 632 BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) { 633 mImpl = std::make_shared<Impl>(accessor); 634 } 635 636 BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) { 637 mImpl = std::make_shared<Impl>(accessor); 638 } 639 640 BufferPoolClient::~BufferPoolClient() { 641 // TODO: how to handle orphaned buffers? 642 } 643 644 bool BufferPoolClient::isValid() { 645 return mImpl && mImpl->isValid(); 646 } 647 648 bool BufferPoolClient::isLocal() { 649 return mImpl && mImpl->isLocal(); 650 } 651 652 bool BufferPoolClient::isActive(int64_t *lastTransactionUs, bool clearCache) { 653 if (!isValid()) { 654 *lastTransactionUs = 0; 655 return false; 656 } 657 return mImpl->isActive(lastTransactionUs, clearCache); 658 } 659 660 ConnectionId BufferPoolClient::getConnectionId() { 661 if (isValid()) { 662 return mImpl->getConnectionId(); 663 } 664 return -1; 665 } 666 667 ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) { 668 if (isValid()) { 669 *accessor = mImpl->getAccessor(); 670 return ResultStatus::OK; 671 } 672 return ResultStatus::CRITICAL_ERROR; 673 } 674 675 ResultStatus BufferPoolClient::allocate( 676 const std::vector<uint8_t> ¶ms, 677 native_handle_t **handle, 678 std::shared_ptr<BufferPoolData> *buffer) { 679 if (isValid()) { 680 return mImpl->allocate(params, handle, buffer); 681 } 682 return ResultStatus::CRITICAL_ERROR; 683 } 684 685 ResultStatus BufferPoolClient::receive( 686 TransactionId transactionId, BufferId bufferId, int64_t timestampUs, 687 native_handle_t **handle, std::shared_ptr<BufferPoolData> *buffer) { 688 if (isValid()) { 689 return mImpl->receive(transactionId, bufferId, timestampUs, handle, buffer); 690 } 691 return ResultStatus::CRITICAL_ERROR; 692 } 693 694 ResultStatus BufferPoolClient::postSend( 695 ConnectionId receiverId, 696 const std::shared_ptr<BufferPoolData> &buffer, 697 TransactionId *transactionId, 698 int64_t *timestampUs) { 699 if (isValid()) { 700 bool result = mImpl->postSend( 701 buffer->mId, receiverId, transactionId, timestampUs); 702 return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR; 703 } 704 return ResultStatus::CRITICAL_ERROR; 705 } 706 707 } // namespace implementation 708 } // namespace V1_0 709 } // namespace bufferpool 710 } // namespace media 711 } // namespace hardware 712 } // namespace android 713