Merge "AudioMixer: Fix aux effect pointer computation"
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 541093a..6da6c13 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -7289,12 +7289,16 @@
}
}
- float rate;
- if (params->findFloat("operating-rate", &rate) && rate > 0) {
- status_t err = setOperatingRate(rate, mIsVideo);
+ int32_t rateInt = -1;
+ float rateFloat = -1;
+ if (!params->findFloat("operating-rate", &rateFloat)) {
+ params->findInt32("operating-rate", &rateInt);
+ rateFloat = (float) rateInt; // 16MHz (FLINTMAX) is OK for upper bound.
+ }
+ if (rateFloat > 0) {
+ status_t err = setOperatingRate(rateFloat, mIsVideo);
if (err != OK) {
- ALOGE("Failed to set parameter 'operating-rate' (err %d)", err);
- return err;
+ ALOGI("Failed to set parameter 'operating-rate' (err %d)", err);
}
}
@@ -7319,10 +7323,8 @@
}
}
- status_t err = configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
- if (err != OK) {
- err = OK; // ignore failure
- }
+ // Ignore errors as failure is expected for codecs that aren't video encoders.
+ (void)configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
return setVendorParameters(params);
}
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index bd2443d..d2eee33 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -64,10 +64,87 @@
return ICrypto::kDestinationTypeNativeHandle;
}
+// Codec2Buffer
+
+bool Codec2Buffer::canCopyLinear(const std::shared_ptr<C2Buffer> &buffer) const {
+ if (const_cast<Codec2Buffer *>(this)->base() == nullptr) {
+ return false;
+ }
+ if (buffer->data().type() != C2BufferData::LINEAR) {
+ return false;
+ }
+ if (buffer->data().linearBlocks().size() == 0u) {
+ // Nothing to copy, so we can copy by doing nothing.
+ return true;
+ } else if (buffer->data().linearBlocks().size() > 1u) {
+ // We don't know how to copy more than one blocks.
+ return false;
+ }
+ if (buffer->data().linearBlocks()[0].size() > capacity()) {
+ // It won't fit.
+ return false;
+ }
+ return true;
+}
+
+bool Codec2Buffer::copyLinear(const std::shared_ptr<C2Buffer> &buffer) {
+ // We assume that all canCopyLinear() checks passed.
+ if (buffer->data().linearBlocks().size() == 0u) {
+ setRange(0, 0);
+ return true;
+ }
+ C2ReadView view = buffer->data().linearBlocks()[0].map().get();
+ if (view.error() != C2_OK) {
+ ALOGD("Error while mapping: %d", view.error());
+ return false;
+ }
+ if (view.capacity() > capacity()) {
+ ALOGD("C2ConstLinearBlock lied --- it actually doesn't fit: view(%u) > this(%zu)",
+ view.capacity(), capacity());
+ return false;
+ }
+ memcpy(base(), view.data(), view.capacity());
+ setRange(0, view.capacity());
+ return true;
+}
+
+// LocalLinearBuffer
+
+bool LocalLinearBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ return canCopyLinear(buffer);
+}
+
+bool LocalLinearBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ return copyLinear(buffer);
+}
+
+// DummyContainerBuffer
+
+DummyContainerBuffer::DummyContainerBuffer(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer)
+ : Codec2Buffer(format, new ABuffer(nullptr, 1)),
+ mBufferRef(buffer) {
+ setRange(0, buffer ? 1 : 0);
+}
+
+std::shared_ptr<C2Buffer> DummyContainerBuffer::asC2Buffer() {
+ return std::move(mBufferRef);
+}
+
+bool DummyContainerBuffer::canCopy(const std::shared_ptr<C2Buffer> &) const {
+ return !mBufferRef;
+}
+
+bool DummyContainerBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ mBufferRef = buffer;
+ setRange(0, mBufferRef ? 1 : 0);
+ return true;
+}
+
// LinearBlockBuffer
// static
-sp<LinearBlockBuffer> LinearBlockBuffer::allocate(
+sp<LinearBlockBuffer> LinearBlockBuffer::Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block) {
C2WriteView writeView(block->map().get());
if (writeView.error() != C2_OK) {
@@ -76,15 +153,23 @@
return new LinearBlockBuffer(format, std::move(writeView), block);
}
-C2ConstLinearBlock LinearBlockBuffer::share() {
- return mBlock->share(offset(), size(), C2Fence());
+std::shared_ptr<C2Buffer> LinearBlockBuffer::asC2Buffer() {
+ return C2Buffer::CreateLinearBuffer(mBlock->share(offset(), size(), C2Fence()));
+}
+
+bool LinearBlockBuffer::canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ return canCopyLinear(buffer);
+}
+
+bool LinearBlockBuffer::copy(const std::shared_ptr<C2Buffer> &buffer) {
+ return copyLinear(buffer);
}
LinearBlockBuffer::LinearBlockBuffer(
const sp<AMessage> &format,
C2WriteView&& writeView,
const std::shared_ptr<C2LinearBlock> &block)
- : MediaCodecBuffer(format, new ABuffer(writeView.data(), writeView.size())),
+ : Codec2Buffer(format, new ABuffer(writeView.data(), writeView.size())),
mWriteView(writeView),
mBlock(block) {
}
@@ -92,23 +177,34 @@
// ConstLinearBlockBuffer
// static
-sp<ConstLinearBlockBuffer> ConstLinearBlockBuffer::allocate(
- const sp<AMessage> &format, const C2ConstLinearBlock &block) {
- C2ReadView readView(block.map().get());
+sp<ConstLinearBlockBuffer> ConstLinearBlockBuffer::Allocate(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer) {
+ if (!buffer
+ || buffer->data().type() != C2BufferData::LINEAR
+ || buffer->data().linearBlocks().size() != 1u) {
+ return nullptr;
+ }
+ C2ReadView readView(buffer->data().linearBlocks()[0].map().get());
if (readView.error() != C2_OK) {
return nullptr;
}
- return new ConstLinearBlockBuffer(format, std::move(readView));
+ return new ConstLinearBlockBuffer(format, std::move(readView), buffer);
}
ConstLinearBlockBuffer::ConstLinearBlockBuffer(
const sp<AMessage> &format,
- C2ReadView&& readView)
- : MediaCodecBuffer(format, new ABuffer(
+ C2ReadView&& readView,
+ const std::shared_ptr<C2Buffer> &buffer)
+ : Codec2Buffer(format, new ABuffer(
// NOTE: ABuffer only takes non-const pointer but this data is
// supposed to be read-only.
const_cast<uint8_t *>(readView.data()), readView.capacity())),
- mReadView(readView) {
+ mReadView(readView),
+ mBufferRef(buffer) {
+}
+
+std::shared_ptr<C2Buffer> ConstLinearBlockBuffer::asC2Buffer() {
+ return std::move(mBufferRef);
}
} // namespace android
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
index 6fba890..449c6aa 100644
--- a/media/libstagefright/CCodecBufferChannel.cpp
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -138,7 +138,7 @@
virtual bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) = 0;
+ sp<MediaCodecBuffer> *clientBuffer) = 0;
/**
* Register codec specific data as a buffer to be consistent with
@@ -147,9 +147,7 @@
virtual bool registerCsd(
const C2StreamCsdInfo::output * /* csd */,
size_t * /* index */,
- sp<MediaCodecBuffer> * /* codecBuffer */) {
- return false;
- }
+ sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
/**
* Release the buffer obtained from registerBuffer() and get the
@@ -181,23 +179,6 @@
const static size_t kMinBufferArraySize = 16;
const static size_t kLinearBufferSize = 524288;
-template <class T>
-ssize_t findBufferSlot(
- std::vector<T> *buffers,
- size_t maxSize,
- std::function<bool(const T&)> pred) {
- auto it = std::find_if(buffers->begin(), buffers->end(), pred);
- if (it == buffers->end()) {
- if (buffers->size() < maxSize) {
- buffers->emplace_back();
- return buffers->size() - 1;
- } else {
- return -1;
- }
- }
- return std::distance(buffers->begin(), it);
-}
-
sp<LinearBlockBuffer> allocateLinearBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
@@ -205,41 +186,209 @@
const C2MemoryUsage &usage) {
std::shared_ptr<C2LinearBlock> block;
- status_t err = pool->fetchLinearBlock(
- size,
- usage,
- &block);
+ status_t err = pool->fetchLinearBlock(size, usage, &block);
if (err != OK) {
return nullptr;
}
- return LinearBlockBuffer::allocate(format, block);
+ return LinearBlockBuffer::Allocate(format, block);
}
-class Buffer1D : public C2Buffer {
+class BuffersArrayImpl;
+
+/**
+ * Flexible buffer slots implementation.
+ */
+class FlexBuffersImpl {
public:
- explicit Buffer1D(C2ConstLinearBlock block) : C2Buffer({ block }) {}
+ FlexBuffersImpl() = default;
+
+ /**
+ * Assign an empty slot for a buffer and return the index. If there's no
+ * empty slot, just add one at the end and return it.
+ *
+ * \param buffer[in] a new buffer to assign a slot.
+ * \return index of the assigned slot.
+ */
+ size_t assignSlot(const sp<Codec2Buffer> &buffer) {
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer.promote() == nullptr
+ && mBuffers[i].compBuffer.expired()) {
+ mBuffers[i].clientBuffer = buffer;
+ return i;
+ }
+ }
+ mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
+ return mBuffers.size() - 1;
+ }
+
+ /**
+ * Release the slot from the client, and get the C2Buffer object back from
+ * the previously assigned buffer. Note that the slot is not completely free
+ * until the returned C2Buffer object is freed.
+ *
+ * \param buffer[in] the buffer previously assigned a slot.
+ * \return C2Buffer object from |buffer|.
+ */
+ std::shared_ptr<C2Buffer> releaseSlot(const sp<MediaCodecBuffer> &buffer) {
+ sp<Codec2Buffer> c2Buffer;
+ size_t index = mBuffers.size();
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer.promote() == buffer) {
+ c2Buffer = mBuffers[i].clientBuffer.promote();
+ index = i;
+ break;
+ }
+ }
+ if (c2Buffer == nullptr) {
+ ALOGD("No matching buffer found");
+ return nullptr;
+ }
+ std::shared_ptr<C2Buffer> result = c2Buffer->asC2Buffer();
+ mBuffers[index].compBuffer = result;
+ return result;
+ }
+
+private:
+ friend class BuffersArrayImpl;
+
+ struct Entry {
+ wp<Codec2Buffer> clientBuffer;
+ std::weak_ptr<C2Buffer> compBuffer;
+ };
+ std::vector<Entry> mBuffers;
};
-class Buffer2D : public C2Buffer {
+/**
+ * Static buffer slots implementation based on a fixed-size array.
+ */
+class BuffersArrayImpl {
public:
- explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {}
+ /**
+ * Initialize buffer array from the original |impl|. The buffers known by
+ * the client is preserved, and the empty slots are populated so that the
+ * array size is at least |minSize|.
+ *
+ * \param impl[in] FlexBuffersImpl object used so far.
+ * \param minSize[in] minimum size of the buffer array.
+ * \param allocate[in] function to allocate a client buffer for an empty slot.
+ */
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
+ sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer.promote();
+ bool ownedByClient = (clientBuffer != nullptr);
+ if (!ownedByClient) {
+ clientBuffer = allocate();
+ }
+ mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
+ }
+ for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
+ mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
+ }
+ }
+
+ /**
+ * Grab a buffer from the underlying array which matches the criteria.
+ *
+ * \param index[out] index of the slot.
+ * \param buffer[out] the matching buffer.
+ * \param match[in] a function to test whether the buffer matches the
+ * criteria or not.
+ * \return OK if successful,
+ * NO_MEMORY if there's no available slot meets the criteria.
+ */
+ status_t grabBuffer(
+ size_t *index,
+ sp<Codec2Buffer> *buffer,
+ std::function<bool(const sp<Codec2Buffer> &)> match =
+ [](const sp<Codec2Buffer> &) { return true; }) {
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()
+ && match(mBuffers[i].clientBuffer)) {
+ mBuffers[i].ownedByClient = true;
+ *buffer = mBuffers[i].clientBuffer;
+ (*buffer)->meta()->clear();
+ (*buffer)->setRange(0, (*buffer)->capacity());
+ *index = i;
+ return OK;
+ }
+ }
+ return NO_MEMORY;
+ }
+
+ /**
+ * Return the buffer from the client, and get the C2Buffer object back from
+ * the buffer. Note that the slot is not completely free until the returned
+ * C2Buffer object is freed.
+ *
+ * \param buffer[in] the buffer previously grabbed.
+ * \return C2Buffer object from |buffer|.
+ */
+ std::shared_ptr<C2Buffer> returnBuffer(const sp<MediaCodecBuffer> &buffer) {
+ sp<Codec2Buffer> c2Buffer;
+ size_t index = mBuffers.size();
+ for (size_t i = 0; i < mBuffers.size(); ++i) {
+ if (mBuffers[i].clientBuffer == buffer) {
+ if (!mBuffers[i].ownedByClient) {
+ ALOGD("Client returned a buffer it does not own according to our record: %zu", i);
+ }
+ c2Buffer = mBuffers[i].clientBuffer;
+ mBuffers[i].ownedByClient = false;
+ index = i;
+ break;
+ }
+ }
+ if (c2Buffer == nullptr) {
+ ALOGD("No matching buffer found");
+ return nullptr;
+ }
+ std::shared_ptr<C2Buffer> result = c2Buffer->asC2Buffer();
+ mBuffers[index].compBuffer = result;
+ return result;
+ }
+
+ /**
+ * Populate |array| with the underlying buffer array.
+ *
+ * \param array[out] an array to be filled with the underlying buffer array.
+ */
+ void getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+ array->clear();
+ for (const Entry &entry : mBuffers) {
+ array->push(entry.clientBuffer);
+ }
+ }
+
+ /**
+ * The client abandoned all known buffers, so reclaim the ownership.
+ */
+ void flush() {
+ for (Entry &entry : mBuffers) {
+ entry.ownedByClient = false;
+ }
+ }
+
+private:
+ struct Entry {
+ const sp<Codec2Buffer> clientBuffer;
+ std::weak_ptr<C2Buffer> compBuffer;
+ bool ownedByClient;
+ };
+ std::vector<Entry> mBuffers;
};
class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
public:
InputBuffersArray() = default;
- void add(
- size_t index,
- const sp<LinearBlockBuffer> &clientBuffer,
- bool available) {
- if (mBufferArray.size() <= index) {
- // TODO: make this more efficient
- mBufferArray.resize(index + 1);
- }
- mBufferArray[index].clientBuffer = clientBuffer;
- mBufferArray[index].available = available;
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ mImpl.initialize(impl, minSize, allocate);
}
bool isArrayMode() const final { return true; }
@@ -249,52 +398,30 @@
}
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
- array->clear();
- for (const Entry &entry : mBufferArray) {
- array->push(entry.clientBuffer);
- }
+ mImpl.getArray(array);
}
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available && mBufferArray[i].compBuffer.expired()) {
- mBufferArray[i].available = false;
- *index = i;
- *buffer = mBufferArray[i].clientBuffer;
- (*buffer)->meta()->clear();
- (*buffer)->setRange(0, (*buffer)->capacity());
- return true;
- }
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(index, &c2Buffer);
+ if (err == OK) {
+ c2Buffer->setFormat(mFormat);
+ *buffer = c2Buffer;
+ return true;
}
return false;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
- std::shared_ptr<C2Buffer> buffer = std::make_shared<Buffer1D>(mBufferArray[i].clientBuffer->share());
- mBufferArray[i].available = true;
- mBufferArray[i].compBuffer = buffer;
- return buffer;
- }
- }
- return nullptr;
+ return mImpl.returnBuffer(buffer);
}
void flush() override {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- mBufferArray[i].available = true;
- }
+ mImpl.flush();
}
private:
- struct Entry {
- sp<LinearBlockBuffer> clientBuffer;
- std::weak_ptr<C2Buffer> compBuffer;
- bool available;
- };
-
- std::vector<Entry> mBufferArray;
+ BuffersArrayImpl mImpl;
};
class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
@@ -302,68 +429,42 @@
using CCodecBufferChannel::InputBuffers::InputBuffers;
bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
- *buffer = nullptr;
- ssize_t ret = findBufferSlot<wp<LinearBlockBuffer>>(
- &mBuffers, kMinBufferArraySize,
- [] (const auto &elem) { return elem.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
- // TODO: proper max input size and usage
+ // TODO: proper max input size
// TODO: read usage from intf
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
sp<LinearBlockBuffer> newBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
if (newBuffer == nullptr) {
return false;
}
- mBuffers[ret] = newBuffer;
- *index = ret;
+ *index = mImpl.assignSlot(newBuffer);
*buffer = newBuffer;
return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
- auto it = std::find(mBuffers.begin(), mBuffers.end(), buffer);
- if (it == mBuffers.end()) {
- return nullptr;
- }
- sp<LinearBlockBuffer> codecBuffer = it->promote();
- // We got sp<> reference from the caller so this should never happen..
- CHECK(codecBuffer != nullptr);
- return std::make_shared<Buffer1D>(codecBuffer->share());
+ return mImpl.releaseSlot(buffer);
}
void flush() override {
+ // This is no-op by default unless we're in array mode where we need to keep
+ // track of the flushed work.
}
std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode() final {
std::unique_ptr<InputBuffersArray> array(new InputBuffersArray);
array->setFormat(mFormat);
- // TODO
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<LinearBlockBuffer> clientBuffer = mBuffers[i].promote();
- bool available = false;
- if (clientBuffer == nullptr) {
- // TODO: proper max input size
- // TODO: read usage from intf
- C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- clientBuffer = allocateLinearBuffer(mPool, mFormat, kLinearBufferSize, usage);
- available = true;
- }
- array->add(
- i,
- clientBuffer,
- available);
- }
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ return allocateLinearBuffer(pool, format, kLinearBufferSize, usage);
+ });
return std::move(array);
}
private:
- // Buffers we passed to the client. The index of a buffer matches what
- // was passed in BufferCallback::onInputBufferAvailable().
- std::vector<wp<LinearBlockBuffer>> mBuffers;
+ FlexBuffersImpl mImpl;
};
class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
@@ -396,18 +497,11 @@
public:
using CCodecBufferChannel::OutputBuffers::OutputBuffers;
- void add(
- size_t index,
- const sp<MediaCodecBuffer> &clientBuffer,
- const std::shared_ptr<C2Buffer> &compBuffer,
- bool available) {
- if (mBufferArray.size() <= index) {
- // TODO: make this more efficient
- mBufferArray.resize(index + 1);
- }
- mBufferArray[index].clientBuffer = clientBuffer;
- mBufferArray[index].compBuffer = compBuffer;
- mBufferArray[index].available = available;
+ void initialize(
+ const FlexBuffersImpl &impl,
+ size_t minSize,
+ std::function<sp<Codec2Buffer>()> allocate) {
+ mImpl.initialize(impl, minSize, allocate);
}
bool isArrayMode() const final { return true; }
@@ -419,125 +513,69 @@
bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available && copy(buffer, mBufferArray[i].clientBuffer)) {
- *index = i;
- *codecBuffer = mBufferArray[i].clientBuffer;
- (*codecBuffer)->setFormat(mFormat);
- (*codecBuffer)->meta()->clear();
- mBufferArray[i].compBuffer = buffer;
- mBufferArray[i].available = false;
- return true;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(
+ index,
+ &c2Buffer,
+ [buffer](const sp<Codec2Buffer> &clientBuffer) {
+ return clientBuffer->canCopy(buffer);
+ });
+ if (err != OK) {
+ return false;
}
- return false;
+ if (!c2Buffer->copy(buffer)) {
+ return false;
+ }
+ c2Buffer->setFormat(mFormat);
+ *clientBuffer = c2Buffer;
+ return true;
}
bool registerCsd(
const C2StreamCsdInfo::output *csd,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (mBufferArray[i].available
- && mBufferArray[i].clientBuffer->capacity() >= csd->flexCount()) {
- // TODO: proper format update
- sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
- mFormat = mFormat->dup();
- mFormat->setBuffer("csd-0", csdBuffer);
-
- memcpy(mBufferArray[i].clientBuffer->base(), csd->m.value, csd->flexCount());
- mBufferArray[i].clientBuffer->setRange(0, csd->flexCount());
- *index = i;
- *codecBuffer = mBufferArray[i].clientBuffer;
- (*codecBuffer)->setFormat(mFormat);
- (*codecBuffer)->meta()->clear();
- mBufferArray[i].available = false;
- return true;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
+ sp<Codec2Buffer> c2Buffer;
+ status_t err = mImpl.grabBuffer(
+ index,
+ &c2Buffer,
+ [csd](const sp<Codec2Buffer> &clientBuffer) {
+ return clientBuffer->base() != nullptr
+ && clientBuffer->capacity() >= csd->flexCount();
+ });
+ if (err != OK) {
+ return false;
}
- return false;
+ // TODO: proper format update
+ sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
+ mFormat = mFormat->dup();
+ mFormat->setBuffer("csd-0", csdBuffer);
+
+ memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
+ c2Buffer->setRange(0, csd->flexCount());
+ c2Buffer->setFormat(mFormat);
+ *clientBuffer = c2Buffer;
+ return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) final {
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- if (!mBufferArray[i].available && mBufferArray[i].clientBuffer == buffer) {
- mBufferArray[i].available = true;
- return std::move(mBufferArray[i].compBuffer);
- }
- }
- return nullptr;
+ return mImpl.returnBuffer(buffer);
}
- void flush(
- const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
+ void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
(void) flushedWork;
- for (size_t i = 0; i < mBufferArray.size(); ++i) {
- mBufferArray[i].available = true;
- mBufferArray[i].compBuffer.reset();
- }
+ mImpl.flush();
}
- virtual bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) = 0;
-
void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
- array->clear();
- for (const Entry &entry : mBufferArray) {
- array->push(entry.clientBuffer);
- }
+ mImpl.getArray(array);
}
private:
- struct Entry {
- sp<MediaCodecBuffer> clientBuffer;
- std::shared_ptr<C2Buffer> compBuffer;
- bool available;
- };
-
- std::vector<Entry> mBufferArray;
+ BuffersArrayImpl mImpl;
};
-class LinearOutputBuffersArray : public OutputBuffersArray {
-public:
- using OutputBuffersArray::OutputBuffersArray;
-
- bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) final {
- if (!buffer) {
- clientBuffer->setRange(0u, 0u);
- return true;
- }
- C2ReadView view = buffer->data().linearBlocks().front().map().get();
- if (clientBuffer->capacity() < view.capacity()) {
- ALOGV("view.capacity() = %u", view.capacity());
- return false;
- }
- clientBuffer->setRange(0u, view.capacity());
- memcpy(clientBuffer->data(), view.data(), view.capacity());
- return true;
- }
-};
-
-class GraphicOutputBuffersArray : public OutputBuffersArray {
-public:
- using OutputBuffersArray::OutputBuffersArray;
-
- bool copy(
- const std::shared_ptr<C2Buffer> &buffer,
- const sp<MediaCodecBuffer> &clientBuffer) final {
- if (!buffer) {
- clientBuffer->setRange(0u, 0u);
- return true;
- }
- clientBuffer->setRange(0u, 1u);
- return true;
- }
-};
-
-// Flexible in a sense that it does not have fixed array size.
class FlexOutputBuffers : public CCodecBufferChannel::OutputBuffers {
public:
using CCodecBufferChannel::OutputBuffers::OutputBuffers;
@@ -545,56 +583,31 @@
bool registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) override {
- *codecBuffer = nullptr;
- ssize_t ret = findBufferSlot<BufferInfo>(
- &mBuffers,
- std::numeric_limits<size_t>::max(),
- [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
- sp<MediaCodecBuffer> newBuffer = convert(buffer);
- mBuffers[ret] = { newBuffer, buffer };
- *index = ret;
- *codecBuffer = newBuffer;
+ sp<MediaCodecBuffer> *clientBuffer) override {
+ sp<Codec2Buffer> newBuffer = wrap(buffer);
+ *index = mImpl.assignSlot(newBuffer);
+ *clientBuffer = newBuffer;
return true;
}
bool registerCsd(
const C2StreamCsdInfo::output *csd,
size_t *index,
- sp<MediaCodecBuffer> *codecBuffer) final {
- *codecBuffer = nullptr;
- ssize_t ret = findBufferSlot<BufferInfo>(
- &mBuffers,
- std::numeric_limits<size_t>::max(),
- [] (const auto &elem) { return elem.clientBuffer.promote() == nullptr; });
- if (ret < 0) {
- return false;
- }
+ sp<MediaCodecBuffer> *clientBuffer) final {
// TODO: proper format update
sp<ABuffer> csdBuffer = ABuffer::CreateAsCopy(csd->m.value, csd->flexCount());
mFormat = mFormat->dup();
mFormat->setBuffer("csd-0", csdBuffer);
- sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(mFormat, csdBuffer);
- mBuffers[ret] = { newBuffer, nullptr };
- *index = ret;
- *codecBuffer = newBuffer;
+
+ sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(mFormat, csdBuffer);
+ *index = mImpl.assignSlot(newBuffer);
+ *clientBuffer = newBuffer;
return true;
}
std::shared_ptr<C2Buffer> releaseBuffer(
const sp<MediaCodecBuffer> &buffer) override {
- auto it = std::find_if(
- mBuffers.begin(), mBuffers.end(),
- [buffer] (const auto &elem) {
- return elem.clientBuffer.promote() == buffer;
- });
- if (it == mBuffers.end()) {
- return nullptr;
- }
- return std::move(it->bufferRef);
+ return mImpl.releaseSlot(buffer);
}
void flush(
@@ -604,27 +617,31 @@
// track of the flushed work.
}
- virtual sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) = 0;
+ std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
+ std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray);
+ array->setFormat(mFormat);
+ array->initialize(
+ mImpl,
+ kMinBufferArraySize,
+ [this]() { return allocateArrayBuffer(); });
+ return std::move(array);
+ }
-protected:
- struct BufferInfo {
- // wp<> of MediaCodecBuffer for MediaCodec.
- wp<MediaCodecBuffer> clientBuffer;
- // Buffer reference to hold until clientBuffer is valid.
- std::shared_ptr<C2Buffer> bufferRef;
- };
- // Buffers we passed to the client. The index of a buffer matches what
- // was passed in BufferCallback::onInputBufferAvailable().
- std::vector<BufferInfo> mBuffers;
+ virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
+
+ virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
+
+private:
+ FlexBuffersImpl mImpl;
};
class LinearOutputBuffers : public FlexOutputBuffers {
public:
using FlexOutputBuffers::FlexOutputBuffers;
- virtual sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
if (buffer == nullptr) {
- return new MediaCodecBuffer(mFormat, new ABuffer(nullptr, 0));
+ return new DummyContainerBuffer(mFormat, buffer);
}
if (buffer->data().type() != C2BufferData::LINEAR) {
// We expect linear output buffers from the component.
@@ -634,28 +651,12 @@
// We expect one and only one linear block from the component.
return nullptr;
}
- return ConstLinearBlockBuffer::allocate(mFormat, buffer->data().linearBlocks().front());
+ return ConstLinearBlockBuffer::Allocate(mFormat, buffer);
}
- std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
- std::unique_ptr<OutputBuffersArray> array(new LinearOutputBuffersArray);
- array->setFormat(mFormat);
-
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
- std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
- bool available = false;
- if (clientBuffer == nullptr) {
- // TODO: proper max input size
- clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(kLinearBufferSize));
- available = true;
- compBuffer.reset();
- }
- array->add(i, clientBuffer, compBuffer, available);
- }
- return std::move(array);
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ // TODO: proper max output size
+ return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
}
};
@@ -663,29 +664,12 @@
public:
using FlexOutputBuffers::FlexOutputBuffers;
- sp<MediaCodecBuffer> convert(const std::shared_ptr<C2Buffer> &buffer) override {
- return new MediaCodecBuffer(
- mFormat, buffer ? new ABuffer(nullptr, 1) : new ABuffer(nullptr, 0));
+ sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
+ return new DummyContainerBuffer(mFormat, buffer);
}
- std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode() override {
- std::unique_ptr<OutputBuffersArray> array(new GraphicOutputBuffersArray);
- array->setFormat(mFormat);
-
- const size_t size = std::max(kMinBufferArraySize, mBuffers.size());
- mBuffers.resize(size);
- for (size_t i = 0; i < size; ++i) {
- sp<MediaCodecBuffer> clientBuffer = mBuffers[i].clientBuffer.promote();
- std::shared_ptr<C2Buffer> compBuffer = mBuffers[i].bufferRef;
- bool available = false;
- if (clientBuffer == nullptr) {
- clientBuffer = new MediaCodecBuffer(mFormat, new ABuffer(nullptr, 1));
- available = true;
- compBuffer.reset();
- }
- array->add(i, clientBuffer, compBuffer, available);
- }
- return std::move(array);
+ sp<Codec2Buffer> allocateArrayBuffer() override {
+ return new DummyContainerBuffer(mFormat);
}
};
diff --git a/media/libstagefright/codec2/SimpleC2Component.cpp b/media/libstagefright/codec2/SimpleC2Component.cpp
index 01e9e43..aaefd31 100644
--- a/media/libstagefright/codec2/SimpleC2Component.cpp
+++ b/media/libstagefright/codec2/SimpleC2Component.cpp
@@ -366,26 +366,6 @@
}
}
-namespace {
-
-class GraphicBuffer : public C2Buffer {
-public:
- GraphicBuffer(
- const std::shared_ptr<C2GraphicBlock> &block,
- const C2Rect &crop)
- : C2Buffer({ block->share(crop, ::android::C2Fence()) }) {}
-};
-
-
-class LinearBuffer : public C2Buffer {
-public:
- LinearBuffer(
- const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size)
- : C2Buffer({ block->share(offset, size, ::android::C2Fence()) }) {}
-};
-
-} // namespace
-
std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
const std::shared_ptr<C2LinearBlock> &block) {
return createLinearBuffer(block, block->offset(), block->size());
@@ -393,7 +373,7 @@
std::shared_ptr<C2Buffer> SimpleC2Component::createLinearBuffer(
const std::shared_ptr<C2LinearBlock> &block, size_t offset, size_t size) {
- return std::make_shared<LinearBuffer>(block, offset, size);
+ return C2Buffer::CreateLinearBuffer(block->share(offset, size, ::android::C2Fence()));
}
std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
@@ -402,9 +382,8 @@
}
std::shared_ptr<C2Buffer> SimpleC2Component::createGraphicBuffer(
- const std::shared_ptr<C2GraphicBlock> &block,
- const C2Rect &crop) {
- return std::make_shared<GraphicBuffer>(block, crop);
+ const std::shared_ptr<C2GraphicBlock> &block, const C2Rect &crop) {
+ return C2Buffer::CreateGraphicBuffer(block->share(crop, ::android::C2Fence()));
}
} // namespace android
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
index 6d85e83..9766b41 100644
--- a/media/libstagefright/include/Codec2Buffer.h
+++ b/media/libstagefright/include/Codec2Buffer.h
@@ -24,17 +24,100 @@
namespace android {
+class Codec2Buffer : public MediaCodecBuffer {
+public:
+ using MediaCodecBuffer::MediaCodecBuffer;
+ ~Codec2Buffer() override = default;
+
+ /**
+ * \return C2Buffer object represents this buffer.
+ */
+ virtual std::shared_ptr<C2Buffer> asC2Buffer() = 0;
+
+ /**
+ * Test if we can copy the content of |buffer| into this object.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if the content of buffer can be copied over to this buffer
+ * false otherwise.
+ */
+ virtual bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const {
+ (void)buffer;
+ return false;
+ }
+
+ /**
+ * Copy the content of |buffer| into this object. This method assumes that
+ * canCopy() check already passed.
+ *
+ * \param buffer C2Buffer object to copy.
+ * \return true if successful
+ * false otherwise.
+ */
+ virtual bool copy(const std::shared_ptr<C2Buffer> &buffer) {
+ (void)buffer;
+ return false;
+ }
+
+protected:
+ /**
+ * canCopy() implementation for linear buffers.
+ */
+ bool canCopyLinear(const std::shared_ptr<C2Buffer> &buffer) const;
+
+ /**
+ * copy() implementation for linear buffers.
+ */
+ bool copyLinear(const std::shared_ptr<C2Buffer> &buffer);
+};
+
+/**
+ * MediaCodecBuffer implementation on top of local linear buffer. This cannot
+ * cross process boundary so asC2Buffer() returns only nullptr.
+ */
+class LocalLinearBuffer : public Codec2Buffer {
+public:
+ using Codec2Buffer::Codec2Buffer;
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override { return nullptr; }
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+};
+
+/**
+ * MediaCodecBuffer implementation to be used only as a dummy wrapper around a
+ * C2Buffer object.
+ */
+class DummyContainerBuffer : public Codec2Buffer {
+public:
+ DummyContainerBuffer(
+ const sp<AMessage> &format,
+ const std::shared_ptr<C2Buffer> &buffer = nullptr);
+
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
+
+private:
+ std::shared_ptr<C2Buffer> mBufferRef;
+};
+
/**
* MediaCodecBuffer implementation wraps around C2LinearBlock.
*/
-class LinearBlockBuffer : public MediaCodecBuffer {
+class LinearBlockBuffer : public Codec2Buffer {
public:
- static sp<LinearBlockBuffer> allocate(
+ /**
+ * Allocate a new LinearBufferBlock wrapping around C2LinearBlock object.
+ */
+ static sp<LinearBlockBuffer> Allocate(
const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
virtual ~LinearBlockBuffer() = default;
- C2ConstLinearBlock share();
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+ bool canCopy(const std::shared_ptr<C2Buffer> &buffer) const override;
+ bool copy(const std::shared_ptr<C2Buffer> &buffer) override;
private:
LinearBlockBuffer(
@@ -50,20 +133,27 @@
/**
* MediaCodecBuffer implementation wraps around C2ConstLinearBlock.
*/
-class ConstLinearBlockBuffer : public MediaCodecBuffer {
+class ConstLinearBlockBuffer : public Codec2Buffer {
public:
- static sp<ConstLinearBlockBuffer> allocate(
- const sp<AMessage> &format, const C2ConstLinearBlock &block);
+ /**
+ * Allocate a new ConstLinearBlockBuffer wrapping around C2Buffer object.
+ */
+ static sp<ConstLinearBlockBuffer> Allocate(
+ const sp<AMessage> &format, const std::shared_ptr<C2Buffer> &buffer);
virtual ~ConstLinearBlockBuffer() = default;
+ std::shared_ptr<C2Buffer> asC2Buffer() override;
+
private:
ConstLinearBlockBuffer(
const sp<AMessage> &format,
- C2ReadView &&readView);
+ C2ReadView &&readView,
+ const std::shared_ptr<C2Buffer> &buffer);
ConstLinearBlockBuffer() = delete;
C2ReadView mReadView;
+ std::shared_ptr<C2Buffer> mBufferRef;
};
} // namespace android
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index 9538c3d..63c0697 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -37,7 +37,7 @@
// not to expose other methods to the controller whose connection wasn't accepted.
// But this would be enough for now because it's the same as existing
// MediaBrowser and MediaBrowserService.
- void connect(String callingPackage, IMediaSession2Callback callback);
+ void connect(IMediaSession2Callback caller, String callingPackage);
void release(IMediaSession2Callback caller);
void setVolumeTo(IMediaSession2Callback caller, int value, int flags);
@@ -52,21 +52,24 @@
void sendCustomCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args,
in ResultReceiver receiver);
- void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
- void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extra);
- void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extra);
- void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extra);
+ void prepareFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void prepareFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void prepareFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void playFromUri(IMediaSession2Callback caller, in Uri uri, in Bundle extras);
+ void playFromSearch(IMediaSession2Callback caller, String query, in Bundle extras);
+ void playFromMediaId(IMediaSession2Callback caller, String mediaId, in Bundle extras);
+ void setRating(IMediaSession2Callback caller, String mediaId, in Bundle rating);
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Get library service specific
//////////////////////////////////////////////////////////////////////////////////////////////
- void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
- void getItem(IMediaSession2Callback callback, String mediaId);
- void getChildren(IMediaSession2Callback callback, String parentId, int page, int pageSize,
+ // library service specific
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ void getBrowserRoot(IMediaSession2Callback caller, in Bundle rootHints);
+ void getItem(IMediaSession2Callback caller, String mediaId);
+ void getChildren(IMediaSession2Callback caller, String parentId, int page, int pageSize,
in Bundle extras);
- void search(IMediaSession2Callback callback, String query, in Bundle extras);
- void getSearchResult(IMediaSession2Callback callback, String query, int page, int pageSize,
+ void search(IMediaSession2Callback caller, String query, in Bundle extras);
+ void getSearchResult(IMediaSession2Callback caller, String query, int page, int pageSize,
in Bundle extras);
+ void subscribe(IMediaSession2Callback caller, String parentId, in Bundle extras);
+ void unsubscribe(IMediaSession2Callback caller, String parentId);
}
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
index b3aa59c..9a0be7a 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl
@@ -36,7 +36,7 @@
// TODO(jaewan): Handle when the playlist becomes too huge.
void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState,
- in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, int ratingType,
+ in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist,
in PendingIntent sessionActivity);
void onDisconnected();
@@ -49,8 +49,10 @@
//////////////////////////////////////////////////////////////////////////////////////////////
void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
void onItemLoaded(String mediaId, in Bundle result);
- void onChildrenLoaded(String parentId, int page, int pageSize, in Bundle extras,
- in List<Bundle> result);
- void onSearchResultLoaded(String query, int page, int pageSize, in Bundle extras,
- in List<Bundle> result);
+ void onChildrenChanged(String rootMediaId, int childCount, in Bundle extras);
+ void onChildrenLoaded(String parentId, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
+ void onSearchResultChanged(String query, int itemCount, in Bundle extras);
+ void onSearchResultLoaded(String query, int page, int pageSize, in List<Bundle> result,
+ in Bundle extras);
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
index 76da42b..c095187 100644
--- a/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaBrowser2Impl.java
@@ -20,7 +20,6 @@
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaItem2;
-import android.media.MediaSession2.CommandButton;
import android.media.SessionToken2;
import android.media.update.MediaBrowser2Provider;
import android.os.Bundle;
@@ -64,12 +63,36 @@
@Override
public void subscribe_impl(String parentId, Bundle extras) {
- // TODO(jaewan): Implement
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.subscribe(getControllerStub(), parentId, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
- public void unsubscribe_impl(String parentId, Bundle extras) {
- // TODO(jaewan): Implement
+ public void unsubscribe_impl(String parentId) {
+ final IMediaSession2 binder = getSessionBinder();
+ if (binder != null) {
+ try {
+ binder.unsubscribe(getControllerStub(), parentId);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle disconnect.
+ if (DEBUG) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "Session isn't active", new IllegalStateException());
+ }
}
@Override
@@ -170,17 +193,29 @@
});
}
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
- List<MediaItem2> result) {
+ public void onChildrenLoaded(String parentId, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
getCallbackExecutor().execute(() -> {
- mCallback.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ mCallback.onChildrenLoaded(parentId, page, pageSize, result, extras);
});
}
- public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
- List<MediaItem2> result) {
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
getCallbackExecutor().execute(() -> {
- mCallback.onSearchResultLoaded(query, page, pageSize, extras, result);
+ mCallback.onSearchResultChanged(query, itemCount, extras);
+ });
+ }
+
+ public void onSearchResultLoaded(String query, int page, int pageSize, List<MediaItem2> result,
+ Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onSearchResultLoaded(query, page, pageSize, result, extras);
+ });
+ }
+
+ public void onChildrenChanged(final String parentId, int childCount, final Bundle extras) {
+ getCallbackExecutor().execute(() -> {
+ mCallback.onChildrenChanged(parentId, childCount, extras);
});
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 5af4240..77db355 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -75,8 +75,6 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private int mRatingType;
- @GuardedBy("mLock")
private PendingIntent mSessionActivity;
@GuardedBy("mLock")
private CommandGroup mCommandGroup;
@@ -164,7 +162,7 @@
private void connectToSession(IMediaSession2 sessionBinder) {
try {
- sessionBinder.connect(mContext.getPackageName(), mSessionCallbackStub);
+ sessionBinder.connect(mSessionCallbackStub, mContext.getPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Failed to call connection request. Framework will retry"
+ " automatically");
@@ -286,11 +284,6 @@
}
@Override
- public int getRatingType_impl() {
- return mRatingType;
- }
-
- @Override
public void setVolumeTo_impl(int value, int flags) {
// TODO(hdmoon): sanity check
final IMediaSession2 binder = mSessionBinder;
@@ -403,9 +396,26 @@
// TODO(jaewan): Handle.
}
}
+
@Override
- public void setRating_impl(Rating2 rating) {
- // TODO(jaewan): Implement
+ public void setRating_impl(String mediaId, Rating2 rating) {
+ if (mediaId == null) {
+ throw new IllegalArgumentException("mediaId shouldn't be null");
+ }
+ if (rating == null) {
+ throw new IllegalArgumentException("rating shouldn't be null");
+ }
+
+ final IMediaSession2 binder = mSessionBinder;
+ if (binder != null) {
+ try {
+ binder.setRating(mSessionCallbackStub, mediaId, rating.toBundle());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+ }
+ } else {
+ // TODO(jaewan): Handle.
+ }
}
@Override
@@ -563,7 +573,7 @@
// Should be used without a lock to prevent potential deadlock.
void onConnectedNotLocked(IMediaSession2 sessionBinder,
final CommandGroup commandGroup, final PlaybackState2 state, final PlaybackInfo info,
- final PlaylistParams params, final List<MediaItem2> playlist, final int ratingType,
+ final PlaylistParams params, final List<MediaItem2> playlist,
final PendingIntent sessionActivity) {
if (DEBUG) {
Log.d(TAG, "onConnectedNotLocked sessionBinder=" + sessionBinder
@@ -591,7 +601,6 @@
mPlaybackInfo = info;
mPlaylistParams = params;
mPlaylist = playlist;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionBinder = sessionBinder;
try {
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index 52db74e..b9d2fa4 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -26,12 +26,12 @@
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaLibraryService2Provider;
import android.os.Bundle;
+import android.text.TextUtils;
import com.android.media.MediaSession2Impl.BuilderBaseImpl;
@@ -68,10 +68,9 @@
implements MediaLibrarySessionProvider {
public MediaLibrarySessionImpl(Context context,
MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider,
- int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
+ PendingIntent sessionActivity, Executor callbackExecutor,
MediaLibrarySessionCallback callback) {
- super(context, player, id, volumeProvider, ratingType, sessionActivity,
- callbackExecutor, callback);
+ super(context, player, id, volumeProvider, sessionActivity, callbackExecutor, callback);
// Don't put any extra initialization here. Here's the reason.
// System service will recognize this session inside of the super constructor and would
// connect to this session assuming that initialization is finished. However, if any
@@ -96,13 +95,36 @@
@Override
public void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
- Bundle options) {
- // TODO(jaewan): Implements
+ int childCount, Bundle extras) {
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(controller, parentId, childCount,
+ extras);
}
@Override
- public void notifyChildrenChanged_impl(String parentId, Bundle options) {
- // TODO(jaewan): Implements
+ public void notifyChildrenChanged_impl(String parentId, int childCount, Bundle extras) {
+ if (parentId == null) {
+ throw new IllegalArgumentException("parentId shouldn't be null");
+ }
+ getSessionStub().notifyChildrenChangedNotLocked(parentId, childCount, extras);
+ }
+
+ @Override
+ public void notifySearchResultChanged_impl(ControllerInfo controller, String query,
+ int itemCount, Bundle extras) {
+ ensureCallingThread();
+ if (controller == null) {
+ throw new IllegalArgumentException("controller shouldn't be null");
+ }
+ if (TextUtils.isEmpty(query)) {
+ throw new IllegalArgumentException("query shouldn't be empty");
+ }
+ getSessionStub().notifySearchResultChanged(controller, query, itemCount, extras);
}
}
@@ -117,7 +139,7 @@
@Override
public MediaLibrarySession build_impl() {
- return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaLibrarySessionImpl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
@@ -148,4 +170,4 @@
return mExtras;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
index 852029a..e174d91 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2CallbackStub.java
@@ -125,7 +125,7 @@
@Override
public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup,
Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle>
- playlist, int ratingType, PendingIntent sessionActivity) {
+ playlist, PendingIntent sessionActivity) {
final MediaController2Impl controller = mController.get();
if (controller == null) {
if (DEBUG) {
@@ -146,7 +146,7 @@
PlaybackState2.fromBundle(context, playbackState),
PlaybackInfoImpl.fromBundle(context, playbackInfo),
PlaylistParams.fromBundle(context, playlistParams),
- list, ratingType, sessionActivity);
+ list, sessionActivity);
}
@Override
@@ -244,8 +244,8 @@
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle extras,
- List<Bundle> itemBundleList) throws RuntimeException {
+ public void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -265,12 +265,29 @@
result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
}
}
- browser.onChildrenLoaded(parentId, page, pageSize, extras, result);
+ browser.onChildrenLoaded(parentId, page, pageSize, result, extras);
}
@Override
- public void onSearchResultLoaded(String query, int page, int pageSize, Bundle extras,
- List<Bundle> itemBundleList) throws RuntimeException {
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras)
+ throws RuntimeException {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onSearchResultChanged(query, itemCount, extras);
+ }
+
+ @Override
+ public void onSearchResultLoaded(String query, int page, int pageSize,
+ List<Bundle> itemBundleList, Bundle extras) throws RuntimeException {
final MediaBrowser2Impl browser;
try {
browser = getBrowser();
@@ -290,6 +307,22 @@
result.add(MediaItem2.fromBundle(browser.getContext(), bundle));
}
}
- browser.onSearchResultLoaded(query, page, pageSize, extras, result);
+ browser.onSearchResultLoaded(query, page, pageSize, result, extras);
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int childCount, Bundle extras) {
+ final MediaBrowser2Impl browser;
+ try {
+ browser = getBrowser();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Don't fail silently here. Highly likely a bug");
+ return;
+ }
+ if (browser == null) {
+ // TODO(jaewan): Revisit here. Could be a bug
+ return;
+ }
+ browser.onChildrenChanged(parentId, childCount, extras);
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 4a9a729..a32ea0a 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -38,7 +38,7 @@
import android.media.MediaLibraryService2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -62,6 +62,7 @@
import android.os.ResultReceiver;
import android.support.annotation.GuardedBy;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -84,8 +85,7 @@
private final MediaSession2Stub mSessionStub;
private final SessionToken2 mSessionToken;
private final AudioManager mAudioManager;
- private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
- private final int mRatingType;
+ private final ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
private final PendingIntent mSessionActivity;
// mPlayer is set to null when the session is closed, and we shouldn't throw an exception
@@ -111,7 +111,7 @@
@GuardedBy("mLock")
private PlaybackInfo mPlaybackInfo;
@GuardedBy("mLock")
- private MyPlaybackListener mListener;
+ private MyEventCallback mEventCallback;
/**
* Can be only called by the {@link Builder#build()}.
@@ -119,13 +119,13 @@
* @param context
* @param player
* @param id
- * @param callback
* @param volumeProvider
- * @param ratingType
* @param sessionActivity
+ * @param callbackExecutor
+ * @param callback
*/
public MediaSession2Impl(Context context, MediaPlayerInterface player, String id,
- VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity,
+ VolumeProvider2 volumeProvider, PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
// TODO(jaewan): Keep other params.
mInstance = createInstance();
@@ -136,7 +136,6 @@
mId = id;
mCallback = callback;
mCallbackExecutor = callbackExecutor;
- mRatingType = ratingType;
mSessionActivity = sessionActivity;
mSessionStub = new MediaSession2Stub(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -225,19 +224,20 @@
}
private void setPlayer(MediaPlayerInterface player, VolumeProvider2 volumeProvider) {
- PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
+ final PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes());
synchronized (mLock) {
- if (mPlayer != null && mListener != null) {
+ if (mPlayer != null && mEventCallback != null) {
// This might not work for a poorly implemented player.
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
}
mPlayer = player;
- mListener = new MyPlaybackListener(this, player);
- player.addPlaybackListener(mCallbackExecutor, mListener);
+ mEventCallback = new MyEventCallback(this, player);
+ player.registerEventCallback(mCallbackExecutor, mEventCallback);
mVolumeProvider = volumeProvider;
mPlaybackInfo = info;
}
mSessionStub.notifyPlaybackInfoChanged(info);
+ notifyPlaybackStateChangedNotLocked(mInstance.getPlaybackState());
}
private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) {
@@ -293,7 +293,7 @@
synchronized (mLock) {
if (mPlayer != null) {
// close can be called multiple times
- mPlayer.removePlaybackListener(mListener);
+ mPlayer.unregisterEventCallback(mEventCallback);
mPlayer = null;
}
}
@@ -522,32 +522,31 @@
}
@Override
- public void addPlaybackListener_impl(Executor executor, PlaybackListener listener) {
+ public void registerPlayerEventCallback_impl(Executor executor, EventCallback callback) {
if (executor == null) {
throw new IllegalArgumentException("executor shouldn't be null");
}
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- if (PlaybackListenerHolder.contains(mListeners, listener)) {
- Log.w(TAG, "listener is already added. Ignoring.");
+ if (mCallbacks.get(callback) != null) {
+ Log.w(TAG, "callback is already added. Ignoring.");
return;
}
- mListeners.add(new PlaybackListenerHolder(executor, listener));
- executor.execute(() -> listener.onPlaybackChanged(getInstance().getPlaybackState()));
+ mCallbacks.put(callback, executor);
+ // TODO(jaewan): Double check if we need this.
+ final PlaybackState2 state = getInstance().getPlaybackState();
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
@Override
- public void removePlaybackListener_impl(PlaybackListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener shouldn't be null");
+ public void unregisterPlayerEventCallback_impl(EventCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback shouldn't be null");
}
ensureCallingThread();
- int idx = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (idx >= 0) {
- mListeners.remove(idx);
- }
+ mCallbacks.remove(callback);
}
@Override
@@ -580,7 +579,7 @@
// 1. Allow calls from random threads for all methods.
// 2. Allow calls from random threads for all methods, except for the
// {@link #setPlayer()}.
- private void ensureCallingThread() {
+ void ensureCallingThread() {
// TODO(jaewan): Uncomment or remove
/*
if (mHandler.getLooper() != Looper.myLooper()) {
@@ -588,19 +587,35 @@
}*/
}
- private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
- List<PlaybackListenerHolder> listeners = new ArrayList<>();
+ private void notifyPlaybackStateChangedNotLocked(final PlaybackState2 state) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
synchronized (mLock) {
- listeners.addAll(mListeners);
+ callbacks.putAll(mCallbacks);
}
- // Notify to listeners added directly to this session
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).postPlaybackChange(state);
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
}
// Notify to controllers as well.
mSessionStub.notifyPlaybackStateChangedNotLocked(state);
}
+ private void notifyErrorNotLocked(String mediaId, int what, int extra) {
+ ArrayMap<EventCallback, Executor> callbacks = new ArrayMap<>();
+ synchronized (mLock) {
+ callbacks.putAll(mCallbacks);
+ }
+ // Notify to callbacks added directly to this session
+ for (int i = 0; i < callbacks.size(); i++) {
+ final EventCallback callback = callbacks.keyAt(i);
+ final Executor executor = callbacks.valueAt(i);
+ executor.execute(() -> callback.onError(mediaId, what, extra));
+ }
+ // TODO(jaewan): Notify to controllers as well.
+ }
+
Context getContext() {
return mContext;
}
@@ -621,6 +636,10 @@
return mCallback;
}
+ MediaSession2Stub getSessionStub() {
+ return mSessionStub;
+ }
+
VolumeProvider2 getVolumeProvider() {
return mVolumeProvider;
}
@@ -631,33 +650,47 @@
}
}
- int getRatingType() {
- return mRatingType;
- }
-
PendingIntent getSessionActivity() {
return mSessionActivity;
}
- private static class MyPlaybackListener implements MediaPlayerInterface.PlaybackListener {
+ private static class MyEventCallback implements EventCallback {
private final WeakReference<MediaSession2Impl> mSession;
private final MediaPlayerInterface mPlayer;
- private MyPlaybackListener(MediaSession2Impl session, MediaPlayerInterface player) {
+ private MyEventCallback(MediaSession2Impl session, MediaPlayerInterface player) {
mSession = new WeakReference<>(session);
mPlayer = player;
}
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
MediaSession2Impl session = mSession.get();
if (mPlayer != session.mInstance.getPlayer()) {
Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
new IllegalStateException());
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackStateChanged from player, state=" + state);
+ }
session.notifyPlaybackStateChangedNotLocked(state);
}
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ MediaSession2Impl session = mSession.get();
+ if (mPlayer != session.mInstance.getPlayer()) {
+ Log.w(TAG, "Unexpected playback state change notifications. Ignoring.",
+ new IllegalStateException());
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onError from player, mediaId=" + mediaId + ", what=" + what
+ + ", extra=" + extra);
+ }
+ session.notifyErrorNotLocked(mediaId, what, extra);
+ }
}
public static final class CommandImpl implements CommandProvider {
@@ -1167,7 +1200,6 @@
Executor mCallbackExecutor;
C mCallback;
VolumeProvider2 mVolumeProvider;
- int mRatingType;
PendingIntent mSessionActivity;
/**
@@ -1196,10 +1228,6 @@
mVolumeProvider = volumeProvider;
}
- public void setRatingType_impl(int type) {
- mRatingType = type;
- }
-
public void setSessionActivity_impl(PendingIntent pi) {
mSessionActivity = pi;
}
@@ -1239,7 +1267,7 @@
mCallback = new SessionCallback(mContext);
}
- return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider, mRatingType,
+ return new MediaSession2Impl(mContext, mPlayer, mId, mVolumeProvider,
mSessionActivity, mCallbackExecutor, mCallback).getInstance();
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index e61934f..914f85e 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -28,6 +28,7 @@
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
import android.media.PlaybackState2;
+import android.media.Rating2;
import android.media.VolumeProvider2;
import android.net.Uri;
import android.os.Binder;
@@ -47,6 +48,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
public class MediaSession2Stub extends IMediaSession2.Stub {
@@ -63,6 +66,8 @@
@GuardedBy("mLock")
private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<ControllerInfo, Set<String>> mSubscriptions = new ArrayMap<>();
public MediaSession2Stub(MediaSession2Impl session) {
mSession = new WeakReference<>(session);
@@ -76,11 +81,11 @@
mControllers.clear();
}
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
((ControllerInfoImpl) list.get(i).getProvider()).getControllerBinder();
try {
// Should be used without a lock hold to prevent potential deadlock.
- callbackBinder.onDisconnected();
+ controllerBinder.onDisconnected();
} catch (RemoteException e) {
// Controller is gone. Should be fine because we're destroying.
}
@@ -117,12 +122,12 @@
//////////////////////////////////////////////////////////////////////////////////////////////
@Override
- public void connect(String callingPackage, final IMediaSession2Callback callback)
+ public void connect(final IMediaSession2Callback caller, String callingPackage)
throws RuntimeException {
final MediaSession2Impl sessionImpl = getSession();
final Context context = sessionImpl.getContext();
final ControllerInfo request = new ControllerInfo(context,
- Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, callback);
+ Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, caller);
sessionImpl.getCallbackExecutor().execute(() -> {
final MediaSession2Impl session = mSession.get();
if (session == null) {
@@ -155,11 +160,10 @@
// TODO(jaewan): Should we protect getting playback state?
final PlaybackState2 state = session.getInstance().getPlaybackState();
final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null;
- final Bundle playbackInfoBundle =
- ((MediaController2Impl.PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle();
+ final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl)
+ session.getPlaybackInfo().getProvider()).toBundle();
final PlaylistParams params = session.getInstance().getPlaylistParams();
final Bundle paramsBundle = (params != null) ? params.toBundle() : null;
- final int ratingType = session.getRatingType();
final PendingIntent sessionActivity = session.getSessionActivity();
final List<MediaItem2> playlist = session.getInstance().getPlaylist();
final List<Bundle> playlistBundle = new ArrayList<>();
@@ -182,9 +186,9 @@
return;
}
try {
- callback.onConnected(MediaSession2Stub.this,
+ caller.onConnected(MediaSession2Stub.this,
allowedCommands.toBundle(), playbackStateBundle, playbackInfoBundle,
- paramsBundle, playlistBundle, ratingType, sessionActivity);
+ paramsBundle, playlistBundle, sessionActivity);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle here.
@@ -194,7 +198,7 @@
Log.d(TAG, "Rejecting connection, request=" + request);
}
try {
- callback.onDisconnected();
+ caller.onDisconnected();
} catch (RemoteException e) {
// Controller may be died prematurely.
// Not an issue because we'll ignore it anyway.
@@ -210,6 +214,7 @@
if (DEBUG) {
Log.d(TAG, "releasing " + controllerInfo);
}
+ mSubscriptions.remove(controllerInfo);
}
}
@@ -389,7 +394,7 @@
@Override
public void prepareFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -403,13 +408,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromUri(controller, uri, extra);
+ session.getCallback().onPrepareFromUri(controller, uri, extras);
});
}
@Override
public void prepareFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -423,13 +428,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromSearch(controller, query, extra);
+ session.getCallback().onPrepareFromSearch(controller, query, extras);
});
}
@Override
public void prepareFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -443,13 +448,13 @@
if (session == null) {
return;
}
- session.getCallback().onPrepareFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPrepareFromMediaId(controller, mediaId, extras);
});
}
@Override
public void playFromUri(final IMediaSession2Callback caller, final Uri uri,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -463,13 +468,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromUri(controller, uri, extra);
+ session.getCallback().onPlayFromUri(controller, uri, extras);
});
}
@Override
public void playFromSearch(final IMediaSession2Callback caller, final String query,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -483,13 +488,13 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromSearch(controller, query, extra);
+ session.getCallback().onPlayFromSearch(controller, query, extras);
});
}
@Override
public void playFromMediaId(final IMediaSession2Callback caller, final String mediaId,
- final Bundle extra) {
+ final Bundle extras) {
final MediaSession2Impl sessionImpl = getSession();
final ControllerInfo controller = getController(caller);
if (controller == null) {
@@ -503,7 +508,28 @@
if (session == null) {
return;
}
- session.getCallback().onPlayFromMediaId(controller, mediaId, extra);
+ session.getCallback().onPlayFromMediaId(controller, mediaId, extras);
+ });
+ }
+
+ @Override
+ public void setRating(final IMediaSession2Callback caller, final String mediaId,
+ final Bundle ratingBundle) {
+ final MediaSession2Impl sessionImpl = getSession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ Rating2 rating = Rating2Impl.fromBundle(session.getContext(), ratingBundle);
+ session.getCallback().onSetRating(controller, mediaId, rating);
});
}
@@ -621,7 +647,7 @@
try {
controllerImpl.getControllerBinder().onChildrenLoaded(
- parentId, page, pageSize, extras, bundleList);
+ parentId, page, pageSize, bundleList, extras);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle this.
@@ -704,7 +730,7 @@
try {
controllerImpl.getControllerBinder().onSearchResultLoaded(
- query, page, pageSize, extras, bundleList);
+ query, page, pageSize, bundleList, extras);
} catch (RemoteException e) {
// Controller may be died prematurely.
// TODO(jaewan): Handle this.
@@ -712,6 +738,56 @@
});
}
+ @Override
+ public void subscribe(final IMediaSession2Callback caller, final String parentId,
+ final Bundle option) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "subscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onSubscribed(controller, parentId, option);
+ synchronized (mLock) {
+ Set<String> subscription = mSubscriptions.get(controller);
+ if (subscription == null) {
+ subscription = new HashSet<>();
+ mSubscriptions.put(controller, subscription);
+ }
+ subscription.add(parentId);
+ }
+ });
+ }
+
+ @Override
+ public void unsubscribe(final IMediaSession2Callback caller, final String parentId) {
+ final MediaLibrarySessionImpl sessionImpl = getLibrarySession();
+ final ControllerInfo controller = getController(caller);
+ if (controller == null) {
+ if (DEBUG) {
+ Log.d(TAG, "unsubscribe() from a browser that hasn't connected. Ignore");
+ }
+ return;
+ }
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaLibrarySessionImpl session = getLibrarySession();
+ if (session == null) {
+ return;
+ }
+ session.getCallback().onUnsubscribed(controller, parentId);
+ synchronized (mLock) {
+ mSubscriptions.remove(controller);
+ }
+ });
+ }
+
//////////////////////////////////////////////////////////////////////////////////////////////
// APIs for MediaSession2Impl
//////////////////////////////////////////////////////////////////////////////////////////////
@@ -731,11 +807,11 @@
public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
final Bundle bundle = state != null ? state.toBundle() : null;
- callbackBinder.onPlaybackStateChanged(bundle);
+ controllerBinder.onPlaybackStateChanged(bundle);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -746,7 +822,7 @@
public void notifyCustomLayoutNotLocked(ControllerInfo controller, List<CommandButton> layout) {
// TODO(jaewan): It's OK to be called while it's connecting, but not OK if the connection
// is rejected. Handle the case.
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
List<Bundle> layoutBundles = new ArrayList<>();
@@ -756,7 +832,7 @@
layoutBundles.add(bundle);
}
}
- callbackBinder.onCustomLayoutChanged(layoutBundles);
+ controllerBinder.onCustomLayoutChanged(layoutBundles);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -778,10 +854,10 @@
}
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistChanged(bundleList);
+ controllerBinder.onPlaylistChanged(bundleList);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -792,10 +868,10 @@
public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaylistParamsChanged(params.toBundle());
+ controllerBinder.onPlaylistParamsChanged(params.toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -806,11 +882,11 @@
public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) {
final List<ControllerInfo> list = getControllers();
for (int i = 0; i < list.size(); i++) {
- IMediaSession2Callback callbackBinder =
+ IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(list.get(i)).getControllerBinder();
try {
- callbackBinder.onPlaybackInfoChanged(
- ((MediaController2Impl.PlaybackInfoImpl) playbackInfo.getProvider()).toBundle());
+ controllerBinder.onPlaybackInfoChanged(((MediaController2Impl.PlaybackInfoImpl)
+ playbackInfo.getProvider()).toBundle());
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
@@ -827,9 +903,9 @@
if (command == null) {
throw new IllegalArgumentException("command shouldn't be null");
}
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
- if (getController(callbackBinder) == null) {
+ if (getController(controllerBinder) == null) {
throw new IllegalArgumentException("Controller is gone");
}
sendCustomCommandInternal(controller, command, args, receiver);
@@ -847,14 +923,64 @@
private void sendCustomCommandInternal(ControllerInfo controller, Command command, Bundle args,
ResultReceiver receiver) {
- final IMediaSession2Callback callbackBinder =
+ final IMediaSession2Callback controllerBinder =
ControllerInfoImpl.from(controller).getControllerBinder();
try {
Bundle commandBundle = command.toBundle();
- callbackBinder.sendCustomCommand(commandBundle, args, receiver);
+ controllerBinder.sendCustomCommand(commandBundle, args, receiver);
} catch (RemoteException e) {
Log.w(TAG, "Controller is gone", e);
// TODO(jaewan): What to do when the controller is gone?
}
}
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // APIs for MediaLibrarySessionImpl
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
+ public void notifySearchResultChanged(ControllerInfo controller, String query, int itemCount,
+ Bundle extras) {
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onSearchResultChanged(query, itemCount, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Controller is gone", e);
+ // TODO(jaewan): What to do when the controller is gone?
+ }
+ }
+
+ public void notifyChildrenChangedNotLocked(ControllerInfo controller, String parentId,
+ int childCount, Bundle extras) {
+ // TODO(jaewan): Handle when controller is disconnected and no longer valid.
+ // Note: Commands may be sent while onConnected() is running. Should we also
+ // consider it as error?
+ notifyChildrenChangedInternalNotLocked(controller, parentId, childCount, extras);
+ }
+
+ public void notifyChildrenChangedNotLocked(String parentId, int childCount, Bundle extras) {
+ final List<ControllerInfo> controllers = getControllers();
+ for (int i = 0; i < controllers.size(); i++) {
+ notifyChildrenChangedInternalNotLocked(controllers.get(i), parentId, childCount,
+ extras);
+ }
+ }
+
+ public void notifyChildrenChangedInternalNotLocked(final ControllerInfo controller,
+ String parentId, int childCount, Bundle extras) {
+ // Ensure subscription
+ synchronized (mLock) {
+ Set<String> subscriptions = mSubscriptions.get(controller);
+ if (subscriptions == null || !subscriptions.contains(parentId)) {
+ return;
+ }
+ }
+ final IMediaSession2Callback callbackBinder =
+ ControllerInfoImpl.from(controller).getControllerBinder();
+ try {
+ callbackBinder.onChildrenChanged(parentId, childCount, extras);
+ } catch (RemoteException e) {
+ // TODO(jaewan): Handle controller removed?
+ }
+ }
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 8773df4..aa5ac84 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -22,7 +22,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSessionService2;
import android.media.MediaSessionService2.MediaNotification;
@@ -42,7 +42,7 @@
private static final boolean DEBUG = true; // TODO(jaewan): Change this.
private final MediaSessionService2 mInstance;
- private final PlaybackListener mListener = new SessionServicePlaybackListener();
+ private final EventCallback mCallback = new SessionServiceEventCallback();
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -94,7 +94,7 @@
+ ", but got " + mSession);
}
// TODO(jaewan): Uncomment here.
- // mSession.addPlaybackListener(mListener, mSession.getExecutor());
+ // mSession.registerPlayerEventCallback(mCallback, mSession.getExecutor());
}
@TokenType int getSessionType() {
@@ -135,9 +135,9 @@
mediaNotification.getNotification());
}
- private class SessionServicePlaybackListener implements PlaybackListener {
+ private class SessionServiceEventCallback implements EventCallback {
@Override
- public void onPlaybackChanged(PlaybackState2 state) {
+ public void onPlaybackStateChanged(PlaybackState2 state) {
if (state == null) {
Log.w(TAG, "Ignoring null playback state");
return;
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 4241f85..0000000
--- a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-package com.android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.media.PlaybackState2;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
index 5eb1129..ee8d6d7 100644
--- a/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/PlaybackState2Impl.java
@@ -30,7 +30,6 @@
private static final String KEY_BUFFERED_POSITION =
"android.media.playbackstate2.buffered_position";
private static final String KEY_SPEED = "android.media.playbackstate2.speed";
- private static final String KEY_ERROR_MESSAGE = "android.media.playbackstate2.error_message";
private static final String KEY_UPDATE_TIME = "android.media.playbackstate2.update_time";
private static final String KEY_ACTIVE_ITEM_ID = "android.media.playbackstate2.active_item_id";
@@ -42,11 +41,9 @@
private final float mSpeed;
private final long mBufferedPosition;
private final long mActiveItemId;
- private final CharSequence mErrorMessage;
public PlaybackState2Impl(Context context, PlaybackState2 instance, int state, long position,
- long updateTime, float speed, long bufferedPosition, long activeItemId,
- CharSequence error) {
+ long updateTime, float speed, long bufferedPosition, long activeItemId) {
mContext = context;
mInstance = instance;
mState = state;
@@ -55,7 +52,6 @@
mUpdateTime = updateTime;
mBufferedPosition = bufferedPosition;
mActiveItemId = activeItemId;
- mErrorMessage = error;
}
@Override
@@ -67,7 +63,6 @@
bob.append(", speed=").append(mSpeed);
bob.append(", updated=").append(mUpdateTime);
bob.append(", active item id=").append(mActiveItemId);
- bob.append(", error=").append(mErrorMessage);
bob.append("}");
return bob.toString();
}
@@ -93,11 +88,6 @@
}
@Override
- public CharSequence getErrorMessage_impl() {
- return mErrorMessage;
- }
-
- @Override
public long getLastPositionUpdateTime_impl() {
return mUpdateTime;
}
@@ -116,7 +106,6 @@
bundle.putFloat(KEY_SPEED, mSpeed);
bundle.putLong(KEY_BUFFERED_POSITION, mBufferedPosition);
bundle.putLong(KEY_ACTIVE_ITEM_ID, mActiveItemId);
- bundle.putCharSequence(KEY_ERROR_MESSAGE, mErrorMessage);
return bundle;
}
@@ -129,18 +118,15 @@
|| !bundle.containsKey(KEY_UPDATE_TIME)
|| !bundle.containsKey(KEY_SPEED)
|| !bundle.containsKey(KEY_BUFFERED_POSITION)
- || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)
- || !bundle.containsKey(KEY_ERROR_MESSAGE)) {
+ || !bundle.containsKey(KEY_ACTIVE_ITEM_ID)) {
return null;
}
-
return new PlaybackState2(context,
bundle.getInt(KEY_STATE),
bundle.getLong(KEY_POSITION),
bundle.getLong(KEY_UPDATE_TIME),
bundle.getFloat(KEY_SPEED),
bundle.getLong(KEY_BUFFERED_POSITION),
- bundle.getLong(KEY_ACTIVE_ITEM_ID),
- bundle.getCharSequence(KEY_ERROR_MESSAGE));
+ bundle.getLong(KEY_ACTIVE_ITEM_ID));
}
}
\ No newline at end of file
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 46812e7..994824d 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -44,7 +44,6 @@
import android.media.MediaSessionService2.MediaNotification;
import android.media.PlaybackState2;
import android.media.Rating2;
-import android.media.SessionPlayer2;
import android.media.SessionToken2;
import android.media.VolumeProvider2;
import android.media.update.MediaBrowser2Provider;
@@ -60,7 +59,6 @@
import android.media.update.MediaSessionService2Provider;
import android.media.update.MediaSessionService2Provider.MediaNotificationProvider;
import android.media.update.PlaybackState2Provider;
-import android.media.update.SessionPlayer2Provider;
import android.media.update.SessionToken2Provider;
import android.media.update.StaticProvider;
import android.media.update.VideoView2Provider;
@@ -230,12 +228,6 @@
}
@Override
- public SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance) {
- // TODO(jaewan): Implement this
- return null;
- }
-
- @Override
public MediaItem2Provider createMediaItem2(Context context, MediaItem2 instance,
String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags) {
return new MediaItem2Impl(context, instance, mediaId, dsd, metadata, flags);
@@ -302,9 +294,9 @@
@Override
public PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance,
int state, long position, long updateTime, float speed, long bufferedPosition,
- long activeItemId, CharSequence error) {
+ long activeItemId) {
return new PlaybackState2Impl(context, instance, state, position, updateTime, speed,
- bufferedPosition, activeItemId, error);
+ bufferedPosition, activeItemId);
}
@Override
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index ecb19c1..a8ce18b 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -92,12 +92,8 @@
private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
private Pair<Executor, VideoView2.OnCustomActionListener> mCustomActionListenerRecord;
- private Pair<Executor, VideoView2.OnPreparedListener> mPreparedListenerRecord;
- private Pair<Executor, VideoView2.OnCompletionListener> mCompletionListenerRecord;
- private Pair<Executor, VideoView2.OnErrorListener> mErrorListenerRecord;
- private Pair<Executor, VideoView2.OnInfoListener> mInfoListenerRecord;
- private Pair<Executor, VideoView2.OnViewTypeChangedListener> mViewTypeChangedListenerRecord;
- private Pair<Executor, VideoView2.OnFullScreenRequestListener> mFullScreenRequestListenerRecord;
+ private VideoView2.OnViewTypeChangedListener mViewTypeChangedListener;
+ private VideoView2.OnFullScreenRequestListener mFullScreenRequestListener;
private VideoViewInterface mCurrentView;
private VideoTextureView mTextureView;
@@ -363,35 +359,13 @@
}
@Override
- public void setOnPreparedListener_impl(Executor executor, VideoView2.OnPreparedListener l) {
- mPreparedListenerRecord = new Pair<>(executor, l);
+ public void setOnViewTypeChangedListener_impl(VideoView2.OnViewTypeChangedListener l) {
+ mViewTypeChangedListener = l;
}
@Override
- public void setOnCompletionListener_impl(Executor executor, VideoView2.OnCompletionListener l) {
- mCompletionListenerRecord = new Pair<>(executor, l);
- }
-
- @Override
- public void setOnErrorListener_impl(Executor executor, VideoView2.OnErrorListener l) {
- mErrorListenerRecord = new Pair<>(executor, l);
- }
-
- @Override
- public void setOnInfoListener_impl(Executor executor, VideoView2.OnInfoListener l) {
- mInfoListenerRecord = new Pair<>(executor, l);
- }
-
- @Override
- public void setOnViewTypeChangedListener_impl(Executor executor,
- VideoView2.OnViewTypeChangedListener l) {
- mViewTypeChangedListenerRecord = new Pair<>(executor, l);
- }
-
- @Override
- public void setFullScreenRequestListener_impl(Executor executor,
- VideoView2.OnFullScreenRequestListener l) {
- mFullScreenRequestListenerRecord = new Pair<>(executor, l);
+ public void setFullScreenRequestListener_impl(VideoView2.OnFullScreenRequestListener l) {
+ mFullScreenRequestListener = l;
}
@Override
@@ -490,10 +464,8 @@
Log.d(TAG, "onSurfaceTakeOverDone(). Now current view is: " + view);
}
mCurrentView = view;
- if (mViewTypeChangedListenerRecord != null) {
- mViewTypeChangedListenerRecord.first.execute(() ->
- mViewTypeChangedListenerRecord.second.onViewTypeChanged(
- mInstance, view.getViewType()));
+ if (mViewTypeChangedListener != null) {
+ mViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
}
if (needToStart()) {
mMediaController.getTransportControls().play();
@@ -845,10 +817,6 @@
updatePlaybackState();
extractSubtitleTracks();
- if (mPreparedListenerRecord != null) {
- mPreparedListenerRecord.first.execute(() ->
- mPreparedListenerRecord.second.onPrepared(mInstance));
- }
if (mMediaControlView != null) {
mMediaControlView.setEnabled(true);
}
@@ -909,10 +877,6 @@
mTargetState = STATE_PLAYBACK_COMPLETED;
updatePlaybackState();
- if (mCompletionListenerRecord != null) {
- mCompletionListenerRecord.first.execute(() ->
- mCompletionListenerRecord.second.onCompletion(mInstance));
- }
if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
mAudioManager.abandonAudioFocus(null);
}
@@ -922,11 +886,6 @@
private MediaPlayer.OnInfoListener mInfoListener =
new MediaPlayer.OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
- if (mInfoListenerRecord != null) {
- mInfoListenerRecord.first.execute(() ->
- mInfoListenerRecord.second.onInfo(mInstance, what, extra));
- }
-
if (what == MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
extractSubtitleTracks();
}
@@ -947,14 +906,6 @@
if (mMediaControlView != null) {
mMediaControlView.setVisibility(View.GONE);
}
-
- /* If an error handler has been supplied, use it and finish. */
- if (mErrorListenerRecord != null) {
- mErrorListenerRecord.first.execute(() ->
- mErrorListenerRecord.second.onError(
- mInstance, frameworkErr, implErr));
- }
-
return true;
}
};
@@ -990,13 +941,10 @@
mInstance.setSubtitleEnabled(false);
break;
case MediaControlView2.COMMAND_SET_FULLSCREEN:
- if (mFullScreenRequestListenerRecord != null) {
- mFullScreenRequestListenerRecord.first.execute(() ->
- mFullScreenRequestListenerRecord.second.onFullScreenRequest(
- mInstance,
- args.getBoolean(
- MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN))
- );
+ if (mFullScreenRequestListener != null) {
+ mFullScreenRequestListener.onFullScreenRequest(
+ mInstance,
+ args.getBoolean(MediaControlView2Impl.ARGUMENT_KEY_FULLSCREEN));
}
break;
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
index b60fde3..7e93232 100644
--- a/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaBrowser2Test.java
@@ -21,16 +21,21 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
import android.annotation.Nullable;
import android.content.Context;
import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.PlaylistParams;
+import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.os.Process;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
@@ -68,8 +73,12 @@
// Browser specific callbacks
default void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {}
default void onItemLoaded(String mediaId, MediaItem2 result) {}
- default void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {}
+ default void onChildrenChanged(String parentId, int childCount, Bundle extras) {}
+ default void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
+ default void onSearchResultChanged(String query, int itemCount, Bundle extras) {}
+ default void onSearchResultLoaded(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {}
}
@Test
@@ -83,7 +92,7 @@
public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
assertTrue(TestUtils.equals(param, rootHints));
assertEquals(MockMediaLibraryService2.ROOT_ID, rootMediaId);
- assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRA, rootExtra));
+ assertTrue(TestUtils.equals(MockMediaLibraryService2.EXTRAS, rootExtra));
latch.countDown();
}
};
@@ -141,23 +150,22 @@
final String parentId = MockMediaLibraryService2.PARENT_ID;
final int page = 4;
final int pageSize = 10;
- final Bundle options = new Bundle();
- options.putString(TAG, TAG);
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
final CountDownLatch latch = new CountDownLatch(1);
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertEquals(parentId, parentIdOut);
assertEquals(page, pageOut);
assertEquals(pageSize, pageSizeOut);
- assertTrue(TestUtils.equals(options, optionsOut));
+ assertTrue(TestUtils.equals(extras, extrasOut));
assertNotNull(result);
int fromIndex = (page - 1) * pageSize;
- int toIndex = Math.min(page * pageSize,
- MockMediaLibraryService2.GET_CHILDREN_RESULT.size());
+ int toIndex = Math.min(page * pageSize, MockMediaLibraryService2.CHILDREN_COUNT);
// Compare the given results with originals.
for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
@@ -173,7 +181,7 @@
final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
- browser.getChildren(parentId, page, pageSize, options);
+ browser.getChildren(parentId, page, pageSize, extras);
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -185,7 +193,7 @@
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertNotNull(result);
assertEquals(0, result.size());
latch.countDown();
@@ -206,7 +214,7 @@
final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
@Override
public void onChildrenLoaded(String parentIdOut, int pageOut, int pageSizeOut,
- Bundle optionsOut, List<MediaItem2> result) {
+ List<MediaItem2> result, Bundle extrasOut) {
assertNull(result);
latch.countDown();
}
@@ -218,6 +226,224 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
+ @Test
+ public void testSearch() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY;
+ final int page = 4;
+ final int pageSize = 10;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latchForSearch = new CountDownLatch(1);
+ final CountDownLatch latchForGetSearchResult = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latchForSearch.countDown();
+ }
+
+ @Override
+ public void onSearchResultLoaded(String queryOut, int pageOut, int pageSizeOut,
+ List<MediaItem2> result, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertEquals(page, pageOut);
+ assertEquals(pageSize, pageSizeOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertNotNull(result);
+
+ int fromIndex = (page - 1) * pageSize;
+ int toIndex = Math.min(
+ page * pageSize, MockMediaLibraryService2.SEARCH_RESULT_COUNT);
+
+ // Compare the given results with originals.
+ for (int originalIndex = fromIndex; originalIndex < toIndex; originalIndex++) {
+ int relativeIndex = originalIndex - fromIndex;
+ assertEquals(
+ MockMediaLibraryService2.SEARCH_RESULT.get(originalIndex).getMediaId(),
+ result.get(relativeIndex).getMediaId());
+ }
+ latchForGetSearchResult.countDown();
+ }
+ };
+
+ // Request the search.
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latchForSearch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ // Get the search result.
+ browser.getSearchResult(query, page, pageSize, extras);
+ assertTrue(latchForGetSearchResult.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchTakesTime() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_TAKES_TIME;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(MockMediaLibraryService2.SEARCH_RESULT_COUNT, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(
+ MockMediaLibraryService2.SEARCH_TIME_IN_MS + WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSearchEmptyResult() throws InterruptedException {
+ final String query = MockMediaLibraryService2.SEARCH_QUERY_EMPTY_RESULT;
+ final Bundle extras = new Bundle();
+ extras.putString(TAG, TAG);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestControllerCallbackInterface callback = new TestBrowserCallbackInterface() {
+ @Override
+ public void onSearchResultChanged(String queryOut, int itemCount, Bundle extrasOut) {
+ assertEquals(query, queryOut);
+ assertTrue(TestUtils.equals(extras, extrasOut));
+ assertEquals(0, itemCount);
+ latch.countDown();
+ }
+ };
+
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token, true, callback);
+ browser.search(query, extras);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+ final String testParentId = "testSubscribeId";
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId, testParentId);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.subscribe(testParentId, testExtras);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testUnsubscribe() throws InterruptedException {
+ final String testParentId = "testUnsubscribeId";
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallbackProxy callbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public void onUnsubscribed(ControllerInfo info, String parentId) {
+ if (Process.myUid() == info.getUid()) {
+ assertEquals(testParentId, parentId);
+ latch.countDown();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(callbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ MediaBrowser2 browser = (MediaBrowser2) createController(token);
+ browser.unsubscribe(testParentId);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testBrowserCallback_notifyChildrenChanged() throws InterruptedException {
+ // TODO(jaewan): Add test for the notifyChildrenChanged itself.
+ final String testParentId1 = "testBrowserCallback_notifyChildrenChanged_unexpectedParent";
+ final String testParentId2 = "testBrowserCallback_notifyChildrenChanged";
+ final int testChildrenCount = 101;
+ final Bundle testExtras = new Bundle();
+ testExtras.putString(testParentId1, testParentId1);
+
+ final CountDownLatch latch = new CountDownLatch(3);
+ final SessionCallbackProxy sessionCallbackProxy = new SessionCallbackProxy(mContext) {
+ @Override
+ public CommandGroup onConnect(ControllerInfo controller) {
+ final MockMediaLibraryService2 service = (MockMediaLibraryService2)
+ TestServiceRegistry.getInstance().getServiceInstance();
+ final MediaLibrarySession session = (MediaLibrarySession) service.getSession();
+ // Shouldn't trigger onChildrenChanged() for the browser, because it hasn't
+ // subscribed.
+ session.notifyChildrenChanged(testParentId1, testChildrenCount, null);
+ session.notifyChildrenChanged(controller, testParentId1, testChildrenCount, null);
+ return super.onConnect(controller);
+ }
+
+ @Override
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extras) {
+ if (Process.myUid() == info.getUid()) {
+ final MediaLibrarySession session = (MediaLibrarySession) mSession;
+ session.notifyChildrenChanged(testParentId2, testChildrenCount, null);
+ session.notifyChildrenChanged(info, testParentId2, testChildrenCount,
+ testExtras);
+ }
+ }
+ };
+ final TestBrowserCallbackInterface controllerCallbackProxy =
+ new TestBrowserCallbackInterface() {
+ @Override
+ public void onChildrenChanged(String parentId, int childCount,
+ Bundle extras) {
+ switch ((int) latch.getCount()) {
+ case 3:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, childCount);
+ assertNull(extras);
+ latch.countDown();
+ break;
+ case 2:
+ assertEquals(testParentId2, parentId);
+ assertEquals(testChildrenCount, childCount);
+ assertTrue(TestUtils.equals(testExtras, extras));
+ latch.countDown();
+ break;
+ default:
+ // Unexpected call.
+ fail();
+ }
+ }
+ };
+ TestServiceRegistry.getInstance().setSessionCallbackProxy(sessionCallbackProxy);
+ final SessionToken2 token = MockMediaLibraryService2.getToken(mContext);
+ final MediaBrowser2 browser = (MediaBrowser2) createController(
+ token, true, controllerCallbackProxy);
+ final MockMediaLibraryService2 service =
+ (MockMediaLibraryService2) TestServiceRegistry.getInstance().getServiceInstance();
+ if (mSession != null) {
+ mSession.close();
+ }
+ mSession = service.getSession();
+ assertTrue(mSession instanceof MediaLibrarySession);
+ browser.subscribe(testParentId2, null);
+ // This ensures that onChildrenChanged() is only called for the expected reasons.
+ assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
public static class TestBrowserCallback extends BrowserCallback
implements WaitForConnectionInterface {
private final TestControllerCallbackInterface mCallbackProxy;
@@ -287,12 +513,40 @@
}
@Override
- public void onChildrenLoaded(String parentId, int page, int pageSize, Bundle options,
- List<MediaItem2> result) {
- super.onChildrenLoaded(parentId, page, pageSize, options, result);
+ public void onChildrenLoaded(String parentId, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onChildrenLoaded(parentId, page, pageSize, result, extras);
if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
((TestBrowserCallbackInterface) mCallbackProxy)
- .onChildrenLoaded(parentId, page, pageSize, options, result);
+ .onChildrenLoaded(parentId, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultChanged(String query, int itemCount, Bundle extras) {
+ super.onSearchResultChanged(query, itemCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onSearchResultChanged(query, itemCount, extras);
+ }
+ }
+
+ @Override
+ public void onSearchResultLoaded(String query, int page, int pageSize,
+ List<MediaItem2> result, Bundle extras) {
+ super.onSearchResultLoaded(query, page, pageSize, result, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onSearchResultLoaded(query, page, pageSize, result, extras);
+ }
+ }
+
+ @Override
+ public void onChildrenChanged(String parentId, int childCount, Bundle extras) {
+ super.onChildrenChanged(parentId, childCount, extras);
+ if (mCallbackProxy instanceof TestBrowserCallbackInterface) {
+ ((TestBrowserCallbackInterface) mCallbackProxy)
+ .onChildrenChanged(parentId, childCount, extras);
}
}
@@ -329,4 +583,4 @@
return mCallback;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index ab63b8e..e162f1d 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -19,7 +19,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
@@ -60,7 +60,6 @@
@FlakyTest
public class MediaController2Test extends MediaSession2TestBase {
private static final String TAG = "MediaController2Test";
- private static final int DEFAULT_RATING_TYPE = Rating2.RATING_5_STARS;
PendingIntent mIntent;
MediaSession2 mSession;
@@ -78,7 +77,6 @@
mPlayer = new MockPlayer(1);
mSession = new MediaSession2.Builder(mContext, mPlayer)
.setSessionCallback(sHandlerExecutor, new SessionCallback(mContext))
- .setRatingType(DEFAULT_RATING_TYPE)
.setSessionActivity(mIntent)
.setId(TAG).build();
mController = createController(mSession.getToken());
@@ -210,11 +208,6 @@
}
@Test
- public void testGetRatingType() throws InterruptedException {
- assertEquals(DEFAULT_RATING_TYPE, mController.getRatingType());
- }
-
- @Test
public void testGetSessionActivity() throws InterruptedException {
PendingIntent sessionActivity = mController.getSessionActivity();
assertEquals(mContext.getPackageName(), sessionActivity.getCreatorPackage());
@@ -389,7 +382,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -413,7 +406,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -437,7 +430,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -458,11 +451,12 @@
final CountDownLatch latch = new CountDownLatch(1);
final SessionCallback callback = new SessionCallback(mContext) {
@Override
- public void onPrepareFromSearch(ControllerInfo controller, String query, Bundle extras) {
+ public void onPrepareFromSearch(ControllerInfo controller, String query,
+ Bundle extras) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, query);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -486,7 +480,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, uri);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -510,7 +504,7 @@
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertEquals(request, id);
assertTrue(TestUtils.equals(bundle, extras));
- latch.countDown();;
+ latch.countDown();
}
};
try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
@@ -523,6 +517,34 @@
}
@Test
+ public void testSetRating() throws InterruptedException {
+ final int ratingType = Rating2.RATING_5_STARS;
+ final float ratingValue = 3.5f;
+ final Rating2 rating = Rating2.newStarRating(mContext, ratingType, ratingValue);
+ final String mediaId = "media_id";
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SessionCallback callback = new SessionCallback(mContext) {
+ @Override
+ public void onSetRating(ControllerInfo controller, String mediaIdOut,
+ Rating2 ratingOut) {
+ assertEquals(mContext.getPackageName(), controller.getPackageName());
+ assertEquals(mediaId, mediaIdOut);
+ assertEquals(rating, ratingOut);
+ latch.countDown();
+ }
+ };
+
+ try (MediaSession2 session = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, callback)
+ .setId("testSetRating").build()) {
+ MediaController2 controller = createController(session.getToken());
+ controller.setRating(mediaId, rating);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+ }
+
+ @Test
public void testIsConnected() throws InterruptedException {
assertTrue(mController.isConnected());
sHandler.postAndSync(()->{
@@ -635,7 +657,7 @@
if (Process.myUid() == controller.getUid()) {
assertEquals(mContext.getPackageName(), controller.getPackageName());
assertFalse(controller.isTrusted());
- latch.countDown();;
+ latch.countDown();
}
return super.onConnect(controller);
}
@@ -653,7 +675,7 @@
// TODO(jaewan): Add equivalent tests again
/*
final CountDownLatch latch = new CountDownLatch(1);
- mController.addPlaybackListener((state) -> {
+ mController.registerPlayerEventCallback((state) -> {
assertNotNull(state);
assertEquals(PlaybackState.STATE_REWINDING, state.getState());
latch.countDown();
@@ -752,16 +774,19 @@
private void testNoInteraction() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- final PlaybackListener playbackListener = (state) -> {
- fail("Controller shouldn't be notified about change in session after the close.");
- latch.countDown();
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ fail("Controller shouldn't be notified about change in session after the close.");
+ latch.countDown();
+ }
};
// TODO(jaewan): Add equivalent tests again
/*
- mController.addPlaybackListener(playbackListener, sHandler);
+ mController.registerPlayerEventCallback(playbackListener, sHandler);
mPlayer.notifyPlaybackState(TestUtils.createPlaybackState(PlaybackState.STATE_BUFFERING));
assertFalse(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- mController.removePlaybackListener(playbackListener);
+ mController.unregisterPlayerEventCallback(playbackListener);
*/
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index f5ac6aa..9de4ce7 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -28,9 +28,8 @@
import static org.junit.Assert.fail;
import android.content.Context;
-import android.media.AudioManager;
import android.media.MediaController2.PlaybackInfo;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -49,7 +48,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -253,51 +251,67 @@
assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
- // TODO(jaewan): Re-enable test..
- @Ignore
@Test
- public void testPlaybackStateChangedListener() throws InterruptedException {
- final CountDownLatch latch = new CountDownLatch(2);
+ public void testRegisterEventCallback() throws InterruptedException {
+ final int testWhat = 1001;
final MockPlayer player = new MockPlayer(0);
- final PlaybackListener listener = (state) -> {
- assertEquals(sHandler.getLooper(), Looper.myLooper());
- assertNotNull(state);
- switch ((int) latch.getCount()) {
- case 2:
- assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
- break;
- case 1:
- assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
- break;
- case 0:
- fail();
+ final CountDownLatch playbackLatch = new CountDownLatch(3);
+ final CountDownLatch errorLatch = new CountDownLatch(1);
+ final EventCallback callback = new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
+ assertEquals(sHandler.getLooper(), Looper.myLooper());
+ switch ((int) playbackLatch.getCount()) {
+ case 3:
+ assertNull(state);
+ break;
+ case 2:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PLAYING, state.getState());
+ break;
+ case 1:
+ assertNotNull(state);
+ assertEquals(PlaybackState2.STATE_PAUSED, state.getState());
+ break;
+ case 0:
+ fail();
+ }
+ playbackLatch.countDown();
}
- latch.countDown();
+
+ @Override
+ public void onError(String mediaId, int what, int extra) {
+ assertEquals(testWhat, what);
+ errorLatch.countDown();
+ }
};
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PLAYING));
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, listener);
- // When the player is set, listeners will be notified about the player's current state.
- mSession.setPlayer(player);
- });
+ // EventCallback will be notified with the mPlayer's playback state (null)
+ mSession.registerPlayerEventCallback(sHandlerExecutor, callback);
+ // When the player is set, EventCallback will be notified about the new player's state.
+ mSession.setPlayer(player);
+ // When the player is set, EventCallback will be notified about the new player's state.
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(playbackLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ player.notifyError(testWhat);
+ assertTrue(errorLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
public void testBadPlayer() throws InterruptedException {
// TODO(jaewan): Add equivalent tests again
- final CountDownLatch latch = new CountDownLatch(3); // expected call + 1
+ final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
final BadPlayer player = new BadPlayer(0);
- sHandler.postAndSync(() -> {
- mSession.addPlaybackListener(sHandlerExecutor, (state) -> {
+ mSession.registerPlayerEventCallback(sHandlerExecutor, new EventCallback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState2 state) {
// This will be called for every setPlayer() calls, but no more.
assertNull(state);
latch.countDown();
- });
- mSession.setPlayer(player);
- mSession.setPlayer(mPlayer);
+ }
});
+ mSession.setPlayer(player);
+ mSession.setPlayer(mPlayer);
player.notifyPlaybackState(createPlaybackState(PlaybackState2.STATE_PAUSED));
assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@@ -308,7 +322,7 @@
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
+ public void unregisterEventCallback(@NonNull EventCallback listener) {
// No-op. This bad player will keep push notification to the listener that is previously
// registered by session.setPlayer().
}
@@ -461,7 +475,8 @@
}
@Override
- public boolean onCommandRequest(ControllerInfo controllerInfo, MediaSession2.Command command) {
+ public boolean onCommandRequest(ControllerInfo controllerInfo,
+ MediaSession2.Command command) {
assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
assertEquals(Process.myUid(), controllerInfo.getUid());
assertFalse(controllerInfo.isTrusted());
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
index f5abfff..7106561 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2TestBase.java
@@ -118,7 +118,7 @@
*/
public PlaybackState2 createPlaybackState(int state) {
return new PlaybackState2(mContext, state, 0, 0, 1.0f,
- 0, 0, null);
+ 0, 0);
}
final MediaController2 createController(SessionToken2 token) throws InterruptedException {
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
index 5fabebc..e1cce25 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaLibraryService2.java
@@ -26,7 +26,6 @@
import android.media.TestServiceRegistry.SessionCallbackProxy;
import android.media.TestUtils.SyncHandler;
import android.os.Bundle;
-import android.os.Process;
import android.util.Log;
import java.io.FileDescriptor;
@@ -34,6 +33,8 @@
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
@@ -45,23 +46,32 @@
public static final String ID = "TestLibrary";
public static final String ROOT_ID = "rootId";
- public static final Bundle EXTRA = new Bundle();
+ public static final Bundle EXTRAS = new Bundle();
public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
public static final String PARENT_ID = "parent_id";
public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
public static final String PARENT_ID_ERROR = "parent_id_error";
- public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
- private static final int CHILDREN_COUNT = 100;
+ public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
+ public static final int CHILDREN_COUNT = 100;
+
+ public static final String SEARCH_QUERY = "search_query";
+ public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
+ public static final int SEARCH_TIME_IN_MS = 5000;
+ public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
+
+ public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
+ public static final int SEARCH_RESULT_COUNT = 50;
+
private static final DataSourceDesc DATA_SOURCE_DESC =
new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
private static final String TAG = "MockMediaLibrarySvc2";
static {
- EXTRA.putString(ROOT_ID, ROOT_ID);
+ EXTRAS.putString(ROOT_ID, ROOT_ID);
}
@GuardedBy("MockMediaLibraryService2.class")
private static SessionToken2 sToken;
@@ -71,9 +81,15 @@
public MockMediaLibraryService2() {
super();
GET_CHILDREN_RESULT.clear();
- String mediaIdPrefix = "media_id_";
+ String getChildrenMediaIdPrefix = "get_children_media_id_";
for (int i = 0; i < CHILDREN_COUNT; i++) {
- GET_CHILDREN_RESULT.add(createMediaItem(mediaIdPrefix + i));
+ GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
+ }
+
+ SEARCH_RESULT.clear();
+ String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
+ for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
+ SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
}
}
@@ -133,7 +149,7 @@
@Override
public LibraryRoot onGetRoot(ControllerInfo controller, Bundle rootHints) {
- return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRA);
+ return new LibraryRoot(MockMediaLibraryService2.this, ROOT_ID, EXTRAS);
}
@Override
@@ -147,7 +163,7 @@
@Override
public List<MediaItem2> onLoadChildren(ControllerInfo controller, String parentId, int page,
- int pageSize, Bundle options) {
+ int pageSize, Bundle extras) {
if (PARENT_ID.equals(parentId)) {
return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
} else if (PARENT_ID_ERROR.equals(parentId)) {
@@ -156,6 +172,47 @@
// Includes the case of PARENT_ID_NO_CHILDREN.
return new ArrayList<>();
}
+
+ @Override
+ public void onSearch(ControllerInfo controllerInfo, String query, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
+ extras);
+ } else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
+ // Searching takes some time. Notify after 5 seconds.
+ Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
+ @Override
+ public void run() {
+ mSession.notifySearchResultChanged(
+ controllerInfo, query, SEARCH_RESULT_COUNT, extras);
+ }
+ }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
+ } else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
+ mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
+ } else {
+ // TODO: For the error case, how should we notify the browser?
+ }
+ }
+
+ @Override
+ public List<MediaItem2> onLoadSearchResult(ControllerInfo controllerInfo,
+ String query, int page, int pageSize, Bundle extras) {
+ if (SEARCH_QUERY.equals(query)) {
+ return getPaginatedResult(SEARCH_RESULT, page, pageSize);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void onSubscribed(ControllerInfo controller, String parentId, Bundle extras) {
+ mCallbackProxy.onSubscribed(controller, parentId, extras);
+ }
+
+ @Override
+ public void onUnsubscribed(ControllerInfo controller, String parentId) {
+ mCallbackProxy.onUnsubscribed(controller, parentId);
+ }
}
private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
@@ -191,4 +248,4 @@
.build(),
0 /* Flags */);
}
-}
\ No newline at end of file
+}
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index 1faf0f4..ae31ce6 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -19,6 +19,7 @@
import android.media.MediaSession2.PlaylistParams;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +47,7 @@
public boolean mSetPlaylistCalled;
public boolean mSetPlaylistParamsCalled;
- public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+ public ArrayMap<EventCallback, Executor> mCallbacks = new ArrayMap<>();
public List<MediaItem2> mPlaylist;
public PlaylistParams mPlaylistParams;
@@ -146,23 +147,30 @@
}
@Override
- public void addPlaybackListener(@NonNull Executor executor,
- @NonNull PlaybackListener listener) {
- mListeners.add(new PlaybackListenerHolder(executor, listener));
+ public void registerEventCallback(@NonNull Executor executor,
+ @NonNull EventCallback callback) {
+ mCallbacks.put(callback, executor);
}
@Override
- public void removePlaybackListener(@NonNull PlaybackListener listener) {
- int index = PlaybackListenerHolder.indexOf(mListeners, listener);
- if (index >= 0) {
- mListeners.remove(index);
- }
+ public void unregisterEventCallback(@NonNull EventCallback callback) {
+ mCallbacks.remove(callback);
}
public void notifyPlaybackState(final PlaybackState2 state) {
mLastPlaybackState = state;
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).postPlaybackChange(state);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onPlaybackStateChanged(state));
+ }
+ }
+
+ public void notifyError(int what) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ final EventCallback callback = mCallbacks.keyAt(i);
+ final Executor executor = mCallbacks.valueAt(i);
+ executor.execute(() -> callback.onError(null, what, 0));
}
}
diff --git a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
deleted file mode 100644
index 0f1644c..0000000
--- a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-package android.media;
-
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Holds {@link PlaybackListener} with the {@link Handler}.
- */
-public class PlaybackListenerHolder {
- public final Executor executor;
- public final PlaybackListener listener;
-
- public PlaybackListenerHolder(Executor executor, @NonNull PlaybackListener listener) {
- this.executor = executor;
- this.listener = listener;
- }
-
- public void postPlaybackChange(final PlaybackState2 state) {
- executor.execute(() -> listener.onPlaybackChanged(state));
- }
-
- /**
- * Returns {@code true} if the given list contains a {@link PlaybackListenerHolder} that holds
- * the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code true} if the given list contains listener. {@code false} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> boolean contains(
- @NonNull List<Holder> list, PlaybackListener listener) {
- return indexOf(list, listener) >= 0;
- }
-
- /**
- * Returns the index of the {@link PlaybackListenerHolder} that contains the given listener.
- *
- * @param list list to check
- * @param listener listener to check
- * @return {@code index} of item if the given list contains listener. {@code -1} otherwise.
- */
- public static <Holder extends PlaybackListenerHolder> int indexOf(
- @NonNull List<Holder> list, PlaybackListener listener) {
- for (int i = 0; i < list.size(); i++) {
- if (list.get(i).listener == listener) {
- return i;
- }
- }
- return -1;
- }
-}
diff --git a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
index 3800c28..a18ad8e 100644
--- a/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
+++ b/packages/MediaComponents/test/src/android/media/TestServiceRegistry.java
@@ -22,6 +22,7 @@
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.TestUtils.SyncHandler;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.support.annotation.GuardedBy;
@@ -73,6 +74,9 @@
* Called when enclosing service is destroyed.
*/
public void onServiceDestroyed() { }
+
+ public void onSubscribed(ControllerInfo info, String parentId, Bundle extra) { }
+ public void onUnsubscribed(ControllerInfo info, String parentId) { }
}
@GuardedBy("TestServiceRegistry.class")
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a3ce1f6..9a30f71 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1099,26 +1099,17 @@
if (status != NO_ERROR) {
return status;
}
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume");
AutoMutex lock(mLock);
- Vector<VolumeInterface *> volumeInterfaces;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface == NULL) {
- return BAD_VALUE;
- }
- volumeInterfaces.add(volumeInterface);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return BAD_VALUE;
}
-
- mStreamTypes[stream].volume = value;
-
- if (volumeInterfaces.size() == 0) {
- volumeInterfaces = getAllVolumeInterfaces_l();
- }
- for (size_t i = 0; i < volumeInterfaces.size(); i++) {
- volumeInterfaces[i]->setStreamVolume(stream, value);
- }
+ volumeInterface->setStreamVolume(stream, value);
return NO_ERROR;
}
@@ -1157,21 +1148,17 @@
if (status != NO_ERROR) {
return 0.0f;
}
-
- AutoMutex lock(mLock);
- float volume;
- if (output != AUDIO_IO_HANDLE_NONE) {
- VolumeInterface *volumeInterface = getVolumeInterface_l(output);
- if (volumeInterface != NULL) {
- volume = volumeInterface->streamVolume(stream);
- } else {
- volume = 0.0f;
- }
- } else {
- volume = streamVolume_l(stream);
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return 0.0f;
}
- return volume;
+ AutoMutex lock(mLock);
+ VolumeInterface *volumeInterface = getVolumeInterface_l(output);
+ if (volumeInterface == NULL) {
+ return 0.0f;
+ }
+
+ return volumeInterface->streamVolume(stream);
}
bool AudioFlinger::streamMute(audio_stream_type_t stream) const
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 7c38bcc..ebd1b18 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -616,9 +616,6 @@
// no range check, AudioFlinger::mLock held
bool streamMute_l(audio_stream_type_t stream) const
{ return mStreamTypes[stream].mute; }
- // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
- float streamVolume_l(audio_stream_type_t stream) const
- { return mStreamTypes[stream].volume; }
void ioConfigChanged(audio_io_config_event event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index cae296e..3134323 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1703,11 +1703,14 @@
readOutputParameters_l();
// ++ operator does not compile
- for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT;
+ for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_FOR_POLICY_CNT;
stream = (audio_stream_type_t) (stream + 1)) {
- mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
+ mStreamTypes[stream].volume = 0.0f;
mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
}
+ // Audio patch volume is always max
+ mStreamTypes[AUDIO_STREAM_PATCH].volume = 1.0f;
+ mStreamTypes[AUDIO_STREAM_PATCH].mute = false;
}
AudioFlinger::PlaybackThread::~PlaybackThread()
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e8416d4..57d9371 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3791,9 +3791,11 @@
// ---
-void AudioPolicyManager::addOutput(audio_io_handle_t output, const sp<SwAudioOutputDescriptor>& outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output,
+ const sp<SwAudioOutputDescriptor>& outputDesc)
{
mOutputs.add(output, outputDesc);
+ applyStreamVolumes(outputDesc, AUDIO_DEVICE_NONE, 0 /* delayMs */, true /* force */);
updateMono(output); // update mono status when adding to output list
selectOutputForMusicEffects();
nextAudioPortGeneration();
@@ -3805,7 +3807,8 @@
selectOutputForMusicEffects();
}
-void AudioPolicyManager::addInput(audio_io_handle_t input, const sp<AudioInputDescriptor>& inputDesc)
+void AudioPolicyManager::addInput(audio_io_handle_t input,
+ const sp<AudioInputDescriptor>& inputDesc)
{
mInputs.add(input, inputDesc);
nextAudioPortGeneration();
@@ -3955,9 +3958,6 @@
// outputs used by dynamic policy mixes
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
- // set initial stream volume for device
- applyStreamVolumes(desc, device, 0, true);
-
//TODO: configure audio effect output stage here
// open a duplicating output thread for the new output and the primary output
@@ -3968,7 +3968,6 @@
if (status == NO_ERROR) {
// add duplicated output descriptor
addOutput(duplicatedOutput, dupOutputDesc);
- applyStreamVolumes(dupOutputDesc, device, 0, true);
} else {
ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
mPrimaryOutput->mIoHandle, output);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 89ca6bb..1df6b6a 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -67,6 +67,7 @@
#include "api1/Camera2Client.h"
#include "api2/CameraDeviceClient.h"
#include "utils/CameraTraces.h"
+#include "utils/TagMonitor.h"
namespace {
const char* kPermissionServiceName = "permission";
@@ -1364,7 +1365,7 @@
LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
__FUNCTION__);
- err = client->initialize(mCameraProviderManager);
+ err = client->initialize(mCameraProviderManager, mMonitorTags);
if (err != OK) {
ALOGE("%s: Could not initialize client from HAL.", __FUNCTION__);
// Errors could be from the HAL module open call or from AppOpsManager
@@ -1733,8 +1734,6 @@
}
bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
- const int callingPid = getCallingPid();
- const int servicePid = getpid();
bool ret = false;
{
// Acquire mServiceLock and prevent other clients from connecting
@@ -1750,8 +1749,7 @@
mActiveClientManager.remove(i);
continue;
}
- if (remote == clientSp->getRemote() && (callingPid == servicePid ||
- callingPid == clientSp->getClientPid())) {
+ if (remote == clientSp->getRemote()) {
mActiveClientManager.remove(i);
evicted.push_back(clientSp);
@@ -2643,6 +2641,16 @@
dprintf(fd, "CameraStates in use, may be deadlocked\n");
}
+ int argSize = args.size();
+ for (int i = 0; i < argSize; i++) {
+ if (args[i] == TagMonitor::kMonitorOption) {
+ if (i + 1 < argSize) {
+ mMonitorTags = String8(args[i + 1]);
+ }
+ break;
+ }
+ }
+
for (auto& state : mCameraStates) {
String8 cameraId = state.first;
@@ -2770,7 +2778,7 @@
* While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
* binder driver
*/
-
+ // PID here is approximate and can be wrong.
logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
// check torch client
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 81048e6..cbfc50b 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -204,7 +204,8 @@
class BasicClient : public virtual RefBase {
public:
- virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) = 0;
virtual binder::Status disconnect();
// because we can't virtually inherit IInterface, which breaks
@@ -606,6 +607,9 @@
RingBuffer<String8> mEventLog;
Mutex mLogLock;
+ // The last monitored tags set by client
+ String8 mMonitorTags;
+
// Currently allowed user IDs
std::set<userid_t> mAllowedUsers;
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 0a82cb9..3578bba 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -69,8 +69,8 @@
mLegacyMode = legacyMode;
}
-status_t Camera2Client::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t Camera2Client::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
bool Camera2Client::isZslEnabledInStillTemplate() {
@@ -88,13 +88,13 @@
}
template<typename TProviderPtr>
-status_t Camera2Client::initializeImpl(TProviderPtr providerPtr)
+status_t Camera2Client::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags)
{
ATRACE_CALL();
ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
status_t res;
- res = Camera2ClientBase::initialize(providerPtr);
+ res = Camera2ClientBase::initialize(providerPtr, monitorTags);
if (res != OK) {
return res;
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 1ebf4b0..44929c3 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -101,7 +101,8 @@
virtual ~Camera2Client();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -224,7 +225,7 @@
status_t overrideVideoSnapshotSize(Parameters ¶ms);
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
bool isZslEnabledInStillTemplate();
};
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 8c6cd3d..f6d27ab 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -62,7 +62,8 @@
LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
}
-status_t CameraClient::initialize(sp<CameraProviderManager> manager) {
+status_t CameraClient::initialize(sp<CameraProviderManager> manager,
+ const String8& /*monitorTags*/) {
int callingPid = getCallingPid();
status_t res;
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 7f93fef..1910536 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -72,7 +72,8 @@
bool legacyMode = false);
~CameraClient();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index a7cc3c3..efe3ca1 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -88,16 +88,17 @@
ALOGI("CameraDeviceClient %s: Opened", cameraId.string());
}
-status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
template<typename TProviderPtr>
-status_t CameraDeviceClient::initializeImpl(TProviderPtr providerPtr) {
+status_t CameraDeviceClient::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags) {
ATRACE_CALL();
status_t res;
- res = Camera2ClientBase::initialize(providerPtr);
+ res = Camera2ClientBase::initialize(providerPtr, monitorTags);
if (res != OK) {
return res;
}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 4e049d8..5aaf5aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -157,7 +157,8 @@
int servicePid);
virtual ~CameraDeviceClient();
- virtual status_t initialize(sp<CameraProviderManager> manager) override;
+ virtual status_t initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) override;
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -225,7 +226,7 @@
std::vector<int32_t> mSupportedPhysicalRequestKeys;
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
/** Utility members */
binder::Status checkPidStatus(const char* checkLocation);
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index db26027..459e45d 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -80,13 +80,15 @@
}
template <typename TClientBase>
-status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager) {
- return initializeImpl(manager);
+status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager,
+ const String8& monitorTags) {
+ return initializeImpl(manager, monitorTags);
}
template <typename TClientBase>
template <typename TProviderPtr>
-status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr) {
+status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr,
+ const String8& monitorTags) {
ATRACE_CALL();
ALOGV("%s: Initializing client for camera %s", __FUNCTION__,
TClientBase::mCameraIdStr.string());
@@ -104,7 +106,7 @@
return NO_INIT;
}
- res = mDevice->initialize(providerPtr);
+ res = mDevice->initialize(providerPtr, monitorTags);
if (res != OK) {
ALOGE("%s: Camera %s: unable to initialize device: %s (%d)",
__FUNCTION__, TClientBase::mCameraIdStr.string(), strerror(-res), res);
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index edeae5b..e74fbdf 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -56,7 +56,7 @@
int servicePid);
virtual ~Camera2ClientBase();
- virtual status_t initialize(sp<CameraProviderManager> manager);
+ virtual status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags);
virtual status_t dumpClient(int fd, const Vector<String16>& args);
/**
@@ -145,7 +145,7 @@
private:
template<typename TProviderPtr>
- status_t initializeImpl(TProviderPtr providerPtr);
+ status_t initializeImpl(TProviderPtr providerPtr, const String8& monitorTags);
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index ad83c3d..0ba7403 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -60,7 +60,7 @@
*/
virtual metadata_vendor_id_t getVendorTagId() const = 0;
- virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
+ virtual status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags) = 0;
virtual status_t disconnect() = 0;
virtual status_t dump(int fd, const Vector<String16> &args) = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 04c2c5b..42bcb2d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -96,7 +96,7 @@
return mId;
}
-status_t Camera3Device::initialize(sp<CameraProviderManager> manager) {
+status_t Camera3Device::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) {
ATRACE_CALL();
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
@@ -169,6 +169,10 @@
mInterface = new HalInterface(session, queue);
std::string providerType;
mVendorTagId = manager->getProviderTagIdLocked(mId.string());
+ mTagMonitor.initialize(mVendorTagId);
+ if (!monitorTags.isEmpty()) {
+ mTagMonitor.parseTagsToMonitor(String8(monitorTags));
+ }
return initializeCommonLocked();
}
@@ -192,8 +196,6 @@
/** Create buffer manager */
mBufferManager = new Camera3BufferManager();
- mTagMonitor.initialize(mVendorTagId);
-
Vector<int32_t> sessionParamKeys;
camera_metadata_entry_t sessionKeysEntry = mDeviceInfo.find(
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
@@ -582,13 +584,12 @@
bool dumpTemplates = false;
String16 templatesOption("-t");
- String16 monitorOption("-m");
int n = args.size();
for (int i = 0; i < n; i++) {
if (args[i] == templatesOption) {
dumpTemplates = true;
}
- if (args[i] == monitorOption) {
+ if (args[i] == TagMonitor::kMonitorOption) {
if (i + 1 < n) {
String8 monitorTags = String8(args[i + 1]);
if (monitorTags == "off") {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 7faa6e5..13b83ba 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -96,7 +96,7 @@
metadata_vendor_id_t getVendorTagId() const override { return mVendorTagId; }
// Transitions to idle state on success.
- status_t initialize(sp<CameraProviderManager> manager) override;
+ status_t initialize(sp<CameraProviderManager> manager, const String8& monitorTags) override;
status_t disconnect() override;
status_t dump(int fd, const Vector<String16> &args) override;
const CameraMetadata& info() const override;
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
index dec97d7..c0a353f 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.cpp
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -33,6 +33,8 @@
mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID)
{}
+const String16 TagMonitor::kMonitorOption = String16("-m");
+
const char* TagMonitor::k3aTags =
"android.control.aeMode, android.control.afMode, android.control.awbMode,"
"android.control.aeState, android.control.afState, android.control.awbState,"
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
index 7155314..2dece62 100644
--- a/services/camera/libcameraservice/utils/TagMonitor.h
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -38,6 +38,10 @@
* buffer log that can be dumped at will. */
class TagMonitor {
public:
+
+ // Monitor argument
+ static const String16 kMonitorOption;
+
enum eventSource {
REQUEST,
RESULT