BufferQueueConsumer.cpp revision 4cbf3c53447215a31f69d3ac615ba0daa902f717
156c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks/* 256c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * Copyright 2014 The Android Open Source Project 356c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * 456c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * Licensed under the Apache License, Version 2.0 (the "License"); 556c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * you may not use this file except in compliance with the License. 656c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * You may obtain a copy of the License at 756c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * 856c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * http://www.apache.org/licenses/LICENSE-2.0 956c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks * 107df30109963092559d3760c0661a020f9daf1030The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 117df30109963092559d3760c0661a020f9daf1030The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 127df30109963092559d3760c0661a020f9daf1030The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137df30109963092559d3760c0661a020f9daf1030The Android Open Source Project * See the License for the specific language governing permissions and 147df30109963092559d3760c0661a020f9daf1030The Android Open Source Project * limitations under the License. 157df30109963092559d3760c0661a020f9daf1030The Android Open Source Project */ 167df30109963092559d3760c0661a020f9daf1030The Android Open Source Project 177df30109963092559d3760c0661a020f9daf1030The Android Open Source Project#define LOG_TAG "BufferQueueConsumer" 187df30109963092559d3760c0661a020f9daf1030The Android Open Source Project#define ATRACE_TAG ATRACE_TAG_GRAPHICS 197df30109963092559d3760c0661a020f9daf1030The Android Open Source Project//#define LOG_NDEBUG 0 207df30109963092559d3760c0661a020f9daf1030The Android Open Source Project 217df30109963092559d3760c0661a020f9daf1030The Android Open Source Project#include <gui/BufferItem.h> 2256c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks#include <gui/BufferQueueConsumer.h> 2356c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks#include <gui/BufferQueueCore.h> 2456c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks#include <gui/IConsumerListener.h> 2556c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks#include <gui/IProducerListener.h> 2656c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks 2756c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparksnamespace android { 2856c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks 2956c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave SparksBufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) : 3056c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks mCore(core), 3156c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks mSlots(core->mSlots), 3256c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks mConsumerName() {} 3356c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks 3456c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave SparksBufferQueueConsumer::~BufferQueueConsumer() {} 3556c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks 3656c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparksstatus_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, 3756c99cd2c2c1e6ab038dac5fced5b92ccf11ff6cDave Sparks nsecs_t expectedPresent) { 38 ATRACE_CALL(); 39 Mutex::Autolock lock(mCore->mMutex); 40 41 // Check that the consumer doesn't currently have the maximum number of 42 // buffers acquired. We allow the max buffer count to be exceeded by one 43 // buffer so that the consumer can successfully set up the newly acquired 44 // buffer before releasing the old one. 45 int numAcquiredBuffers = 0; 46 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { 47 if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) { 48 ++numAcquiredBuffers; 49 } 50 } 51 if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { 52 BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)", 53 numAcquiredBuffers, mCore->mMaxAcquiredBufferCount); 54 return INVALID_OPERATION; 55 } 56 57 // Check if the queue is empty. 58 // In asynchronous mode the list is guaranteed to be one buffer deep, 59 // while in synchronous mode we use the oldest buffer. 60 if (mCore->mQueue.empty()) { 61 return NO_BUFFER_AVAILABLE; 62 } 63 64 BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); 65 66 // If expectedPresent is specified, we may not want to return a buffer yet. 67 // If it's specified and there's more than one buffer queued, we may want 68 // to drop a buffer. 69 if (expectedPresent != 0) { 70 const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second 71 72 // The 'expectedPresent' argument indicates when the buffer is expected 73 // to be presented on-screen. If the buffer's desired present time is 74 // earlier (less) than expectedPresent -- meaning it will be displayed 75 // on time or possibly late if we show it as soon as possible -- we 76 // acquire and return it. If we don't want to display it until after the 77 // expectedPresent time, we return PRESENT_LATER without acquiring it. 78 // 79 // To be safe, we don't defer acquisition if expectedPresent is more 80 // than one second in the future beyond the desired present time 81 // (i.e., we'd be holding the buffer for a long time). 82 // 83 // NOTE: Code assumes monotonic time values from the system clock 84 // are positive. 85 86 // Start by checking to see if we can drop frames. We skip this check if 87 // the timestamps are being auto-generated by Surface. If the app isn't 88 // generating timestamps explicitly, it probably doesn't want frames to 89 // be discarded based on them. 90 while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) { 91 // If entry[1] is timely, drop entry[0] (and repeat). We apply an 92 // additional criterion here: we only drop the earlier buffer if our 93 // desiredPresent falls within +/- 1 second of the expected present. 94 // Otherwise, bogus desiredPresent times (e.g., 0 or a small 95 // relative timestamp), which normally mean "ignore the timestamp 96 // and acquire immediately", would cause us to drop frames. 97 // 98 // We may want to add an additional criterion: don't drop the 99 // earlier buffer if entry[1]'s fence hasn't signaled yet. 100 const BufferItem& bufferItem(mCore->mQueue[1]); 101 nsecs_t desiredPresent = bufferItem.mTimestamp; 102 if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC || 103 desiredPresent > expectedPresent) { 104 // This buffer is set to display in the near future, or 105 // desiredPresent is garbage. Either way we don't want to drop 106 // the previous buffer just to get this on the screen sooner. 107 BQ_LOGV("acquireBuffer: nodrop desire=%lld expect=%lld " 108 "(%lld) now=%lld", desiredPresent, expectedPresent, 109 desiredPresent - expectedPresent, 110 systemTime(CLOCK_MONOTONIC)); 111 break; 112 } 113 114 BQ_LOGV("acquireBuffer: drop desire=%lld expect=%lld size=%d", 115 desiredPresent, expectedPresent, mCore->mQueue.size()); 116 if (mCore->stillTracking(front)) { 117 // Front buffer is still in mSlots, so mark the slot as free 118 mSlots[front->mSlot].mBufferState = BufferSlot::FREE; 119 } 120 mCore->mQueue.erase(front); 121 front = mCore->mQueue.begin(); 122 } 123 124 // See if the front buffer is due 125 nsecs_t desiredPresent = front->mTimestamp; 126 if (desiredPresent > expectedPresent && 127 desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) { 128 BQ_LOGV("acquireBuffer: defer desire=%lld expect=%lld " 129 "(%lld) now=%lld", desiredPresent, expectedPresent, 130 desiredPresent - expectedPresent, 131 systemTime(CLOCK_MONOTONIC)); 132 return PRESENT_LATER; 133 } 134 135 BQ_LOGV("acquireBuffer: accept desire=%lld expect=%lld " 136 "(%lld) now=%lld", desiredPresent, expectedPresent, 137 desiredPresent - expectedPresent, 138 systemTime(CLOCK_MONOTONIC)); 139 } 140 141 int slot = front->mSlot; 142 *outBuffer = *front; 143 ATRACE_BUFFER_INDEX(slot); 144 145 BQ_LOGV("acquireBuffer: acquiring { slot=%d/%llu buffer=%p }", 146 slot, front->mFrameNumber, front->mGraphicBuffer->handle); 147 // If the front buffer is still being tracked, update its slot state 148 if (mCore->stillTracking(front)) { 149 mSlots[slot].mAcquireCalled = true; 150 mSlots[slot].mNeedsCleanupOnRelease = false; 151 mSlots[slot].mBufferState = BufferSlot::ACQUIRED; 152 mSlots[slot].mFence = Fence::NO_FENCE; 153 } 154 155 // If the buffer has previously been acquired by the consumer, set 156 // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer 157 // on the consumer side 158 if (outBuffer->mAcquireCalled) { 159 outBuffer->mGraphicBuffer = NULL; 160 } 161 162 mCore->mQueue.erase(front); 163 164 // We might have freed a slot while dropping old buffers, or the producer 165 // may be blocked waiting for the number of buffers in the queue to 166 // decrease. 167 mCore->mDequeueCondition.broadcast(); 168 169 ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size()); 170 171 return NO_ERROR; 172} 173 174status_t BufferQueueConsumer::detachBuffer(int slot) { 175 ATRACE_CALL(); 176 ATRACE_BUFFER_INDEX(slot); 177 BQ_LOGV("detachBuffer(C): slot %d", slot); 178 Mutex::Autolock lock(mCore->mMutex); 179 180 if (mCore->mIsAbandoned) { 181 BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned"); 182 return NO_INIT; 183 } 184 185 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { 186 BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)", 187 slot, BufferQueueDefs::NUM_BUFFER_SLOTS); 188 return BAD_VALUE; 189 } else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) { 190 BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer " 191 "(state = %d)", slot, mSlots[slot].mBufferState); 192 return BAD_VALUE; 193 } 194 195 mCore->freeBufferLocked(slot); 196 mCore->mDequeueCondition.broadcast(); 197 198 return NO_ERROR; 199} 200 201status_t BufferQueueConsumer::attachBuffer(int* outSlot, 202 const sp<android::GraphicBuffer>& buffer) { 203 ATRACE_CALL(); 204 205 if (outSlot == NULL) { 206 BQ_LOGE("attachBuffer(P): outSlot must not be NULL"); 207 return BAD_VALUE; 208 } else if (buffer == NULL) { 209 BQ_LOGE("attachBuffer(P): cannot attach NULL buffer"); 210 return BAD_VALUE; 211 } 212 213 Mutex::Autolock lock(mCore->mMutex); 214 215 // Make sure we don't have too many acquired buffers and find a free slot 216 // to put the buffer into (the oldest if there are multiple). 217 int numAcquiredBuffers = 0; 218 int found = BufferQueueCore::INVALID_BUFFER_SLOT; 219 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { 220 if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) { 221 ++numAcquiredBuffers; 222 } else if (mSlots[s].mBufferState == BufferSlot::FREE) { 223 if (found == BufferQueueCore::INVALID_BUFFER_SLOT || 224 mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) { 225 found = s; 226 } 227 } 228 } 229 230 if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { 231 BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d " 232 "(max %d)", numAcquiredBuffers, 233 mCore->mMaxAcquiredBufferCount); 234 return INVALID_OPERATION; 235 } 236 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { 237 BQ_LOGE("attachBuffer(P): could not find free buffer slot"); 238 return NO_MEMORY; 239 } 240 241 *outSlot = found; 242 ATRACE_BUFFER_INDEX(*outSlot); 243 BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot); 244 245 mSlots[*outSlot].mGraphicBuffer = buffer; 246 mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED; 247 mSlots[*outSlot].mAttachedByConsumer = true; 248 mSlots[*outSlot].mNeedsCleanupOnRelease = false; 249 mSlots[*outSlot].mFence = Fence::NO_FENCE; 250 mSlots[*outSlot].mFrameNumber = 0; 251 252 // mAcquireCalled tells BufferQueue that it doesn't need to send a valid 253 // GraphicBuffer pointer on the next acquireBuffer call, which decreases 254 // Binder traffic by not un/flattening the GraphicBuffer. However, it 255 // requires that the consumer maintain a cached copy of the slot <--> buffer 256 // mappings, which is why the consumer doesn't need the valid pointer on 257 // acquire. 258 // 259 // The StreamSplitter is one of the primary users of the attach/detach 260 // logic, and while it is running, all buffers it acquires are immediately 261 // detached, and all buffers it eventually releases are ones that were 262 // attached (as opposed to having been obtained from acquireBuffer), so it 263 // doesn't make sense to maintain the slot/buffer mappings, which would 264 // become invalid for every buffer during detach/attach. By setting this to 265 // false, the valid GraphicBuffer pointer will always be sent with acquire 266 // for attached buffers. 267 mSlots[*outSlot].mAcquireCalled = false; 268 269 return NO_ERROR; 270} 271 272status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, 273 const sp<Fence>& releaseFence, EGLDisplay eglDisplay, 274 EGLSyncKHR eglFence) { 275 ATRACE_CALL(); 276 ATRACE_BUFFER_INDEX(slot); 277 278 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || 279 releaseFence == NULL) { 280 return BAD_VALUE; 281 } 282 283 sp<IProducerListener> listener; 284 { // Autolock scope 285 Mutex::Autolock lock(mCore->mMutex); 286 287 // If the frame number has changed because the buffer has been reallocated, 288 // we can ignore this releaseBuffer for the old buffer 289 if (frameNumber != mSlots[slot].mFrameNumber) { 290 return STALE_BUFFER_SLOT; 291 } 292 293 // Make sure this buffer hasn't been queued while acquired by the consumer 294 BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); 295 while (current != mCore->mQueue.end()) { 296 if (current->mSlot == slot) { 297 BQ_LOGE("releaseBuffer: buffer slot %d pending release is " 298 "currently queued", slot); 299 return BAD_VALUE; 300 } 301 ++current; 302 } 303 304 if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) { 305 mSlots[slot].mEglDisplay = eglDisplay; 306 mSlots[slot].mEglFence = eglFence; 307 mSlots[slot].mFence = releaseFence; 308 mSlots[slot].mBufferState = BufferSlot::FREE; 309 listener = mCore->mConnectedProducerListener; 310 BQ_LOGV("releaseBuffer: releasing slot %d", slot); 311 } else if (mSlots[slot].mNeedsCleanupOnRelease) { 312 BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d " 313 "(state = %d)", slot, mSlots[slot].mBufferState); 314 mSlots[slot].mNeedsCleanupOnRelease = false; 315 return STALE_BUFFER_SLOT; 316 } else { 317 BQ_LOGV("releaseBuffer: attempted to release buffer slot %d " 318 "but its state was %d", slot, mSlots[slot].mBufferState); 319 return BAD_VALUE; 320 } 321 322 mCore->mDequeueCondition.broadcast(); 323 } // Autolock scope 324 325 // Call back without lock held 326 if (listener != NULL) { 327 listener->onBufferReleased(); 328 } 329 330 return NO_ERROR; 331} 332 333status_t BufferQueueConsumer::connect( 334 const sp<IConsumerListener>& consumerListener, bool controlledByApp) { 335 ATRACE_CALL(); 336 337 if (consumerListener == NULL) { 338 BQ_LOGE("connect(C): consumerListener may not be NULL"); 339 return BAD_VALUE; 340 } 341 342 BQ_LOGV("connect(C): controlledByApp=%s", 343 controlledByApp ? "true" : "false"); 344 345 Mutex::Autolock lock(mCore->mMutex); 346 347 if (mCore->mIsAbandoned) { 348 BQ_LOGE("connect(C): BufferQueue has been abandoned"); 349 return NO_INIT; 350 } 351 352 mCore->mConsumerListener = consumerListener; 353 mCore->mConsumerControlledByApp = controlledByApp; 354 355 return NO_ERROR; 356} 357 358status_t BufferQueueConsumer::disconnect() { 359 ATRACE_CALL(); 360 361 BQ_LOGV("disconnect(C)"); 362 363 Mutex::Autolock lock(mCore->mMutex); 364 365 if (mCore->mConsumerListener == NULL) { 366 BQ_LOGE("disconnect(C): no consumer is connected"); 367 return BAD_VALUE; 368 } 369 370 mCore->mIsAbandoned = true; 371 mCore->mConsumerListener = NULL; 372 mCore->mQueue.clear(); 373 mCore->freeAllBuffersLocked(); 374 mCore->mDequeueCondition.broadcast(); 375 return NO_ERROR; 376} 377 378status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { 379 ATRACE_CALL(); 380 381 if (outSlotMask == NULL) { 382 BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL"); 383 return BAD_VALUE; 384 } 385 386 Mutex::Autolock lock(mCore->mMutex); 387 388 if (mCore->mIsAbandoned) { 389 BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned"); 390 return NO_INIT; 391 } 392 393 uint64_t mask = 0; 394 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { 395 if (!mSlots[s].mAcquireCalled) { 396 mask |= (1ULL << s); 397 } 398 } 399 400 // Remove from the mask queued buffers for which acquire has been called, 401 // since the consumer will not receive their buffer addresses and so must 402 // retain their cached information 403 BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); 404 while (current != mCore->mQueue.end()) { 405 if (current->mAcquireCalled) { 406 mask &= ~(1ULL << current->mSlot); 407 } 408 ++current; 409 } 410 411 BQ_LOGV("getReleasedBuffers: returning mask %#" PRIx64, mask); 412 *outSlotMask = mask; 413 return NO_ERROR; 414} 415 416status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width, 417 uint32_t height) { 418 ATRACE_CALL(); 419 420 if (width == 0 || height == 0) { 421 BQ_LOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u " 422 "height=%u)", width, height); 423 return BAD_VALUE; 424 } 425 426 BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height); 427 428 Mutex::Autolock lock(mCore->mMutex); 429 mCore->mDefaultWidth = width; 430 mCore->mDefaultHeight = height; 431 return NO_ERROR; 432} 433 434status_t BufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) { 435 ATRACE_CALL(); 436 Mutex::Autolock lock(mCore->mMutex); 437 return mCore->setDefaultMaxBufferCountLocked(bufferCount); 438} 439 440status_t BufferQueueConsumer::disableAsyncBuffer() { 441 ATRACE_CALL(); 442 443 Mutex::Autolock lock(mCore->mMutex); 444 445 if (mCore->mConsumerListener != NULL) { 446 BQ_LOGE("disableAsyncBuffer: consumer already connected"); 447 return INVALID_OPERATION; 448 } 449 450 BQ_LOGV("disableAsyncBuffer"); 451 mCore->mUseAsyncBuffer = false; 452 return NO_ERROR; 453} 454 455status_t BufferQueueConsumer::setMaxAcquiredBufferCount( 456 int maxAcquiredBuffers) { 457 ATRACE_CALL(); 458 459 if (maxAcquiredBuffers < 1 || 460 maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) { 461 BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d", 462 maxAcquiredBuffers); 463 return BAD_VALUE; 464 } 465 466 Mutex::Autolock lock(mCore->mMutex); 467 468 if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { 469 BQ_LOGE("setMaxAcquiredBufferCount: producer is already connected"); 470 return INVALID_OPERATION; 471 } 472 473 BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); 474 mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; 475 return NO_ERROR; 476} 477 478void BufferQueueConsumer::setConsumerName(const String8& name) { 479 ATRACE_CALL(); 480 BQ_LOGV("setConsumerName: '%s'", name.string()); 481 Mutex::Autolock lock(mCore->mMutex); 482 mCore->mConsumerName = name; 483 mConsumerName = name; 484} 485 486status_t BufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { 487 ATRACE_CALL(); 488 BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat); 489 Mutex::Autolock lock(mCore->mMutex); 490 mCore->mDefaultBufferFormat = defaultFormat; 491 return NO_ERROR; 492} 493 494status_t BufferQueueConsumer::setConsumerUsageBits(uint32_t usage) { 495 ATRACE_CALL(); 496 BQ_LOGV("setConsumerUsageBits: %#x", usage); 497 Mutex::Autolock lock(mCore->mMutex); 498 mCore->mConsumerUsageBits = usage; 499 return NO_ERROR; 500} 501 502status_t BufferQueueConsumer::setTransformHint(uint32_t hint) { 503 ATRACE_CALL(); 504 BQ_LOGV("setTransformHint: %#x", hint); 505 Mutex::Autolock lock(mCore->mMutex); 506 mCore->mTransformHint = hint; 507 return NO_ERROR; 508} 509 510sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const { 511 return mCore->mSidebandStream; 512} 513 514void BufferQueueConsumer::dump(String8& result, const char* prefix) const { 515 mCore->dump(result, prefix); 516} 517 518} // namespace android 519