Merge "C2SoftAmrWbEnc: Fix integer overflows leading to crash by changing data type"
diff --git a/include/OWNERS b/include/OWNERS
index 88de595..e1d4db7 100644
--- a/include/OWNERS
+++ b/include/OWNERS
@@ -3,3 +3,4 @@
jtinker@google.com
lajos@google.com
essick@google.com
+philburk@google.com
diff --git a/media/bufferpool/2.0/tests/Android.bp b/media/bufferpool/2.0/tests/Android.bp
index 803a813..5e26e3a 100644
--- a/media/bufferpool/2.0/tests/Android.bp
+++ b/media/bufferpool/2.0/tests/Android.bp
@@ -80,3 +80,22 @@
],
compile_multilib: "both",
}
+
+cc_test {
+ name: "BufferpoolUnitTest",
+ test_suites: ["device-tests"],
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "allocator.cpp",
+ "BufferpoolUnitTest.cpp",
+ ],
+ static_libs: [
+ "android.hardware.media.bufferpool@2.0",
+ "libcutils",
+ "libstagefright_bufferpool@2.0.1",
+ ],
+ shared_libs: [
+ "libfmq",
+ ],
+ compile_multilib: "both",
+}
diff --git a/media/bufferpool/2.0/tests/AndroidTest.xml b/media/bufferpool/2.0/tests/AndroidTest.xml
new file mode 100644
index 0000000..b027ad0
--- /dev/null
+++ b/media/bufferpool/2.0/tests/AndroidTest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<configuration description="Test module config for bufferpool unit tests">
+ <option name="test-suite-tag" value="BufferpoolUnitTest" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="BufferpoolUnitTest" />
+ </test>
+</configuration>
diff --git a/media/bufferpool/2.0/tests/BufferpoolUnitTest.cpp b/media/bufferpool/2.0/tests/BufferpoolUnitTest.cpp
new file mode 100644
index 0000000..b448405
--- /dev/null
+++ b/media/bufferpool/2.0/tests/BufferpoolUnitTest.cpp
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "BufferpoolUnitTest"
+#include <utils/Log.h>
+
+#include <binder/ProcessState.h>
+#include <bufferpool/ClientManager.h>
+#include <gtest/gtest.h>
+#include <hidl/LegacySupport.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unordered_set>
+#include <vector>
+#include "allocator.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::media::bufferpool::BufferPoolData;
+using android::hardware::media::bufferpool::V2_0::IClientManager;
+using android::hardware::media::bufferpool::V2_0::ResultStatus;
+using android::hardware::media::bufferpool::V2_0::implementation::BufferId;
+using android::hardware::media::bufferpool::V2_0::implementation::ClientManager;
+using android::hardware::media::bufferpool::V2_0::implementation::ConnectionId;
+using android::hardware::media::bufferpool::V2_0::implementation::TransactionId;
+
+using namespace android;
+
+// communication message types between processes.
+enum PipeCommand : int32_t {
+ INIT,
+ TRANSFER,
+ STOP,
+
+ INIT_OK,
+ INIT_ERROR,
+ TRANSFER_OK,
+ TRANSFER_ERROR,
+ STOP_OK,
+ STOP_ERROR,
+};
+
+// communication message between processes.
+union PipeMessage {
+ struct {
+ int32_t command;
+ int32_t memsetValue;
+ BufferId bufferId;
+ ConnectionId connectionId;
+ TransactionId transactionId;
+ int64_t timestampUs;
+ } data;
+ char array[0];
+};
+
+static int32_t kNumIterationCount = 10;
+
+class BufferpoolTest {
+ public:
+ BufferpoolTest() : mConnectionValid(false), mManager(nullptr), mAllocator(nullptr) {
+ mConnectionId = -1;
+ mReceiverId = -1;
+ }
+
+ ~BufferpoolTest() {
+ if (mConnectionValid) {
+ mManager->close(mConnectionId);
+ }
+ }
+
+ protected:
+ bool mConnectionValid;
+ ConnectionId mConnectionId;
+ ConnectionId mReceiverId;
+
+ android::sp<ClientManager> mManager;
+ std::shared_ptr<BufferPoolAllocator> mAllocator;
+
+ void setupBufferpoolManager();
+};
+
+void BufferpoolTest::setupBufferpoolManager() {
+ // retrieving per process bufferpool object sp<ClientManager>
+ mManager = ClientManager::getInstance();
+ ASSERT_NE(mManager, nullptr) << "unable to get ClientManager\n";
+
+ mAllocator = std::make_shared<TestBufferPoolAllocator>();
+ ASSERT_NE(mAllocator, nullptr) << "unable to create TestBufferPoolAllocator\n";
+
+ // set-up local bufferpool connection for sender
+ ResultStatus status = mManager->create(mAllocator, &mConnectionId);
+ ASSERT_EQ(status, ResultStatus::OK)
+ << "unable to set-up local bufferpool connection for sender\n";
+ mConnectionValid = true;
+}
+
+class BufferpoolUnitTest : public BufferpoolTest, public ::testing::Test {
+ public:
+ virtual void SetUp() override { setupBufferpoolManager(); }
+
+ virtual void TearDown() override {}
+};
+
+class BufferpoolFunctionalityTest : public BufferpoolTest, public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ mReceiverPid = -1;
+
+ ASSERT_TRUE(pipe(mCommandPipeFds) == 0) << "pipe connection failed for commandPipe\n";
+ ASSERT_TRUE(pipe(mResultPipeFds) == 0) << "pipe connection failed for resultPipe\n";
+
+ mReceiverPid = fork();
+ ASSERT_TRUE(mReceiverPid >= 0) << "fork failed\n";
+
+ if (mReceiverPid == 0) {
+ doReceiver();
+ // In order to ignore gtest behaviour, wait for being killed from tearDown
+ pause();
+ }
+ setupBufferpoolManager();
+ }
+
+ virtual void TearDown() override {
+ if (mReceiverPid > 0) {
+ kill(mReceiverPid, SIGKILL);
+ int wstatus;
+ wait(&wstatus);
+ }
+ }
+
+ protected:
+ pid_t mReceiverPid;
+ int mCommandPipeFds[2];
+ int mResultPipeFds[2];
+
+ bool sendMessage(int* pipes, const PipeMessage& message) {
+ int ret = write(pipes[1], message.array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ bool receiveMessage(int* pipes, PipeMessage* message) {
+ int ret = read(pipes[0], message->array, sizeof(PipeMessage));
+ return ret == sizeof(PipeMessage);
+ }
+
+ void doReceiver();
+};
+
+void BufferpoolFunctionalityTest::doReceiver() {
+ // Configures the threadpool used for handling incoming RPC calls in this process.
+ configureRpcThreadpool(1 /*threads*/, false /*willJoin*/);
+ bool receiverRunning = true;
+ while (receiverRunning) {
+ PipeMessage message;
+ receiveMessage(mCommandPipeFds, &message);
+ ResultStatus err = ResultStatus::OK;
+ switch (message.data.command) {
+ case PipeCommand::INIT: {
+ // receiver manager creation
+ mManager = ClientManager::getInstance();
+ if (!mManager) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+
+ android::status_t status = mManager->registerAsService();
+ if (status != android::OK) {
+ message.data.command = PipeCommand::INIT_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ message.data.command = PipeCommand::INIT_OK;
+ sendMessage(mResultPipeFds, message);
+ break;
+ }
+ case PipeCommand::TRANSFER: {
+ native_handle_t* receiveHandle = nullptr;
+ std::shared_ptr<BufferPoolData> receiveBuffer;
+ err = mManager->receive(message.data.connectionId, message.data.transactionId,
+ message.data.bufferId, message.data.timestampUs,
+ &receiveHandle, &receiveBuffer);
+ if (err != ResultStatus::OK) {
+ message.data.command = PipeCommand::TRANSFER_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ if (!TestBufferPoolAllocator::Verify(receiveHandle, message.data.memsetValue)) {
+ message.data.command = PipeCommand::TRANSFER_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ if (receiveHandle) {
+ native_handle_close(receiveHandle);
+ native_handle_delete(receiveHandle);
+ }
+ receiveHandle = nullptr;
+ receiveBuffer.reset();
+ message.data.command = PipeCommand::TRANSFER_OK;
+ sendMessage(mResultPipeFds, message);
+ break;
+ }
+ case PipeCommand::STOP: {
+ err = mManager->close(message.data.connectionId);
+ if (err != ResultStatus::OK) {
+ message.data.command = PipeCommand::STOP_ERROR;
+ sendMessage(mResultPipeFds, message);
+ return;
+ }
+ message.data.command = PipeCommand::STOP_OK;
+ sendMessage(mResultPipeFds, message);
+ receiverRunning = false;
+ break;
+ }
+ default:
+ ALOGE("unknown command. try again");
+ break;
+ }
+ }
+}
+
+// Buffer allocation test.
+// Check whether each buffer allocation is done successfully with unique buffer id.
+TEST_F(BufferpoolUnitTest, AllocateBuffer) {
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ std::vector<std::shared_ptr<BufferPoolData>> buffers{};
+ std::vector<native_handle_t*> allocHandle{};
+ ResultStatus status;
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ native_handle_t* handle = nullptr;
+ std::shared_ptr<BufferPoolData> buffer{};
+ status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
+
+ buffers.push_back(std::move(buffer));
+ if (handle) {
+ allocHandle.push_back(std::move(handle));
+ }
+ }
+
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ for (int j = i + 1; j < kNumIterationCount; ++j) {
+ ASSERT_TRUE(buffers[i]->mId != buffers[j]->mId) << "allocated buffers are not unique";
+ }
+ }
+ // delete the buffer handles
+ for (auto handle : allocHandle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ // clear the vectors
+ buffers.clear();
+ allocHandle.clear();
+}
+
+// Buffer recycle test.
+// Check whether de-allocated buffers are recycled.
+TEST_F(BufferpoolUnitTest, RecycleBuffer) {
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ ResultStatus status;
+ std::vector<BufferId> bid{};
+ std::vector<native_handle_t*> allocHandle{};
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ native_handle_t* handle = nullptr;
+ std::shared_ptr<BufferPoolData> buffer;
+ status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
+
+ bid.push_back(buffer->mId);
+ if (handle) {
+ allocHandle.push_back(std::move(handle));
+ }
+ buffer.reset();
+ }
+
+ std::unordered_set<BufferId> set(bid.begin(), bid.end());
+ ASSERT_EQ(set.size(), 1) << "buffers are not recycled properly";
+
+ // delete the buffer handles
+ for (auto handle : allocHandle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ allocHandle.clear();
+}
+
+// Validate cache evict and invalidate APIs.
+TEST_F(BufferpoolUnitTest, FlushTest) {
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ ResultStatus status = mManager->registerSender(mManager, mConnectionId, &mReceiverId);
+ ASSERT_TRUE(status == ResultStatus::ALREADY_EXISTS && mReceiverId == mConnectionId);
+
+ // testing empty flush
+ status = mManager->flush(mConnectionId);
+ ASSERT_EQ(status, ResultStatus::OK) << "failed to flush connection : " << mConnectionId;
+
+ std::vector<std::shared_ptr<BufferPoolData>> senderBuffer{};
+ std::vector<native_handle_t*> allocHandle{};
+ std::vector<TransactionId> tid{};
+ std::vector<int64_t> timestampUs{};
+
+ std::map<TransactionId, BufferId> bufferMap{};
+
+ for (int i = 0; i < kNumIterationCount; i++) {
+ int64_t postUs;
+ TransactionId transactionId;
+ native_handle_t* handle = nullptr;
+ std::shared_ptr<BufferPoolData> buffer{};
+ status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << " iteration";
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(handle, i));
+
+ status = mManager->postSend(mReceiverId, buffer, &transactionId, &postUs);
+ ASSERT_EQ(status, ResultStatus::OK) << "unable to post send transaction on bufferpool";
+
+ timestampUs.push_back(postUs);
+ tid.push_back(transactionId);
+ bufferMap.insert({transactionId, buffer->mId});
+
+ senderBuffer.push_back(std::move(buffer));
+ if (handle) {
+ allocHandle.push_back(std::move(handle));
+ }
+ buffer.reset();
+ }
+
+ status = mManager->flush(mConnectionId);
+ ASSERT_EQ(status, ResultStatus::OK) << "failed to flush connection : " << mConnectionId;
+
+ std::shared_ptr<BufferPoolData> receiverBuffer{};
+ native_handle_t* recvHandle = nullptr;
+ for (int i = 0; i < kNumIterationCount; i++) {
+ status = mManager->receive(mReceiverId, tid[i], senderBuffer[i]->mId, timestampUs[i],
+ &recvHandle, &receiverBuffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "receive failed for buffer " << senderBuffer[i]->mId;
+
+ // find the buffer id from transaction id
+ auto findIt = bufferMap.find(tid[i]);
+ ASSERT_NE(findIt, bufferMap.end()) << "inconsistent buffer mapping";
+
+ // buffer id received must be same as the buffer id sent
+ ASSERT_EQ(findIt->second, receiverBuffer->mId) << "invalid buffer received";
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Verify(recvHandle, i))
+ << "Message received not same as that sent";
+
+ bufferMap.erase(findIt);
+ if (recvHandle) {
+ native_handle_close(recvHandle);
+ native_handle_delete(recvHandle);
+ }
+ recvHandle = nullptr;
+ receiverBuffer.reset();
+ }
+
+ ASSERT_EQ(bufferMap.size(), 0) << "buffers received is less than the number of buffers sent";
+
+ for (auto handle : allocHandle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ allocHandle.clear();
+ senderBuffer.clear();
+ timestampUs.clear();
+}
+
+// Buffer transfer test between processes.
+TEST_F(BufferpoolFunctionalityTest, TransferBuffer) {
+ // initialize the receiver
+ PipeMessage message;
+ message.data.command = PipeCommand::INIT;
+ sendMessage(mCommandPipeFds, message);
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::INIT_OK) << "receiver init failed";
+
+ android::sp<IClientManager> receiver = IClientManager::getService();
+ ASSERT_NE(receiver, nullptr) << "getService failed for receiver\n";
+
+ ConnectionId receiverId;
+ ResultStatus status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+ ASSERT_EQ(status, ResultStatus::OK)
+ << "registerSender failed for connection id " << mConnectionId << "\n";
+
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ for (int i = 0; i < kNumIterationCount; ++i) {
+ native_handle_t* handle = nullptr;
+ std::shared_ptr<BufferPoolData> buffer;
+ status = mManager->allocate(mConnectionId, vecParams, &handle, &buffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for " << i << "iteration";
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(handle, i))
+ << "Fill fail for buffer handle " << handle << "\n";
+
+ // send the buffer to the receiver
+ int64_t postUs;
+ TransactionId transactionId;
+ status = mManager->postSend(receiverId, buffer, &transactionId, &postUs);
+ ASSERT_EQ(status, ResultStatus::OK)
+ << "postSend failed for receiver " << receiverId << "\n";
+
+ // PipeMessage message;
+ message.data.command = PipeCommand::TRANSFER;
+ message.data.memsetValue = i;
+ message.data.bufferId = buffer->mId;
+ message.data.connectionId = receiverId;
+ message.data.transactionId = transactionId;
+ message.data.timestampUs = postUs;
+ sendMessage(mCommandPipeFds, message);
+ // delete buffer handle
+ if (handle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::TRANSFER_OK)
+ << "received error during buffer transfer\n";
+ }
+ message.data.command = PipeCommand::STOP;
+ sendMessage(mCommandPipeFds, message);
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::STOP_OK)
+ << "received error during buffer transfer\n";
+}
+
+/* Validate bufferpool for following corner cases:
+ 1. invalid connectionID
+ 2. invalid receiver
+ 3. when sender is not registered
+ 4. when connection is closed
+*/
+// TODO: Enable when the issue in b/212196495 is fixed
+TEST_F(BufferpoolFunctionalityTest, DISABLED_ValidityTest) {
+ std::vector<uint8_t> vecParams;
+ getTestAllocatorParams(&vecParams);
+
+ std::shared_ptr<BufferPoolData> senderBuffer;
+ native_handle_t* allocHandle = nullptr;
+
+ // call allocate() on a random connection id
+ ConnectionId randomId = rand();
+ ResultStatus status = mManager->allocate(randomId, vecParams, &allocHandle, &senderBuffer);
+ EXPECT_TRUE(status == ResultStatus::NOT_FOUND);
+
+ // initialize the receiver
+ PipeMessage message;
+ message.data.command = PipeCommand::INIT;
+ sendMessage(mCommandPipeFds, message);
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::INIT_OK) << "receiver init failed";
+
+ allocHandle = nullptr;
+ senderBuffer.reset();
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &senderBuffer);
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x77));
+
+ // send buffers w/o registering sender
+ int64_t postUs;
+ TransactionId transactionId;
+
+ // random receiver
+ status = mManager->postSend(randomId, senderBuffer, &transactionId, &postUs);
+ ASSERT_NE(status, ResultStatus::OK) << "bufferpool shouldn't allow send on random receiver";
+
+ // establish connection
+ android::sp<IClientManager> receiver = IClientManager::getService();
+ ASSERT_NE(receiver, nullptr) << "getService failed for receiver\n";
+
+ ConnectionId receiverId;
+ status = mManager->registerSender(receiver, mConnectionId, &receiverId);
+ ASSERT_EQ(status, ResultStatus::OK)
+ << "registerSender failed for connection id " << mConnectionId << "\n";
+
+ allocHandle = nullptr;
+ senderBuffer.reset();
+ status = mManager->allocate(mConnectionId, vecParams, &allocHandle, &senderBuffer);
+ ASSERT_EQ(status, ResultStatus::OK) << "allocate failed for connection " << mConnectionId;
+
+ ASSERT_TRUE(TestBufferPoolAllocator::Fill(allocHandle, 0x88));
+
+ // send the buffer to the receiver
+ status = mManager->postSend(receiverId, senderBuffer, &transactionId, &postUs);
+ ASSERT_EQ(status, ResultStatus::OK) << "postSend failed for receiver " << receiverId << "\n";
+
+ // PipeMessage message;
+ message.data.command = PipeCommand::TRANSFER;
+ message.data.memsetValue = 0x88;
+ message.data.bufferId = senderBuffer->mId;
+ message.data.connectionId = receiverId;
+ message.data.transactionId = transactionId;
+ message.data.timestampUs = postUs;
+ sendMessage(mCommandPipeFds, message);
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::TRANSFER_OK)
+ << "received error during buffer transfer\n";
+
+ if (allocHandle) {
+ native_handle_close(allocHandle);
+ native_handle_delete(allocHandle);
+ }
+
+ message.data.command = PipeCommand::STOP;
+ sendMessage(mCommandPipeFds, message);
+ ASSERT_TRUE(receiveMessage(mResultPipeFds, &message)) << "receiveMessage failed\n";
+ ASSERT_EQ(message.data.command, PipeCommand::STOP_OK)
+ << "received error during buffer transfer\n";
+
+ // try to send msg to closed connection
+ status = mManager->postSend(receiverId, senderBuffer, &transactionId, &postUs);
+ ASSERT_NE(status, ResultStatus::OK) << "bufferpool shouldn't allow send on closed connection";
+}
+
+int main(int argc, char** argv) {
+ android::hardware::details::setTrebleTestingOverride(true);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGV("Test result = %d\n", status);
+ return status;
+}
diff --git a/media/bufferpool/2.0/tests/README.md b/media/bufferpool/2.0/tests/README.md
new file mode 100644
index 0000000..5efd966
--- /dev/null
+++ b/media/bufferpool/2.0/tests/README.md
@@ -0,0 +1,33 @@
+## Media Testing ##
+---
+#### Bufferpool :
+The Bufferpool Test Suite validates bufferpool library in android.
+
+Run the following steps to build the test suite:
+```
+m BufferpoolUnitTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/BufferpoolUnitTest/BufferpoolUnitTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/BufferpoolUnitTest/BufferpoolUnitTest /data/local/tmp/
+```
+
+usage: BufferpoolUnitTest
+```
+adb shell /data/local/tmp/BufferpoolUnitTest
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest BufferpoolUnitTest
+```
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
index f477f1c..90bb054 100644
--- a/media/codec2/TEST_MAPPING
+++ b/media/codec2/TEST_MAPPING
@@ -8,7 +8,7 @@
],
"presubmit-large": [
{
- "name": "CtsMediaTestCases",
+ "name": "CtsMediaMiscTestCases",
"options": [
{
"include-annotation": "android.platform.test.annotations.Presubmit"
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
index bb63e1f..7afea91 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.cpp
@@ -225,7 +225,7 @@
work->result = C2_CORRUPTED;
return;
}
- uint64_t outTimeStamp =
+ int64_t outTimeStamp =
mProcessedSamples * 1000000ll / mIntf->getSampleRate();
size_t inPos = 0;
size_t outPos = 0;
@@ -266,7 +266,7 @@
ALOGV("causal sample size %d", mFilledLen);
if (mIsFirst && outPos != 0) {
mIsFirst = false;
- mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+ mAnchorTimeStamp = work->input.ordinal.timestamp.peekll();
}
fillEmptyWork(work);
if (outPos != 0) {
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.h b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.h
index 6ab14db..4920b23 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.h
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrNbEnc.h
@@ -54,7 +54,7 @@
bool mIsFirst;
bool mSignalledError;
bool mSignalledOutputEos;
- uint64_t mAnchorTimeStamp;
+ int64_t mAnchorTimeStamp;
uint64_t mProcessedSamples;
int32_t mFilledLen;
int16_t mInputFrame[kNumSamplesPerFrame];
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.cpp b/media/codec2/components/opus/C2SoftOpusEnc.cpp
index 370d33c..cdc3be0 100644
--- a/media/codec2/components/opus/C2SoftOpusEnc.cpp
+++ b/media/codec2/components/opus/C2SoftOpusEnc.cpp
@@ -245,7 +245,7 @@
mIsFirstFrame = true;
mEncoderFlushed = false;
mBufferAvailable = false;
- mAnchorTimeStamp = 0ull;
+ mAnchorTimeStamp = 0;
mProcessedSamples = 0;
mFilledLen = 0;
mFrameDurationMs = DEFAULT_FRAME_DURATION_MS;
@@ -266,7 +266,7 @@
mIsFirstFrame = true;
mEncoderFlushed = false;
mBufferAvailable = false;
- mAnchorTimeStamp = 0ull;
+ mAnchorTimeStamp = 0;
mProcessedSamples = 0u;
mFilledLen = 0;
if (mEncoder) {
@@ -363,7 +363,7 @@
}
}
if (mIsFirstFrame && inSize > 0) {
- mAnchorTimeStamp = work->input.ordinal.timestamp.peekull();
+ mAnchorTimeStamp = work->input.ordinal.timestamp.peekll();
mIsFirstFrame = false;
}
@@ -386,7 +386,7 @@
size_t inPos = 0;
size_t processSize = 0;
mBytesEncoded = 0;
- uint64_t outTimeStamp = 0u;
+ int64_t outTimeStamp = 0;
std::shared_ptr<C2Buffer> buffer;
uint64_t inputIndex = work->input.ordinal.frameIndex.peeku();
const uint8_t* inPtr = rView.data() + inOffset;
@@ -584,7 +584,7 @@
mOutputBlock.reset();
}
mProcessedSamples += (mNumPcmBytesPerInputFrame / sizeof(int16_t));
- uint64_t outTimeStamp =
+ int64_t outTimeStamp =
mProcessedSamples * 1000000ll / mChannelCount / mSampleRate;
outOrdinal.frameIndex = mOutIndex++;
outOrdinal.timestamp = mAnchorTimeStamp + outTimeStamp;
@@ -612,7 +612,7 @@
return C2_OMITTED;
}
mIsFirstFrame = true;
- mAnchorTimeStamp = 0ull;
+ mAnchorTimeStamp = 0;
mProcessedSamples = 0u;
return drainInternal(pool, nullptr);
}
diff --git a/media/codec2/components/opus/C2SoftOpusEnc.h b/media/codec2/components/opus/C2SoftOpusEnc.h
index 2b4d8f2..733a6bc 100644
--- a/media/codec2/components/opus/C2SoftOpusEnc.h
+++ b/media/codec2/components/opus/C2SoftOpusEnc.h
@@ -67,7 +67,7 @@
uint32_t mSampleRate;
uint32_t mChannelCount;
uint32_t mFrameDurationMs;
- uint64_t mAnchorTimeStamp;
+ int64_t mAnchorTimeStamp;
uint64_t mProcessedSamples;
// Codec delay in ns
uint64_t mCodecDelay;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 5df28f0..def8a18 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -2619,7 +2619,10 @@
std::vector<std::unique_ptr<C2Param>> params;
err = intf->query(
{&mApiFeatures},
- {C2PortAllocatorsTuning::input::PARAM_TYPE},
+ {
+ C2StreamBufferTypeSetting::input::PARAM_TYPE,
+ C2PortAllocatorsTuning::input::PARAM_TYPE
+ },
C2_MAY_BLOCK,
¶ms);
if (err != C2_OK && err != C2_BAD_INDEX) {
@@ -2632,7 +2635,10 @@
if (!param) {
continue;
}
- if (param->type() == C2PortAllocatorsTuning::input::PARAM_TYPE) {
+ if (param->type() == C2StreamBufferTypeSetting::input::PARAM_TYPE) {
+ mInputStreamFormat.reset(
+ C2StreamBufferTypeSetting::input::From(param));
+ } else if (param->type() == C2PortAllocatorsTuning::input::PARAM_TYPE) {
mInputAllocators.reset(
C2PortAllocatorsTuning::input::From(param));
}
@@ -2652,6 +2658,16 @@
return mApiFeatures;
}
+ const C2StreamBufferTypeSetting::input &getInputStreamFormat() const {
+ static std::unique_ptr<C2StreamBufferTypeSetting::input> sInvalidated = []{
+ std::unique_ptr<C2StreamBufferTypeSetting::input> param;
+ param.reset(new C2StreamBufferTypeSetting::input(0u, C2BufferData::INVALID));
+ param->invalidate();
+ return param;
+ }();
+ return mInputStreamFormat ? *mInputStreamFormat : *sInvalidated;
+ }
+
const C2PortAllocatorsTuning::input &getInputAllocators() const {
static std::unique_ptr<C2PortAllocatorsTuning::input> sInvalidated = []{
std::unique_ptr<C2PortAllocatorsTuning::input> param =
@@ -2667,6 +2683,7 @@
std::vector<C2FieldSupportedValuesQuery> mFields;
C2ApiFeaturesSetting mApiFeatures;
+ std::unique_ptr<C2StreamBufferTypeSetting::input> mInputStreamFormat;
std::unique_ptr<C2PortAllocatorsTuning::input> mInputAllocators;
};
@@ -2708,6 +2725,24 @@
if (intfCache.initCheck() != OK) {
continue;
}
+ const C2StreamBufferTypeSetting::input &streamFormat = intfCache.getInputStreamFormat();
+ if (streamFormat) {
+ C2Allocator::type_t allocatorType = C2Allocator::LINEAR;
+ if (streamFormat.value == C2BufferData::GRAPHIC
+ || streamFormat.value == C2BufferData::GRAPHIC_CHUNKS) {
+ allocatorType = C2Allocator::GRAPHIC;
+ }
+
+ if (type != allocatorType) {
+ // requested type is not supported at input allocators
+ ids->clear();
+ ids->insert(defaultAllocatorId);
+ ALOGV("name(%s) does not support a type(0x%x) as input allocator."
+ " uses default allocator id(%d)", name.c_str(), type, defaultAllocatorId);
+ break;
+ }
+ }
+
const C2PortAllocatorsTuning::input &allocators = intfCache.getInputAllocators();
if (firstIteration) {
firstIteration = false;
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index b5200a5..d8d6f06 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -265,6 +265,7 @@
for (const PlaneLayoutComponent &component : plane.components) {
if (!gralloc4::isStandardPlaneLayoutComponentType(component.type)) {
+ mapper.unlock(handle);
return C2_CANNOT_DO;
}
@@ -287,6 +288,7 @@
channel = C2PlaneInfo::CHANNEL_CR;
break;
default:
+ mapper.unlock(handle);
return C2_CORRUPTED;
}
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index fb935b6..e0cc5bf 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -3501,7 +3501,7 @@
}
unsigned mask = br.getBits(8);
for (unsigned i = 0; i < 8; i++) {
- if (((0x1 << i) && mask) == 0)
+ if (((0x1 << i) & mask) == 0)
continue;
if (br.numBitsLeft() < 8) {
diff --git a/media/libmediametrics/Android.bp b/media/libmediametrics/Android.bp
index d758391..4a3973e6 100644
--- a/media/libmediametrics/Android.bp
+++ b/media/libmediametrics/Android.bp
@@ -65,6 +65,7 @@
"//frameworks/base/apex/media/framework",
"//frameworks/base/core/jni",
"//frameworks/base/media/jni",
+ "//packages/modules/Media/apex/framework",
],
}
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 91a44d1..22885c9 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -13,7 +13,7 @@
"presubmit-large": [
{
- "name": "CtsMediaTestCases",
+ "name": "CtsMediaMiscTestCases",
"options": [
{
"include-annotation": "android.platform.test.annotations.Presubmit"
diff --git a/media/libstagefright/id3/TEST_MAPPING b/media/libstagefright/id3/TEST_MAPPING
index d82d26e..6106908 100644
--- a/media/libstagefright/id3/TEST_MAPPING
+++ b/media/libstagefright/id3/TEST_MAPPING
@@ -9,14 +9,15 @@
"presubmit-large": [
// this doesn't seem to run any tests.
- // but: cts-tradefed run -m CtsMediaTestCases -t android.media.cts.MediaMetadataRetrieverTest
+ // but: cts-tradefed run -m CtsMediaMiscTestCases -t \
+ // android.media.misc.cts.MediaMetadataRetrieverTest
// does run he 32 and 64 bit tests, but not the instant tests
// but all I know is that with 'atest', it's not running
{
- "name": "CtsMediaTestCases",
+ "name": "CtsMediaMiscTestCases",
"options": [
{
- "include-filter": "android.media.cts.MediaMetadataRetrieverTest"
+ "include-filter": "android.media.misc.cts.MediaMetadataRetrieverTest"
}
]
}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 1f9b535..638430a 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -158,7 +158,7 @@
virtual bool isDuplicated() const { return false; }
virtual uint32_t latency() { return 0; }
virtual bool isFixedVolume(const DeviceTypeSet& deviceTypes);
- virtual bool setVolume(float volumeDb,
+ virtual bool setVolume(float volumeDb, bool muted,
VolumeSource volumeSource, const StreamTypeVector &streams,
const DeviceTypeSet& deviceTypes,
uint32_t delayMs,
@@ -352,7 +352,22 @@
setClientActive(client, false);
}
}
- virtual bool setVolume(float volumeDb,
+
+ /**
+ * @brief setSwMute for SwOutput routed on a device that supports Hw Gain, this function allows
+ * to mute the tracks associated to a given volume source only.
+ * As an output may host one or more source(s), and as AudioPolicyManager may dispatch or not
+ * the volume change request according to the priority of the volume source to control the
+ * unique hw gain controller, a separated API allows to force a mute/unmute of a volume source.
+ * @param muted true to mute, false otherwise
+ * @param vs volume source to be considered
+ * @param device scoped for the change
+ * @param delayMs potentially applyed to prevent cut sounds.
+ */
+ void setSwMute(bool muted, VolumeSource vs, const StreamTypeVector &streams,
+ const DeviceTypeSet& device, uint32_t delayMs);
+
+ virtual bool setVolume(float volumeDb, bool muted,
VolumeSource volumeSource, const StreamTypeVector &streams,
const DeviceTypeSet& device,
uint32_t delayMs,
@@ -435,7 +450,7 @@
void dump(String8 *dst) const override;
- virtual bool setVolume(float volumeDb,
+ virtual bool setVolume(float volumeDb, bool muted,
VolumeSource volumeSource, const StreamTypeVector &streams,
const DeviceTypeSet& deviceTypes,
uint32_t delayMs,
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 6b08f7c..b2f70e8 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -155,7 +155,7 @@
return false;
}
-bool AudioOutputDescriptor::setVolume(float volumeDb,
+bool AudioOutputDescriptor::setVolume(float volumeDb, bool /*muted*/,
VolumeSource volumeSource,
const StreamTypeVector &/*streams*/,
const DeviceTypeSet& deviceTypes,
@@ -435,14 +435,36 @@
mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
}
-bool SwAudioOutputDescriptor::setVolume(float volumeDb,
+void SwAudioOutputDescriptor::setSwMute(
+ bool muted, VolumeSource vs, const StreamTypeVector &streamTypes,
+ const DeviceTypeSet& deviceTypes, uint32_t delayMs) {
+ // volume source active and more than one volume source is active, otherwise, no-op or let
+ // setVolume controlling SW and/or HW Gains
+ if (!streamTypes.empty() && isActive(vs) && (getActiveVolumeSources().size() > 1)) {
+ for (const auto& devicePort : devices()) {
+ if (isSingleDeviceType(deviceTypes, devicePort->type()) &&
+ devicePort->hasGainController(true /*canUseForVolume*/)) {
+ float volumeAmpl = muted ? 0.0f : Volume::DbToAmpl(0);
+ ALOGV("%s: output: %d, vs: %d, muted: %d, active vs count: %zu", __func__,
+ mIoHandle, vs, muted, getActiveVolumeSources().size());
+ for (const auto &stream : streamTypes) {
+ mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ }
+ return;
+ }
+ }
+ }
+}
+
+bool SwAudioOutputDescriptor::setVolume(float volumeDb, bool muted,
VolumeSource vs, const StreamTypeVector &streamTypes,
const DeviceTypeSet& deviceTypes,
uint32_t delayMs,
bool force)
{
StreamTypeVector streams = streamTypes;
- if (!AudioOutputDescriptor::setVolume(volumeDb, vs, streamTypes, deviceTypes, delayMs, force)) {
+ if (!AudioOutputDescriptor::setVolume(
+ volumeDb, muted, vs, streamTypes, deviceTypes, delayMs, force)) {
return false;
}
if (streams.empty()) {
@@ -459,11 +481,17 @@
// different Volume Source (or if we allow several curves within same volume group)
//
// @todo: default stream volume to max (0) when using HW Port gain?
- float volumeAmpl = Volume::DbToAmpl(0);
- for (const auto &stream : streams) {
- mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ // Allows to set SW Gain on AudioFlinger if:
+ // -volume group has explicit stream(s) associated
+ // -volume group with no explicit stream(s) is the only active source on this output
+ // Allows to mute SW Gain on AudioFlinger only for volume group with explicit stream(s)
+ if (!streamTypes.empty() || (getActiveVolumeSources().size() == 1)) {
+ const bool canMute = muted && (volumeDb != 0.0f) && !streamTypes.empty();
+ float volumeAmpl = canMute ? 0.0f : Volume::DbToAmpl(0);
+ for (const auto &stream : streams) {
+ mClientInterface->setStreamVolume(stream, volumeAmpl, mIoHandle, delayMs);
+ }
}
-
AudioGains gains = devicePort->getGains();
int gainMinValueInMb = gains[0]->getMinValueInMb();
int gainMaxValueInMb = gains[0]->getMaxValueInMb();
@@ -679,14 +707,14 @@
}
-bool HwAudioOutputDescriptor::setVolume(float volumeDb,
+bool HwAudioOutputDescriptor::setVolume(float volumeDb, bool muted,
VolumeSource volumeSource, const StreamTypeVector &streams,
const DeviceTypeSet& deviceTypes,
uint32_t delayMs,
bool force)
{
bool changed = AudioOutputDescriptor::setVolume(
- volumeDb, volumeSource, streams, deviceTypes, delayMs, force);
+ volumeDb, muted, volumeSource, streams, deviceTypes, delayMs, force);
if (changed) {
// TODO: use gain controller on source device if any to adjust volume
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index b028e8f..fb7ae4f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1810,7 +1810,7 @@
if (stream == AUDIO_STREAM_TTS) {
ALOGV("\t found BEACON stream");
if (!mTtsOutputAvailable && mOutputs.isAnyOutputActive(
- toVolumeSource(AUDIO_STREAM_TTS) /*sourceToIgnore*/)) {
+ toVolumeSource(AUDIO_STREAM_TTS, false) /*sourceToIgnore*/)) {
return INVALID_OPERATION;
} else {
beaconMuteLatency = handleEventForBeacon(STARTING_BEACON);
@@ -2032,12 +2032,20 @@
if (outputDesc->getActivityCount(clientVolSrc) == 0 || forceDeviceUpdate) {
outputDesc->setStopTime(client, systemTime());
DeviceVector newDevices = getNewOutputDevices(outputDesc, false /*fromCache*/);
+
+ // If the routing does not change, if an output is routed on a device using HwGain
+ // (aka setAudioPortConfig) and there are still active clients following different
+ // volume group(s), force reapply volume
+ bool requiresVolumeCheck = outputDesc->getActivityCount(clientVolSrc) == 0 &&
+ outputDesc->useHwGain() && outputDesc->isAnyActive(VOLUME_SOURCE_NONE);
+
// delay the device switch by twice the latency because stopOutput() is executed when
// the track stop() command is received and at that time the audio track buffer can
// still contain data that needs to be drained. The latency only covers the audio HAL
// and kernel buffers. Also the latency does not always include additional delay in the
// audio path (audio DSP, CODEC ...)
- setOutputDevices(outputDesc, newDevices, false, outputDesc->latency()*2);
+ setOutputDevices(outputDesc, newDevices, false, outputDesc->latency()*2,
+ nullptr, true /*requiresMuteCheck*/, requiresVolumeCheck);
// force restoring the device selection on other active outputs if it differs from the
// one being selected for this output
@@ -2793,6 +2801,8 @@
// HW Gain management, do not change the volume
if (desc->useHwGain()) {
applyVolume = false;
+ // If the volume source is active with higher priority source, ensure at least Sw Muted
+ desc->setSwMute((index == 0), vs, curves.getStreamTypes(), curDevices, 0 /*delayMs*/);
for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
auto activeClients = desc->clientsList(true /*activeOnly*/, productStrategy,
false /*preferredDevice*/);
@@ -2832,7 +2842,7 @@
// handled by system UI
status_t volStatus = checkAndSetVolume(
curves, vs, index, desc, curDevices,
- ((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))?
+ ((vs == toVolumeSource(AUDIO_STREAM_SYSTEM, false))?
TOUCH_SOUND_FIXED_DELAY_MS : 0));
if (volStatus != NO_ERROR) {
status = volStatus;
@@ -3034,12 +3044,14 @@
bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
{
- return mOutputs.isActive(toVolumeSource(stream), inPastMs);
+ auto vs = toVolumeSource(stream, false);
+ return vs != VOLUME_SOURCE_NONE ? mOutputs.isActive(vs, inPastMs) : false;
}
bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
{
- return mOutputs.isActiveRemotely(toVolumeSource(stream), inPastMs);
+ auto vs = toVolumeSource(stream, false);
+ return vs != VOLUME_SOURCE_NONE ? mOutputs.isActiveRemotely(vs, inPastMs) : false;
}
bool AudioPolicyManager::isSourceActive(audio_source_t source) const
@@ -5737,14 +5749,20 @@
client->getSecondaryOutputs().begin(),
client->getSecondaryOutputs().end(),
secondaryDescs.begin(), secondaryDescs.end())) {
- std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
- std::vector<audio_io_handle_t> secondaryOutputIds;
- for (const auto& secondaryDesc : secondaryDescs) {
- secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
- weakSecondaryDescs.push_back(secondaryDesc);
+ if (!audio_is_linear_pcm(client->config().format)) {
+ // If the format is not PCM, the tracks should be invalidated to get correct
+ // behavior when the secondary output is changed.
+ streamsToInvalidate.insert(client->stream());
+ } else {
+ std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
+ std::vector<audio_io_handle_t> secondaryOutputIds;
+ for (const auto &secondaryDesc: secondaryDescs) {
+ secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
+ weakSecondaryDescs.push_back(secondaryDesc);
+ }
+ trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
+ client->setSecondaryOutputs(std::move(weakSecondaryDescs));
}
- trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
- client->setSecondaryOutputs(std::move(weakSecondaryDescs));
}
}
}
@@ -5858,7 +5876,7 @@
auto doGetOutputDevicesForVoice = [&]() {
return hasVoiceStream(streams) && (outputDesc == mPrimaryOutput ||
- outputDesc->isActive(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) &&
+ outputDesc->isActive(toVolumeSource(AUDIO_STREAM_VOICE_CALL, false))) &&
(isInCall() ||
mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) &&
!isStreamActive(AUDIO_STREAM_ENFORCED_AUDIBLE, 0);
@@ -5954,7 +5972,7 @@
devices.merge(curDevices);
for (audio_io_handle_t output : getOutputsForDevices(curDevices, mOutputs)) {
sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
- if (outputDesc->isActive(toVolumeSource(curStream))) {
+ if (outputDesc->isActive(toVolumeSource(curStream, false))) {
activeDevices.merge(outputDesc->devices());
}
}
@@ -6055,7 +6073,11 @@
// mute/unmute AUDIO_STREAM_TTS on all outputs
ALOGV("\t muting %d", mute);
uint32_t maxLatency = 0;
- auto ttsVolumeSource = toVolumeSource(AUDIO_STREAM_TTS);
+ auto ttsVolumeSource = toVolumeSource(AUDIO_STREAM_TTS, false);
+ if (ttsVolumeSource == VOLUME_SOURCE_NONE) {
+ ALOGV("\t no tts volume source available");
+ return 0;
+ }
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
setVolumeSourceMute(ttsVolumeSource, mute/*on*/, desc, 0 /*delay*/, DeviceTypeSet());
@@ -6165,7 +6187,7 @@
bool force,
int delayMs,
audio_patch_handle_t *patchHandle,
- bool requiresMuteCheck)
+ bool requiresMuteCheck, bool requiresVolumeCheck)
{
ALOGV("%s device %s delayMs %d", __func__, devices.toString().c_str(), delayMs);
uint32_t muteWaitMs;
@@ -6181,6 +6203,7 @@
// filter devices according to output selected
DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
DeviceVector prevDevices = outputDesc->devices();
+ DeviceVector availPrevDevices = mAvailableOutputDevices.filter(prevDevices);
ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());
@@ -6199,8 +6222,7 @@
// no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
// output profile or if new device is not supported AND previous device(s) is(are) still
// available (otherwise reset device must be done on the output)
- if (!devices.isEmpty() && filteredDevices.isEmpty() &&
- !mAvailableOutputDevices.filter(prevDevices).empty()) {
+ if (!devices.isEmpty() && filteredDevices.isEmpty() && !availPrevDevices.empty()) {
ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
// restore previous device after evaluating strategy mute state
outputDesc->setDevices(prevDevices);
@@ -6214,16 +6236,20 @@
// AND the output is connected by a valid audio patch.
// Doing this check here allows the caller to call setOutputDevices() without conditions
if ((filteredDevices.isEmpty() || filteredDevices == prevDevices) &&
- !force && outputDesc->getPatchHandle() != 0) {
+ !force && outputDesc->getPatchHandle() != AUDIO_PATCH_HANDLE_NONE) {
ALOGV("%s setting same device %s or null device, force=%d, patch handle=%d", __func__,
filteredDevices.toString().c_str(), force, outputDesc->getPatchHandle());
+ if (requiresVolumeCheck && !filteredDevices.isEmpty()) {
+ ALOGV("%s setting same device on routed output, force apply volumes", __func__);
+ applyStreamVolumes(outputDesc, filteredDevices.types(), delayMs, true /*force*/);
+ }
return muteWaitMs;
}
ALOGV("%s changing device to %s", __func__, filteredDevices.toString().c_str());
// do the routing
- if (filteredDevices.isEmpty()) {
+ if (filteredDevices.isEmpty() || mAvailableOutputDevices.filter(filteredDevices).empty()) {
resetOutputDevice(outputDesc, delayMs, NULL);
} else {
PatchBuilder patchBuilder;
@@ -6387,11 +6413,11 @@
// louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch
// exploration of the dialer UI. In this situation, bring the accessibility volume closer to
// the ringtone volume
- const auto callVolumeSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
- const auto ringVolumeSrc = toVolumeSource(AUDIO_STREAM_RING);
- const auto musicVolumeSrc = toVolumeSource(AUDIO_STREAM_MUSIC);
- const auto alarmVolumeSrc = toVolumeSource(AUDIO_STREAM_ALARM);
- const auto a11yVolumeSrc = toVolumeSource(AUDIO_STREAM_ACCESSIBILITY);
+ const auto callVolumeSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL, false);
+ const auto ringVolumeSrc = toVolumeSource(AUDIO_STREAM_RING, false);
+ const auto musicVolumeSrc = toVolumeSource(AUDIO_STREAM_MUSIC, false);
+ const auto alarmVolumeSrc = toVolumeSource(AUDIO_STREAM_ALARM, false);
+ const auto a11yVolumeSrc = toVolumeSource(AUDIO_STREAM_ACCESSIBILITY, false);
if (volumeSource == a11yVolumeSrc
&& (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) &&
@@ -6404,12 +6430,12 @@
// in-call: always cap volume by voice volume + some low headroom
if ((volumeSource != callVolumeSrc && (isInCall() ||
mOutputs.isActiveLocally(callVolumeSrc))) &&
- (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM) ||
+ (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM, false) ||
volumeSource == ringVolumeSrc || volumeSource == musicVolumeSrc ||
volumeSource == alarmVolumeSrc ||
- volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION) ||
- volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) ||
- volumeSource == toVolumeSource(AUDIO_STREAM_DTMF) ||
+ volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION, false) ||
+ volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE, false) ||
+ volumeSource == toVolumeSource(AUDIO_STREAM_DTMF, false) ||
volumeSource == a11yVolumeSrc)) {
auto &voiceCurves = getVolumeCurves(callVolumeSrc);
int voiceVolumeIndex = voiceCurves.getVolumeIndex(deviceTypes);
@@ -6447,9 +6473,9 @@
AUDIO_DEVICE_OUT_BLE_HEADSET}).empty() &&
((volumeSource == alarmVolumeSrc ||
volumeSource == ringVolumeSrc) ||
- (volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION)) ||
- (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM)) ||
- ((volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
+ (volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION, false)) ||
+ (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM, false)) ||
+ ((volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE, false)) &&
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
curves.canBeMuted()) {
@@ -6535,10 +6561,10 @@
outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
return NO_ERROR;
}
- VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
- VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO);
- bool isVoiceVolSrc = callVolSrc == volumeSource;
- bool isBtScoVolSrc = btScoVolSrc == volumeSource;
+ VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL, false);
+ VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO, false);
+ bool isVoiceVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (callVolSrc == volumeSource);
+ bool isBtScoVolSrc = (volumeSource != VOLUME_SOURCE_NONE) && (btScoVolSrc == volumeSource);
bool isScoRequested = isScoRequestedForComm();
// do not change in call volume if bluetooth is connected and vice versa
@@ -6563,8 +6589,9 @@
isSingleDeviceType(deviceTypes, audio_is_bluetooth_out_sco_device))) {
volumeDb = 0.0f;
}
+ const bool muted = (index == 0) && (volumeDb != 0.0f);
outputDesc->setVolume(
- volumeDb, volumeSource, curves.getStreamTypes(), deviceTypes, delayMs, force);
+ volumeDb, muted, volumeSource, curves.getStreamTypes(), deviceTypes, delayMs, force);
if (outputDesc == mPrimaryOutput && (isVoiceVolSrc || isBtScoVolSrc)) {
float voiceVolume;
@@ -6606,8 +6633,10 @@
for (auto attributes: mEngine->getAllAttributesForProductStrategy(strategy)) {
ALOGVV("%s() attributes %s, mute %d, output ID %d", __func__,
toString(attributes).c_str(), on, outputDesc->getId());
- VolumeSource source = toVolumeSource(attributes);
- if (std::find(begin(sourcesToMute), end(sourcesToMute), source) == end(sourcesToMute)) {
+ VolumeSource source = toVolumeSource(attributes, false);
+ if ((source != VOLUME_SOURCE_NONE) &&
+ (std::find(begin(sourcesToMute), end(sourcesToMute), source)
+ == end(sourcesToMute))) {
sourcesToMute.push_back(source);
}
}
@@ -6630,7 +6659,7 @@
if (on) {
if (!outputDesc->isMuted(volumeSource)) {
if (curves.canBeMuted() &&
- (volumeSource != toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) ||
+ (volumeSource != toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE, false) ||
(mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) ==
AUDIO_POLICY_FORCE_NONE))) {
checkAndSetVolume(curves, volumeSource, 0, outputDesc, deviceTypes, delayMs);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index c4885e4..eac3783 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -421,13 +421,30 @@
{
return static_cast<VolumeSource>(volumeGroup);
}
- VolumeSource toVolumeSource(const audio_attributes_t &attributes) const
+ /**
+ * @brief toVolumeSource converts an audio attributes into a volume source
+ * (either a legacy stream or a volume group). If fallback on default is allowed, and if
+ * the audio attributes do not follow any specific product strategy's rule, it will be
+ * associated to default volume source, e.g. music. Thus, any of call of volume API
+ * using this translation function may affect the default volume source.
+ * If fallback is not allowed and no matching rule is identified for the given attributes,
+ * the volume source will be undefined, thus, no volume will be altered/modified.
+ * @param attributes to be considered
+ * @param fallbackOnDefault
+ * @return volume source associated with given attributes, otherwise either music if
+ * fallbackOnDefault is set or none.
+ */
+ VolumeSource toVolumeSource(
+ const audio_attributes_t &attributes, bool fallbackOnDefault = true) const
{
- return toVolumeSource(mEngine->getVolumeGroupForAttributes(attributes));
+ return toVolumeSource(mEngine->getVolumeGroupForAttributes(
+ attributes, fallbackOnDefault));
}
- VolumeSource toVolumeSource(audio_stream_type_t stream) const
+ VolumeSource toVolumeSource(
+ audio_stream_type_t stream, bool fallbackOnDefault = true) const
{
- return toVolumeSource(mEngine->getVolumeGroupForStreamType(stream));
+ return toVolumeSource(mEngine->getVolumeGroupForStreamType(
+ stream, fallbackOnDefault));
}
IVolumeCurves &getVolumeCurves(VolumeSource volumeSource)
{
@@ -453,14 +470,27 @@
void removeOutput(audio_io_handle_t output);
void addInput(audio_io_handle_t input, const sp<AudioInputDescriptor>& inputDesc);
- // change the route of the specified output. Returns the number of ms we have slept to
- // allow new routing to take effect in certain cases.
+ /**
+ * @brief setOutputDevices change the route of the specified output.
+ * @param outputDesc to be considered
+ * @param device to be considered to route the output
+ * @param force if true, force the routing even if no change.
+ * @param delayMs if specified, delay to apply for mute/volume op when changing device
+ * @param patchHandle if specified, the patch handle this output is connected through.
+ * @param requiresMuteCheck if specified, for e.g. when another output is on a shared device
+ * and currently active, allow to have proper drain and avoid pops
+ * @param requiresVolumeCheck true if called requires to reapply volume if the routing did
+ * not change (but the output is still routed).
+ * @return the number of ms we have slept to allow new routing to take effect in certain
+ * cases.
+ */
uint32_t setOutputDevices(const sp<SwAudioOutputDescriptor>& outputDesc,
const DeviceVector &device,
bool force = false,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL,
- bool requiresMuteCheck = true);
+ bool requiresMuteCheck = true,
+ bool requiresVolumeCheck = false);
status_t resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
int delayMs = 0,
audio_patch_handle_t *patchHandle = NULL);
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 45c9f56..2d0d2f4 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -967,10 +967,10 @@
if (channelMask != 0) {
switch (direction) {
case 1: // Output, keep sync with AudioTypes#getAAudioDirection()
- channelCount = audio_channel_count_from_out_mask(channelMask);
+ channelCount = (int32_t)audio_channel_count_from_out_mask(channelMask);
break;
case 2: // Input, keep sync with AudioTypes#getAAudioDirection()
- channelCount = audio_channel_count_from_in_mask(channelMask);
+ channelCount = (int32_t)audio_channel_count_from_in_mask(channelMask);
break;
default:
ALOGW("Invalid direction %d", direction);
diff --git a/services/mediametrics/statsd_drm.cpp b/services/mediametrics/statsd_drm.cpp
index 287fb8d..e06a605 100644
--- a/services/mediametrics/statsd_drm.cpp
+++ b/services/mediametrics/statsd_drm.cpp
@@ -171,7 +171,7 @@
std::vector<uint8_t> buf(str.length() / 4 * 3, 0);
size_t size = buf.size();
if (decodeBase64(buf.data(), &size, str.c_str()) && size <= buf.size()) {
- buf.erase(buf.begin() + size, buf.end());
+ buf.erase(buf.begin() + (ptrdiff_t)size, buf.end());
return buf;
}
return {};