Merge "Pass full AttributionSourceState across Java/Cpp boundaries." into main
diff --git a/Android.bp b/Android.bp
index c11e38a..0c7ed6e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -160,11 +160,11 @@
imports: ["android.hardware.audio.core-V2"],
},
],
- frozen: true,
+ frozen: false,
}
-latest_av_audio_types_aidl = "av-audio-types-aidl-V1"
+latest_av_audio_types_aidl = "av-audio-types-aidl-V2"
cc_defaults {
name: "latest_av_audio_types_aidl_ndk_shared",
diff --git a/media/audio/aconfig/audioserver.aconfig b/media/audio/aconfig/audioserver.aconfig
index 6f76408..39fa187 100644
--- a/media/audio/aconfig/audioserver.aconfig
+++ b/media/audio/aconfig/audioserver.aconfig
@@ -29,6 +29,14 @@
}
flag {
+ name: "fix_call_audio_patch"
+ namespace: "media_audio"
+ description:
+ "optimize creation and release of audio patches for call routing"
+ bug: "292492229"
+}
+
+flag {
name: "fix_concurrent_playback_behavior_with_bit_perfect_client"
namespace: "media_audio"
description:
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 39aadd7..ae66a58 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -2605,18 +2605,31 @@
c2_status_t status = GetCodec2BlockPool(C2BlockPool::BASIC_LINEAR, nullptr, &pool);
if (status == C2_OK) {
+ int width, height;
+ config->mInputFormat->findInt32("width", &width);
+ config->mInputFormat->findInt32("height", &height);
+ // The length of the qp-map corresponds to the number of 16x16 blocks in one frame
+ int expectedMapSize = ((width + 15) / 16) * ((height + 15) / 16);
size_t mapSize = qpOffsetMap->size();
- std::shared_ptr<C2LinearBlock> block;
- status = pool->fetchLinearBlock(mapSize,
- C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
- if (status == C2_OK && !block->map().get().error()) {
- C2WriteView wView = block->map().get();
- uint8_t* outData = wView.data();
- memcpy(outData, qpOffsetMap->data(), mapSize);
- C2InfoBuffer info = C2InfoBuffer::CreateLinearBuffer(
- kParamIndexQpOffsetMapBuffer,
- block->share(0, mapSize, C2Fence()));
- mChannel->setInfoBuffer(std::make_shared<C2InfoBuffer>(info));
+ if (mapSize >= expectedMapSize) {
+ std::shared_ptr<C2LinearBlock> block;
+ status = pool->fetchLinearBlock(
+ expectedMapSize,
+ C2MemoryUsage{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+ &block);
+ if (status == C2_OK && !block->map().get().error()) {
+ C2WriteView wView = block->map().get();
+ uint8_t* outData = wView.data();
+ memcpy(outData, qpOffsetMap->data(), expectedMapSize);
+ C2InfoBuffer info = C2InfoBuffer::CreateLinearBuffer(
+ kParamIndexQpOffsetMapBuffer,
+ block->share(0, expectedMapSize, C2Fence()));
+ mChannel->setInfoBuffer(std::make_shared<C2InfoBuffer>(info));
+ }
+ } else {
+ ALOGE("Ignoring param key %s as buffer size %d is less than expected "
+ "buffer size %d",
+ PARAMETER_KEY_QP_OFFSET_MAP, mapSize, expectedMapSize);
}
}
params->removeEntryByName(PARAMETER_KEY_QP_OFFSET_MAP);
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index db59227..36725ec 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -1890,6 +1890,9 @@
if (mDomain == (IS_VIDEO | IS_ENCODER)) {
AString qpOffsetRects;
if (params->findString(PARAMETER_KEY_QP_OFFSET_RECTS, &qpOffsetRects)) {
+ int width, height;
+ mInputFormat->findInt32("width", &width);
+ mInputFormat->findInt32("height", &height);
std::vector<C2QpOffsetRectStruct> c2QpOffsetRects;
char mutableStrQpOffsetRects[strlen(qpOffsetRects.c_str()) + 1];
strcpy(mutableStrQpOffsetRects, qpOffsetRects.c_str());
@@ -1899,11 +1902,17 @@
if (sscanf(box, "%d,%d-%d,%d=%d", &top, &left, &bottom, &right, &offset) == 5) {
left = c2_max(0, left);
top = c2_max(0, top);
+ right = c2_min(right, width);
+ bottom = c2_min(bottom, height);
if (right > left && bottom > top) {
C2Rect rect(right - left, bottom - top);
rect.at(left, top);
c2QpOffsetRects.push_back(C2QpOffsetRectStruct(rect, offset));
+ } else {
+ ALOGE("Rects configuration %s is not valid.", box);
}
+ } else {
+ ALOGE("Rects configuration %s doesn't follow the string pattern.", box);
}
box = strtok(nullptr, ";");
}
diff --git a/media/codec2/tests/Android.bp b/media/codec2/tests/Android.bp
index 02c356c..7d671a7 100644
--- a/media/codec2/tests/Android.bp
+++ b/media/codec2/tests/Android.bp
@@ -45,6 +45,11 @@
"C2SampleComponent_test.cpp",
"C2UtilTest.cpp",
"vndk/C2BufferTest.cpp",
+ "vndk/C2FenceTest.cpp",
+ ],
+
+ static_libs: [
+ "libgmock",
],
shared_libs: [
@@ -52,6 +57,7 @@
"libcodec2_vndk",
"libcutils",
"liblog",
+ "libui",
"libutils",
],
diff --git a/media/codec2/tests/vndk/C2FenceTest.cpp b/media/codec2/tests/vndk/C2FenceTest.cpp
new file mode 100644
index 0000000..9292381
--- /dev/null
+++ b/media/codec2/tests/vndk/C2FenceTest.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <C2Buffer.h>
+#include <C2FenceFactory.h>
+
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <linux/kcmp.h> /* Definition of KCMP_* constants */
+#include <sys/mman.h>
+#include <sys/syscall.h> /* Definition of SYS_* constants */
+#include <ui/Fence.h>
+
+namespace android {
+
+static int fd_kcmp(int fd1, int fd2) {
+ static pid_t pid = getpid();
+
+ return syscall(SYS_kcmp, pid, pid, KCMP_FILE, fd1, fd2);
+}
+
+// matcher to check if value (arg) and fd refers to the same file
+MATCHER_P(RefersToTheSameFile, fd, "") {
+ return fd_kcmp(fd, arg) == 0;
+}
+
+// matcher to check if value (arg) is a dup of an fd
+MATCHER_P(IsDupOf, fd, "") {
+ return (ExplainMatchResult(::testing::Ne(-1), arg, result_listener) &&
+ ExplainMatchResult(::testing::Ne(fd), arg, result_listener) &&
+ ExplainMatchResult(RefersToTheSameFile(fd), arg, result_listener));
+}
+
+class C2FenceTest : public ::testing::Test {
+public:
+ C2FenceTest() = default;
+
+ ~C2FenceTest() = default;
+
+
+protected:
+ enum : int32_t {
+ SYNC_FENCE_DEPRECATED_MAGIC = 3,
+ SYNC_FENCE_UNORDERED_MAGIC = '\302fsu',
+ SYNC_FENCE_MAGIC = '\302fso',
+ };
+
+ // Validate a null fence
+ void validateNullFence(const C2Fence &fence);
+
+ // Validate a single fd sync fence
+ void validateSingleFdFence(const C2Fence &fence, int fd);
+
+ // Validate a two fd unordered sync fence
+ void validateTwoFdUnorderedFence(const C2Fence &fence, int fd1, int fd2, int mergeFd);
+
+ // Validate a three fd sync fence
+ void validateThreeFdFence(const C2Fence &fence, int fd1, int fd2, int fd3);
+};
+
+TEST_F(C2FenceTest, IsDupOf_sanity) {
+ int fd1 = memfd_create("test1", 0 /* flags */);
+ int fd2 = memfd_create("test2", 0 /* flags */);
+ int fd3 = memfd_create("test3", 0 /* flags */);
+
+ EXPECT_THAT(fd1, ::testing::Not(IsDupOf(fd2)));
+ EXPECT_THAT(-1, ::testing::Not(IsDupOf(fd2)));
+ EXPECT_THAT(-1, ::testing::Not(IsDupOf(-1)));
+ EXPECT_THAT(fd3, ::testing::Not(IsDupOf(fd3)));
+
+ int fd4 = dup(fd3);
+ EXPECT_THAT(fd4, IsDupOf(fd3));
+ EXPECT_THAT(fd3, IsDupOf(fd4));
+
+ close(fd1);
+ close(fd2);
+ close(fd3);
+ close(fd4);
+}
+
+TEST_F(C2FenceTest, NullFence) {
+ validateNullFence(C2Fence());
+}
+
+void C2FenceTest::validateNullFence(const C2Fence &fence) {
+ // Verify that the fence is valid.
+ EXPECT_TRUE(fence.valid());
+ EXPECT_TRUE(fence.ready());
+ base::unique_fd fenceFd{fence.fd()};
+ EXPECT_EQ(fenceFd.get(), -1);
+ EXPECT_FALSE(fence.isHW()); // perhaps this should be false for a null fence
+
+ // A null fence has no fds
+ std::vector<int> fds = ExtractFdsFromCodec2SyncFence(fence);
+ EXPECT_THAT(fds, ::testing::IsEmpty());
+ for (int fd : fds) {
+ close(fd);
+ }
+
+ // A null fence has no native handle
+ native_handle_t *handle = _C2FenceFactory::CreateNativeHandle(fence);
+ EXPECT_THAT(handle, ::testing::IsNull());
+ if (handle) {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+}
+
+TEST_F(C2FenceTest, SyncFence_with_negative_fd) {
+ // Create a SyncFence with a negative fd.
+ C2Fence fence = _C2FenceFactory::CreateSyncFence(-1, false /* validate */);
+
+ validateNullFence(fence);
+}
+
+TEST_F(C2FenceTest, SyncFence_with_valid_fd) {
+ // Create a SyncFence with a valid fd. We cannot create an actual sync fd,
+ // so we cannot test wait(), but we can verify the ABI APIs
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ C2Fence fence = _C2FenceFactory::CreateSyncFence(fd, false /* validate */);
+ validateSingleFdFence(fence, fd);
+}
+
+void C2FenceTest::validateSingleFdFence(const C2Fence &fence, int fd) {
+ // EXPECT_TRUE(fence.valid()); // need a valid sync fd to test this
+ // EXPECT_TRUE(fence.ready());
+ // Verify that the fence says it is a HW sync fence.
+ EXPECT_TRUE(fence.isHW()); // FIXME this may be an implementation detail
+
+ // Verify that the fd returned is a duped version of the initial fd
+ base::unique_fd fenceFd{fence.fd()};
+ EXPECT_THAT(fenceFd.get(), IsDupOf(fd));
+
+ // Verify that fds returns a duped version of the initial fd
+ std::vector<int> fds = ExtractFdsFromCodec2SyncFence(fence);
+ EXPECT_THAT(fds, ::testing::SizeIs(1));
+ EXPECT_THAT(fds, ::testing::ElementsAre(IsDupOf(fd)));
+ for (int fd_i : fds) {
+ close(fd_i);
+ }
+
+ native_handle_t *handle = _C2FenceFactory::CreateNativeHandle(fence);
+ EXPECT_THAT(handle, ::testing::NotNull());
+ if (handle) {
+ EXPECT_EQ(handle->numFds, 1);
+ EXPECT_EQ(handle->numInts, 1);
+ EXPECT_THAT(handle->data[0], IsDupOf(fd));
+ EXPECT_EQ(handle->data[1], SYNC_FENCE_MAGIC);
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+}
+
+TEST_F(C2FenceTest, UnorderedMultiSyncFence_with_one_valid_test_fd) {
+ // Create a multi SyncFence with a single valid fd. This should create
+ // a single fd sync fence. We can only validate this through its public
+ // methods: fd/fds and verify the native handle ABI.
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ c2_status_t status = C2_BAD_VALUE;
+ C2Fence fence = _C2FenceFactory::CreateUnorderedMultiSyncFence(
+ { -1, fd, -1 }, &status);
+ // if we only have one valid fd, we are not merging fences, so the test fd is not validated
+ EXPECT_EQ(status, C2_OK);
+
+ validateSingleFdFence(fence, fd);
+}
+
+TEST_F(C2FenceTest, UnorderedMultiSyncFence_with_one_valid_test_fd_null_status) {
+ // Create a multi SyncFence with a single valid fd. This should create
+ // a single fd sync fence. We can only validate this through its public
+ // methods: fd/fds and verify the native handle ABI.
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ C2Fence fence = _C2FenceFactory::CreateUnorderedMultiSyncFence(
+ { -1, fd, -1 });
+
+ validateSingleFdFence(fence, fd);
+}
+
+TEST_F(C2FenceTest, UnorderedMultiSyncFence_with_merge_failure) {
+ // Create a multi SyncFence with a multiple non-sync fence fds. This should
+ // result in a fence created, but also an error.
+
+ int fd1 = memfd_create("test1", 0 /* flags */);
+ int fd2 = memfd_create("test2", 0 /* flags */);
+ int fd3 = memfd_create("test3", 0 /* flags */);
+
+ c2_status_t status = C2_BAD_VALUE;
+ C2Fence fence = _C2FenceFactory::CreateUnorderedMultiSyncFence(
+ { fd1, fd2, fd3 }, &status);
+ EXPECT_EQ(status, C2_CORRUPTED);
+
+ validateThreeFdFence(fence, fd1, fd2, fd3);
+}
+
+TEST_F(C2FenceTest, UnorderedMultiSyncFence_with_merge_failure_null_status) {
+ // Create a multi SyncFence with a multiple non-sync fence fds. This should
+ // result in a fence created, but also an error.
+
+ int fd1 = memfd_create("test1", 0 /* flags */);
+ int fd2 = memfd_create("test2", 0 /* flags */);
+ int fd3 = memfd_create("test3", 0 /* flags */);
+
+ C2Fence fence = _C2FenceFactory::CreateUnorderedMultiSyncFence(
+ { fd1, fd2, fd3 });
+
+ validateThreeFdFence(fence, fd1, fd2, fd3);
+}
+
+TEST_F(C2FenceTest, UnorderedMultiSyncFence_with_multiple_fds) {
+ // We cannot create a true unordered multi sync fence as we can only
+ // create test fds and those cannot be merged. As such, we cannot
+ // test the factory method CreateUnorderedMultiSyncFence. We can however
+ // create a test fence from a constructed native handle.
+
+ // Technically, we need 3 fds as if we end up with only 2, we wouldn't
+ // actually need a 2nd (final fence fd) since it is equivalent to the
+ // first. In fact we will generate (and always generated) a single fd
+ // fence in that case.
+ int fd1 = memfd_create("test1", 0 /* flags */);
+ int fd2 = memfd_create("test2", 0 /* flags */);
+ int mergeFd = memfd_create("test3", 0 /* flags */);
+
+ native_handle_t *handle = native_handle_create(3 /* numfds */, 1 /* numints */);
+ handle->data[0] = fd1;
+ handle->data[1] = fd2;
+ handle->data[2] = mergeFd;
+ handle->data[3] = SYNC_FENCE_UNORDERED_MAGIC;
+ C2Fence fence = _C2FenceFactory::CreateFromNativeHandle(handle, true /* takeOwnership */);
+ native_handle_delete(handle);
+
+ validateTwoFdUnorderedFence(fence, fd1, fd2, mergeFd);
+}
+
+void C2FenceTest::validateTwoFdUnorderedFence(
+ const C2Fence &fence, int fd1, int fd2, int mergeFd) {
+ // EXPECT_TRUE(fence.valid()); // need a valid sync fd to test this
+ // EXPECT_TRUE(fence.ready());
+ // Verify that the fence says it is a HW sync fence.
+ EXPECT_TRUE(fence.isHW()); // FIXME this may be an implementation detail
+
+ // Verify that the fd returned is a duped version of the merge fd
+ base::unique_fd fenceFd{fence.fd()};
+ EXPECT_THAT(fenceFd.get(), IsDupOf(mergeFd));
+
+ // Verify that fds returns a duped versions of the initial fds (but not the merge fd)
+ std::vector<int> fds = ExtractFdsFromCodec2SyncFence(fence);
+ EXPECT_THAT(fds, ::testing::SizeIs(2));
+ EXPECT_THAT(fds, ::testing::ElementsAre(IsDupOf(fd1), IsDupOf(fd2)));
+ for (int fd_i : fds) {
+ close(fd_i);
+ }
+
+ native_handle_t *handle = _C2FenceFactory::CreateNativeHandle(fence);
+ EXPECT_THAT(handle, ::testing::NotNull());
+ if (handle) {
+ EXPECT_EQ(handle->numFds, 3);
+ EXPECT_EQ(handle->numInts, 1);
+ EXPECT_THAT(handle->data[0], IsDupOf(fd1));
+ EXPECT_THAT(handle->data[1], IsDupOf(fd2));
+ EXPECT_THAT(handle->data[2], IsDupOf(mergeFd));
+ EXPECT_EQ(handle->data[3], SYNC_FENCE_UNORDERED_MAGIC);
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+}
+
+TEST_F(C2FenceTest, MultiSyncFence_with_one_valid_test_fd) {
+ // Create a multi SyncFence with a single valid fd. This should create
+ // a single fd sync fence. We can only validate this through its public
+ // methods: fd/fds and verify the native handle ABI.
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ c2_status_t status = C2_BAD_VALUE;
+ C2Fence fence = _C2FenceFactory::CreateMultiSyncFence(
+ { -1, fd, -1 }, &status);
+ // if we only have one valid fd, we are not merging fences, so the test fds are not validated
+ EXPECT_EQ(status, C2_OK);
+
+ validateSingleFdFence(fence, fd);
+}
+
+TEST_F(C2FenceTest, MultiSyncFence_with_one_valid_test_fd_null_status) {
+ // Create a multi SyncFence with a single valid fd. This should create
+ // a single fd sync fence. We can only validate this through its public
+ // methods: fd/fds and verify the native handle ABI.
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ C2Fence fence = _C2FenceFactory::CreateMultiSyncFence(
+ { -1, fd, -1 });
+
+ validateSingleFdFence(fence, fd);
+}
+
+TEST_F(C2FenceTest, MultiSyncFence_with_multiple_fds) {
+ int fd1 = memfd_create("test1", 0 /* flags */);
+ int fd2 = memfd_create("test2", 0 /* flags */);
+ int fd3 = memfd_create("test3", 0 /* flags */);
+
+ c2_status_t status = C2_BAD_VALUE;
+ C2Fence fence = _C2FenceFactory::CreateMultiSyncFence(
+ { fd1, fd2, fd3 }, &status);
+ // test fds are not validated
+ EXPECT_EQ(status, C2_OK);
+
+ validateThreeFdFence(fence, fd1, fd2, fd3);
+}
+
+void C2FenceTest::validateThreeFdFence(const C2Fence &fence, int fd1, int fd2, int fd3) {
+ // EXPECT_TRUE(fence.valid()); // need a valid sync fd to test this
+ // EXPECT_TRUE(fence.ready());
+ // Verify that the fence says it is a HW sync fence.
+ EXPECT_TRUE(fence.isHW()); // FIXME this may be an implementation detail
+
+ // Verify that the fd returned is a duped version of the final fd
+ base::unique_fd fenceFd{fence.fd()};
+ EXPECT_THAT(fenceFd.get(), IsDupOf(fd3));
+
+ // Verify that fds returns a duped versions of all 3 initial fds
+ std::vector<int> fds = ExtractFdsFromCodec2SyncFence(fence);
+ EXPECT_THAT(fds, ::testing::SizeIs(3));
+ EXPECT_THAT(fds, ::testing::ElementsAre(IsDupOf(fd1), IsDupOf(fd2), IsDupOf(fd3)));
+ for (int fd_i : fds) {
+ close(fd_i);
+ }
+
+ native_handle_t *handle = _C2FenceFactory::CreateNativeHandle(fence);
+ EXPECT_THAT(handle, ::testing::NotNull());
+ if (handle) {
+ EXPECT_EQ(handle->numFds, 3);
+ EXPECT_EQ(handle->numInts, 1);
+ EXPECT_THAT(handle->data[0], IsDupOf(fd1));
+ EXPECT_THAT(handle->data[1], IsDupOf(fd2));
+ EXPECT_THAT(handle->data[2], IsDupOf(fd3));
+ EXPECT_EQ(handle->data[3], SYNC_FENCE_MAGIC);
+
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
+}
+
+TEST_F(C2FenceTest, BackwardCompat_UDC_sync_fence) {
+ // Create a single SyncFence from a UDC native handle
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ native_handle_t *handle = native_handle_create(1 /* numfds */, 1 /* numints */);
+ handle->data[0] = fd;
+ handle->data[1] = SYNC_FENCE_DEPRECATED_MAGIC;
+ C2Fence fence = _C2FenceFactory::CreateFromNativeHandle(handle, true /* takeOwnership */);
+ native_handle_delete(handle);
+
+ validateSingleFdFence(fence, fd);
+}
+
+TEST_F(C2FenceTest, BackwardCompat_24Q1_single_fd_fence) {
+ // Create a single SyncFence from a 24Q1 native handle
+ // This had the same (albeit separately duped) fd twice, and used the legacy
+ // magic number.
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ native_handle_t *handle = native_handle_create(2 /* numfds */, 1 /* numints */);
+ handle->data[0] = fd;
+ handle->data[1] = dup(fd);
+ handle->data[2] = SYNC_FENCE_DEPRECATED_MAGIC;
+ C2Fence fence = _C2FenceFactory::CreateFromNativeHandle(handle, true /* takeOwnership */);
+ native_handle_delete(handle);
+
+ validateSingleFdFence(fence, fd);
+}
+
+TEST_F(C2FenceTest, BackwardCompat_24Q3_single_fd_fence) {
+ // Create a single SyncFence from the defined native handle
+
+ int fd = memfd_create("test", 0 /* flags */);
+
+ native_handle_t *handle = native_handle_create(1 /* numfds */, 1 /* numints */);
+ handle->data[0] = fd;
+ handle->data[1] = SYNC_FENCE_MAGIC;
+ C2Fence fence = _C2FenceFactory::CreateFromNativeHandle(handle, true /* takeOwnership */);
+ native_handle_delete(handle);
+
+ validateSingleFdFence(fence, fd);
+}
+
+TEST_F(C2FenceTest, BackwardCompat_24Q1_multi_fd_fence) {
+ // Create a single SyncFence from a 24Q1 era native handle with
+ // the legacy magic number.
+
+ int fd1 = memfd_create("test1", 0 /* flags */);
+ int fd2 = memfd_create("test2", 0 /* flags */);
+ int mergeFd = memfd_create("test3", 0 /* flags */);
+
+ native_handle_t *handle = native_handle_create(3 /* numfds */, 1 /* numints */);
+ handle->data[0] = fd1;
+ handle->data[1] = fd2;
+ handle->data[2] = mergeFd;
+ handle->data[3] = SYNC_FENCE_DEPRECATED_MAGIC;
+ C2Fence fence = _C2FenceFactory::CreateFromNativeHandle(handle, true /* takeOwnership */);
+ native_handle_delete(handle);
+
+ validateTwoFdUnorderedFence(fence, fd1, fd2, mergeFd);
+}
+
+// No need to create BackwardCompat_24Q3_unordered_multi_fd_fence because
+// we are creating that fence already from the 24Q3 native handle layout
+// in the UnorderedMultiSyncFence_with_multiple_fds test.
+
+TEST_F(C2FenceTest, BackwardCompat_24Q3_multi_fd_fence) {
+ // Create a single SyncFence from a 24Q1 era native handle with
+ // the legacy magic number.
+
+ int fd1 = memfd_create("test1", 0 /* flags */);
+ int fd2 = memfd_create("test2", 0 /* flags */);
+ int fd3 = memfd_create("test3", 0 /* flags */);
+
+ native_handle_t *handle = native_handle_create(3 /* numfds */, 1 /* numints */);
+ handle->data[0] = fd1;
+ handle->data[1] = fd2;
+ handle->data[2] = fd3;
+ handle->data[3] = SYNC_FENCE_MAGIC;
+ C2Fence fence = _C2FenceFactory::CreateFromNativeHandle(handle, true /* takeOwnership */);
+ native_handle_delete(handle);
+
+ validateThreeFdFence(fence, fd1, fd2, fd3);
+}
+
+} // namespace android
diff --git a/media/codec2/vndk/C2Fence.cpp b/media/codec2/vndk/C2Fence.cpp
index 3438406..d28f926 100644
--- a/media/codec2/vndk/C2Fence.cpp
+++ b/media/codec2/vndk/C2Fence.cpp
@@ -28,16 +28,27 @@
#include <utility>
-#define MAX_FENCE_FDS 1
+// support up to 32 sync fds (and an optional merged fd), and 1 int
+#define MAX_FENCE_FDS 33
+#define MAX_FENCE_INTS 1
class C2Fence::Impl {
public:
- enum type_t : uint32_t {
- INVALID_FENCE,
- NULL_FENCE,
- SURFACE_FENCE,
- SYNC_FENCE,
- PIPE_FENCE,
+ // These enums are not part of the ABI, so can be changed.
+ enum type_t : int32_t {
+ INVALID_FENCE = -1,
+ NULL_FENCE = 0,
+ SURFACE_FENCE = 2,
+
+ SYNC_FENCE = 3,
+ PIPE_FENCE = 4,
+ };
+
+ // magic numbers for native handles
+ enum : int32_t {
+ SYNC_FENCE_DEPRECATED_MAGIC = 3,
+ SYNC_FENCE_UNORDERED_MAGIC = '\302fsu',
+ SYNC_FENCE_MAGIC = '\302fso',
};
virtual c2_status_t wait(c2_nsecs_t timeoutNs) = 0;
@@ -54,7 +65,8 @@
/**
* Create a native handle for the fence so it can be marshalled.
- * The native handle must store fence type in the first integer.
+ * All native handles must store fence type in the last integer.
+ * The created native handle (if not null) must be closed by the caller.
*
* \return a valid native handle if the fence can be marshalled, otherwise return null.
*/
@@ -64,11 +76,29 @@
Impl() = default;
+ /**
+ * Get the type of the fence from the native handle.
+ *
+ * \param nh the native handle to get the type from.
+ * \return the type of the fence, or INVALID_FENCE if the native handle is
+ * invalid or malformed.
+ */
static type_t GetTypeFromNativeHandle(const native_handle_t* nh) {
- if (nh && nh->numFds >= 0 && nh->numFds <= MAX_FENCE_FDS && nh->numInts > 0) {
- return static_cast<type_t>(nh->data[nh->numFds]);
+ if (!nh || nh->numFds < 0 || nh->numFds > MAX_FENCE_FDS
+ || nh->numInts < 1 || nh->numInts > MAX_FENCE_INTS) {
+ return INVALID_FENCE;
}
- return INVALID_FENCE;
+
+ // the magic number for Codec 2.0 native handles is the last integer
+ switch (nh->data[nh->numFds + nh->numInts - 1]) {
+ case SYNC_FENCE_MAGIC:
+ case SYNC_FENCE_UNORDERED_MAGIC:
+ case SYNC_FENCE_DEPRECATED_MAGIC:
+ return SYNC_FENCE;
+
+ default:
+ return INVALID_FENCE;
+ }
}
};
@@ -189,6 +219,53 @@
using namespace android;
+/**
+ * Implementation for a sync fence.
+ *
+ * A sync fence is fundamentally a fence that is created from an android sync
+ * fd (which represents a HW fence).
+ *
+ * The native handle layout for a single sync fence is:
+ * fd[0] - sync fd
+ * int[0] - magic (SYNC_FENCE_MAGIC (=`\302fso'))
+ *
+ * Note: Between Android T and 24Q3, the magic number was erroneously
+ * SYNC_FENCE (=3).
+ *
+ * Multi(ple) Sync Fences
+ *
+ * Since Android 24Q3, this implementation also supports a sequence of
+ * sync fences. When this is the case, there is an expectation that the last
+ * sync fence being ready will guarantee that all other sync fences are
+ * also ready. (This guarantees backward compatibility to a single fd sync fence,
+ * and mFence will be that final fence.)
+ *
+ * It is furthermore recommended that the fences be in order - either by
+ * expected signaling time, or by the order in which they need to be ready. The
+ * specific ordering is not specified or enforced, but it could be an
+ * implementation requirement of the specific use case in the future.
+ *
+ * This implementation also supports an unordered set of sync fences. In this
+ * case, it will merge all the fences into a single merged fence, which will
+ * be the backward compatible singular fence (stored in mFence).
+ *
+ * The native handle layout for an unordered multi-fence sync fence (from Android
+ * 24Q3) is:
+ *
+ * fd[0] - sync fd 1
+ * ...
+ * fd[n-1] - sync fd N
+ * fd[n] - merged fence fd
+ * int[0] - magic (SYNC_FENCE_UNORDERED_MAGIC (='\302fsu'))
+ *
+ * The native handle layout for an ordered multi-fence sync fence (from Android
+ * 24Q3) is:
+ *
+ * fd[0] - sync fd 1
+ * ...
+ * fd[n-1] - sync fd N
+ * int[0] - magic (SYNC_FENCE_MAGIC (='\302fso'))
+ */
class _C2FenceFactory::SyncFenceImpl : public C2Fence::Impl {
public:
virtual c2_status_t wait(c2_nsecs_t timeoutNs) {
@@ -218,11 +295,19 @@
return mFence->dup();
}
+ /**
+ * Returns a duped list of fds used when creating this fence. It will
+ * not return the internally created merged fence fd.
+ */
std::vector<int> fds() const {
std::vector<int> retFds;
for (int index = 0; index < mListFences.size(); index++) {
retFds.push_back(mListFences[index]->dup());
}
+ // ensure that at least one fd is returned
+ if (mListFences.empty()) {
+ retFds.push_back(mFence->dup());
+ }
return retFds;
}
@@ -236,7 +321,18 @@
virtual native_handle_t *createNativeHandle() const {
std::vector<int> nativeFds = fds();
- nativeFds.push_back(fd());
+ int32_t magic = SYNC_FENCE_MAGIC;
+
+ // Also parcel the singular fence if it is not already part of the list.
+ // If this was a single-fd fence, mListFences will be empty, but fds()
+ // already returned that a list with that single fd.
+ if (!mListFences.empty() && mListFences.back() != mFence) {
+ nativeFds.push_back(fd());
+ if (!mListFences.empty()) {
+ magic = SYNC_FENCE_UNORDERED_MAGIC;
+ }
+ }
+
native_handle_t* nh = native_handle_create(nativeFds.size(), 1);
if (!nh) {
ALOGE("Failed to allocate native handle for sync fence");
@@ -249,71 +345,122 @@
for (int i = 0; i < nativeFds.size(); i++) {
nh->data[i] = nativeFds[i];
}
- nh->data[nativeFds.size()] = type();
+ nh->data[nativeFds.size()] = magic;
return nh;
}
virtual ~SyncFenceImpl() {};
+ /**
+ * Constructs a SyncFenceImpl from a single sync fd. No error checking is
+ * performed on the fd here as we cannot make this a null fence.
+ *
+ * \param fenceFd the fence fd to create the SyncFenceImpl from.
+ */
SyncFenceImpl(int fenceFd) :
mFence(sp<Fence>::make(fenceFd)) {
- mListFences.clear();
- if (mFence) {
- mListFences.push_back(mFence);
- }
}
- SyncFenceImpl(const std::vector<int>& fenceFds, int mergedFd) {
- mListFences.clear();
-
- for (int fenceFd : fenceFds) {
- if (fenceFd < 0) {
- continue;
- } else {
- mListFences.push_back(sp<Fence>::make(fenceFd));
- if (!mListFences.back()) {
- mFence.clear();
- break;
- }
- if (mergedFd == -1) {
- mFence = (mFence == nullptr) ? (mListFences.back()) :
- (Fence::merge("syncFence", mFence, mListFences.back()));
- }
- }
- }
- if (mergedFd != -1)
- {
- mFence = sp<Fence>::make(mergedFd);
- }
- if (!mFence) {
- mListFences.clear();
- }
+ SyncFenceImpl(const sp<Fence> &fence) :
+ mFence(fence) {
}
- static std::shared_ptr<SyncFenceImpl> CreateFromNativeHandle(const native_handle_t* nh) {
- if (!nh || nh->numFds < 1 || nh->numInts < 1) {
- ALOGE("Invalid handle for sync fence");
+ /**
+ * Constructs a SyncFenceImpl from a list of sync fds.
+ *
+ * \param fenceFds the list of fence fds to create the SyncFenceImpl from.
+ * \param finalFence the singular fence for this multi-fd fence. This can
+ * be either the last fence in fences or a sepearate (merged) fence.
+ */
+ SyncFenceImpl(const std::vector<sp<Fence>>& fences, const sp<Fence> &finalFence) :
+ mListFences(fences),
+ mFence(finalFence) {
+ }
+
+ /**
+ * Creates a SyncFenceImpl from a native handle.
+ *
+ * \param nh the native handle to create the SyncFenceImpl from.
+ * \param takeOwnership if true, the SyncFenceImpl will take ownership of the
+ * file descriptors in the native handle. Otherwise,
+ * the SyncFenceImpl will dup the file descriptors.
+ *
+ * \return a shared_ptr to the SyncFenceImpl, or nullptr if the native
+ * handle is invalid or malformed.
+ */
+ static std::shared_ptr<SyncFenceImpl> CreateFromNativeHandle(
+ const native_handle_t* nh, bool takeOwnership) {
+ // we should only call this method if _C2FenceFactory::GetTypeFromNativeHandle
+ // returned SYNC_FENCE, but do these checks anyways to avoid overflows
+ // in case that does not happen.
+ if (!nh) {
+ ALOGE("Invalid handle for a sync fence (nullptr)");
+ return nullptr;
+ } else if (nh->numFds < 1 || nh->numInts < 1
+ || nh->numFds > MAX_FENCE_FDS || nh->numInts > MAX_FENCE_INTS) {
+ ALOGE("Invalid handle for a sync fence (%d fds, %d ints)", nh->numFds, nh->numInts);
return nullptr;
}
- std::vector<int> fds;
- for (int i = 0; i < nh->numFds-1; i++) {
- fds.push_back(dup(nh->data[i]));
- }
- std::shared_ptr<SyncFenceImpl> p = (nh->numFds == 1)?
- (std::make_shared<SyncFenceImpl>(fds.back())):
- (std::make_shared<SyncFenceImpl>(fds, (dup(nh->data[nh->numFds-1]))));
- if (!p) {
- ALOGE("Failed to allocate sync fence impl");
- for (int fd : fds) {
- close(fd);
+ std::vector<sp<Fence>> fences;
+ for (int i = 0; i < nh->numFds; i++) {
+ int fd = nh->data[i];
+ if (!takeOwnership && fd >= 0) {
+ fd = dup(fd);
+ }
+ if (fd >= 0) {
+ sp<Fence> fence = sp<Fence>::make(fd);
+ if (fence) {
+ fences.push_back(fence);
+ } else {
+ ALOGW("Failed to create fence from fd %d", fd);
+ }
}
}
+
+ std::shared_ptr<SyncFenceImpl> p;
+ if (fences.size() == 0) {
+ ALOGE("No valid fences found in handle for a sync fence");
+ return nullptr;
+ } else if (fences.size() == 1) {
+ p = std::make_shared<SyncFenceImpl>(fences[0]);
+ } else {
+ int32_t magic = nh->data[nh->numFds + nh->numInts - 1];
+ if (magic != SYNC_FENCE_MAGIC) {
+ // The last fence is the merged fence. Separate it.
+ sp<Fence> finalFence = fences.back();
+ fences.pop_back();
+
+ // Special case: if we end up with only a single element list
+ // with another merged fence, that merged fence must be the
+ // same fence. This happened in an early version of multi fd
+ // support for single-fd sync fences.
+ if (fences.size() == 1) {
+ // For single-fd fence the sp-s must be equal
+ finalFence = fences.back();
+ }
+ p = std::make_shared<SyncFenceImpl>(fences, finalFence);
+ } else {
+ // Use the last fence as the standalone fence.
+ p = std::make_shared<SyncFenceImpl>(fences, fences.back());
+ }
+ }
+
+ ALOGE_IF(!p, "Failed to allocate sync fence impl");
return p;
}
private:
+ /**
+ * The list of fences in case of a multi-fence sync fence. Otherwise, this
+ * list is empty.
+ */
std::vector<sp<Fence>> mListFences;
- sp<Fence> mFence; //merged fence in case mListFences size > 0
+
+ /**
+ * The singular fence for this sync fence. For multi-fence sync fences,
+ * this could be a merged fence, or simply the final fence.
+ */
+ sp<Fence> mFence;
};
std::vector<int> ExtractFdsFromCodec2SyncFence(const C2Fence& fence) {
@@ -324,39 +471,155 @@
return retFds;
}
-C2Fence _C2FenceFactory::CreateSyncFence(int fenceFd) {
+C2Fence _C2FenceFactory::CreateSyncFence(int fenceFd, bool validate) {
std::shared_ptr<C2Fence::Impl> p;
if (fenceFd >= 0) {
p = std::make_shared<_C2FenceFactory::SyncFenceImpl>(fenceFd);
if (!p) {
ALOGE("Failed to allocate sync fence impl");
close(fenceFd);
- } else if (!p->valid()) {
+ } else if (validate && (!p->valid() || p->ready())) {
+ // don't create a fence object if the sync fd already signaled or is invalid
p.reset();
}
} else {
- ALOGV("Create sync fence from invalid fd");
- return C2Fence();
+ ALOGV("Won't create sync fence from invalid fd");
}
return C2Fence(p);
}
-C2Fence _C2FenceFactory::CreateMultipleFdSyncFence(const std::vector<int>& fenceFds) {
- std::shared_ptr<C2Fence::Impl> p;
- if (fenceFds.size() > 0) {
- p = std::make_shared<_C2FenceFactory::SyncFenceImpl>(fenceFds, -1);
- if (!p) {
- ALOGE("Failed to allocate sync fence impl closing FDs");
- for (int fenceFd : fenceFds) {
- close(fenceFd);
- }
- } else if (!p->valid()) {
- ALOGE("Invalid sync fence created");
- p.reset();
- }
- } else {
- ALOGE("Create sync fence from invalid fd list of size 0");
+C2Fence _C2FenceFactory::CreateUnorderedMultiSyncFence(
+ const std::vector<int>& fenceFds, c2_status_t *status) {
+ if (status) {
+ *status = C2_OK;
}
+
+ sp<Fence> finalFence;
+ std::vector<sp<Fence>> fences;
+
+ bool mergeFailed = false;
+ for (int fenceFd : fenceFds) {
+ if (fenceFd < 0) {
+ // ignore invalid fences
+ continue;
+ }
+ sp<Fence> fence = sp<Fence>::make(fenceFd);
+
+ // If we could not create an sp, further sp-s will also fail.
+ if (fence == nullptr) {
+ if (status) {
+ *status = C2_NO_MEMORY;
+ }
+ break;
+ }
+ fences.push_back(fence);
+
+ if (finalFence == nullptr) {
+ finalFence = fence;
+ } else {
+ sp<Fence> mergedFence = Fence::merge("syncFence", finalFence, fence);
+ if (mergedFence == nullptr || mergedFence == Fence::NO_FENCE) {
+ ALOGE_IF(!mergeFailed, "Could not merge fences for sync fence.");
+ mergeFailed = true;
+ if (status) {
+ *status = (mergedFence == nullptr) ? C2_NO_MEMORY : C2_CORRUPTED;
+ }
+
+ if (mergedFence == nullptr) {
+ break;
+ }
+ // If we cannot merge one of the fences, the best course of action
+ // is to keep going, as the alternative would be to clear all fences
+ // (making this a null fence) but that will always be ready.
+ } else {
+ finalFence = mergedFence;
+ }
+ }
+ }
+
+ // we may have ended up with a single or no fence due to merging failures or
+ // invalid fds.
+ if (fences.size() == 0) {
+ // we have no fds, we have a null fence.
+ return C2Fence();
+ }
+
+ std::shared_ptr<C2Fence::Impl> p;
+
+ if (fences.size() == 1) {
+ // We have a single sync fd. We don't need the merged fence, which is
+ // already simply that sole fence.
+ p = std::make_shared<_C2FenceFactory::SyncFenceImpl>(finalFence);
+ } else {
+ // if we couldn't merge any fences just use the last one
+ if (finalFence == fences[0]) {
+ finalFence = fences.back();
+ }
+
+ p = std::make_shared<_C2FenceFactory::SyncFenceImpl>(fences, finalFence);
+ }
+
+ if (!p) {
+ ALOGE("Failed to allocate sync fence impl closing FDs");
+ // all fds were moved into Fence objects which will close them.
+ if (status) {
+ *status = C2_NO_MEMORY;
+ }
+ return C2Fence();
+ }
+
+ return C2Fence(p);
+}
+
+C2Fence _C2FenceFactory::CreateMultiSyncFence(
+ const std::vector<int>& fenceFds, c2_status_t *status) {
+ if (status) {
+ *status = C2_OK;
+ }
+
+ std::vector<sp<Fence>> fences;
+
+ for (int fenceFd : fenceFds) {
+ if (fenceFd < 0) {
+ // ignore invalid fences
+ continue;
+ }
+ sp<Fence> fence = sp<Fence>::make(fenceFd);
+
+ // If we could not create an sp, keep going with the existing fences.
+ if (fence == nullptr) {
+ if (status) {
+ *status = C2_NO_MEMORY;
+ }
+ break;
+ }
+ fences.push_back(fence);
+ }
+
+ // we may have ended up with a single or no fence due to invalid fds.
+ if (fences.size() == 0) {
+ // we have no fds, we have a null fence.
+ return C2Fence();
+ }
+
+ std::shared_ptr<C2Fence::Impl> p;
+
+ if (fences.size() == 1) {
+ // We have a single sync fd, this is a simple sync fence.
+ p = std::make_shared<_C2FenceFactory::SyncFenceImpl>(fences[0]);
+ } else {
+ p = std::make_shared<_C2FenceFactory::SyncFenceImpl>(fences, fences.back());
+ }
+
+ if (!p) {
+ ALOGE("Failed to allocate sync fence impl closing FDs");
+ // all fds were moved into Fence objects which will close them.
+ if (status) {
+ *status = C2_NO_MEMORY;
+ }
+ return C2Fence();
+ }
+
return C2Fence(p);
}
@@ -521,7 +784,8 @@
return fence.mImpl? fence.mImpl->createNativeHandle() : nullptr;
}
-C2Fence _C2FenceFactory::CreateFromNativeHandle(const native_handle_t* handle) {
+C2Fence _C2FenceFactory::CreateFromNativeHandle(
+ const native_handle_t* handle, bool takeOwnership) {
if (!handle) {
return C2Fence();
}
@@ -529,11 +793,14 @@
std::shared_ptr<C2Fence::Impl> p;
switch (type) {
case C2Fence::Impl::SYNC_FENCE:
- p = SyncFenceImpl::CreateFromNativeHandle(handle);
+ p = SyncFenceImpl::CreateFromNativeHandle(handle, takeOwnership);
break;
default:
ALOGV("Unsupported fence type %d", type);
- // Nothing else to do. The handle is owned by the caller.
+ // Still close the handle here if taking ownership.
+ if (takeOwnership) {
+ (void) native_handle_close(handle);
+ }
// return a null-fence in this case
break;
}
diff --git a/media/codec2/vndk/include/C2FenceFactory.h b/media/codec2/vndk/include/C2FenceFactory.h
index 4f974ca..cabd5d9 100644
--- a/media/codec2/vndk/include/C2FenceFactory.h
+++ b/media/codec2/vndk/include/C2FenceFactory.h
@@ -23,13 +23,19 @@
#include <android-base/unique_fd.h>
/*
- * Create a list of fds from fence
+ * Extract a list of sync fence fds from a potentially multi-sync C2Fence.
+ * This will return dupped file descriptors of the fences used to creating the
+ * sync fence. Specifically, for an unordered mult-sync fence, the merged
+ * singular fence will not be returned even though it is created aspart of
+ * constructing the C2Fence object. On the other hand, for a single fd sync
+ * fence, the returned list will contain the sole file descriptor.
*
* \param fence C2Fence object from which associated
* file descriptors need to be extracted
- * \return a vector of fds otherwise return vector of size 0
+ * \return a vector of sync fence fds. This will be a vector of size 0 if C2Fence
+ * is not a sync fence. The caller is responsible for closing the
+ * fds in the returned vector.
*/
-
std::vector<int> ExtractFdsFromCodec2SyncFence(const C2Fence& fence);
class C2SurfaceSyncMemory;
@@ -54,20 +60,76 @@
uint32_t waitId);
/*
- * Create C2Fence from a fence file fd.
+ * Create C2Fence from a sync fence fd.
*
- * \param fenceFd Fence file descriptor.
+ * \param fenceFd Sync fence file descriptor.
* It will be owned and closed by the returned fence object.
+ * \param validate If true, the fence fd will be validated to ensure
+ * it is a valid pending sync fence fd.
*/
- static C2Fence CreateSyncFence(int fenceFd);
+ static C2Fence CreateSyncFence(int fenceFd, bool validate = true);
/*
- * Create C2Fence from list of fence file fds.
+ * Create C2Fence from list of sync fence fds, while also merging them to
+ * create a singular fence, which can be used as a backward compatible sync
+ * fence.
*
- * \param fenceFds Vector of file descriptor for fence.
- * It will be owned and closed by the returned fence object.
+ * \param fenceFds Vector of sync fence file descriptors.
+ * All file descriptors will be owned (and closed) by
+ * the returned fence object.
*/
- static C2Fence CreateMultipleFdSyncFence(const std::vector<int>& fenceFds);
+ [[deprecated("Use CreateUnorderedMultiSyncFence instead.")]]
+ static C2Fence CreateMultipleFdSyncFence(const std::vector<int>& fenceFds) {
+ return CreateUnorderedMultiSyncFence(fenceFds);
+ }
+
+ /*
+ * Create C2Fence from a list of unordered sync fence fds, while also merging
+ * them to create a singular fence, which can be used as a backward compatible
+ * sync fence.
+ *
+ * \param fenceFds Vector of sync fence file descriptors.
+ * All file descriptors will be owned (and closed) by
+ * the returned fence object.
+ * \param status Optional pointer to a status field. If not null, it will be
+ * updated with the status of the operation. Possible values
+ * are:
+ * - C2_OK: The operation succeeded.
+ * - C2_NO_MEMORY: The operation failed because of lack of
+ * memory.
+ * - C2_CORRUPTED: The operation failed because the sync
+ * fence fds could bot be merged.
+ * \return A C2Fence object representing the sync fence fds, or
+ * an empty C2Fence if the no C2Fence could be created.
+ * It is possible for the operation to fail but still return
+ * a possibly viable C2Fence object, e.g. if the merge
+ * operation failed only partially. Similarly, it is possible
+ * for the operation to succeed but still return an empty
+ * C2Fence object, e.g. if all fence fds were invalid.
+ */
+ static C2Fence CreateUnorderedMultiSyncFence(
+ const std::vector<int>& fenceFds, c2_status_t *status = nullptr /* nullable */);
+
+ /*
+ * Create C2Fence from a list of sync fence fds. Waiting for the last fence
+ * must guarantee that all other fences are also signaled.
+ *
+ * \param fenceFds Vector of sync fence file descriptors.
+ * All file descriptors will be owned (and closed) by
+ * the returned fence object.
+ * \param status Optional pointer to a status field. If not null, it will be
+ * updated with the status of the operation. Possible values
+ * are:
+ * - C2_OK: The operation succeeded.
+ * - C2_NO_MEMORY: The operation failed because of lack of
+ * memory.
+ * \return A C2Fence object representing the sync fence fds, or
+ * an empty C2Fence if the operation failed. It is possible
+ * for the operation to succeed but still return an empty
+ * C2Fence object, e.g. if all fence fds were invalid.
+ */
+ static C2Fence CreateMultiSyncFence(
+ const std::vector<int>& fenceFds, c2_status_t *status = nullptr /* nullable */);
/*
* Create C2Fence from an fd created by pipe()/pipe2() syscall.
@@ -97,13 +159,18 @@
/*
* Create C2Fence from a native handle.
-
+ *
* \param handle A native handle representing a fence
- * The fd in the native handle will be duplicated, so the caller will
- * still own the handle and have to close it.
+ * \param takeOwnership If true, the native handle and the file descriptors
+ * within will be owned by the returned fence object.
+ * If false (default), the caller will still own the
+ * handle and its file descriptors and will have to
+ * close it.
+ * In either case the caller is responsible for
+ * deleting the native handle.
*/
- static C2Fence CreateFromNativeHandle(const native_handle_t* handle);
+ static C2Fence CreateFromNativeHandle(
+ const native_handle_t* handle, bool takeOwnership = false);
};
-
#endif // STAGEFRIGHT_CODEC2_FENCE_FACTORY_H_
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index ba231c1..a1551f8 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -46,9 +46,9 @@
],
static_libs: [
"aaudio-aidl-cpp",
+ "audio-permission-aidl-cpp",
"audioclient-types-aidl-cpp",
"audioflinger-aidl-cpp",
- "audio-permission-aidl-cpp",
"audiopolicy-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"av-types-aidl-cpp",
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 8595308..255bd0f 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -261,6 +261,11 @@
void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
audio_port_handle_t deviceId) {
+ // Check for an invalid deviceId. Why change to UNSPECIFIED?
+ if (deviceId == AAUDIO_UNSPECIFIED) {
+ ALOGE("%s(, deviceId = AAUDIO_UNSPECIFIED)! Why?", __func__);
+ return;
+ }
// Device routing is a common source of errors and DISCONNECTS.
// Please leave this log in place. If there is a bug then this might
// get called after the stream has been deleted so log before we
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 9c3ad44..ddf14a3 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -134,6 +134,9 @@
"libaudiomanager",
"libaudiopolicy",
],
+ cflags: [
+ "-Wthread-safety",
+ ],
data: ["bbb*.raw"],
srcs: [
"audio_test_utils.cpp",
diff --git a/media/libaudioclient/tests/audio_test_utils.cpp b/media/libaudioclient/tests/audio_test_utils.cpp
index 9a202cc3..745c7d1 100644
--- a/media/libaudioclient/tests/audio_test_utils.cpp
+++ b/media/libaudioclient/tests/audio_test_utils.cpp
@@ -28,25 +28,35 @@
void OnAudioDeviceUpdateNotifier::onAudioDeviceUpdate(audio_io_handle_t audioIo,
audio_port_handle_t deviceId) {
- std::unique_lock<std::mutex> lock{mMutex};
ALOGI("%s: audioIo=%d deviceId=%d", __func__, audioIo, deviceId);
- mAudioIo = audioIo;
- mDeviceId = deviceId;
+ {
+ std::lock_guard lock(mMutex);
+ mAudioIo = audioIo;
+ mDeviceId = deviceId;
+ }
mCondition.notify_all();
}
status_t OnAudioDeviceUpdateNotifier::waitForAudioDeviceCb(audio_port_handle_t expDeviceId) {
- std::unique_lock<std::mutex> lock{mMutex};
+ std::unique_lock lock(mMutex);
+ android::base::ScopedLockAssertion lock_assertion(mMutex);
if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
(expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId)) {
mCondition.wait_for(lock, std::chrono::milliseconds(500));
if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
- (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId))
+ (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId)) {
return TIMED_OUT;
+ }
}
return OK;
}
+std::pair<audio_io_handle_t, audio_port_handle_t>
+OnAudioDeviceUpdateNotifier::getLastPortAndDevice() const {
+ std::lock_guard lock(mMutex);
+ return {mAudioIo, mDeviceId};
+}
+
AudioPlayback::AudioPlayback(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, audio_output_flags_t flags,
audio_session_t sessionId, AudioTrack::transfer_type transferType,
@@ -147,9 +157,8 @@
}
void AudioPlayback::onBufferEnd() {
- std::unique_lock<std::mutex> lock{mMutex};
+ std::lock_guard lock(mMutex);
mStopPlaying = true;
- mCondition.notify_all();
}
status_t AudioPlayback::fillBuffer() {
@@ -187,7 +196,12 @@
const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
int counter = 0;
size_t totalFrameCount = mMemCapacity / mTrack->frameSize();
- while (!mStopPlaying && counter < maxTries) {
+ bool stopPlaying;
+ {
+ std::lock_guard lock(mMutex);
+ stopPlaying = mStopPlaying;
+ }
+ while (!stopPlaying && counter < maxTries) {
uint32_t currPosition;
mTrack->getPosition(&currPosition);
if (currPosition >= totalFrameCount) counter++;
@@ -213,7 +227,10 @@
mTrack->start();
}
std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_PERIOD_MS));
+ std::lock_guard lock(mMutex);
+ stopPlaying = mStopPlaying;
}
+ std::lock_guard lock(mMutex);
if (!mStopPlaying && counter == maxTries) return TIMED_OUT;
return OK;
}
@@ -228,8 +245,10 @@
}
void AudioPlayback::stop() {
- std::unique_lock<std::mutex> lock{mMutex};
- mStopPlaying = true;
+ {
+ std::lock_guard lock(mMutex);
+ mStopPlaying = true;
+ }
if (mState != PLAY_STOPPED && mState != PLAY_NO_INIT) {
int32_t msec = 0;
(void)mTrack->pendingDuration(&msec);
@@ -257,10 +276,13 @@
return 0;
}
- // no more frames to read
- if (mNumFramesReceived >= mNumFramesToRecord || mStopRecording) {
- mStopRecording = true;
- return 0;
+ {
+ std::lock_guard l(mMutex);
+ // no more frames to read
+ if (mNumFramesReceived >= mNumFramesToRecord || mStopRecording) {
+ mStopRecording = true;
+ return 0;
+ }
}
int64_t timeUs = 0, position = 0, timeNs = 0;
@@ -324,9 +346,12 @@
}
if (tmpQueue.size() > 0) {
- std::unique_lock<std::mutex> lock{mMutex};
- for (auto it = tmpQueue.begin(); it != tmpQueue.end(); it++)
- mBuffersReceived.push_back(std::move(*it));
+ {
+ std::lock_guard lock(mMutex);
+ mBuffersReceived.insert(mBuffersReceived.end(),
+ std::make_move_iterator(tmpQueue.begin()),
+ std::make_move_iterator(tmpQueue.end()));
+ }
mCondition.notify_all();
}
return buffer.size();
@@ -484,7 +509,10 @@
status_t AudioCapture::stop() {
status_t status = OK;
- mStopRecording = true;
+ {
+ std::lock_guard l(mMutex);
+ mStopRecording = true;
+ }
if (mState != REC_STOPPED && mState != REC_NO_INIT) {
if (mInputSource != AUDIO_SOURCE_DEFAULT) {
bool state = false;
@@ -530,7 +558,8 @@
if (REC_STARTED != mState) return INVALID_OPERATION;
const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
int counter = 0;
- std::unique_lock<std::mutex> lock{mMutex};
+ std::unique_lock lock(mMutex);
+ android::base::ScopedLockAssertion lock_assertion(mMutex);
while (mBuffersReceived.empty() && !mStopRecording && counter < maxTries) {
mCondition.wait_for(lock, std::chrono::milliseconds(WAIT_PERIOD_MS));
counter++;
diff --git a/media/libaudioclient/tests/audio_test_utils.h b/media/libaudioclient/tests/audio_test_utils.h
index 76e4642..40c3365 100644
--- a/media/libaudioclient/tests/audio_test_utils.h
+++ b/media/libaudioclient/tests/audio_test_utils.h
@@ -19,14 +19,13 @@
#include <sys/stat.h>
#include <unistd.h>
-#include <atomic>
-#include <chrono>
-#include <cinttypes>
#include <deque>
#include <memory>
#include <mutex>
#include <thread>
+#include <utility>
+#include <android-base/thread_annotations.h>
#include <binder/MemoryDealer.h>
#include <media/AidlConversion.h>
#include <media/AudioRecord.h>
@@ -63,13 +62,15 @@
class OnAudioDeviceUpdateNotifier : public AudioSystem::AudioDeviceCallback {
public:
- audio_io_handle_t mAudioIo = AUDIO_IO_HANDLE_NONE;
- audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE;
- std::mutex mMutex;
- std::condition_variable mCondition;
-
- void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+ void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId) override;
status_t waitForAudioDeviceCb(audio_port_handle_t expDeviceId = AUDIO_PORT_HANDLE_NONE);
+ std::pair<audio_io_handle_t, audio_port_handle_t> getLastPortAndDevice() const;
+
+ private:
+ audio_io_handle_t mAudioIo GUARDED_BY(mMutex) = AUDIO_IO_HANDLE_NONE;
+ audio_port_handle_t mDeviceId GUARDED_BY(mMutex) = AUDIO_PORT_HANDLE_NONE;
+ mutable std::mutex mMutex;
+ std::condition_variable mCondition;
};
// Simple AudioPlayback class.
@@ -86,15 +87,14 @@
status_t create();
sp<AudioTrack> getAudioTrackHandle();
status_t start();
- status_t waitForConsumption(bool testSeek = false);
+ status_t waitForConsumption(bool testSeek = false) EXCLUDES(mMutex);
status_t fillBuffer();
status_t onProcess(bool testSeek = false);
- virtual void onBufferEnd() override;
- void stop();
+ void onBufferEnd() override EXCLUDES(mMutex);
+ void stop() EXCLUDES(mMutex);
- bool mStopPlaying;
- std::mutex mMutex;
- std::condition_variable mCondition;
+ bool mStopPlaying GUARDED_BY(mMutex);
+ mutable std::mutex mMutex;
enum State {
PLAY_NO_INIT,
@@ -144,7 +144,7 @@
AudioRecord::transfer_type transferType = AudioRecord::TRANSFER_CALLBACK,
const audio_attributes_t* attributes = nullptr);
~AudioCapture();
- size_t onMoreData(const AudioRecord::Buffer& buffer) override;
+ size_t onMoreData(const AudioRecord::Buffer& buffer) override EXCLUDES(mMutex);
void onOverrun() override;
void onMarker(uint32_t markerPosition) override;
void onNewPos(uint32_t newPos) override;
@@ -156,10 +156,10 @@
sp<AudioRecord> getAudioRecordHandle();
status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);
- status_t obtainBufferCb(RawBuffer& buffer);
+ status_t obtainBufferCb(RawBuffer& buffer) EXCLUDES(mMutex);
status_t obtainBuffer(RawBuffer& buffer);
status_t audioProcess();
- status_t stop();
+ status_t stop() EXCLUDES(mMutex);
uint32_t mFrameCount;
uint32_t mNotificationFrames;
@@ -192,13 +192,13 @@
size_t mMaxBytesPerCallback = 2048;
sp<AudioRecord> mRecord;
State mState;
- bool mStopRecording;
+ bool mStopRecording GUARDED_BY(mMutex);
std::string mFileName;
int mOutFileFd = -1;
- std::mutex mMutex;
+ mutable std::mutex mMutex;
std::condition_variable mCondition;
- std::deque<RawBuffer> mBuffersReceived;
+ std::deque<RawBuffer> mBuffersReceived GUARDED_BY(mMutex);
};
#endif // AUDIO_TEST_UTILS_H_
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
index be6c581..9908f33 100644
--- a/media/libaudioclient/tests/audiorecord_tests.cpp
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -120,10 +120,12 @@
EXPECT_EQ(OK, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cb));
EXPECT_EQ(OK, mAC->start()) << "record creation failed";
EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
- EXPECT_EQ(AUDIO_IO_HANDLE_NONE, cbOld->mAudioIo);
- EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, cbOld->mDeviceId);
- EXPECT_NE(AUDIO_IO_HANDLE_NONE, cb->mAudioIo);
- EXPECT_NE(AUDIO_PORT_HANDLE_NONE, cb->mDeviceId);
+ const auto [oldAudioIo, oldDeviceId] = cbOld->getLastPortAndDevice();
+ EXPECT_EQ(AUDIO_IO_HANDLE_NONE, oldAudioIo);
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, oldDeviceId);
+ const auto [audioIo, deviceId] = cb->getLastPortAndDevice();
+ EXPECT_NE(AUDIO_IO_HANDLE_NONE, audioIo);
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, deviceId);
EXPECT_EQ(BAD_VALUE, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(nullptr));
EXPECT_EQ(INVALID_OPERATION, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cbOld));
EXPECT_EQ(OK, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cb));
@@ -174,6 +176,7 @@
<< "getMarkerPosition() failed";
EXPECT_EQ(OK, mAC->start()) << "start recording failed";
EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ // TODO(b/348658586): Properly synchronize callback updates with the test thread.
EXPECT_EQ(marker, mAC->mMarkerPosition)
<< "configured marker and received marker are different";
EXPECT_EQ(mAC->mReceivedCbMarkerAtPosition, mAC->mMarkerPosition)
@@ -189,6 +192,7 @@
<< "getPositionUpdatePeriod() failed";
EXPECT_EQ(OK, mAC->start()) << "start recording failed";
EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ // TODO(b/348658586): Properly synchronize callback updates with the test thread.
EXPECT_EQ(marker, mAC->mMarkerPeriod) << "configured marker and received marker are different";
EXPECT_EQ(mAC->mReceivedCbMarkerCount, mAC->mNumFramesToRecord / mAC->mMarkerPeriod)
<< "configured marker and received cb marker are different";
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
index 3b2285e..8151d39 100644
--- a/media/libaudioclient/tests/audiorouting_tests.cpp
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -64,16 +64,17 @@
EXPECT_EQ(OK, ap->start()) << "audio track start failed";
EXPECT_EQ(OK, ap->onProcess());
EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
- EXPECT_TRUE(checkPatchPlayback(cb->mAudioIo, cb->mDeviceId));
+ const auto [audioIo, deviceId] = cb->getLastPortAndDevice();
+ EXPECT_TRUE(checkPatchPlayback(audioIo, deviceId));
EXPECT_NE(0, ap->getAudioTrackHandle()->getFlags() & output_flags[i]);
audio_patch patch;
- EXPECT_EQ(OK, getPatchForOutputMix(cb->mAudioIo, patch));
+ EXPECT_EQ(OK, getPatchForOutputMix(audioIo, patch));
if (output_flags[i] != AUDIO_OUTPUT_FLAG_FAST) {
// A "normal" output can still have a FastMixer, depending on the buffer size.
// Thus, a fast track can be created on a mix port which does not have the FAST flag.
for (auto j = 0; j < patch.num_sources; j++) {
if (patch.sources[j].type == AUDIO_PORT_TYPE_MIX &&
- patch.sources[j].ext.mix.handle == cb->mAudioIo) {
+ patch.sources[j].ext.mix.handle == audioIo) {
SCOPED_TRACE(dumpPortConfig(patch.sources[j]));
EXPECT_NE(0, patch.sources[j].flags.output & output_flags[i])
<< "expected output flag "
diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp
index 03c15f4..db998cd 100644
--- a/media/libaudioclient/tests/audiosystem_tests.cpp
+++ b/media/libaudioclient/tests/audiosystem_tests.cpp
@@ -108,30 +108,32 @@
// UNIT TESTS
TEST_F(AudioSystemTest, CheckServerSideValues) {
ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
- EXPECT_GT(mAF->sampleRate(mCbPlayback->mAudioIo), 0);
- EXPECT_NE(mAF->format(mCbPlayback->mAudioIo), AUDIO_FORMAT_INVALID);
- EXPECT_GT(mAF->frameCount(mCbPlayback->mAudioIo), 0);
+ const auto [pbAudioIo, _] = mCbPlayback->getLastPortAndDevice();
+ EXPECT_GT(mAF->sampleRate(pbAudioIo), 0);
+ EXPECT_NE(mAF->format(pbAudioIo), AUDIO_FORMAT_INVALID);
+ EXPECT_GT(mAF->frameCount(pbAudioIo), 0);
size_t frameCountHal, frameCountHalCache;
- frameCountHal = mAF->frameCountHAL(mCbPlayback->mAudioIo);
+ frameCountHal = mAF->frameCountHAL(pbAudioIo);
EXPECT_GT(frameCountHal, 0);
- EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(mCbPlayback->mAudioIo, &frameCountHalCache));
+ EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(pbAudioIo, &frameCountHalCache));
EXPECT_EQ(frameCountHal, frameCountHalCache);
- EXPECT_GT(mAF->latency(mCbPlayback->mAudioIo), 0);
+ EXPECT_GT(mAF->latency(pbAudioIo), 0);
// client side latency is at least server side latency
- EXPECT_LE(mAF->latency(mCbPlayback->mAudioIo), mPlayback->getAudioTrackHandle()->latency());
+ EXPECT_LE(mAF->latency(pbAudioIo), mPlayback->getAudioTrackHandle()->latency());
ASSERT_NO_FATAL_FAILURE(createRecordSession());
- EXPECT_GT(mAF->sampleRate(mCbRecord->mAudioIo), 0);
- // EXPECT_NE(mAF->format(mCbRecord->mAudioIo), AUDIO_FORMAT_INVALID);
- EXPECT_GT(mAF->frameCount(mCbRecord->mAudioIo), 0);
- EXPECT_GT(mAF->frameCountHAL(mCbRecord->mAudioIo), 0);
- frameCountHal = mAF->frameCountHAL(mCbRecord->mAudioIo);
+ const auto [recAudioIo, __] = mCbRecord->getLastPortAndDevice();
+ EXPECT_GT(mAF->sampleRate(recAudioIo), 0);
+ // EXPECT_NE(mAF->format(recAudioIo), AUDIO_FORMAT_INVALID);
+ EXPECT_GT(mAF->frameCount(recAudioIo), 0);
+ EXPECT_GT(mAF->frameCountHAL(recAudioIo), 0);
+ frameCountHal = mAF->frameCountHAL(recAudioIo);
EXPECT_GT(frameCountHal, 0);
- EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(mCbRecord->mAudioIo, &frameCountHalCache));
+ EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(recAudioIo, &frameCountHalCache));
EXPECT_EQ(frameCountHal, frameCountHalCache);
- // EXPECT_GT(mAF->latency(mCbRecord->mAudioIo), 0);
+ // EXPECT_GT(mAF->latency(recAudioIo), 0);
// client side latency is at least server side latency
- // EXPECT_LE(mAF->latency(mCbRecord->mAudioIo), mCapture->getAudioRecordHandle()->latency());
+ // EXPECT_LE(mAF->latency(recAudioIo), mCapture->getAudioRecordHandle()->latency());
EXPECT_GT(AudioSystem::getPrimaryOutputSamplingRate(), 0); // first fast mixer sample rate
EXPECT_GT(AudioSystem::getPrimaryOutputFrameCount(), 0); // fast mixer frame count
@@ -200,8 +202,9 @@
TEST_F(AudioSystemTest, GetStreamVolume) {
ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
float origStreamVol;
- EXPECT_EQ(NO_ERROR, AudioSystem::getStreamVolume(AUDIO_STREAM_MUSIC, &origStreamVol,
- mCbPlayback->mAudioIo));
+ const auto [pbAudioIo, _] = mCbPlayback->getLastPortAndDevice();
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::getStreamVolume(AUDIO_STREAM_MUSIC, &origStreamVol, pbAudioIo));
}
TEST_F(AudioSystemTest, GetStreamMute) {
diff --git a/media/libaudioclient/tests/audiotrack_tests.cpp b/media/libaudioclient/tests/audiotrack_tests.cpp
index cb667a0..cf7d926 100644
--- a/media/libaudioclient/tests/audiotrack_tests.cpp
+++ b/media/libaudioclient/tests/audiotrack_tests.cpp
@@ -157,18 +157,20 @@
EXPECT_EQ(OK, ap->start()) << "audio track start failed";
EXPECT_EQ(OK, ap->onProcess());
EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
- EXPECT_EQ(AUDIO_IO_HANDLE_NONE, cbOld->mAudioIo);
- EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, cbOld->mDeviceId);
- EXPECT_NE(AUDIO_IO_HANDLE_NONE, cb->mAudioIo);
- EXPECT_NE(AUDIO_PORT_HANDLE_NONE, cb->mDeviceId);
- EXPECT_EQ(cb->mAudioIo, ap->getAudioTrackHandle()->getOutput());
- EXPECT_EQ(cb->mDeviceId, ap->getAudioTrackHandle()->getRoutedDeviceId());
+ const auto [oldAudioIo, oldDeviceId] = cbOld->getLastPortAndDevice();
+ EXPECT_EQ(AUDIO_IO_HANDLE_NONE, oldAudioIo);
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, oldDeviceId);
+ const auto [audioIo, deviceId] = cb->getLastPortAndDevice();
+ EXPECT_NE(AUDIO_IO_HANDLE_NONE, audioIo);
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, deviceId);
+ EXPECT_EQ(audioIo, ap->getAudioTrackHandle()->getOutput());
+ EXPECT_EQ(deviceId, ap->getAudioTrackHandle()->getRoutedDeviceId());
String8 keys;
keys = ap->getAudioTrackHandle()->getParameters(keys);
if (!keys.empty()) {
std::cerr << "track parameters :: " << keys << std::endl;
}
- EXPECT_TRUE(checkPatchPlayback(cb->mAudioIo, cb->mDeviceId));
+ EXPECT_TRUE(checkPatchPlayback(audioIo, deviceId));
EXPECT_EQ(BAD_VALUE, ap->getAudioTrackHandle()->removeAudioDeviceCallback(nullptr));
EXPECT_EQ(INVALID_OPERATION, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cbOld));
EXPECT_EQ(OK, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cb));
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.cpp b/media/libaudiohal/impl/Hal2AidlMapper.cpp
index 052522f..a01ac4b 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.cpp
+++ b/media/libaudiohal/impl/Hal2AidlMapper.cpp
@@ -929,6 +929,7 @@
!status.isOk()) {
ALOGE("%s: error while resetting port config %d: %s",
__func__, portConfigId, status.getDescription().c_str());
+ return;
}
mPortConfigs.erase(it);
return;
diff --git a/media/libaudiohal/impl/StreamHalAidl.cpp b/media/libaudiohal/impl/StreamHalAidl.cpp
index 2fb4756..38e1ea4 100644
--- a/media/libaudiohal/impl/StreamHalAidl.cpp
+++ b/media/libaudiohal/impl/StreamHalAidl.cpp
@@ -86,8 +86,8 @@
mStream(stream),
mVendorExt(vext),
mLastReplyLifeTimeNs(
- std::min(static_cast<size_t>(100),
- 2 * mContext.getBufferDurationMs(mConfig.sample_rate))
+ std::min(static_cast<size_t>(20),
+ mContext.getBufferDurationMs(mConfig.sample_rate))
* NANOS_PER_MILLISECOND)
{
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
@@ -449,6 +449,11 @@
state == StreamDescriptor::State::TRANSFER_PAUSED ||
state == StreamDescriptor::State::DRAIN_PAUSED) {
return sendCommand(makeHalCommand<HalCommand::Tag::start>(), reply);
+ } else if (state == StreamDescriptor::State::ACTIVE ||
+ state == StreamDescriptor::State::TRANSFERRING ||
+ state == StreamDescriptor::State::DRAINING) {
+ ALOGD("%s: already in stream state: %s", __func__, toString(state).c_str());
+ return OK;
} else {
ALOGE("%s: unexpected stream state: %s (expected IDLE or one of *PAUSED states)",
__func__, toString(state).c_str());
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 9cd0e6e..e5323a6 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -364,7 +364,6 @@
"av-types-aidl-cpp",
"liblog",
"libcutils",
- "libprocessgroup",
"libutils",
"libbinder",
"libbinder_ndk",
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index cdb1837..ef6250f 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -23,7 +23,6 @@
#include <binder/Parcel.h>
#include <media/IMediaHTTPService.h>
#include <media/IMediaMetadataRetriever.h>
-#include <processgroup/sched_policy.h>
#include <utils/String8.h>
#include <utils/KeyedVector.h>
diff --git a/media/libmedia/include/media/RingBuffer.h b/media/libmedia/include/media/RingBuffer.h
index 4d92d87..a08f35e 100644
--- a/media/libmedia/include/media/RingBuffer.h
+++ b/media/libmedia/include/media/RingBuffer.h
@@ -44,8 +44,14 @@
/**
* Forward iterator to this class. Implements an std:forward_iterator.
*/
- class iterator : public std::iterator<std::forward_iterator_tag, T> {
+ class iterator {
public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+
iterator(T* ptr, size_t size, size_t pos, size_t ctr);
iterator& operator++();
@@ -357,5 +363,3 @@
}; // namespace android
#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H
-
-
diff --git a/media/mtp/tests/MtpFuzzer/Android.bp b/media/mtp/tests/MtpFuzzer/Android.bp
index acae06a..2e9c58b 100644
--- a/media/mtp/tests/MtpFuzzer/Android.bp
+++ b/media/mtp/tests/MtpFuzzer/Android.bp
@@ -29,7 +29,6 @@
"liblog",
"libutils",
],
- static_libs: ["libc++fs",],
cflags: [
"-Wall",
"-Wextra",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 60be37d..a17ac58 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3812,7 +3812,11 @@
IAfPlaybackThread* AudioFlinger::primaryPlaybackThread_l() const
{
- audio_utils::lock_guard lock(hardwareMutex());
+ // The atomic ptr mPrimaryHardwareDev requires both the
+ // AudioFlinger and the Hardware mutex for modification.
+ // As we hold the AudioFlinger mutex, we access it
+ // safely without the Hardware mutex, to avoid mutex order
+ // inversion with Thread methods and the ThreadBase mutex.
if (mPrimaryHardwareDev == nullptr) {
return nullptr;
}
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index 287d838..3af51d5 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -108,6 +108,7 @@
}
audio_io_handle_t io() const final { return AUDIO_IO_HANDLE_NONE; }
+ bool shouldDispatchAddRemoveToHal(bool isAdded __unused) const final { return true; }
bool isOutput() const final { return false; }
bool isOffload() const final { return false; }
bool isOffloadOrDirect() const final { return false; }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 8356339..bceba4b 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1040,12 +1040,11 @@
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- if (mCurrentHalStream == getCallback()->io()) {
+ if (!getCallback()->shouldDispatchAddRemoveToHal(/* isAdded= */ true)) {
return;
}
(void)getCallback()->addEffectToHal(mEffectInterface);
- mCurrentHalStream = getCallback()->io();
}
}
@@ -1142,11 +1141,10 @@
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- if (mCurrentHalStream != getCallback()->io()) {
- return (mCurrentHalStream == AUDIO_IO_HANDLE_NONE) ? NO_ERROR : INVALID_OPERATION;
+ if (!getCallback()->shouldDispatchAddRemoveToHal(/* isAdded= */ false)) {
+ return (getCallback()->io() == AUDIO_IO_HANDLE_NONE) ? NO_ERROR : INVALID_OPERATION;
}
getCallback()->removeEffectFromHal(mEffectInterface);
- mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
}
return NO_ERROR;
}
@@ -3122,12 +3120,16 @@
return result;
}
result = st->addEffect(effect);
+ if (result == OK) {
+ mCurrentHalStream = t->id();
+ }
ALOGE_IF(result != OK, "Error when adding effect: %d", result);
return result;
}
status_t EffectChain::EffectCallback::removeEffectFromHal(
const sp<EffectHalInterface>& effect) {
+ mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
status_t result = NO_INIT;
const sp<IAfThreadBase> t = thread().promote();
if (t == nullptr) {
@@ -3142,6 +3144,11 @@
return result;
}
+bool EffectChain::EffectCallback::shouldDispatchAddRemoveToHal(bool isAdded) const {
+ const bool currentHalStreamMatchesThreadId = (io() == mCurrentHalStream);
+ return isAdded != currentHalStreamMatchesThreadId;
+}
+
audio_io_handle_t EffectChain::EffectCallback::io() const {
const sp<IAfThreadBase> t = thread().promote();
if (t == nullptr) {
@@ -3734,11 +3741,14 @@
if (proxy == nullptr) {
return NO_INIT;
}
- return proxy->addEffectToHal(effect);
+ status_t ret = proxy->addEffectToHal(effect);
+ mAddedToHal = (ret == OK);
+ return ret;
}
status_t DeviceEffectProxy::ProxyCallback::removeEffectFromHal(
const sp<EffectHalInterface>& effect) {
+ mAddedToHal = false;
sp<DeviceEffectProxy> proxy = mProxy.promote();
if (proxy == nullptr) {
return NO_INIT;
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index d107543..549cff2 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -279,8 +279,6 @@
// sending disable command.
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mOffloaded; // effect is currently offloaded to the audio DSP
- // effect has been added to this HAL input stream
- audio_io_handle_t mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
bool mIsOutput; // direction of the AF thread
bool mSupportsFloat; // effect supports float processing
@@ -596,6 +594,7 @@
status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
bool updateOrphanEffectChains(const sp<IAfEffectBase>& effect) override;
+ bool shouldDispatchAddRemoveToHal(bool isAdded) const override;
audio_io_handle_t io() const override;
bool isOutput() const override;
bool isOffload() const override;
@@ -653,6 +652,8 @@
mediautils::atomic_wp<IAfThreadBase> mThread;
sp<IAfThreadCallback> mAfThreadCallback;
IAfThreadBase::type_t mThreadType = IAfThreadBase::MIXER;
+ // effect has been added to this HAL input stream
+ audio_io_handle_t mCurrentHalStream = AUDIO_IO_HANDLE_NONE;
};
DISALLOW_COPY_AND_ASSIGN(EffectChain);
@@ -784,6 +785,9 @@
}
audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; }
+ bool shouldDispatchAddRemoveToHal(bool isAdded) const override {
+ return isAdded != mAddedToHal;
+ }
bool isOutput() const override;
bool isOffload() const override { return false; }
bool isOffloadOrDirect() const override { return false; }
@@ -824,6 +828,7 @@
private:
const wp<DeviceEffectProxy> mProxy;
const sp<DeviceEffectManagerCallback> mManagerCallback;
+ bool mAddedToHal = false;
};
status_t checkPort(const IAfPatchPanel::Patch& patch,
diff --git a/services/audioflinger/IAfEffect.h b/services/audioflinger/IAfEffect.h
index 2bf252a..98a0fcb 100644
--- a/services/audioflinger/IAfEffect.h
+++ b/services/audioflinger/IAfEffect.h
@@ -46,6 +46,7 @@
public:
// Trivial methods usually implemented with help from ThreadBase
virtual audio_io_handle_t io() const = 0;
+ virtual bool shouldDispatchAddRemoveToHal(bool isAdded) const = 0;
virtual bool isOutput() const = 0;
virtual bool isOffload() const = 0;
virtual bool isOffloadOrDirect() const = 0;
diff --git a/services/audioflinger/IAfThread.h b/services/audioflinger/IAfThread.h
index 4518d48..a7da658 100644
--- a/services/audioflinger/IAfThread.h
+++ b/services/audioflinger/IAfThread.h
@@ -19,8 +19,9 @@
#include <android/media/IAudioTrackCallback.h>
#include <android/media/IEffectClient.h>
#include <audiomanager/IAudioManager.h>
-#include <audio_utils/mutex.h>
+#include <audio_utils/DeferredExecutor.h>
#include <audio_utils/MelProcessor.h>
+#include <audio_utils/mutex.h>
#include <binder/MemoryDealer.h>
#include <datapath/AudioStreamIn.h>
#include <datapath/AudioStreamOut.h>
@@ -395,6 +396,12 @@
// avoid races.
virtual void waitWhileThreadBusy_l(audio_utils::unique_lock& ul)
REQUIRES(mutex()) = 0;
+
+ // The ThreadloopExecutor is used to defer functors or dtors
+ // to when the Threadloop does not hold any mutexes (at the end of the
+ // processing period cycle).
+ virtual audio_utils::DeferredExecutor& getThreadloopExecutor() = 0;
+
// Dynamic cast to derived interface
virtual sp<IAfDirectOutputThread> asIAfDirectOutputThread() { return nullptr; }
virtual sp<IAfDuplicatingThread> asIAfDuplicatingThread() { return nullptr; }
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 7a66e5b..2cc6236 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -455,7 +455,7 @@
void queueBuffer(Buffer& inBuffer);
void clearBufferQueue();
- void restartIfDisabled();
+ void restartIfDisabled() override;
// Maximum number of pending buffers allocated by OutputTrack::write()
static const uint8_t kMaxOverFlowBuffers = 10;
@@ -519,7 +519,7 @@
void releaseBuffer(Proxy::Buffer* buffer) final;
private:
- void restartIfDisabled();
+ void restartIfDisabled() override;
}; // end of PatchTrack
} // namespace android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 66e89e4..e4e4017 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4646,7 +4646,11 @@
// FIXME Note that the above .clear() is no longer necessary since effectChains
// is now local to this block, but will keep it for now (at least until merge done).
+
+ mThreadloopExecutor.process();
}
+ mThreadloopExecutor.process(); // process any remaining deferred actions.
+ // deferred actions after this point are ignored.
threadLoop_exit();
@@ -5089,7 +5093,6 @@
// mPipeSink below
// mNormalSink below
{
- setMasterBalance(afThreadCallback->getMasterBalance_l());
ALOGV("MixerThread() id=%d type=%d", id, type);
ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%u, mFormat=%#x, mFrameSize=%zu, "
"mFrameCount=%zu, mNormalFrameCount=%zu",
@@ -5101,6 +5104,8 @@
// The Duplicating thread uses the AudioMixer and delivers data to OutputTracks
// (downstream MixerThreads) in DuplicatingThread::threadLoop_write().
// Do not create or use mFastMixer, mOutputSink, mPipeSink, or mNormalSink.
+ // Balance is *not* set in the DuplicatingThread here (or from AudioFlinger),
+ // as the downstream MixerThreads implement it.
return;
}
// create an NBAIO sink for the HAL output stream, and negotiate
@@ -5260,6 +5265,9 @@
mNormalSink = initFastMixer ? mPipeSink : mOutputSink;
break;
}
+ // setMasterBalance needs to be called after the FastMixer
+ // (if any) is set up, in order to deliver the balance settings to it.
+ setMasterBalance(afThreadCallback->getMasterBalance_l());
}
MixerThread::~MixerThread()
@@ -7185,14 +7193,11 @@
{
PlaybackThread::flushHw_l();
mOutput->flush();
+ mHwPaused = false;
mFlushPending = false;
mTimestampVerifier.discontinuity(discontinuityForStandbyOrFlush());
mTimestamp.clear();
mMonotonicFrameCounter.onFlush();
- // We do not reset mHwPaused which is hidden from the Track client.
- // Note: the client track in Tracks.cpp and AudioTrack.cpp
- // has a FLUSHED state but the DirectOutputThread does not;
- // those tracks will continue to show isStopped().
}
int64_t DirectOutputThread::computeWaitTimeNs_l() const {
@@ -8670,6 +8675,9 @@
// loop over each active track
for (size_t i = 0; i < size; i++) {
+ if (activeTrack) { // ensure track release is outside lock.
+ oldActiveTracks.emplace_back(std::move(activeTrack));
+ }
activeTrack = activeTracks[i];
// skip fast tracks, as those are handled directly by FastCapture
@@ -8813,11 +8821,14 @@
mIoJitterMs.add(jitterMs);
mProcessTimeMs.add(processMs);
}
+ mThreadloopExecutor.process();
// update timing info.
mLastIoBeginNs = lastIoBeginNs;
mLastIoEndNs = lastIoEndNs;
lastLoopCountRead = loopCount;
}
+ mThreadloopExecutor.process(); // process any remaining deferred actions.
+ // deferred actions after this point are ignored.
standbyIfNotAlreadyInStandby();
@@ -10603,7 +10614,10 @@
unlockEffectChains(effectChains);
// Effect chains will be actually deleted here if they were removed from
// mEffectChains list during mixing or effects processing
+ mThreadloopExecutor.process();
}
+ mThreadloopExecutor.process(); // process any remaining deferred actions.
+ // deferred actions after this point are ignored.
threadLoop_exit();
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 4b2ade4..654b841 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -571,6 +571,10 @@
void stopMelComputation_l() override
REQUIRES(audio_utils::AudioFlinger_Mutex);
+ audio_utils::DeferredExecutor& getThreadloopExecutor() override {
+ return mThreadloopExecutor;
+ }
+
protected:
// entry describing an effect being suspended in mSuspendedSessions keyed vector
@@ -877,6 +881,14 @@
SimpleLog mLocalLog; // locked internally
+ // mThreadloopExecutor contains deferred functors and object (dtors) to
+ // be executed at the end of the processing period, without any
+ // mutexes held.
+ //
+ // mThreadloopExecutor is locked internally, so its methods are thread-safe
+ // for access.
+ audio_utils::DeferredExecutor mThreadloopExecutor;
+
private:
void dumpBase_l(int fd, const Vector<String16>& args) REQUIRES(mutex());
void dumpEffectChains_l(int fd, const Vector<String16>& args) REQUIRES(mutex());
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 5708c61..a0b85f7 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -333,6 +333,9 @@
// true for Track, false for RecordTrack,
// this could be a track type if needed later
+ void deferRestartIfDisabled();
+ virtual void restartIfDisabled() {}
+
const wp<IAfThreadBase> mThread;
const alloc_type mAllocType;
/*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index a268df8..f5f11cc 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -314,6 +314,17 @@
return NO_ERROR;
}
+void TrackBase::deferRestartIfDisabled()
+{
+ const auto thread = mThread.promote();
+ if (thread == nullptr) return;
+ thread->getThreadloopExecutor().defer(
+ [track = wp<TrackBase>::fromExisting(this)] {
+ const auto actual = track.promote();
+ if (actual) actual->restartIfDisabled();
+ });
+}
+
PatchTrackBase::PatchTrackBase(const sp<ClientProxy>& proxy,
IAfThreadBase* thread, const Timeout& timeout)
: mProxy(proxy)
@@ -2310,7 +2321,7 @@
waitTimeLeftMs = 0;
}
if (status == NOT_ENOUGH_DATA) {
- restartIfDisabled();
+ deferRestartIfDisabled();
continue;
}
}
@@ -2322,7 +2333,7 @@
buf.mFrameCount = outFrames;
buf.mRaw = NULL;
mClientProxy->releaseBuffer(&buf);
- restartIfDisabled();
+ deferRestartIfDisabled();
pInBuffer->frameCount -= outFrames;
pInBuffer->raw = (int8_t *)pInBuffer->raw + outFrames * mFrameSize;
mOutBuffer.frameCount -= outFrames;
@@ -2577,7 +2588,7 @@
const size_t originalFrameCount = buffer->mFrameCount;
do {
if (status == NOT_ENOUGH_DATA) {
- restartIfDisabled();
+ deferRestartIfDisabled();
buffer->mFrameCount = originalFrameCount; // cleared on error, must be restored.
}
status = mProxy->obtainBuffer(buffer, timeOut);
@@ -2588,7 +2599,7 @@
void PatchTrack::releaseBuffer(Proxy::Buffer* buffer)
{
mProxy->releaseBuffer(buffer);
- restartIfDisabled();
+ deferRestartIfDisabled();
// Check if the PatchTrack has enough data to write once in releaseBuffer().
// If not, prevent an underrun from occurring by moving the track into FS_FILLING;
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index e91e2a3..deb7345 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -294,8 +294,7 @@
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,
- uid_t uid,
- bool internal = false) = 0;
+ uid_t uid) = 0;
virtual status_t stopAudioSource(audio_port_handle_t portId) = 0;
virtual status_t setMasterMono(bool mono) = 0;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
index 6167f95..3edd4de 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioIODescriptorInterface.h
@@ -47,13 +47,17 @@
if (active) {
// On MMAP IOs, the preferred device is selected by the first client (virtual client
- // created when the mmap stream is opened). This client is never active.
+ // created when the mmap stream is opened). This client is never active and we only
+ // consider the Filter criteria, not the active state.
// On non MMAP IOs, the preferred device is honored only if all active clients have
// a preferred device in which case the first client drives the selection.
if (desc->isMmap()) {
- // The client list is never empty on a MMAP IO
- return devices.getDeviceFromId(
- desc->clientsList(false /*activeOnly*/)[0]->preferredDeviceId());
+ auto matchingClients = desc->clientsList(
+ false /*activeOnly*/, filter, true /*preferredDevice*/);
+ if (matchingClients.empty()) {
+ return nullptr;
+ }
+ return devices.getDeviceFromId(matchingClients[0]->preferredDeviceId());
} else {
auto activeClientsWithRoute =
desc->clientsList(true /*activeOnly*/, filter, true /*preferredDevice*/);
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index c26ea10..0f2fe24 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -41,7 +41,8 @@
{
public:
AudioInputDescriptor(const sp<IOProfile>& profile,
- AudioPolicyClientInterface *clientInterface);
+ AudioPolicyClientInterface *clientInterface,
+ bool isPreemptor);
virtual ~AudioInputDescriptor() = default;
@@ -127,6 +128,8 @@
// active use case
void checkSuspendEffects();
+ bool isPreemptor() const { return mIsPreemptor; }
+
private:
void updateClientRecordingConfiguration(int event, const sp<RecordClientDescriptor>& client);
@@ -145,6 +148,7 @@
int32_t mGlobalActiveCount = 0; // non-client-specific activity ref count
EffectDescriptorCollection mEnabledEffects;
audio_input_flags_t& mFlags = AudioPortConfig::mFlags.input;
+ bool mIsPreemptor; // true if this input was opened after preemting another one
};
class AudioInputCollection :
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index a596c43..60da405 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -63,6 +63,8 @@
* HW Audio Source.
*/
virtual bool isInternal() const { return false; }
+ virtual bool isCallRx() const { return false; }
+ virtual bool isCallTx() const { return false; }
audio_port_handle_t portId() const { return mPortId; }
uid_t uid() const { return mUid; }
audio_session_t session() const { return mSessionId; };
@@ -236,7 +238,7 @@
const sp<DeviceDescriptor>& srcDevice,
audio_stream_type_t stream, product_strategy_t strategy,
VolumeSource volumeSource,
- bool isInternal);
+ bool isInternal, bool isCallRx, bool isCallTx);
~SourceClientDescriptor() override = default;
@@ -263,6 +265,8 @@
wp<HwAudioOutputDescriptor> hwOutput() const { return mHwOutput; }
void setHwOutput(const sp<HwAudioOutputDescriptor>& hwOutput);
bool isInternal() const override { return mIsInternal; }
+ bool isCallRx() const override { return mIsCallRx; }
+ bool isCallTx() const override { return mIsCallTx; }
using ClientDescriptor::dump;
void dump(String8 *dst, int spaces) const override;
@@ -294,6 +298,8 @@
* requester to prevent rerouting SwOutput involved in raw patches.
*/
bool mIsInternal = false;
+ bool mIsCallRx = false;
+ bool mIsCallTx = false;
};
class SourceClientCollection :
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index 44f84b9..5a0fd97 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -30,9 +30,10 @@
namespace android {
AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile,
- AudioPolicyClientInterface *clientInterface)
+ AudioPolicyClientInterface *clientInterface,
+ bool isPreemptor)
: mProfile(profile)
- , mClientInterface(clientInterface)
+ , mClientInterface(clientInterface), mIsPreemptor(isPreemptor)
{
if (profile != NULL) {
profile->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
@@ -275,6 +276,9 @@
"%s invalid profile active count %u",
__func__, mProfile->curActiveCount);
mProfile->curActiveCount--;
+ // allow preemption again now that at least one client was able to
+ // capture on this input
+ mIsPreemptor = false;
}
}
diff --git a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
index 667c189..ad6977b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/ClientDescriptor.cpp
@@ -96,12 +96,14 @@
SourceClientDescriptor::SourceClientDescriptor(audio_port_handle_t portId, uid_t uid,
audio_attributes_t attributes, const struct audio_port_config &config,
const sp<DeviceDescriptor>& srcDevice, audio_stream_type_t stream,
- product_strategy_t strategy, VolumeSource volumeSource, bool isInternal) :
+ product_strategy_t strategy, VolumeSource volumeSource,
+ bool isInternal, bool isCallRx, bool isCallTx) :
TrackClientDescriptor::TrackClientDescriptor(portId, uid, AUDIO_SESSION_NONE, attributes,
{config.sample_rate, config.channel_mask, config.format}, AUDIO_PORT_HANDLE_NONE,
stream, strategy, volumeSource, AUDIO_OUTPUT_FLAG_NONE, false,
{} /* Sources do not support secondary outputs*/, nullptr),
- mSrcDevice(srcDevice), mIsInternal(isInternal)
+ mSrcDevice(srcDevice), mIsInternal(isInternal),
+ mIsCallRx(isCallRx), mIsCallTx(isCallTx)
{
}
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 406bc4f..7666a2b 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -428,10 +428,12 @@
// LE audio broadcast device is only used if:
// - No call is active
- // - either MEDIA or SONIFICATION_RESPECTFUL is the highest priority active strategy
- // OR the LE audio unicast device is not active
+ // - either MEDIA, SONIFICATION_RESPECTFUL or SONIFICATION is the highest priority
+ // active strategy
+ // OR the LE audio unicast device is not active
if (devices2.isEmpty() && !isInCall()
- && (strategy == STRATEGY_MEDIA || strategy == STRATEGY_SONIFICATION_RESPECTFUL)) {
+ && (strategy == STRATEGY_MEDIA || strategy == STRATEGY_SONIFICATION_RESPECTFUL
+ || strategy == STRATEGY_SONIFICATION)) {
legacy_strategy topActiveStrategy = STRATEGY_NONE;
for (const auto &ps : getOrderedProductStrategies()) {
if (outputs.isStrategyActive(ps)) {
@@ -443,6 +445,7 @@
if (topActiveStrategy == STRATEGY_NONE || topActiveStrategy == STRATEGY_MEDIA
|| topActiveStrategy == STRATEGY_SONIFICATION_RESPECTFUL
+ || topActiveStrategy == STRATEGY_SONIFICATION
|| !outputs.isAnyDeviceTypeActive(getAudioDeviceOutLeAudioUnicastSet())) {
devices2 =
availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_BLE_BROADCAST);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a39e083..7d2dfbc 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -66,6 +66,7 @@
using android::media::audio::common::AudioDeviceAddress;
using android::media::audio::common::AudioPortDeviceExt;
using android::media::audio::common::AudioPortExt;
+using com::android::media::audioserver::fix_call_audio_patch;
using content::AttributionSourceState;
//FIXME: workaround for truncated touch sounds
@@ -704,8 +705,10 @@
audio_attributes_t attr = { .source = AUDIO_SOURCE_VOICE_COMMUNICATION };
auto txSourceDevice = mEngine->getInputDeviceForAttributes(attr);
- disconnectTelephonyAudioSource(mCallRxSourceClient);
- disconnectTelephonyAudioSource(mCallTxSourceClient);
+ if (!fix_call_audio_patch()) {
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+ }
if (rxDevices.isEmpty()) {
ALOGW("%s() no selected output device", __func__);
@@ -758,6 +761,9 @@
// Use legacy routing method for voice calls via setOutputDevice() on primary output.
// Otherwise, create two audio patches for TX and RX path.
if (!createRxPatch) {
+ if (fix_call_audio_patch()) {
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ }
if (!hasPrimaryOutput()) {
ALOGW("%s() no primary output available", __func__);
return INVALID_OPERATION;
@@ -780,6 +786,8 @@
}
}
connectTelephonyTxAudioSource(txSourceDevice, txSinkDevice, delayMs);
+ } else if (fix_call_audio_patch()) {
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
}
if (waitMs != nullptr) {
*waitMs = muteWaitMs;
@@ -801,17 +809,38 @@
void AudioPolicyManager::connectTelephonyRxAudioSource()
{
- disconnectTelephonyAudioSource(mCallRxSourceClient);
+ const auto aa = mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL);
+
+ if (fix_call_audio_patch()) {
+ if (mCallRxSourceClient != nullptr) {
+ DeviceVector rxDevices =
+ mEngine->getOutputDevicesForAttributes(aa, nullptr, false /*fromCache*/);
+ ALOG_ASSERT(!rxDevices.isEmpty() || !mCallRxSourceClient->isConnected(),
+ "connectTelephonyRxAudioSource(): no device found for call RX source");
+ sp<DeviceDescriptor> rxDevice = rxDevices.itemAt(0);
+ if (mCallRxSourceClient->isConnected()
+ && mCallRxSourceClient->sinkDevice()->equals(rxDevice)) {
+ return;
+ }
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ }
+ } else {
+ disconnectTelephonyAudioSource(mCallRxSourceClient);
+ }
+
const struct audio_port_config source = {
.role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE,
.ext.device.type = AUDIO_DEVICE_IN_TELEPHONY_RX, .ext.device.address = ""
};
- const auto aa = mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL);
-
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
- status_t status = startAudioSource(&source, &aa, &portId, 0 /*uid*/, true /*internal*/);
+
+ status_t status = startAudioSourceInternal(&source, &aa, &portId, 0 /*uid*/,
+ true /*internal*/, true /*isCallRx*/);
ALOGE_IF(status != OK, "%s: failed to start audio source (%d)", __func__, status);
mCallRxSourceClient = mAudioSources.valueFor(portId);
+ ALOGV("%s portdID %d between source %s and sink %s", __func__, portId,
+ mCallRxSourceClient->srcDevice()->toString().c_str(),
+ mCallRxSourceClient->sinkDevice()->toString().c_str());
ALOGE_IF(mCallRxSourceClient == nullptr,
"%s failed to start Telephony Rx AudioSource", __func__);
}
@@ -830,15 +859,26 @@
const sp<DeviceDescriptor> &srcDevice, const sp<DeviceDescriptor> &sinkDevice,
uint32_t delayMs)
{
- disconnectTelephonyAudioSource(mCallTxSourceClient);
if (srcDevice == nullptr || sinkDevice == nullptr) {
ALOGW("%s could not create patch, invalid sink and/or source device(s)", __func__);
return;
}
+
+ if (fix_call_audio_patch()) {
+ if (mCallTxSourceClient != nullptr) {
+ if (mCallTxSourceClient->isConnected()
+ && mCallTxSourceClient->srcDevice()->equals(srcDevice)) {
+ return;
+ }
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+ }
+ } else {
+ disconnectTelephonyAudioSource(mCallTxSourceClient);
+ }
+
PatchBuilder patchBuilder;
patchBuilder.addSource(srcDevice).addSink(sinkDevice);
- ALOGV("%s between source %s and sink %s", __func__,
- srcDevice->toString().c_str(), sinkDevice->toString().c_str());
+
auto callTxSourceClientPortId = PolicyAudioPort::getNextUniqueId();
const auto aa = mEngine->getAttributesForStreamType(AUDIO_STREAM_VOICE_CALL);
@@ -846,7 +886,8 @@
srcDevice->toAudioPortConfig(&source);
mCallTxSourceClient = new SourceClientDescriptor(
callTxSourceClientPortId, mUidCached, aa, source, srcDevice, AUDIO_STREAM_PATCH,
- mCommunnicationStrategy, toVolumeSource(aa), true);
+ mCommunnicationStrategy, toVolumeSource(aa), true,
+ false /*isCallRx*/, true /*isCallTx*/);
mCallTxSourceClient->setPreferredDeviceId(sinkDevice->getId());
audio_patch_handle_t patchHandle = AUDIO_PATCH_HANDLE_NONE;
@@ -854,6 +895,8 @@
mCallTxSourceClient, sinkDevice, patchBuilder.patch(), patchHandle, mUidCached,
delayMs);
ALOGE_IF(status != NO_ERROR, "%s() error %d creating TX audio patch", __func__, status);
+ ALOGV("%s portdID %d between source %s and sink %s", __func__, callTxSourceClientPortId,
+ srcDevice->toString().c_str(), sinkDevice->toString().c_str());
if (status == NO_ERROR) {
mAudioSources.add(callTxSourceClientPortId, mCallTxSourceClient);
}
@@ -3082,43 +3125,115 @@
}
}
+ bool isPreemptor = false;
if (!profile->canOpenNewIo()) {
- for (size_t i = 0; i < mInputs.size(); ) {
- sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
- if (desc->mProfile != profile) {
- i++;
- continue;
- }
- // if sound trigger, reuse input if used by other sound trigger on same session
- // else
- // reuse input if active client app is not in IDLE state
- //
- RecordClientVector clients = desc->clientsList();
- bool doClose = false;
- for (const auto& client : clients) {
- if (isSoundTrigger != client->isSoundTrigger()) {
+ if (com::android::media::audioserver::fix_input_sharing_logic()) {
+ // First pick best candidate for preemption (there may not be any):
+ // - Preempt and input if:
+ // - It has only strictly lower priority use cases than the new client
+ // - It has equal priority use cases than the new client, was not
+ // opened thanks to preemption or has been active since opened.
+ // - Order the preemption candidates by inactive first and priority second
+ sp<AudioInputDescriptor> closeCandidate;
+ int leastCloseRank = INT_MAX;
+ static const int sCloseActive = 0x100;
+
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
+ if (desc->mProfile != profile) {
continue;
}
- if (client->isSoundTrigger()) {
- if (session == client->session()) {
+ sp<RecordClientDescriptor> topPrioClient = desc->getHighestPriorityClient();
+ if (topPrioClient == nullptr) {
+ continue;
+ }
+ int topPrio = source_priority(topPrioClient->source());
+ if (topPrio < source_priority(attributes.source)
+ || (topPrio == source_priority(attributes.source)
+ && !desc->isPreemptor())) {
+ int closeRank = (desc->isActive() ? sCloseActive : 0) + topPrio;
+ if (closeRank < leastCloseRank) {
+ leastCloseRank = closeRank;
+ closeCandidate = desc;
+ }
+ }
+ }
+
+ if (closeCandidate != nullptr) {
+ closeInput(closeCandidate->mIoHandle);
+ // Mark the new input as being issued from a preemption
+ // so that is will not be preempted later
+ isPreemptor = true;
+ } else {
+ // Then pick the best reusable input (There is always one)
+ // The order of preference is:
+ // 1) active inputs with same use case as the new client
+ // 2) inactive inputs with same use case
+ // 3) active inputs with different use cases
+ // 4) inactive inputs with different use cases
+ sp<AudioInputDescriptor> reuseCandidate;
+ int leastReuseRank = INT_MAX;
+ static const int sReuseDifferentUseCase = 0x100;
+
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
+ if (desc->mProfile != profile) {
+ continue;
+ }
+ int reuseRank = sReuseDifferentUseCase;
+ for (const auto& client: desc->getClientIterable()) {
+ if (client->source() == attributes.source) {
+ reuseRank = 0;
+ break;
+ }
+ }
+ reuseRank += desc->isActive() ? 0 : 1;
+ if (reuseRank < leastReuseRank) {
+ leastReuseRank = reuseRank;
+ reuseCandidate = desc;
+ }
+ }
+ return reuseCandidate->mIoHandle;
+ }
+ } else { // fix_input_sharing_logic()
+ for (size_t i = 0; i < mInputs.size(); ) {
+ sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
+ if (desc->mProfile != profile) {
+ i++;
+ continue;
+ }
+ // if sound trigger, reuse input if used by other sound trigger on same session
+ // else
+ // reuse input if active client app is not in IDLE state
+ //
+ RecordClientVector clients = desc->clientsList();
+ bool doClose = false;
+ for (const auto& client : clients) {
+ if (isSoundTrigger != client->isSoundTrigger()) {
+ continue;
+ }
+ if (client->isSoundTrigger()) {
+ if (session == client->session()) {
+ return desc->mIoHandle;
+ }
+ continue;
+ }
+ if (client->active() && client->appState() != APP_STATE_IDLE) {
return desc->mIoHandle;
}
- continue;
+ doClose = true;
}
- if (client->active() && client->appState() != APP_STATE_IDLE) {
- return desc->mIoHandle;
+ if (doClose) {
+ closeInput(desc->mIoHandle);
+ } else {
+ i++;
}
- doClose = true;
- }
- if (doClose) {
- closeInput(desc->mIoHandle);
- } else {
- i++;
}
}
}
- sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile, mpClientInterface);
+ sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(
+ profile, mpClientInterface, isPreemptor);
audio_config_t lConfig = AUDIO_CONFIG_INITIALIZER;
lConfig.sample_rate = profileSamplingRate;
@@ -3584,7 +3699,9 @@
if (isVolumeConsistentForCalls(vs, {mCallRxSourceClient->sinkDevice()->type()},
isVoiceVolSrc, isBtScoVolSrc, __func__)
&& (isVoiceVolSrc || isBtScoVolSrc)) {
- setVoiceVolume(index, curves, isVoiceVolSrc, 0);
+ bool voiceVolumeManagedByHost = isVoiceVolSrc &&
+ !audio_is_ble_out_device(mCallRxSourceClient->sinkDevice()->type());
+ setVoiceVolume(index, curves, voiceVolumeManagedByHost, 0);
}
}
@@ -5095,7 +5212,7 @@
new SourceClientDescriptor(
portId, uid, attributes, *source, srcDevice, AUDIO_STREAM_PATCH,
mEngine->getProductStrategyForAttributes(attributes), toVolumeSource(attributes),
- true);
+ true, false /*isCallRx*/, false /*isCallTx*/);
sourceDesc->setPreferredDeviceId(sinkDevice->getId());
status_t status =
@@ -5427,7 +5544,7 @@
outputDesc->toAudioPortConfig(&srcMixPortConfig, nullptr);
// for volume control, we may need a valid stream
srcMixPortConfig.ext.mix.usecase.stream =
- (!sourceDesc->isInternal() || isCallTxAudioSource(sourceDesc)) ?
+ (!sourceDesc->isInternal() || sourceDesc->isCallTx()) ?
mEngine->getStreamTypeForAttributes(sourceDesc->attributes()) :
AUDIO_STREAM_PATCH;
patchBuilder.addSource(srcMixPortConfig);
@@ -5765,7 +5882,15 @@
status_t AudioPolicyManager::startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,
- uid_t uid, bool internal)
+ uid_t uid) {
+ return startAudioSourceInternal(source, attributes, portId, uid,
+ false /*internal*/, false /*isCallRx*/);
+}
+
+status_t AudioPolicyManager::startAudioSourceInternal(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_port_handle_t *portId,
+ uid_t uid, bool internal, bool isCallRx)
{
ALOGV("%s", __FUNCTION__);
*portId = AUDIO_PORT_HANDLE_NONE;
@@ -5798,7 +5923,7 @@
new SourceClientDescriptor(*portId, uid, *attributes, *source, srcDevice,
mEngine->getStreamTypeForAttributes(*attributes),
mEngine->getProductStrategyForAttributes(*attributes),
- toVolumeSource(*attributes), internal);
+ toVolumeSource(*attributes), internal, isCallRx, false);
status_t status = connectAudioSource(sourceDesc);
if (status == NO_ERROR) {
@@ -5891,7 +6016,8 @@
float AudioPolicyManager::getStreamVolumeDB(
audio_stream_type_t stream, int index, audio_devices_t device)
{
- return computeVolume(getVolumeCurves(stream), toVolumeSource(stream), index, {device});
+ return computeVolume(getVolumeCurves(stream), toVolumeSource(stream), index,
+ {device}, /* adjustAttenuation= */false);
}
status_t AudioPolicyManager::getSurroundFormats(unsigned int *numSurroundFormats,
@@ -6621,8 +6747,8 @@
__func__, inProfile->getTagName().c_str());
continue;
}
- sp<AudioInputDescriptor> inputDesc =
- new AudioInputDescriptor(inProfile, mpClientInterface);
+ sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(
+ inProfile, mpClientInterface, false /*isPreemptor*/);
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
status_t status = inputDesc->open(nullptr,
@@ -6932,7 +7058,7 @@
continue;
}
- desc = new AudioInputDescriptor(profile, mpClientInterface);
+ desc = new AudioInputDescriptor(profile, mpClientInterface, false /*isPreemptor*/);
audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
status = desc->open(nullptr, device, AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE, &input);
@@ -7178,7 +7304,7 @@
sp<SourceClientDescriptor> sourceDesc = mAudioSources.valueAt(i);
if (sourceDesc != nullptr && followsSameRouting(attr, sourceDesc->attributes())
&& sourceDesc->getPatchHandle() == AUDIO_PATCH_HANDLE_NONE
- && !isCallRxAudioSource(sourceDesc) && !sourceDesc->isInternal()) {
+ && !sourceDesc->isCallRx() && !sourceDesc->isInternal()) {
connectAudioSource(sourceDesc);
}
}
@@ -7285,7 +7411,7 @@
}
}
sp<SourceClientDescriptor> source = getSourceForAttributesOnOutput(srcOut, attr);
- if (source != nullptr && !isCallRxAudioSource(source) && !source->isInternal()) {
+ if (source != nullptr && !source->isCallRx() && !source->isInternal()) {
connectAudioSource(source);
}
}
@@ -8069,9 +8195,15 @@
VolumeSource volumeSource,
int index,
const DeviceTypeSet& deviceTypes,
+ bool adjustAttenuation,
bool computeInternalInteraction)
{
- float volumeDb = adjustDeviceAttenuationForAbsVolume(curves, volumeSource, index, deviceTypes);
+ float volumeDb;
+ if (adjustAttenuation) {
+ volumeDb = adjustDeviceAttenuationForAbsVolume(curves, volumeSource, index, deviceTypes);
+ } else {
+ volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(deviceTypes), index);
+ }
ALOGV("%s volume source %d, index %d, devices %s, compute internal %b ", __func__,
volumeSource, index, dumpDeviceTypes(deviceTypes).c_str(), computeInternalInteraction);
@@ -8092,7 +8224,8 @@
mOutputs.isActive(ringVolumeSrc, 0)) {
auto &ringCurves = getVolumeCurves(AUDIO_STREAM_RING);
const float ringVolumeDb = computeVolume(ringCurves, ringVolumeSrc, index, deviceTypes,
- /* computeInternalInteraction= */ false);
+ adjustAttenuation,
+ /* computeInternalInteraction= */false);
return ringVolumeDb - 4 > volumeDb ? ringVolumeDb - 4 : volumeDb;
}
@@ -8110,7 +8243,7 @@
int voiceVolumeIndex = voiceCurves.getVolumeIndex(deviceTypes);
const float maxVoiceVolDb =
computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, deviceTypes,
- /* computeInternalInteraction= */ false)
+ adjustAttenuation, /* computeInternalInteraction= */false)
+ IN_CALL_EARPIECE_HEADROOM_DB;
// FIXME: Workaround for call screening applications until a proper audio mode is defined
// to support this scenario : Exempt the RING stream from the audio cap if the audio was
@@ -8163,7 +8296,8 @@
musicVolumeSrc,
musicCurves.getVolumeIndex(musicDevice),
musicDevice,
- /* computeInternalInteraction= */ false);
+ adjustAttenuation,
+ /* computeInternalInteraction= */ false);
float minVolDb = (musicVolDb > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
musicVolDb : SONIFICATION_HEADSET_VOLUME_MIN_DB;
if (volumeDb > minVolDb) {
@@ -8257,9 +8391,10 @@
float volumeDb = computeVolume(curves, volumeSource, index, deviceTypes);
if (outputDesc->isFixedVolume(deviceTypes) ||
- // Force VoIP volume to max for bluetooth SCO device except if muted
+ // Force VoIP volume to max for bluetooth SCO/BLE device except if muted
(index != 0 && (isVoiceVolSrc || isBtScoVolSrc) &&
- isSingleDeviceType(deviceTypes, audio_is_bluetooth_out_sco_device))) {
+ (isSingleDeviceType(deviceTypes, audio_is_bluetooth_out_sco_device)
+ || isSingleDeviceType(deviceTypes, audio_is_ble_out_device)))) {
volumeDb = 0.0f;
}
const bool muted = (index == 0) && (volumeDb != 0.0f);
@@ -8267,17 +8402,19 @@
deviceTypes, delayMs, force, isVoiceVolSrc);
if (outputDesc == mPrimaryOutput && (isVoiceVolSrc || isBtScoVolSrc)) {
- setVoiceVolume(index, curves, isVoiceVolSrc, delayMs);
+ bool voiceVolumeManagedByHost = isVoiceVolSrc &&
+ !isSingleDeviceType(deviceTypes, audio_is_ble_out_device);
+ setVoiceVolume(index, curves, voiceVolumeManagedByHost, delayMs);
}
return NO_ERROR;
}
void AudioPolicyManager::setVoiceVolume(
- int index, IVolumeCurves &curves, bool isVoiceVolSrc, int delayMs) {
+ int index, IVolumeCurves &curves, bool voiceVolumeManagedByHost, int delayMs) {
float voiceVolume;
- // Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed
+ // Force voice volume to max or mute for Bluetooth SCO/BLE as other attenuations are managed
// by the headset
- if (isVoiceVolSrc) {
+ if (voiceVolumeManagedByHost) {
voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
} else {
voiceVolume = index == 0 ? 0.0 : 1.0;
@@ -8455,7 +8592,7 @@
sp<SourceClientDescriptor> sourceDesc = mAudioSources.valueAt(i);
if (sourceDesc->isConnected() && (sourceDesc->srcDevice()->equals(deviceDesc) ||
sourceDesc->sinkDevice()->equals(deviceDesc))
- && !isCallRxAudioSource(sourceDesc)) {
+ && !sourceDesc->isCallRx()) {
disconnectAudioSource(sourceDesc);
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 011e867..f899cd5 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -345,8 +345,7 @@
virtual status_t startAudioSource(const struct audio_port_config *source,
const audio_attributes_t *attributes,
audio_port_handle_t *portId,
- uid_t uid,
- bool internal = false);
+ uid_t uid);
virtual status_t stopAudioSource(audio_port_handle_t portId);
virtual status_t setMasterMono(bool mono);
@@ -591,6 +590,8 @@
* @param index index to match in the volume curves for the calculation
* @param deviceTypes devices that should be considered in the volume curves for the
* calculation
+ * @param adjustAttenuation boolean indicating whether we should adjust the value to
+ * avoid double attenuation when controlling an avrcp device
* @param computeInternalInteraction boolean indicating whether recursive volume computation
* should continue within the volume computation. Defaults to {@code true} so the
* volume interactions can be computed. Calls within the method should always set the
@@ -599,6 +600,7 @@
*/
virtual float computeVolume(IVolumeCurves &curves, VolumeSource volumeSource,
int index, const DeviceTypeSet& deviceTypes,
+ bool adjustAttenuation = true,
bool computeInternalInteraction = true);
// rescale volume index from srcStream within range of dstStream
@@ -705,14 +707,6 @@
void updateCallAndOutputRouting(bool forceVolumeReeval = true, uint32_t delayMs = 0,
bool skipDelays = false);
- bool isCallRxAudioSource(const sp<SourceClientDescriptor> &source) {
- return mCallRxSourceClient != nullptr && source == mCallRxSourceClient;
- }
-
- bool isCallTxAudioSource(const sp<SourceClientDescriptor> &source) {
- return mCallTxSourceClient != nullptr && source == mCallTxSourceClient;
- }
-
void connectTelephonyRxAudioSource();
void disconnectTelephonyAudioSource(sp<SourceClientDescriptor> &clientDesc);
@@ -976,6 +970,12 @@
void checkLeBroadcastRoutes(bool wasUnicastActive,
sp<SwAudioOutputDescriptor> ignoredOutput, uint32_t delayMs);
+ status_t startAudioSourceInternal(const struct audio_port_config *source,
+ const audio_attributes_t *attributes,
+ audio_port_handle_t *portId,
+ uid_t uid,
+ bool internal,
+ bool isCallRx);
const uid_t mUidCached; // AID_AUDIOSERVER
sp<const AudioPolicyConfig> mConfig;
EngineInstance mEngine; // Audio Policy Engine instance
diff --git a/services/audiopolicy/permission/Android.bp b/services/audiopolicy/permission/Android.bp
index d5f59a0..cfbeaae 100644
--- a/services/audiopolicy/permission/Android.bp
+++ b/services/audiopolicy/permission/Android.bp
@@ -34,8 +34,8 @@
shared_libs: [
"libbase",
"libbinder",
- "libutils",
"liblog",
+ "libutils",
],
host_supported: true,
@@ -43,21 +43,21 @@
integer_overflow: true,
},
cflags: [
- "-Wall",
- "-Wdeprecated",
- "-Wextra",
- "-Werror=format",
- "-Wextra-semi",
- "-Wthread-safety",
- "-Wconditional-uninitialized",
- "-Wimplicit-fallthrough",
- "-Wreorder-init-list",
- "-Werror=reorder-init-list",
- "-Wshadow-all",
- "-Wunreachable-code-aggressive",
- "-Werror",
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ "-Wall",
+ "-Wconditional-uninitialized",
+ "-Wdeprecated",
+ "-Werror",
+ "-Werror=format",
+ "-Werror=reorder-init-list",
+ "-Wextra",
+ "-Wextra-semi",
+ "-Wimplicit-fallthrough",
+ "-Wreorder-init-list",
+ "-Wshadow-all",
+ "-Wthread-safety",
+ "-Wunreachable-code-aggressive",
],
tidy: true,
tidy_checks: [
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index dc61115..4006489 100644
--- a/services/audiopolicy/tests/Android.bp
+++ b/services/audiopolicy/tests/Android.bp
@@ -12,6 +12,7 @@
name: "audiopolicy_tests",
defaults: [
+ "aconfig_lib_cc_shared_link.defaults",
"latest_android_media_audio_common_types_cpp_static",
],
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index ba0a1eb..d1dd1e9 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -72,6 +72,7 @@
*input = mNextIoHandle++;
mOpenedInputs.insert(*input);
ALOGD("%s: opened input %d", __func__, *input);
+ mOpenInputCallsCount++;
return NO_ERROR;
}
@@ -86,6 +87,7 @@
return BAD_VALUE;
}
ALOGD("%s: closed input %d", __func__, input);
+ mCloseInputCallsCount++;
return NO_ERROR;
}
@@ -260,6 +262,18 @@
auto it = mTracksInternalMute.find(portId);
return it == mTracksInternalMute.end() ? false : it->second;
}
+ void resetInputApiCallsCounters() {
+ mOpenInputCallsCount = 0;
+ mCloseInputCallsCount = 0;
+ }
+
+ size_t getCloseInputCallsCount() const {
+ return mCloseInputCallsCount;
+ }
+
+ size_t getOpenInputCallsCount() const {
+ return mOpenInputCallsCount;
+ }
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
@@ -275,6 +289,8 @@
std::set<audio_channel_mask_t> mSupportedChannelMasks;
std::map<audio_port_handle_t, bool> mTracksInternalMute;
std::set<audio_io_handle_t> mOpenedInputs;
+ size_t mOpenInputCallsCount = 0;
+ size_t mCloseInputCallsCount = 0;
};
} // namespace android
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 4c98687..07aad0c 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -3830,3 +3830,92 @@
testing::Values(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
AUDIO_USAGE_ALARM)
);
+
+class AudioPolicyManagerInputPreemptionTest : public AudioPolicyManagerTestWithConfigurationFile {
+};
+
+TEST_F_WITH_FLAGS(
+ AudioPolicyManagerInputPreemptionTest,
+ SameSessionReusesInput,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
+) {
+ mClient->resetInputApiCallsCounters();
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+
+ audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+ EXPECT_EQ(0, mClient->getCloseInputCallsCount());
+ EXPECT_EQ(input1, input2);
+}
+
+TEST_F_WITH_FLAGS(
+ AudioPolicyManagerInputPreemptionTest,
+ LesserPriorityReusesInput,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
+) {
+ mClient->resetInputApiCallsCounters();
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+
+ audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
+ attr.source = AUDIO_SOURCE_VOICE_RECOGNITION;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, OTHER_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+ EXPECT_EQ(0, mClient->getCloseInputCallsCount());
+ EXPECT_EQ(input1, input2);
+}
+
+TEST_F_WITH_FLAGS(
+ AudioPolicyManagerInputPreemptionTest,
+ HigherPriorityPreemptsInput,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
+) {
+ mClient->resetInputApiCallsCounters();
+
+ audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+ attr.source = AUDIO_SOURCE_MIC;
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ EXPECT_EQ(1, mClient->getOpenInputCallsCount());
+
+ audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
+ attr.source = AUDIO_SOURCE_CAMCORDER;
+ ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, OTHER_SESSION_ID, 1, &selectedDeviceId,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+ 48000));
+
+ EXPECT_EQ(2, mClient->getOpenInputCallsCount());
+ EXPECT_EQ(1, mClient->getCloseInputCallsCount());
+ EXPECT_NE(input1, input2);
+}
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index bbc19fa..67e99f2 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -30,7 +30,7 @@
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
- <mixPort name="primary input" role="sink">
+ <mixPort name="primary input" role="sink" maxActiveCount="1" maxOpenCount="1">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 22f1961..13cffa7 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -513,7 +513,7 @@
void CameraService::onDeviceStatusChanged(const std::string& cameraId,
CameraDeviceStatus newHalStatus) {
ALOGI("%s: Status changed for cameraId=%s, newStatus=%d", __FUNCTION__,
- cameraId.c_str(), newHalStatus);
+ cameraId.c_str(), eToI(newHalStatus));
StatusInternal newStatus = mapToInternal(newHalStatus);
@@ -537,7 +537,8 @@
StatusInternal oldStatus = state->getStatus();
if (oldStatus == newStatus) {
- ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus);
+ ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__,
+ eToI(newStatus));
return;
}
@@ -580,7 +581,7 @@
const std::string& physicalId,
CameraDeviceStatus newHalStatus) {
ALOGI("%s: Status changed for cameraId=%s, physicalCameraId=%s, newStatus=%d",
- __FUNCTION__, id.c_str(), physicalId.c_str(), newHalStatus);
+ __FUNCTION__, id.c_str(), physicalId.c_str(), eToI(newHalStatus));
StatusInternal newStatus = mapToInternal(newHalStatus);
@@ -596,7 +597,7 @@
if (logicalCameraStatus != StatusInternal::PRESENT &&
logicalCameraStatus != StatusInternal::NOT_AVAILABLE) {
ALOGE("%s: Physical camera id %s status %d change for an invalid logical camera state %d",
- __FUNCTION__, physicalId.c_str(), newHalStatus, logicalCameraStatus);
+ __FUNCTION__, physicalId.c_str(), eToI(newHalStatus), eToI(logicalCameraStatus));
return;
}
@@ -688,7 +689,7 @@
void CameraService::onTorchStatusChangedLocked(const std::string& cameraId,
TorchModeStatus newStatus, SystemCameraKind systemCameraKind) {
ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d",
- __FUNCTION__, cameraId.c_str(), newStatus);
+ __FUNCTION__, cameraId.c_str(), eToI(newStatus));
TorchModeStatus status;
status_t res = getTorchStatusLocked(cameraId, &status);
@@ -1557,7 +1558,7 @@
serviceStatus = ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON;
break;
default:
- ALOGW("Unknown new flash status: %d", status);
+ ALOGW("Unknown new flash status: %d", eToI(status));
}
return serviceStatus;
}
@@ -1575,7 +1576,7 @@
serviceStatus = StatusInternal::ENUMERATING;
break;
default:
- ALOGW("Unknown new HAL device status: %d", status);
+ ALOGW("Unknown new HAL device status: %d", eToI(status));
}
return serviceStatus;
}
@@ -1599,7 +1600,7 @@
serviceStatus = ICameraServiceListener::STATUS_UNKNOWN;
break;
default:
- ALOGW("Unknown new internal device status: %d", status);
+ ALOGW("Unknown new internal device status: %d", eToI(status));
}
return serviceStatus;
}
@@ -5784,7 +5785,7 @@
}
ALOGV("%s: Status has changed for camera ID %s from %#x to %#x", __FUNCTION__,
- cameraId.c_str(), oldStatus, status);
+ cameraId.c_str(), eToI(oldStatus), eToI(status));
if (oldStatus == StatusInternal::NOT_PRESENT &&
(status != StatusInternal::PRESENT &&
diff --git a/services/camera/libcameraservice/aidl/AidlUtils.cpp b/services/camera/libcameraservice/aidl/AidlUtils.cpp
index d23566c..1ec5072 100644
--- a/services/camera/libcameraservice/aidl/AidlUtils.cpp
+++ b/services/camera/libcameraservice/aidl/AidlUtils.cpp
@@ -26,6 +26,7 @@
#include <device3/Camera3StreamInterface.h>
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
#include <mediautils/AImageReaderUtils.h>
+#include "utils/Utils.h"
namespace android::hardware::cameraservice::utils::conversion::aidl {
@@ -347,8 +348,8 @@
// Ensure the vendor ID are the same before attempting
// anything else. If vendor IDs differ we cannot safely copy the characteristics.
if (from.getVendorId() != to->getVendorId()) {
- ALOGE("%s: Incompatible CameraMetadata objects. Vendor IDs differ. From: %lu; To: %lu",
- __FUNCTION__, from.getVendorId(), to->getVendorId());
+ ALOGE("%s: Incompatible CameraMetadata objects. Vendor IDs differ. From: %" PRIu64
+ "; To: %" PRIu64, __FUNCTION__, from.getVendorId(), to->getVendorId());
return BAD_VALUE;
}
@@ -364,7 +365,7 @@
for (size_t i = 0; i < get_camera_metadata_entry_count(src); i++) {
int ret = get_camera_metadata_ro_entry(src, i, &entry);
if (ret != OK) {
- ALOGE("%s: Could not fetch entry at index %lu. Error: %d", __FUNCTION__, i, ret);
+ ALOGE("%s: Could not fetch entry at index %zu. Error: %d", __FUNCTION__, i, ret);
from.unlock(src);
return BAD_VALUE;
}
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index 225d7f5..cf6ff84 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -40,6 +40,7 @@
#include "common/CameraDeviceBase.h"
#include "utils/ExifUtils.h"
#include "utils/SessionConfigurationUtils.h"
+#include "utils/Utils.h"
#include "HeicEncoderInfoManager.h"
#include "HeicCompositeStream.h"
@@ -1464,7 +1465,7 @@
const uint8_t *header = appSegmentBuffer + (maxSize - sizeof(CameraBlob));
const CameraBlob *blob = (const CameraBlob*)(header);
if (blob->blobId != CameraBlobId::JPEG_APP_SEGMENTS) {
- ALOGE("%s: Invalid EXIF blobId %d", __FUNCTION__, blob->blobId);
+ ALOGE("%s: Invalid EXIF blobId %d", __FUNCTION__, eToI(blob->blobId));
return 0;
}
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 6416c11..2440c37 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -51,6 +51,7 @@
#include "api2/HeicCompositeStream.h"
#include "device3/ZoomRatioMapper.h"
+#include "utils/Utils.h"
namespace android {
@@ -92,7 +93,7 @@
case TorchModeStatus::AVAILABLE_ON:
return "AVAILABLE_ON";
}
- ALOGW("Unexpected HAL torch mode status code %d", s);
+ ALOGW("Unexpected HAL torch mode status code %d", eToI(s));
return "UNKNOWN_STATUS";
}
@@ -105,7 +106,7 @@
case CameraDeviceStatus::ENUMERATING:
return "ENUMERATING";
}
- ALOGW("Unexpected HAL device status code %d", s);
+ ALOGW("Unexpected HAL device status code %d", eToI(s));
return "UNKNOWN_STATUS";
}
@@ -2279,7 +2280,7 @@
return tryToInitializeAidlProviderLocked(removedAidlProviderName,
providerInfo);
default:
- ALOGE("%s Unsupported Transport %d", __FUNCTION__, providerTransport);
+ ALOGE("%s Unsupported Transport %d", __FUNCTION__, eToI(providerTransport));
}
}
}
@@ -2366,7 +2367,7 @@
}
break;
default:
- ALOGE("%s Invalid transport %d", __FUNCTION__, transport);
+ ALOGE("%s Invalid transport %d", __FUNCTION__, eToI(transport));
return BAD_VALUE;
}
@@ -2712,7 +2713,7 @@
}
if (!known) {
ALOGW("Camera provider %s says an unknown camera %s now has torch status %d. Curious.",
- mProviderName.c_str(), cameraDeviceName.c_str(), newStatus);
+ mProviderName.c_str(), cameraDeviceName.c_str(), eToI(newStatus));
return;
}
// no lock needed since listener is set up only once during
diff --git a/services/camera/libcameraservice/common/HalConversionsTemplated.h b/services/camera/libcameraservice/common/HalConversionsTemplated.h
index 96a715c..c586062 100644
--- a/services/camera/libcameraservice/common/HalConversionsTemplated.h
+++ b/services/camera/libcameraservice/common/HalConversionsTemplated.h
@@ -19,6 +19,7 @@
#include "common/CameraProviderManager.h"
#include <device3/Camera3StreamInterface.h>
+#include <utils/Utils.h>
namespace android {
@@ -48,7 +49,7 @@
case HalCameraDeviceStatus::ENUMERATING:
return CameraDeviceStatus::ENUMERATING;
}
- ALOGW("Unexpectedcamera device status code %d", s);
+ ALOGW("Unexpectedcamera device status code %d", eToI(s));
return CameraDeviceStatus::NOT_PRESENT;
}
@@ -74,7 +75,7 @@
case HalTorchModeStatus::AVAILABLE_ON:
return TorchModeStatus::AVAILABLE_ON;
}
- ALOGW("Unexpectedcamera torch mode status code %d", s);
+ ALOGW("Unexpectedcamera torch mode status code %d", eToI(s));
return TorchModeStatus::NOT_AVAILABLE;
}
@@ -88,7 +89,7 @@
case HalCameraDeviceStatus::ENUMERATING:
return "ENUMERATING";
}
- ALOGW("Unexpected HAL device status code %d", s);
+ ALOGW("Unexpected HAL device status code %d", eToI(s));
return "UNKNOWN_STATUS";
}
diff --git a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
index 1e546fb..40800d9 100644
--- a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.cpp
@@ -26,6 +26,7 @@
#include "device3/ZoomRatioMapper.h"
#include <utils/SessionConfigurationUtilsHidl.h>
#include <utils/Trace.h>
+#include <utils/Utils.h>
#include <android/hardware/camera/device/3.7/ICameraDevice.h>
@@ -69,7 +70,7 @@
case Status::INTERNAL_ERROR:
return INVALID_OPERATION;
}
- ALOGW("Unexpected HAL status code %d", s);
+ ALOGW("Unexpected HAL status code %d", eToI(s));
return INVALID_OPERATION;
}
@@ -111,7 +112,7 @@
case Status::INTERNAL_ERROR:
return "INTERNAL_ERROR";
}
- ALOGW("Unexpected HAL status code %d", s);
+ ALOGW("Unexpected HAL status code %d", eToI(s));
return "UNKNOWN_ERROR";
}
@@ -591,7 +592,7 @@
}
if (status != Status::OK) {
ALOGE("%s: Unable to get camera characteristics for device %s: %s (%d)",
- __FUNCTION__, id.c_str(), statusToString(status), status);
+ __FUNCTION__, id.c_str(), statusToString(status), eToI(status));
return;
}
@@ -770,7 +771,7 @@
if (status != Status::OK) {
ALOGE("%s: Unable to get physical camera %s characteristics for device %s: %s (%d)",
__FUNCTION__, id.c_str(), mId.c_str(),
- statusToString(status), status);
+ statusToString(status), eToI(status));
return;
}
@@ -928,7 +929,7 @@
res = INVALID_OPERATION;
break;
default:
- ALOGE("%s: Session configuration query failed: %d", __FUNCTION__, callStatus);
+ ALOGE("%s: Session configuration query failed: %d", __FUNCTION__, eToI(callStatus));
res = UNKNOWN_ERROR;
}
} else {
@@ -1076,7 +1077,7 @@
break;
default:
ALOGE("%s: Session configuration query failed: %d", __FUNCTION__,
- callStatus);
+ eToI(callStatus));
res = UNKNOWN_ERROR;
}
} else {
diff --git a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp
index 9dacaf6..09299e6 100644
--- a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp
+++ b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp
@@ -56,6 +56,7 @@
#include "device3/hidl/HidlCamera3OfflineSession.h"
#include "utils/SessionConfigurationUtilsHidl.h"
#include "utils/TraceHFR.h"
+#include "utils/Utils.h"
#include "../../common/hidl/HidlProviderInfo.h"
#include "HidlCamera3Device.h"
@@ -881,7 +882,7 @@
ret = true;
break;
default:
- ALOGV("%s: Reconfiguration query failed: %d", __FUNCTION__, callStatus);
+ ALOGV("%s: Reconfiguration query failed: %d", __FUNCTION__, eToI(callStatus));
ret = true;
}
} else {
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp
index cfa1815..9986a84 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtilsHidl.cpp
@@ -23,6 +23,7 @@
#include "device3/aidl/AidlCamera3Device.h"
#include "device3/hidl/HidlCamera3Device.h"
#include "device3/Camera3OutputStream.h"
+#include "utils/Utils.h"
using android::camera3::OutputStreamInfo;
using android::hardware::camera2::ICameraDeviceUser;
@@ -48,16 +49,16 @@
hidl.streams.resize(aidl.streams.size());
size_t i = 0;
for (const auto &stream : aidl.streams) {
- if (static_cast<int>(stream.dynamicRangeProfile) !=
+ if (eToI(stream.dynamicRangeProfile) !=
ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD) {
ALOGE("%s Dynamic range profile %" PRId64 " not supported by HIDL", __FUNCTION__,
- stream.dynamicRangeProfile);
+ eToI(stream.dynamicRangeProfile));
return BAD_VALUE;
}
- if (static_cast<int>(stream.useCase) != ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
+ if (eToI(stream.useCase) != ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
ALOGE("%s Stream use case %" PRId64 "not supported by HIDL", __FUNCTION__,
- stream.useCase);
+ eToI(stream.useCase));
return BAD_VALUE;
}
diff --git a/services/camera/libcameraservice/utils/Utils.h b/services/camera/libcameraservice/utils/Utils.h
index 4e90871..0eb5e2c 100644
--- a/services/camera/libcameraservice/utils/Utils.h
+++ b/services/camera/libcameraservice/utils/Utils.h
@@ -19,10 +19,21 @@
#include <sched.h>
#include <unistd.h>
+#include <type_traits>
namespace android {
/**
+ * Magically convert an enum to its underlying integer type, mostly so they can be
+ * printed with printf-style formatters without warnings.
+ * Backport of C++23 std::to_underlying()
+ */
+template<typename Enum>
+constexpr std::underlying_type_t<Enum> eToI(Enum val) {
+ return static_cast<std::underlying_type_t<Enum>>(val);
+}
+
+/**
* As of Android V, ro.board.api_level returns the year and month of release (ex. 202404)
* instead of release SDK version. This function maps year/month format back to release
* SDK version.
diff --git a/services/mediaresourcemanager/test/Android.bp b/services/mediaresourcemanager/test/Android.bp
index 5dfec30..ac41959 100644
--- a/services/mediaresourcemanager/test/Android.bp
+++ b/services/mediaresourcemanager/test/Android.bp
@@ -30,7 +30,7 @@
"server_configurable_flags",
],
defaults: [
- "aconfig_lib_cc_static_link.defaults",
+ "aconfig_lib_cc_shared_link.defaults",
],
include_dirs: [
"frameworks/av/include",
@@ -84,6 +84,9 @@
"libactivitymanager_aidl",
"server_configurable_flags",
],
+ defaults: [
+ "aconfig_lib_cc_shared_link.defaults",
+ ],
include_dirs: [
"frameworks/av/include",
"frameworks/av/services/mediaresourcemanager",