diff options
Diffstat (limited to 'libs/gui/BufferQueueProducer.cpp')
-rw-r--r-- | libs/gui/BufferQueueProducer.cpp | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp new file mode 100644 index 0000000000..9d569ada3f --- /dev/null +++ b/libs/gui/BufferQueueProducer.cpp @@ -0,0 +1,705 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define EGL_EGLEXT_PROTOTYPES + +#include <gui/BufferItem.h> +#include <gui/BufferQueueCore.h> +#include <gui/BufferQueueProducer.h> +#include <gui/IConsumerListener.h> +#include <gui/IGraphicBufferAlloc.h> + +#include <utils/Log.h> +#include <utils/Trace.h> + +namespace android { + +BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) : + mCore(core), + mSlots(core->mSlots), + mConsumerName() {} + +BufferQueueProducer::~BufferQueueProducer() {} + +status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { + ATRACE_CALL(); + BQ_LOGV("requestBuffer: slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("requestBuffer: BufferQueue has been abandoned"); + return NO_INIT; + } + + if (slot < 0 || slot >= BufferQueueCore::NUM_BUFFER_SLOTS) { + BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", + slot, BufferQueueCore::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + BQ_LOGE("requestBuffer: slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } + + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; +} + +status_t BufferQueueProducer::setBufferCount(int bufferCount) { + ATRACE_CALL(); + BQ_LOGV("setBufferCount: count = %d", bufferCount); + + sp<IConsumerListener> listener; + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("setBufferCount: BufferQueue has been abandoned"); + return NO_INIT; + } + + if (bufferCount > BufferQueueCore::NUM_BUFFER_SLOTS) { + BQ_LOGE("setBufferCount: bufferCount %d too large (max %d)", + bufferCount, BufferQueueCore::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } + + // There must be no dequeued buffers when changing the buffer count. + for (int s = 0; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) { + if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) { + BQ_LOGE("setBufferCount: buffer owned by producer"); + return -EINVAL; + } + } + + if (bufferCount == 0) { + mCore->mOverrideMaxBufferCount = 0; + mCore->mDequeueCondition.broadcast(); + return NO_ERROR; + } + + const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false); + if (bufferCount < minBufferSlots) { + BQ_LOGE("setBufferCount: requested buffer count %d is less than " + "minimum %d", bufferCount, minBufferSlots); + return BAD_VALUE; + } + + // Here we are guaranteed that the producer doesn't have any dequeued + // buffers and will release all of its buffer references. We don't + // clear the queue, however, so that currently queued buffers still + // get displayed. + mCore->freeAllBuffersLocked(); + mCore->mOverrideMaxBufferCount = bufferCount; + mCore->mDequeueCondition.broadcast(); + listener = mCore->mConsumerListener; + } // Autolock scope + + // Call back without lock held + if (listener != NULL) { + listener->onBuffersReleased(); + } + + return NO_ERROR; +} + +status_t BufferQueueProducer::dequeueBuffer(int *outSlot, + sp<android::Fence> *outFence, bool async, + uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { + ATRACE_CALL(); + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + mConsumerName = mCore->mConsumerName; + } // Autolock scope + + BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x", + async ? "true" : "false", width, height, format, usage); + + if ((width && !height) || (!width && height)) { + BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height); + return BAD_VALUE; + } + + status_t returnFlags = NO_ERROR; + EGLDisplay eglDisplay = EGL_NO_DISPLAY; + EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; + + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + if (format == 0) { + format = mCore->mDefaultBufferFormat; + } + + // Enable the usage bits the consumer requested + usage |= mCore->mConsumerUsageBits; + + int found = -1; + bool tryAgain = true; + while (tryAgain) { + if (mCore->mIsAbandoned) { + BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); + return NO_INIT; + } + + const int maxBufferCount = mCore->getMaxBufferCountLocked(async); + if (async && mCore->mOverrideMaxBufferCount) { + // FIXME: Some drivers are manually setting the buffer count + // (which they shouldn't), so we do this extra test here to + // handle that case. This is TEMPORARY until we get this fixed. + if (mCore->mOverrideMaxBufferCount < maxBufferCount) { + BQ_LOGE("dequeueBuffer: async mode is invalid with " + "buffer count override"); + return BAD_VALUE; + } + } + + // Free up any buffers that are in slots beyond the max buffer count + for (int s = maxBufferCount; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) { + assert(mSlots[s].mBufferState == BufferSlot::FREE); + if (mSlots[s].mGraphicBuffer != NULL) { + mCore->freeBufferLocked(s); + returnFlags |= RELEASE_ALL_BUFFERS; + } + } + + // Look for a free buffer to give to the client + found = BufferQueueCore::INVALID_BUFFER_SLOT; + int dequeuedCount = 0; + int acquiredCount = 0; + for (int s = 0; s < maxBufferCount; ++s) { + switch (mSlots[s].mBufferState) { + case BufferSlot::DEQUEUED: + ++dequeuedCount; + break; + case BufferSlot::ACQUIRED: + ++acquiredCount; + break; + case BufferSlot::FREE: + // We return the oldest of the free buffers to avoid + // stalling the producer if possible, since the consumer + // may still have pending reads of in-flight buffers + if (found == BufferQueueCore::INVALID_BUFFER_SLOT || + mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) { + found = s; + } + break; + default: + break; + } + } + + // Producers are not allowed to dequeue more than one buffer if they + // did not set a buffer count + if (!mCore->mOverrideMaxBufferCount && dequeuedCount) { + BQ_LOGE("dequeueBuffer: can't dequeue multiple buffers " + "without setting the buffer count"); + return -EINVAL; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the min undequeued + // buffers check below + if (mCore->mBufferHasBeenQueued) { + // Make sure the producer is not trying to dequeue more buffers + // than allowed + const int newUndequeuedCount = + maxBufferCount - (dequeuedCount + 1); + const int minUndequeuedCount = + mCore->getMinUndequeuedBufferCountLocked(async); + if (newUndequeuedCount < minUndequeuedCount) { + BQ_LOGE("dequeueBuffer: min undequeued buffer count (%d) " + "exceeded (dequeued=%d undequeued=%d)", + minUndequeuedCount, dequeuedCount, + newUndequeuedCount); + return -EBUSY; + } + } + + // If no buffer is found, wait for a buffer to be released or for + // the max buffer count to change + tryAgain = (found == BufferQueueCore::INVALID_BUFFER_SLOT); + if (tryAgain) { + // Return an error if we're in non-blocking mode (producer and + // consumer are controlled by the application). + // However, the consumer is allowed to briefly acquire an extra + // buffer (which could cause us to have to wait here), which is + // okay, since it is only used to implement an atomic acquire + + // release (e.g., in GLConsumer::updateTexImage()) + if (mCore->mDequeueBufferCannotBlock && + (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { + return WOULD_BLOCK; + } + mCore->mDequeueCondition.wait(mCore->mMutex); + } + } // while (tryAgain) + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + BQ_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } + + *outSlot = found; + ATRACE_BUFFER_INDEX(found); + + const bool useDefaultSize = !width && !height; + if (useDefaultSize) { + width = mCore->mDefaultWidth; + height = mCore->mDefaultHeight; + } + + mSlots[found].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); + if ((buffer == NULL) || + (static_cast<uint32_t>(buffer->width) != width) || + (static_cast<uint32_t>(buffer->height) != height) || + (static_cast<uint32_t>(buffer->format) != format) || + ((static_cast<uint32_t>(buffer->usage) & usage) != usage)) + { + mSlots[found].mAcquireCalled = false; + mSlots[found].mGraphicBuffer = NULL; + mSlots[found].mRequestBufferCalled = false; + mSlots[found].mEglDisplay = EGL_NO_DISPLAY; + mSlots[found].mEglFence = EGL_NO_SYNC_KHR; + mSlots[found].mFence = Fence::NO_FENCE; + + returnFlags |= BUFFER_NEEDS_REALLOCATION; + } + + if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { + BQ_LOGE("dequeueBuffer: about to return a NULL fence - " + "slot=%d w=%d h=%d format=%u", + found, buffer->width, buffer->height, buffer->format); + } + + eglDisplay = mSlots[found].mEglDisplay; + eglFence = mSlots[found].mEglFence; + *outFence = mSlots[found].mFence; + mSlots[found].mEglFence = EGL_NO_SYNC_KHR; + mSlots[found].mFence = Fence::NO_FENCE; + } // Autolock scope + + if (returnFlags & BUFFER_NEEDS_REALLOCATION) { + status_t error; + sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( + width, height, format, usage, &error)); + if (graphicBuffer == NULL) { + BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); + return error; + } + + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); + return NO_INIT; + } + + mSlots[*outSlot].mFrameNumber = ~0; + mSlots[*outSlot].mGraphicBuffer = graphicBuffer; + } // Autolock scope + } + + if (eglFence != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, + 1000000000); + // If something goes wrong, log the error, but return the buffer without + // synchronizing access to it. It's too late at this point to abort the + // dequeue operation. + if (result == EGL_FALSE) { + BQ_LOGE("dequeueBuffer: error %#x waiting for fence", + eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + BQ_LOGE("dequeueBuffer: timeout waiting for fence"); + } + eglDestroySyncKHR(eglDisplay, eglFence); + } + + BQ_LOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outSlot, + mSlots[*outSlot].mFrameNumber, + mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); + + return returnFlags; +} + +status_t BufferQueueProducer::queueBuffer(int slot, + const QueueBufferInput &input, QueueBufferOutput *output) { + ATRACE_CALL(); + ATRACE_BUFFER_INDEX(slot); + + int64_t timestamp; + bool isAutoTimestamp; + Rect crop; + int scalingMode; + uint32_t transform; + bool async; + sp<Fence> fence; + input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, + &async, &fence); + + if (fence == NULL) { + BQ_LOGE("queueBuffer: fence is NULL"); + return BAD_VALUE; + } + + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: + break; + default: + BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode); + return -EINVAL; + } + + sp<IConsumerListener> listener; + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("queueBuffer: BufferQueue has been abandoned"); + return NO_INIT; + } + + const int maxBufferCount = mCore->getMaxBufferCountLocked(async); + if (async && mCore->mOverrideMaxBufferCount) { + // FIXME: Some drivers are manually setting the buffer count + // (which they shouldn't), so we do this extra test here to + // handle that case. This is TEMPORARY until we get this fixed. + if (mCore->mOverrideMaxBufferCount < maxBufferCount) { + BQ_LOGE("queueBuffer: async mode is invalid with " + "buffer count override"); + return BAD_VALUE; + } + } + + if (slot < 0 || slot >= maxBufferCount) { + BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", + slot, maxBufferCount); + return -EINVAL; + } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + BQ_LOGE("queueBuffer: slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return -EINVAL; + } else if (!mSlots[slot].mRequestBufferCalled) { + BQ_LOGE("queueBuffer: slot %d was queued without requesting " + "a buffer", slot); + return -EINVAL; + } + + BQ_LOGV("queueBuffer: slot=%d/%llu time=%llu crop=[%d,%d,%d,%d] " + "transform=%#x scale=%s", + slot, mCore->mFrameCounter + 1, timestamp, + crop.left, crop.top, crop.right, crop.bottom, + transform, BufferItem::scalingModeName(scalingMode)); + + const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer); + Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); + Rect croppedRect; + crop.intersect(bufferRect, &croppedRect); + if (croppedRect != crop) { + BQ_LOGE("queueBuffer: crop rect is not contained within the " + "buffer in slot %d", slot); + return -EINVAL; + } + + mSlots[slot].mFence = fence; + mSlots[slot].mBufferState = BufferSlot::QUEUED; + ++mCore->mFrameCounter; + mSlots[slot].mFrameNumber = mCore->mFrameCounter; + + BufferItem item; + item.mAcquireCalled = mSlots[slot].mAcquireCalled; + item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; + item.mCrop = crop; + item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; + item.mTransformToDisplayInverse = + bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); + item.mScalingMode = scalingMode; + item.mTimestamp = timestamp; + item.mIsAutoTimestamp = isAutoTimestamp; + item.mFrameNumber = mCore->mFrameCounter; + item.mSlot = slot; + item.mFence = fence; + item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async; + + if (mCore->mQueue.empty()) { + // When the queue is empty, we can ignore mDequeueBufferCannotBlock + // and simply queue this buffer + mCore->mQueue.push_back(item); + listener = mCore->mConsumerListener; + } else { + // When the queue is not empty, we need to look at the front buffer + // state to see if we need to replace it + BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); + if (front->mIsDroppable) { + // If the front queued buffer is still being tracked, we first + // mark it as freed + if (mCore->stillTracking(front)) { + mSlots[front->mSlot].mBufferState = BufferSlot::FREE; + // Reset the frame number of the freed buffer so that it is + // the first in line to be dequeued again + mSlots[front->mSlot].mFrameNumber = 0; + } + // Overwrite the droppable buffer with the incoming one + *front = item; + } else { + mCore->mQueue.push_back(item); + listener = mCore->mConsumerListener; + } + } + + mCore->mBufferHasBeenQueued = true; + mCore->mDequeueCondition.broadcast(); + + output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, + mCore->mTransformHint, mCore->mQueue.size()); + + ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size()); + } // Autolock scope + + // Call back without lock held + if (listener != NULL) { + listener->onFrameAvailable(); + } + + return NO_ERROR; +} + +void BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { + ATRACE_CALL(); + BQ_LOGV("cancelBuffer: slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + BQ_LOGE("cancelBuffer: BufferQueue has been abandoned"); + return; + } + + if (slot < 0 || slot >= BufferQueueCore::NUM_BUFFER_SLOTS) { + BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", + slot, BufferQueueCore::NUM_BUFFER_SLOTS); + return; + } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return; + } else if (fence == NULL) { + BQ_LOGE("cancelBuffer: fence is NULL"); + return; + } + + mSlots[slot].mBufferState = BufferSlot::FREE; + mSlots[slot].mFrameNumber = 0; + mSlots[slot].mFence = fence; + mCore->mDequeueCondition.broadcast(); +} + +int BufferQueueProducer::query(int what, int *outValue) { + ATRACE_CALL(); + Mutex::Autolock lock(mCore->mMutex); + + if (outValue == NULL) { + BQ_LOGE("query: outValue was NULL"); + return BAD_VALUE; + } + + if (mCore->mIsAbandoned) { + BQ_LOGE("query: BufferQueue has been abandoned"); + return NO_INIT; + } + + int value; + switch (what) { + case NATIVE_WINDOW_WIDTH: + value = mCore->mDefaultWidth; + break; + case NATIVE_WINDOW_HEIGHT: + value = mCore->mDefaultHeight; + break; + case NATIVE_WINDOW_FORMAT: + value = mCore->mDefaultBufferFormat; + break; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + value = mCore->getMinUndequeuedBufferCountLocked(false); + break; + case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: + value = (mCore->mQueue.size() > 1); + break; + case NATIVE_WINDOW_CONSUMER_USAGE_BITS: + value = mCore->mConsumerUsageBits; + break; + default: + return BAD_VALUE; + } + + BQ_LOGV("query: %d? %d", what, value); + *outValue = value; + return NO_ERROR; +} + +status_t BufferQueueProducer::connect(const sp<android::IBinder> &token, + int api, bool producerControlledByApp, QueueBufferOutput *output) { + ATRACE_CALL(); + Mutex::Autolock lock(mCore->mMutex); + mConsumerName = mCore->mConsumerName; + BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api, + producerControlledByApp ? "true" : "false"); + + // If we disconnect and reconnect quickly, we can be in a state where our + // slots are empty but we have many buffers in the queue. This can cause us + // to run out of memory if we outrun the consumer. Wait here if it looks + // like we have too many buffers queued up. + while (true) { + if (mCore->mIsAbandoned) { + BQ_LOGE("connect(P): BufferQueue has been abandoned"); + return NO_INIT; + } + + if (mCore->mConsumerListener == NULL) { + BQ_LOGE("connect(P): BufferQueue has no consumer"); + return NO_INIT; + } + + if (output == NULL) { + BQ_LOGE("connect(P): output was NULL"); + return BAD_VALUE; + } + + if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { + BQ_LOGE("connect(P): already connected (cur=%d req=%d)", + mCore->mConnectedApi, api); + return BAD_VALUE; + } + + size_t maxBufferCount = mCore->getMaxBufferCountLocked(false); + if (mCore->mQueue.size() <= maxBufferCount) { + // The queue size seems small enough to proceed + // TODO: Make this bound tighter? + break; + } + + BQ_LOGV("connect(P): queue size is %d, waiting", mCore->mQueue.size()); + mCore->mDequeueCondition.wait(mCore->mMutex); + } + + int status = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + mCore->mConnectedApi = api; + output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, + mCore->mTransformHint, mCore->mQueue.size()); + + // Set up a death notification so that we can disconnect + // automatically if the remote producer dies + if (token != NULL && token->remoteBinder() != NULL) { + status = token->linkToDeath( + static_cast<IBinder::DeathRecipient*>(this)); + if (status == NO_ERROR) { + mCore->mConnectedProducerToken = token; + } else { + BQ_LOGE("connect(P): linkToDeath failed: %s (%d)", + strerror(-status), status); + } + } + break; + default: + BQ_LOGE("connect(P): unknown API %d", api); + status = BAD_VALUE; + break; + } + + mCore->mBufferHasBeenQueued = false; + mCore->mDequeueBufferCannotBlock = + mCore->mConsumerControlledByApp && producerControlledByApp; + + return status; +} + +status_t BufferQueueProducer::disconnect(int api) { + ATRACE_CALL(); + BQ_LOGV("disconnect(P): api %d", api); + + int status = NO_ERROR; + sp<IConsumerListener> listener; + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + // It's not really an error to disconnect after the surface has + // been abandoned; it should just be a no-op. + return NO_ERROR; + } + + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mCore->mConnectedApi == api) { + mCore->freeAllBuffersLocked(); + + // Remove our death notification callback if we have one + sp<IBinder> token = mCore->mConnectedProducerToken; + if (token != NULL) { + // This can fail if we're here because of the death + // notification, but we just ignore it + token->unlinkToDeath( + static_cast<IBinder::DeathRecipient*>(this)); + } + mCore->mConnectedProducerToken = NULL; + mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; + mCore->mDequeueCondition.broadcast(); + listener = mCore->mConsumerListener; + } else { + BQ_LOGE("disconnect(P): connected to another API " + "(cur=%d req=%d)", mCore->mConnectedApi, api); + status = -EINVAL; + } + break; + default: + BQ_LOGE("disconnect(P): unknown API %d", api); + status = -EINVAL; + break; + } + } // Autolock scope + + // Call back without lock held + if (listener != NULL) { + listener->onBuffersReleased(); + } + + return status; +} + +void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) { + // If we're here, it means that a producer we were connected to died. + // We're guaranteed that we are still connected to it because we remove + // this callback upon disconnect. It's therefore safe to read mConnectedApi + // without synchronization here. + int api = mCore->mConnectedApi; + disconnect(api); +} + +} // namespace android |