[automerger skipped] RESTRICT AUTOMERGE - resolve merge conflicts of dab37c25e3337387809fd35c7cd46abf76088b83 to qt-qpr1-dev am: ec0754c9b5 -s ours am: 8537cde345 -s ours am: b60b4588d5 -s ours am: d4e19b2899 -s ours am: 946443b1ed -s ours am: d344dffc38 -s ours am: 9a970aeaf8 -s ours am: 1f74d704d8 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/19823968
Change-Id: I5695b50037c7a1fc00eea45b2386f4f433e757f5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/apex/manifest.json b/apex/manifest.json
index 4b75b04..1a26bff 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,6 +1,6 @@
{
"name": "com.android.media",
- "version": 339990000,
+ "version": 330400000,
"requireNativeLibs": [
"libandroid.so",
"libbinder_ndk.so",
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index fbcbb69..9426e59 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,6 +1,6 @@
{
"name": "com.android.media.swcodec",
- "version": 339990000,
+ "version": 330400000,
"requireNativeLibs": [
":sphal"
]
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index a4c03ba..048301f 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -23,8 +23,51 @@
{
"path": "frameworks/av/drm/mediadrm/plugins"
}
+ ],
+
+ "platinum-postsubmit": [
+ // runs regularly, independent of changes in this tree.
+ // signals if changes elsewhere break media functionality
+ // @FlakyTest: in staged-postsubmit, but not postsubmit
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.EncodeDecodeTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.DecodeEditEncodeTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+
+ "staged-platinum-postsubmit": [
+ // runs every four hours
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.EncodeDecodeTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsMediaCodecTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.codec.cts.DecodeEditEncodeTest"
+ }
+ ]
+ }
]
- // TODO (b/229286407) Add EncodeDecodeTest and DecodeEditEncodeTest to
- // platinum-postsubmit once issues in cuttlefish are fixed
}
diff --git a/media/codec2/hidl/1.0/utils/types.cpp b/media/codec2/hidl/1.0/utils/types.cpp
index 5c24bd7..35a3b53 100644
--- a/media/codec2/hidl/1.0/utils/types.cpp
+++ b/media/codec2/hidl/1.0/utils/types.cpp
@@ -1447,6 +1447,10 @@
bool objcpy(C2BaseBlock* d, const BaseBlock& s) {
switch (s.getDiscriminator()) {
case BaseBlock::hidl_discriminator::nativeBlock: {
+ if (s.nativeBlock() == nullptr) {
+ LOG(ERROR) << "Null BaseBlock::nativeBlock handle";
+ return false;
+ }
native_handle_t* sHandle =
native_handle_clone(s.nativeBlock());
if (sHandle == nullptr) {
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 296d7ed..124a893 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1899,6 +1899,7 @@
comp = state->comp;
}
status_t err = comp->stop();
+ mChannel->stopUseOutputSurface();
if (err != C2_OK) {
// TODO: convert err into status_t
mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
@@ -1972,6 +1973,7 @@
}
mChannel->reset();
+ mChannel->stopUseOutputSurface();
// thiz holds strong ref to this while the thread is running.
sp<CCodec> thiz(this);
std::thread([thiz, sendCallback] { thiz->release(sendCallback); }).detach();
@@ -2139,7 +2141,8 @@
std::map<size_t, sp<MediaCodecBuffer>> clientInputBuffers;
status_t err = mChannel->prepareInitialInputBuffers(&clientInputBuffers);
- if (err != OK) {
+ // FIXME(b/237656746)
+ if (err != OK && err != NO_MEMORY) {
ALOGE("Resume request for Input Buffers failed");
mCallback->onError(err, ACTION_CODE_FATAL);
return;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 5ecb130..4bf8dce 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -960,9 +960,9 @@
hdrDynamicInfo->m.data + hdrDynamicInfo->flexCount());
}
qbi.setHdrMetadata(hdr);
-
- SetHdrMetadataToGralloc4Handle(hdrStaticInfo, hdrDynamicInfo, block.handle());
}
+ SetMetadataToGralloc4Handle(dataSpace, hdrStaticInfo, hdrDynamicInfo, block.handle());
+
// we don't have dirty regions
qbi.setSurfaceDamage(Region::INVALID_REGION);
android::IGraphicBufferProducer::QueueBufferOutput qbo;
@@ -1578,6 +1578,17 @@
mFirstValidFrameIndex = mFrameIndex.load(std::memory_order_relaxed);
}
+void CCodecBufferChannel::stopUseOutputSurface() {
+ if (mOutputSurface.lock()->surface) {
+ C2BlockPool::local_id_t outputPoolId;
+ {
+ Mutexed<BlockPools>::Locked pools(mBlockPools);
+ outputPoolId = pools->outputPoolId;
+ }
+ if (mComponent) mComponent->stopUsingOutputSurface(outputPoolId);
+ }
+}
+
void CCodecBufferChannel::reset() {
stop();
if (mInputSurface != nullptr) {
@@ -1593,14 +1604,6 @@
Mutexed<Output>::Locked output(mOutput);
output->buffers.reset();
}
- if (mOutputSurface.lock()->surface) {
- C2BlockPool::local_id_t outputPoolId;
- {
- Mutexed<BlockPools>::Locked pools(mBlockPools);
- outputPoolId = pools->outputPoolId;
- }
- mComponent->stopUsingOutputSurface(outputPoolId);
- }
}
void CCodecBufferChannel::release() {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index f29a225..61fb06f 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -149,6 +149,12 @@
std::map<size_t, sp<MediaCodecBuffer>> &&clientInputBuffers);
/**
+ * Stop using buffers of the current output surface for other Codec
+ * instances to use the surface safely.
+ */
+ void stopUseOutputSurface();
+
+ /**
* Stop queueing buffers to the component. This object should never queue
* buffers after this call, until start() is called.
*/
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index cde4c72..55e0c45 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -998,9 +998,11 @@
}
using ::aidl::android::hardware::graphics::common::Cta861_3;
+using ::aidl::android::hardware::graphics::common::Dataspace;
using ::aidl::android::hardware::graphics::common::Smpte2086;
using ::android::gralloc4::MetadataType_Cta861_3;
+using ::android::gralloc4::MetadataType_Dataspace;
using ::android::gralloc4::MetadataType_Smpte2086;
using ::android::gralloc4::MetadataType_Smpte2094_40;
@@ -1156,7 +1158,8 @@
return err;
}
-c2_status_t SetHdrMetadataToGralloc4Handle(
+c2_status_t SetMetadataToGralloc4Handle(
+ android_dataspace_t dataSpace,
const std::shared_ptr<const C2StreamHdrStaticMetadataInfo::output> &staticInfo,
const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo,
const C2Handle *const handle) {
@@ -1167,6 +1170,17 @@
// Gralloc4 not supported; nothing to do
return err;
}
+ {
+ hidl_vec<uint8_t> metadata;
+ if (gralloc4::encodeDataspace(static_cast<Dataspace>(dataSpace), &metadata) == OK) {
+ Return<Error4> ret = mapper->set(buffer.get(), MetadataType_Dataspace, metadata);
+ if (!ret.isOk()) {
+ err = C2_REFUSED;
+ } else if (ret != Error4::NONE) {
+ err = C2_CORRUPTED;
+ }
+ }
+ }
if (staticInfo && *staticInfo) {
ALOGV("Setting static HDR info as gralloc4 metadata");
std::optional<Smpte2086> smpte2086 = Smpte2086{
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index b02b042..b73acab 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -410,14 +410,16 @@
std::shared_ptr<C2StreamHdrDynamicMetadataInfo::input> *dynamicInfo);
/**
- * Set HDR metadata to Gralloc4 handle.
+ * Set metadata to Gralloc4 handle.
*
+ * \param[in] dataSpace Dataspace to set.
* \param[in] staticInfo HDR static info to set. Ignored if null or invalid.
* \param[in] dynamicInfo HDR dynamic info to set. Ignored if null or invalid.
* \param[out] handle handle of the allocation.
* \return C2_OK if successful
*/
-c2_status_t SetHdrMetadataToGralloc4Handle(
+c2_status_t SetMetadataToGralloc4Handle(
+ const android_dataspace_t dataSpace,
const std::shared_ptr<const C2StreamHdrStaticMetadataInfo::output> &staticInfo,
const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo,
const C2Handle *const handle);
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 11724e0..4133bd0 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -3388,4 +3388,24 @@
enumToMask_index<int32_t, media::AudioDirectMode>);
}
+ConversionResult<audio_latency_mode_t>
+aidl2legacy_LatencyMode_audio_latency_mode_t(media::LatencyMode aidl) {
+ switch (aidl) {
+ case media::LatencyMode::FREE:
+ return AUDIO_LATENCY_MODE_FREE;
+ case media::LatencyMode::LOW:
+ return AUDIO_LATENCY_MODE_LOW;
+ }
+ return unexpected(BAD_VALUE);
+}
+ConversionResult<media::LatencyMode>
+legacy2aidl_audio_latency_mode_t_LatencyMode(audio_latency_mode_t legacy) {
+ switch (legacy) {
+ case AUDIO_LATENCY_MODE_FREE:
+ return media::LatencyMode::FREE;
+ case AUDIO_LATENCY_MODE_LOW:
+ return media::LatencyMode::LOW;
+ }
+ return unexpected(BAD_VALUE);
+}
} // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 69a9c68..dccef0e 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -342,6 +342,7 @@
"aidl/android/media/AudioUniqueIdUse.aidl",
"aidl/android/media/AudioVibratorInfo.aidl",
"aidl/android/media/EffectDescriptor.aidl",
+ "aidl/android/media/LatencyMode.aidl",
"aidl/android/media/TrackSecondaryOutputInfo.aidl",
],
imports: [
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index 7b273ec..dc21c52 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -540,7 +540,6 @@
if (cb != nullptr) {
cb->onError(mStatus);
}
- mIEffect.clear();
}
// -------------------------------------------------------------------------
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index de8c298..bacca3a 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -670,6 +670,30 @@
return Status::ok();
}
+Status AudioSystem::AudioFlingerClient::onSupportedLatencyModesChanged(
+ int output, const std::vector<media::LatencyMode>& latencyModes) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ std::vector<audio_latency_mode_t> modesLegacy = VALUE_OR_RETURN_BINDER_STATUS(
+ convertContainer<std::vector<audio_latency_mode_t>>(
+ latencyModes, aidl2legacy_LatencyMode_audio_latency_mode_t));
+
+ std::vector<sp<SupportedLatencyModesCallback>> callbacks;
+ {
+ Mutex::Autolock _l(mLock);
+ for (auto callback : mSupportedLatencyModesCallbacks) {
+ if (auto ref = callback.promote(); ref != nullptr) {
+ callbacks.push_back(ref);
+ }
+ }
+ }
+ for (const auto& callback : callbacks) {
+ callback->onSupportedLatencyModesChanged(outputLegacy, modesLegacy);
+ }
+
+ return Status::ok();
+}
+
status_t AudioSystem::AudioFlingerClient::getInputBufferSize(
uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask, size_t* buffSize) {
@@ -749,6 +773,31 @@
return NO_ERROR;
}
+status_t AudioSystem::AudioFlingerClient::addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ Mutex::Autolock _l(mLock);
+ if (std::find(mSupportedLatencyModesCallbacks.begin(),
+ mSupportedLatencyModesCallbacks.end(),
+ callback) != mSupportedLatencyModesCallbacks.end()) {
+ return INVALID_OPERATION;
+ }
+ mSupportedLatencyModesCallbacks.push_back(callback);
+ return NO_ERROR;
+}
+
+status_t AudioSystem::AudioFlingerClient::removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ Mutex::Autolock _l(mLock);
+ auto it = std::find(mSupportedLatencyModesCallbacks.begin(),
+ mSupportedLatencyModesCallbacks.end(),
+ callback);
+ if (it == mSupportedLatencyModesCallbacks.end()) {
+ return INVALID_OPERATION;
+ }
+ mSupportedLatencyModesCallbacks.erase(it);
+ return NO_ERROR;
+}
+
/* static */ uintptr_t AudioSystem::addErrorCallback(audio_error_callback cb) {
Mutex::Autolock _l(gLockErrorCallbacks);
gAudioErrorCallbacks.insert(cb);
@@ -1662,6 +1711,24 @@
return afc->removeAudioDeviceCallback(callback, audioIo, portId);
}
+status_t AudioSystem::addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc == 0) {
+ return NO_INIT;
+ }
+ return afc->addSupportedLatencyModesCallback(callback);
+}
+
+status_t AudioSystem::removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback) {
+ const sp<AudioFlingerClient> afc = getAudioFlingerClient();
+ if (afc == 0) {
+ return NO_INIT;
+ }
+ return afc->removeSupportedLatencyModesCallback(callback);
+}
+
audio_port_handle_t AudioSystem::getDeviceIdForIo(audio_io_handle_t audioIo) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
@@ -2338,6 +2405,24 @@
return NO_ERROR;
}
+status_t AudioSystem::setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) {
+ return PERMISSION_DENIED;
+ }
+ return af->setRequestedLatencyMode(output, mode);
+}
+
+status_t AudioSystem::getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes) {
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == nullptr) {
+ return PERMISSION_DENIED;
+ }
+ return af->getSupportedLatencyModes(output, modes);
+}
+
class CaptureStateListenerImpl : public media::BnCaptureStateListener,
public IBinder::DeathRecipient {
public:
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 292d92f..6ad97d1 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -810,6 +810,33 @@
return statusTFromBinderStatus(mDelegate->setDeviceConnectedState(aidlPort, connected));
}
+status_t AudioFlingerClientAdapter::setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) {
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ media::LatencyMode modeAidl = VALUE_OR_RETURN_STATUS(
+ legacy2aidl_audio_latency_mode_t_LatencyMode(mode));
+ return statusTFromBinderStatus(mDelegate->setRequestedLatencyMode(outputAidl, modeAidl));
+}
+
+status_t AudioFlingerClientAdapter::getSupportedLatencyModes(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>* modes) {
+ if (modes == nullptr) {
+ return BAD_VALUE;
+ }
+
+ int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output));
+ std::vector<media::LatencyMode> modesAidl;
+
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+ mDelegate->getSupportedLatencyModes(outputAidl, &modesAidl)));
+
+ *modes = VALUE_OR_RETURN_STATUS(
+ convertContainer<std::vector<audio_latency_mode_t>>(modesAidl,
+ aidl2legacy_LatencyMode_audio_latency_mode_t));
+
+ return NO_ERROR;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// AudioFlingerServerAdapter
AudioFlingerServerAdapter::AudioFlingerServerAdapter(
@@ -1304,4 +1331,28 @@
return Status::fromStatusT(mDelegate->setDeviceConnectedState(&portLegacy, connected));
}
+Status AudioFlingerServerAdapter::setRequestedLatencyMode(
+ int32_t output, media::LatencyMode modeAidl) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ audio_latency_mode_t modeLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_LatencyMode_audio_latency_mode_t(modeAidl));
+ return Status::fromStatusT(mDelegate->setRequestedLatencyMode(
+ outputLegacy, modeLegacy));
+}
+
+Status AudioFlingerServerAdapter::getSupportedLatencyModes(
+ int output, std::vector<media::LatencyMode>* _aidl_return) {
+ audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER(
+ aidl2legacy_int32_t_audio_io_handle_t(output));
+ std::vector<audio_latency_mode_t> modesLegacy;
+
+ RETURN_BINDER_IF_ERROR(mDelegate->getSupportedLatencyModes(outputLegacy, &modesLegacy));
+
+ *_aidl_return = VALUE_OR_RETURN_BINDER(
+ convertContainer<std::vector<media::LatencyMode>>(
+ modesLegacy, legacy2aidl_audio_latency_mode_t_LatencyMode));
+ return Status::ok();
+}
+
} // namespace android
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
index 421c31c..a2bb024 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerClient.aidl
@@ -18,6 +18,7 @@
import android.media.AudioIoConfigEvent;
import android.media.AudioIoDescriptor;
+import android.media.LatencyMode;
/**
* A callback interface for AudioFlinger.
@@ -27,4 +28,10 @@
interface IAudioFlingerClient {
oneway void ioConfigChanged(AudioIoConfigEvent event,
in AudioIoDescriptor ioDesc);
+ /**
+ * Called when the latency modes supported on a given output stream change.
+ * output is the I/O handle of the output stream for which the change is signalled.
+ * latencyModes is the new list of supported latency modes (See LatencyMode.aidl).
+ */
+ oneway void onSupportedLatencyModesChanged(int output, in LatencyMode[] latencyModes);
}
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index 10da028..9b8a843 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -35,6 +35,7 @@
import android.media.IAudioFlingerClient;
import android.media.IAudioRecord;
import android.media.IAudioTrack;
+import android.media.LatencyMode;
import android.media.MicrophoneInfoData;
import android.media.RenderPosition;
import android.media.TrackSecondaryOutputInfo;
@@ -228,6 +229,23 @@
void setDeviceConnectedState(in AudioPort devicePort, boolean connected);
+ /**
+ * Requests a given latency mode (See LatencyMode.aidl) on an output stream.
+ * This can be used when some use case on a given mixer/stream can only be enabled
+ * if a specific latency mode is selected on the audio path below the HAL.
+ * For instance spatial audio with head tracking.
+ * output is the I/O handle of the output stream for which the request is made.
+ * latencyMode is the requested latency mode.
+ */
+ void setRequestedLatencyMode(int output, LatencyMode latencyMode);
+
+ /**
+ * Queries the list of latency modes (See LatencyMode.aidl) supported by an output stream.
+ * output is the I/O handle of the output stream to which the query applies.
+ * returns the list of supported latency modes.
+ */
+ LatencyMode[] getSupportedLatencyModes(int output);
+
// When adding a new method, please review and update
// IAudioFlinger.h AudioFlingerServerAdapter::Delegate::TransactionCode
// AudioFlinger.cpp AudioFlinger::onTransactWrapper()
diff --git a/media/libaudioclient/aidl/android/media/LatencyMode.aidl b/media/libaudioclient/aidl/android/media/LatencyMode.aidl
new file mode 100644
index 0000000..0b2a72b
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/LatencyMode.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * The latency mode currently used by the spatializer mixer.
+ * {@hide}
+ */
+@Backing(type="byte")
+enum LatencyMode {
+ /** No specific constraint on the latency */
+ FREE = 0,
+ /** A relatively low latency compatible with head tracking operation (e.g less than 100ms) */
+ LOW = 1,
+}
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index e769303..1e66164 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -35,6 +35,7 @@
#include <android/media/AudioTimestampInternal.h>
#include <android/media/AudioUniqueIdUse.h>
#include <android/media/EffectDescriptor.h>
+#include <android/media/LatencyMode.h>
#include <android/media/TrackSecondaryOutputInfo.h>
#include <android/media/audio/common/AudioChannelLayout.h>
#include <android/media/audio/common/AudioConfig.h>
@@ -454,5 +455,9 @@
ConversionResult<audio_direct_mode_t> aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl);
ConversionResult<int32_t> legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy);
+ConversionResult<audio_latency_mode_t>
+aidl2legacy_LatencyMode_audio_latency_mode_t(media::LatencyMode aidl);
+ConversionResult<media::LatencyMode>
+legacy2aidl_audio_latency_mode_t_LatencyMode(audio_latency_mode_t legacy);
} // namespace android
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 360b83d..9411f46 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -569,6 +569,12 @@
static status_t getDirectProfilesForAttributes(const audio_attributes_t* attr,
std::vector<audio_profile>* audioProfiles);
+ static status_t setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode);
+
+ static status_t getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes);
+
// A listener for capture state changes.
class CaptureStateListener : public virtual RefBase {
public:
@@ -644,6 +650,22 @@
audio_io_handle_t audioIo,
audio_port_handle_t portId);
+ class SupportedLatencyModesCallback : public virtual RefBase
+ {
+ public:
+
+ SupportedLatencyModesCallback() = default;
+ virtual ~SupportedLatencyModesCallback() = default;
+
+ virtual void onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) = 0;
+ };
+
+ static status_t addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+ static status_t removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+
static audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
static status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
@@ -682,6 +704,9 @@
media::AudioIoConfigEvent event,
const media::AudioIoDescriptor& ioDesc) override;
+ binder::Status onSupportedLatencyModesChanged(
+ int output, const std::vector<media::LatencyMode>& latencyModes) override;
+
status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
audio_io_handle_t audioIo,
audio_port_handle_t portId);
@@ -689,6 +714,11 @@
audio_io_handle_t audioIo,
audio_port_handle_t portId);
+ status_t addSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+ status_t removeSupportedLatencyModesCallback(
+ const sp<SupportedLatencyModesCallback>& callback);
+
audio_port_handle_t getDeviceIdForIo(audio_io_handle_t audioIo);
private:
@@ -697,6 +727,10 @@
std::map<audio_io_handle_t, std::map<audio_port_handle_t, wp<AudioDeviceCallback>>>
mAudioDeviceCallbacks;
+
+ std::vector<wp<SupportedLatencyModesCallback>>
+ mSupportedLatencyModesCallbacks GUARDED_BY(mLock);
+
// cached values for recording getInputBufferSize() queries
size_t mInBuffSize; // zero indicates cache is invalid
uint32_t mInSamplingRate;
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 3c3715d..c891ae6 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -360,6 +360,13 @@
virtual int32_t getAAudioHardwareBurstMinUsec() = 0;
virtual status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) = 0;
+
+ virtual status_t setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) = 0;
+
+ virtual status_t getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes) = 0;
+
};
/**
@@ -462,6 +469,10 @@
int32_t getAAudioMixerBurstCount() override;
int32_t getAAudioHardwareBurstMinUsec() override;
status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) override;
+ status_t setRequestedLatencyMode(audio_io_handle_t output,
+ audio_latency_mode_t mode) override;
+ status_t getSupportedLatencyModes(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>* modes) override;
private:
const sp<media::IAudioFlingerService> mDelegate;
@@ -551,6 +562,8 @@
GET_AAUDIO_MIXER_BURST_COUNT = media::BnAudioFlingerService::TRANSACTION_getAAudioMixerBurstCount,
GET_AAUDIO_HARDWARE_BURST_MIN_USEC = media::BnAudioFlingerService::TRANSACTION_getAAudioHardwareBurstMinUsec,
SET_DEVICE_CONNECTED_STATE = media::BnAudioFlingerService::TRANSACTION_setDeviceConnectedState,
+ SET_REQUESTED_LATENCY_MODE = media::BnAudioFlingerService::TRANSACTION_setRequestedLatencyMode,
+ GET_SUPPORTED_LATENCY_MODES = media::BnAudioFlingerService::TRANSACTION_getSupportedLatencyModes,
};
protected:
@@ -672,7 +685,9 @@
Status getAAudioMixerBurstCount(int32_t* _aidl_return) override;
Status getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override;
Status setDeviceConnectedState(const media::AudioPort& port, bool connected) override;
-
+ Status setRequestedLatencyMode(int output, media::LatencyMode mode) override;
+ Status getSupportedLatencyModes(int output,
+ std::vector<media::LatencyMode>* _aidl_return) override;
private:
const sp<AudioFlingerServerAdapter::Delegate> mDelegate;
};
diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp
index fdfe225..8743c04 100644
--- a/media/libaudiohal/impl/EffectHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectHalHidl.cpp
@@ -93,13 +93,13 @@
}
status_t EffectHalHidl::process() {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS));
}
status_t EffectHalHidl::processReverse() {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS_REVERSE));
}
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
index d7217fc..3470380 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
@@ -17,6 +17,9 @@
#define LOG_TAG "EffectsFactoryHalHidl"
//#define LOG_NDEBUG 0
+#include <optional>
+#include <tuple>
+
#include <cutils/native_handle.h>
#include <UuidUtils.h>
@@ -38,51 +41,71 @@
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
+class EffectDescriptorCache {
+ public:
+ using QueryResult = std::tuple<Return<void>, Result, hidl_vec<EffectDescriptor>>;
+ QueryResult queryAllDescriptors(IEffectsFactory* effectsFactory);
+ private:
+ std::mutex mLock;
+ std::optional<hidl_vec<EffectDescriptor>> mLastDescriptors; // GUARDED_BY(mLock)
+};
+
+EffectDescriptorCache::QueryResult EffectDescriptorCache::queryAllDescriptors(
+ IEffectsFactory* effectsFactory) {
+ {
+ std::lock_guard l(mLock);
+ if (mLastDescriptors.has_value()) {
+ return {::android::hardware::Void(), Result::OK, mLastDescriptors.value()};
+ }
+ }
+ Result retval = Result::NOT_INITIALIZED;
+ hidl_vec<EffectDescriptor> descriptors;
+ Return<void> ret = effectsFactory->getAllDescriptors(
+ [&](Result r, const hidl_vec<EffectDescriptor>& result) {
+ retval = r;
+ if (retval == Result::OK) {
+ descriptors = result;
+ }
+ });
+ if (ret.isOk() && retval == Result::OK) {
+ std::lock_guard l(mLock);
+ mLastDescriptors = descriptors;
+ }
+ return {std::move(ret), retval, std::move(descriptors)};
+}
+
EffectsFactoryHalHidl::EffectsFactoryHalHidl(sp<IEffectsFactory> effectsFactory)
- : EffectConversionHelperHidl("EffectsFactory") {
+ : EffectConversionHelperHidl("EffectsFactory"), mCache(new EffectDescriptorCache) {
ALOG_ASSERT(effectsFactory != nullptr, "Provided IEffectsFactory service is NULL");
mEffectsFactory = effectsFactory;
}
-status_t EffectsFactoryHalHidl::queryAllDescriptors() {
- if (mEffectsFactory == 0) return NO_INIT;
- Result retval = Result::NOT_INITIALIZED;
- Return<void> ret = mEffectsFactory->getAllDescriptors(
- [&](Result r, const hidl_vec<EffectDescriptor>& result) {
- retval = r;
- if (retval == Result::OK) {
- mLastDescriptors = result;
- }
- });
- if (ret.isOk()) {
- return retval == Result::OK ? OK : NO_INIT;
- }
- mLastDescriptors.resize(0);
- return processReturn(__FUNCTION__, ret);
-}
-
status_t EffectsFactoryHalHidl::queryNumberEffects(uint32_t *pNumEffects) {
- status_t queryResult = queryAllDescriptors();
- if (queryResult == OK) {
- *pNumEffects = mLastDescriptors.size();
+ if (mEffectsFactory == 0) return NO_INIT;
+ auto [ret, retval, descriptors] = mCache->queryAllDescriptors(mEffectsFactory.get());
+ if (ret.isOk() && retval == Result::OK) {
+ *pNumEffects = descriptors.size();
+ return OK;
+ } else if (ret.isOk()) {
+ return NO_INIT;
}
- return queryResult;
+ return processReturn(__FUNCTION__, ret);
}
status_t EffectsFactoryHalHidl::getDescriptor(
uint32_t index, effect_descriptor_t *pDescriptor) {
- // TODO: We need somehow to track the changes on the server side
- // or figure out how to convert everybody to query all the descriptors at once.
if (pDescriptor == nullptr) {
return BAD_VALUE;
}
- if (mLastDescriptors.size() == 0) {
- status_t queryResult = queryAllDescriptors();
- if (queryResult != OK) return queryResult;
+ if (mEffectsFactory == 0) return NO_INIT;
+ auto [ret, retval, descriptors] = mCache->queryAllDescriptors(mEffectsFactory.get());
+ if (ret.isOk() && retval == Result::OK) {
+ if (index >= descriptors.size()) return NAME_NOT_FOUND;
+ EffectUtils::effectDescriptorToHal(descriptors[index], pDescriptor);
+ } else if (ret.isOk()) {
+ return NO_INIT;
}
- if (index >= mLastDescriptors.size()) return NAME_NOT_FOUND;
- EffectUtils::effectDescriptorToHal(mLastDescriptors[index], pDescriptor);
- return OK;
+ return processReturn(__FUNCTION__, ret);
}
status_t EffectsFactoryHalHidl::getDescriptor(
@@ -114,21 +137,15 @@
if (pEffectType == nullptr || descriptors == nullptr) {
return BAD_VALUE;
}
+ if (mEffectsFactory == 0) return NO_INIT;
- uint32_t numEffects = 0;
- status_t status = queryNumberEffects(&numEffects);
- if (status != NO_ERROR) {
- ALOGW("%s error %d from FactoryHal queryNumberEffects", __func__, status);
- return status;
+ auto [ret, retval, hidlDescs] = mCache->queryAllDescriptors(mEffectsFactory.get());
+ if (!ret.isOk() || retval != Result::OK) {
+ return processReturn(__FUNCTION__, ret, retval);
}
-
- for (uint32_t i = 0; i < numEffects; i++) {
+ for (const auto& hidlDesc : hidlDescs) {
effect_descriptor_t descriptor;
- status = getDescriptor(i, &descriptor);
- if (status != NO_ERROR) {
- ALOGW("%s error %d from FactoryHal getDescriptor", __func__, status);
- continue;
- }
+ EffectUtils::effectDescriptorToHal(hidlDesc, &descriptor);
if (memcmp(&descriptor.type, pEffectType, sizeof(effect_uuid_t)) == 0) {
descriptors->push_back(descriptor);
}
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
index e1882e1..7ccddc6 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
#define ANDROID_HARDWARE_EFFECTS_FACTORY_HAL_HIDL_H
+#include <memory>
+
#include PATH(android/hardware/audio/effect/FILE_VERSION/IEffectsFactory.h)
#include <media/audiohal/EffectsFactoryHalInterface.h>
@@ -28,6 +30,8 @@
using ::android::hardware::hidl_vec;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
+class EffectDescriptorCache;
+
class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public EffectConversionHelperHidl
{
public:
@@ -63,9 +67,7 @@
private:
sp<IEffectsFactory> mEffectsFactory;
- hidl_vec<EffectDescriptor> mLastDescriptors;
-
- status_t queryAllDescriptors();
+ std::unique_ptr<EffectDescriptorCache> mCache;
};
} // namespace effect
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 021ec51..76f9a60 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -441,7 +441,7 @@
#endif
status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
*written = 0;
@@ -587,7 +587,7 @@
}
status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
Result retval;
Return<void> ret = mStream->getRenderPosition(
@@ -668,7 +668,7 @@
}
status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
if (mWriterClient == gettid() && mCommandMQ) {
return callWriterThread(
@@ -1012,7 +1012,7 @@
}
status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
*read = 0;
@@ -1146,7 +1146,7 @@
}
status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) {
- TIME_CHECK();
+ // TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (mStream == 0) return NO_INIT;
if (mReaderClient == gettid() && mCommandMQ) {
ReadParameters params;
diff --git a/media/libheadtracking/Android.bp b/media/libheadtracking/Android.bp
index 9d63f9b..7e2c762 100644
--- a/media/libheadtracking/Android.bp
+++ b/media/libheadtracking/Android.bp
@@ -22,6 +22,10 @@
"StillnessDetector.cpp",
"Twist.cpp",
],
+ shared_libs: [
+ "libaudioutils",
+ "libbase",
+ ],
export_include_dirs: [
"include",
],
@@ -39,6 +43,7 @@
"SensorPoseProvider.cpp",
],
shared_libs: [
+ "libbase",
"libheadtracking",
"liblog",
"libsensor",
diff --git a/media/libheadtracking/HeadTrackingProcessor.cpp b/media/libheadtracking/HeadTrackingProcessor.cpp
index 71fae8a..ccb75af 100644
--- a/media/libheadtracking/HeadTrackingProcessor.cpp
+++ b/media/libheadtracking/HeadTrackingProcessor.cpp
@@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <inttypes.h>
+#include <android-base/stringprintf.h>
+#include <audio_utils/SimpleLog.h>
#include "media/HeadTrackingProcessor.h"
#include "ModeSelector.h"
@@ -26,6 +29,7 @@
namespace media {
namespace {
+using android::base::StringAppendF;
using Eigen::Quaternionf;
using Eigen::Vector3f;
@@ -84,10 +88,12 @@
}
void calculate(int64_t timestamp) override {
- // Handle the screen first, since it might trigger a recentering of the head.
+ bool screenStable = true;
+
+ // Handle the screen first, since it might: trigger a recentering of the head.
if (mWorldToScreenTimestamp.has_value()) {
const Pose3f worldToLogicalScreen = mScreenPoseBias.getOutput();
- bool screenStable = mScreenStillnessDetector.calculate(timestamp);
+ screenStable = mScreenStillnessDetector.calculate(timestamp);
mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
// Whenever the screen is unstable, recenter the head pose.
if (!screenStable) {
@@ -101,7 +107,8 @@
if (mWorldToHeadTimestamp.has_value()) {
Pose3f worldToHead = mHeadPoseBias.getOutput();
// Auto-recenter.
- if (mHeadStillnessDetector.calculate(timestamp)) {
+ bool headStable = mHeadStillnessDetector.calculate(timestamp);
+ if (headStable || !screenStable) {
recenter(true, false);
worldToHead = mHeadPoseBias.getOutput();
}
@@ -136,10 +143,12 @@
if (recenterHead) {
mHeadPoseBias.recenter();
mHeadStillnessDetector.reset();
+ mLocalLog.log("recenter Head");
}
if (recenterScreen) {
mScreenPoseBias.recenter();
mScreenStillnessDetector.reset();
+ mLocalLog.log("recenter Screen");
}
// If a sensor being recentered is included in the current mode, apply rate limiting to
@@ -152,6 +161,35 @@
}
}
+ std::string toString_l(unsigned level) const override {
+ std::string prefixSpace(level, ' ');
+ std::string ss = prefixSpace + "HeadTrackingProcessor:\n";
+ StringAppendF(&ss, "%smaxTranslationalVelocity: %f\n", prefixSpace.c_str(),
+ mOptions.maxTranslationalVelocity);
+ StringAppendF(&ss, "%smaxRotationalVelocity: %f\n", prefixSpace.c_str(),
+ mOptions.maxRotationalVelocity);
+ StringAppendF(&ss, "%sfreshnessTimeout: %" PRId64 "\n", prefixSpace.c_str(),
+ mOptions.freshnessTimeout);
+ StringAppendF(&ss, "%spredictionDuration: %f\n", prefixSpace.c_str(),
+ mOptions.predictionDuration);
+ StringAppendF(&ss, "%sautoRecenterWindowDuration: %" PRId64 "\n", prefixSpace.c_str(),
+ mOptions.autoRecenterWindowDuration);
+ StringAppendF(&ss, "%sautoRecenterTranslationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.autoRecenterTranslationalThreshold);
+ StringAppendF(&ss, "%sautoRecenterRotationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.autoRecenterRotationalThreshold);
+ StringAppendF(&ss, "%sscreenStillnessWindowDuration: %" PRId64 "\n", prefixSpace.c_str(),
+ mOptions.screenStillnessWindowDuration);
+ StringAppendF(&ss, "%sscreenStillnessTranslationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.screenStillnessTranslationalThreshold);
+ StringAppendF(&ss, "%sscreenStillnessRotationalThreshold: %f\n", prefixSpace.c_str(),
+ mOptions.screenStillnessRotationalThreshold);
+ ss.append(prefixSpace + "ReCenterHistory:\n");
+ ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
+ // TODO: 233092747 add string from PoseRateLimiter/PoseRateLimiter etc...
+ return ss;
+ }
+
private:
const Options mOptions;
float mPhysicalToLogicalAngle = 0;
@@ -168,6 +206,8 @@
ScreenHeadFusion mScreenHeadFusion;
ModeSelector mModeSelector;
PoseRateLimiter mRateLimiter;
+ static constexpr std::size_t mMaxLocalLogLine = 10;
+ SimpleLog mLocalLog{mMaxLocalLogLine};
};
} // namespace
diff --git a/media/libheadtracking/SensorPoseProvider.cpp b/media/libheadtracking/SensorPoseProvider.cpp
index 8ebaf6e..6c0a96d 100644
--- a/media/libheadtracking/SensorPoseProvider.cpp
+++ b/media/libheadtracking/SensorPoseProvider.cpp
@@ -24,6 +24,7 @@
#include <map>
#include <thread>
+#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <log/log_main.h>
#include <sensor/SensorEventQueue.h>
@@ -36,6 +37,8 @@
namespace media {
namespace {
+using android::base::StringAppendF;
+
// Identifier to use for our event queue on the loop.
// The number 19 is arbitrary, only useful if using multiple objects on the same looper.
constexpr int kIdent = 19;
@@ -153,6 +156,38 @@
mEnabledSensorsExtra.erase(handle);
}
+ std::string toString(unsigned level) override {
+ std::string prefixSpace(level, ' ');
+ std::string ss = prefixSpace + "SensorPoseProvider:\n";
+ bool needUnlock = false;
+
+ prefixSpace += " ";
+ auto now = std::chrono::steady_clock::now();
+ if (!mMutex.try_lock_until(now + media::kSpatializerDumpSysTimeOutInSecond)) {
+ ss.append(prefixSpace).append("try_lock failed, dumpsys below maybe INACCURATE!\n");
+ } else {
+ needUnlock = true;
+ }
+
+ // Enabled sensor information
+ StringAppendF(&ss, "%sSensors total number %zu:\n", prefixSpace.c_str(),
+ mEnabledSensorsExtra.size());
+ for (auto sensor : mEnabledSensorsExtra) {
+ StringAppendF(&ss, "%s[Handle: 0x%08x, Format %s", prefixSpace.c_str(), sensor.first,
+ toString(sensor.second.format).c_str());
+ if (sensor.second.discontinuityCount.has_value()) {
+ StringAppendF(&ss, ", DiscontinuityCount: %d",
+ sensor.second.discontinuityCount.value());
+ }
+ ss += "]\n";
+ }
+
+ if (needUnlock) {
+ mMutex.unlock();
+ }
+ return ss;
+ }
+
private:
enum DataFormat {
kUnknown,
@@ -174,8 +209,7 @@
sp<Looper> mLooper;
Listener* const mListener;
SensorManager* const mSensorManager;
- std::thread mThread;
- std::mutex mMutex;
+ std::timed_mutex mMutex;
std::map<int32_t, SensorEnableGuard> mEnabledSensors;
std::map<int32_t, SensorExtra> mEnabledSensorsExtra GUARDED_BY(mMutex);
sp<SensorEventQueue> mQueue;
@@ -187,12 +221,13 @@
// the worker thread and that thread would notify, via the promise below whenever initialization
// is finished, and whether it was successful.
std::promise<bool> mInitPromise;
+ std::thread mThread;
SensorPoseProviderImpl(const char* packageName, Listener* listener)
: mListener(listener),
- mSensorManager(&SensorManager::getInstanceForPackage(String16(packageName))),
- mThread([this] { threadFunc(); }) {}
-
+ mSensorManager(&SensorManager::getInstanceForPackage(String16(packageName))) {
+ mThread = std::thread([this] { threadFunc(); });
+ }
void initFinished(bool success) { mInitPromise.set_value(success); }
bool waitInitFinished() { return mInitPromise.get_future().get(); }
@@ -345,6 +380,19 @@
LOG_ALWAYS_FATAL("Unexpected sensor type: %d", static_cast<int>(format));
}
}
+
+ const std::string toString(DataFormat format) {
+ switch (format) {
+ case DataFormat::kUnknown:
+ return "kUnknown";
+ case DataFormat::kQuaternion:
+ return "kQuaternion";
+ case DataFormat::kRotationVectorsAndDiscontinuityCount:
+ return "kRotationVectorsAndDiscontinuityCount";
+ default:
+ return "NotImplemented";
+ }
+ }
};
} // namespace
diff --git a/media/libheadtracking/StillnessDetector.cpp b/media/libheadtracking/StillnessDetector.cpp
index be7c893..5232084 100644
--- a/media/libheadtracking/StillnessDetector.cpp
+++ b/media/libheadtracking/StillnessDetector.cpp
@@ -26,6 +26,9 @@
mFifo.clear();
mWindowFull = false;
mSuppressionDeadline.reset();
+ // A "true" state indicates stillness is detected (default = true)
+ mCurrentState = true;
+ mPreviousState = true;
}
void StillnessDetector::setInput(int64_t timestamp, const Pose3f& input) {
@@ -33,7 +36,15 @@
discardOld(timestamp);
}
+bool StillnessDetector::getPreviousState() const {
+ return mPreviousState;
+}
+
bool StillnessDetector::calculate(int64_t timestamp) {
+ // Move the current stillness state to the previous state.
+ // This allows us to detect transitions into and out of stillness.
+ mPreviousState = mCurrentState;
+
discardOld(timestamp);
// Check whether all the poses in the queue are in the proximity of the new one. We want to do
@@ -60,15 +71,17 @@
// If the window has not been full, return the default value.
if (!mWindowFull) {
- return mOptions.defaultValue;
+ mCurrentState = mOptions.defaultValue;
}
-
// Force "in motion" while the suppression deadline is active.
- if (mSuppressionDeadline.has_value()) {
- return false;
+ else if (mSuppressionDeadline.has_value()) {
+ mCurrentState = false;
+ }
+ else {
+ mCurrentState = !moved;
}
- return !moved;
+ return mCurrentState;
}
void StillnessDetector::discardOld(int64_t timestamp) {
diff --git a/media/libheadtracking/StillnessDetector.h b/media/libheadtracking/StillnessDetector.h
index ee4b2d8..abd7cc4 100644
--- a/media/libheadtracking/StillnessDetector.h
+++ b/media/libheadtracking/StillnessDetector.h
@@ -80,7 +80,8 @@
void setInput(int64_t timestamp, const Pose3f& input);
/** Calculate whether the stream is still at the given timestamp. */
bool calculate(int64_t timestamp);
-
+ /** Return the stillness state from the previous call to calculate() */
+ bool getPreviousState() const;
private:
struct TimestampedPose {
int64_t timestamp;
@@ -92,6 +93,8 @@
const float mCosHalfRotationalThreshold;
std::deque<TimestampedPose> mFifo;
bool mWindowFull = false;
+ bool mCurrentState = true;
+ bool mPreviousState = true;
// As soon as motion is detected, this will be set for the time of detection + window duration,
// and during this time we will always consider outselves in motion without checking. This is
// used for hyteresis purposes, since because of the approximate method we use for determining
diff --git a/media/libheadtracking/include/media/HeadTrackingProcessor.h b/media/libheadtracking/include/media/HeadTrackingProcessor.h
index 1744be3..8ef8ab0 100644
--- a/media/libheadtracking/include/media/HeadTrackingProcessor.h
+++ b/media/libheadtracking/include/media/HeadTrackingProcessor.h
@@ -96,8 +96,12 @@
* This causes the current poses for both the head and/or screen to be considered "center".
*/
virtual void recenter(bool recenterHead = true, bool recenterScreen = true) = 0;
-};
+ /**
+ * Dump HeadTrackingProcessor parameters under caller lock.
+ */
+ virtual std::string toString_l(unsigned level) const = 0;
+};
/**
* Creates an instance featuring a default implementation of the HeadTrackingProcessor interface.
*/
diff --git a/media/libheadtracking/include/media/SensorPoseProvider.h b/media/libheadtracking/include/media/SensorPoseProvider.h
index 0f42074..4609e0c 100644
--- a/media/libheadtracking/include/media/SensorPoseProvider.h
+++ b/media/libheadtracking/include/media/SensorPoseProvider.h
@@ -28,6 +28,9 @@
namespace android {
namespace media {
+// Timeout for Spatializer dumpsys trylock, don't block for more than 3 seconds.
+constexpr auto kSpatializerDumpSysTimeOutInSecond = std::chrono::seconds(3);
+
/**
* A utility providing streaming of pose data from motion sensors provided by the Sensor Framework.
*
@@ -100,6 +103,11 @@
* discover properties of the sensor.
*/
virtual std::optional<const Sensor> getSensorByHandle(int32_t handle) = 0;
+
+ /**
+ * Dump SensorPoseProvider parameters and history data.
+ */
+ virtual std::string toString(unsigned level) = 0;
};
} // namespace media
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index 1c30510..bebd382 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -57,6 +57,10 @@
// The Audio Spatializer key appends the spatializerId (currently 0)
#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER AMEDIAMETRICS_KEY_PREFIX_AUDIO "spatializer."
+// The Audio Spatializer device key appends the device type.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER_DEVICE \
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER "device."
+
// The AudioStream key appends the "streamId" to the prefix.
#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM AMEDIAMETRICS_KEY_PREFIX_AUDIO "stream."
@@ -107,6 +111,7 @@
// of suppressed in the Time Machine.
#define AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED '#'
+#define AMEDIAMETRICS_PROP_ADDRESS "address" // string, for example MAC address
#define AMEDIAMETRICS_PROP_ALLOWUID "_allowUid" // int32_t, allow client uid to post
#define AMEDIAMETRICS_PROP_AUDIOMODE "audioMode" // string (audio.flinger)
#define AMEDIAMETRICS_PROP_AUXEFFECTID "auxEffectId" // int32 (AudioTrack)
@@ -116,6 +121,8 @@
#define AMEDIAMETRICS_PROP_CALLERNAME "callerName" // string, eg. "aaudio"
#define AMEDIAMETRICS_PROP_CHANNELCOUNT "channelCount" // int32
#define AMEDIAMETRICS_PROP_CHANNELMASK "channelMask" // int32
+#define AMEDIAMETRICS_PROP_CHANNELMASKS "channelMasks" // string with channelMask values
+ // separated by |.
#define AMEDIAMETRICS_PROP_CONTENTTYPE "contentType" // string attributes (AudioTrack)
#define AMEDIAMETRICS_PROP_CUMULATIVETIMENS "cumulativeTimeNs" // int64_t playback/record time
// since start
@@ -132,6 +139,7 @@
#define AMEDIAMETRICS_PROP_DIRECTION "direction" // string AAudio input or output
#define AMEDIAMETRICS_PROP_DURATIONNS "durationNs" // int64 duration time span
+#define AMEDIAMETRICS_PROP_ENABLED "enabled" // string true/false.
#define AMEDIAMETRICS_PROP_ENCODING "encoding" // string value of format
#define AMEDIAMETRICS_PROP_EVENT "event#" // string value (often func name)
@@ -141,6 +149,8 @@
#define AMEDIAMETRICS_PROP_FLAGS "flags"
#define AMEDIAMETRICS_PROP_FRAMECOUNT "frameCount" // int32
+#define AMEDIAMETRICS_PROP_HASHEADTRACKER "hasHeadTracker" // string true/false
+#define AMEDIAMETRICS_PROP_HEADTRACKERENABLED "headTrackerEnabled" // string true/false
#define AMEDIAMETRICS_PROP_HEADTRACKINGMODES "headTrackingModes" // string |, like modes.
#define AMEDIAMETRICS_PROP_INPUTDEVICES "inputDevices" // string value
#define AMEDIAMETRICS_PROP_INTERNALTRACKID "internalTrackId" // int32
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index ea1fdf4..a0bc8ca 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1966,6 +1966,10 @@
format->setString("mime", MEDIA_MIMETYPE_VIDEO_HEVC);
break;
+ case VIDEO_ENCODER_DOLBY_VISION:
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
+ break;
+
default:
CHECK(!"Should not be here, unsupported video encoding.");
break;
diff --git a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
index c3bd207..25a8ae4 100644
--- a/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
+++ b/media/libmediaplayerservice/nuplayer/AWakeLock.cpp
@@ -59,11 +59,10 @@
if (mPowerManager != NULL) {
sp<IBinder> binder = new BBinder();
int64_t token = IPCThreadState::self()->clearCallingIdentity();
- binder::Status status = mPowerManager->acquireWakeLock(
+ binder::Status status = mPowerManager->acquireWakeLockAsync(
binder, POWERMANAGER_PARTIAL_WAKE_LOCK,
String16("AWakeLock"), String16("media"),
- {} /* workSource */, {} /* historyTag */, -1 /* displayId */,
- nullptr /* callback */);
+ {} /* workSource */, {} /* historyTag */);
IPCThreadState::self()->restoreCallingIdentity(token);
if (status.isOk()) {
mWakeLockToken = binder;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
index 6788b56..5e29b3f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
@@ -191,8 +191,8 @@
uint8_t key[kBlockSize],
uint8_t iv[kBlockSize],
CryptoPlugin::Mode mode,
- size_t *clearbytes,
- size_t *encryptedbytes)
+ uint32_t *clearbytes,
+ uint32_t *encryptedbytes)
{
// size needed to store all the crypto data
size_t cryptosize;
@@ -236,7 +236,7 @@
if (!meta.findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
return NULL;
}
- size_t numSubSamples = cryptedsize / sizeof(size_t);
+ size_t numSubSamples = cryptedsize / sizeof(uint32_t);
if (numSubSamples <= 0) {
ALOGE("getSampleCryptoInfo INVALID numSubSamples: %zu", numSubSamples);
@@ -285,8 +285,8 @@
(uint8_t*) key,
(uint8_t*) iv,
(CryptoPlugin::Mode)mode,
- (size_t*) cleardata,
- (size_t*) crypteddata);
+ (uint32_t*) cleardata,
+ (uint32_t*) crypteddata);
}
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 45fac76..0382df3 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1947,22 +1947,12 @@
CHECK(format->findInt32("channel-count", &numChannels));
// channel mask info as read from the audio format
- int32_t channelMaskFromFormat;
+ int32_t mediaFormatChannelMask;
// channel mask to use for native playback
audio_channel_mask_t channelMask;
- if (format->findInt32("channel-mask", &channelMaskFromFormat)) {
+ if (format->findInt32("channel-mask", &mediaFormatChannelMask)) {
// KEY_CHANNEL_MASK follows the android.media.AudioFormat java mask
- // which is left-bitshifted by 2 relative to the native mask
- if ((channelMaskFromFormat & 0b11) != 0) {
- // received an unexpected mask (supposed to follow AudioFormat constants
- // for output masks with the 2 least-significant bits at 0), but
- // it may come from an extractor that uses native masks: keeping
- // the mask as given is ok as it contains at least mono or stereo
- // and potentially the haptic channels
- channelMask = static_cast<audio_channel_mask_t>(channelMaskFromFormat);
- } else {
- channelMask = static_cast<audio_channel_mask_t>(channelMaskFromFormat >> 2);
- }
+ channelMask = audio_channel_mask_from_media_format_mask(mediaFormatChannelMask);
} else {
// no mask found: the mask will be derived from the channel count
channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
diff --git a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerDrm.h b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerDrm.h
index 4360656..232638c 100644
--- a/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerDrm.h
+++ b/media/libmediaplayerservice/nuplayer/include/nuplayer/NuPlayerDrm.h
@@ -106,8 +106,8 @@
uint8_t key[kBlockSize],
uint8_t iv[kBlockSize],
CryptoPlugin::Mode mode,
- size_t *clearbytes,
- size_t *encryptedbytes);
+ uint32_t *clearbytes,
+ uint32_t *encryptedbytes);
static CryptoInfo *getSampleCryptoInfo(MetaDataBase &meta);
diff --git a/media/libstagefright/HevcUtils.cpp b/media/libstagefright/HevcUtils.cpp
index 5f9c20e..60df162 100644
--- a/media/libstagefright/HevcUtils.cpp
+++ b/media/libstagefright/HevcUtils.cpp
@@ -102,10 +102,11 @@
static bool findParam(uint32_t key, T *param,
KeyedVector<uint32_t, uint64_t> ¶ms) {
CHECK(param);
- if (params.indexOfKey(key) < 0) {
+ ssize_t index = params.indexOfKey(key);
+ if (index < 0) {
return false;
}
- *param = (T) params[key];
+ *param = (T) params[index];
return true;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index e50880a..c963e19 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -3534,6 +3534,17 @@
}
setState(STARTED);
postPendingRepliesAndDeferredMessages("kWhatStartCompleted");
+
+ // Now that the codec has started, configure, by default, the peek behavior to
+ // be undefined for backwards compatibility with older releases. Later, if an
+ // app explicitly enables or disables peek, the parameter will be turned off and
+ // the legacy undefined behavior is disallowed.
+ // See updateTunnelPeek called in onSetParameters for more details.
+ if (mTunneled && mTunnelPeekState == TunnelPeekState::kLegacyMode) {
+ sp<AMessage> params = new AMessage;
+ params->setInt32("android._tunnel-peek-set-legacy", 1);
+ mCodec->signalSetParameters(params);
+ }
break;
}
@@ -3973,14 +3984,6 @@
mTunneled = false;
}
- // If mTunnelPeekState is still in kLegacyMode at this point,
- // configure the codec in legacy mode
- if (mTunneled && (mTunnelPeekState == TunnelPeekState::kLegacyMode)) {
- sp<AMessage> params = new AMessage;
- params->setInt32("android._tunnel-peek-set-legacy", 1);
- onSetParameters(params);
- }
-
int32_t background = 0;
if (format->findInt32("android._background-mode", &background) && background) {
androidSetThreadPriority(gettid(), ANDROID_PRIORITY_BACKGROUND);
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 38e422d..ed31c02 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <charconv>
#include <inttypes.h>
#include <mutex>
#include <set>
@@ -324,29 +325,61 @@
}
AMessage::Type type;
- int64_t mediaTimeUs, systemNano;
- size_t index = 0;
+ size_t n = data->countEntries();
- // TODO. This code has dependency with MediaCodec::CreateFramesRenderedMessage.
- for (size_t ix = 0; ix < data->countEntries(); ix++) {
- AString name = data->getEntryNameAt(ix, &type);
- if (name.startsWith(AStringPrintf("%zu-media-time-us", index).c_str())) {
- AMessage::ItemData data = msg->getEntryAt(index);
- data.find(&mediaTimeUs);
- } else if (name.startsWith(AStringPrintf("%zu-system-nano", index).c_str())) {
- AMessage::ItemData data = msg->getEntryAt(index);
- data.find(&systemNano);
+ thread_local std::vector<std::optional<int64_t>> mediaTimesInUs;
+ thread_local std::vector<std::optional<int64_t>> systemTimesInNs;
+ mediaTimesInUs.resize(n);
+ systemTimesInNs.resize(n);
+ std::fill_n(mediaTimesInUs.begin(), n, std::nullopt);
+ std::fill_n(systemTimesInNs.begin(), n, std::nullopt);
+ for (size_t i = 0; i < n; i++) {
+ AString name = data->getEntryNameAt(i, &type);
+ if (name.endsWith("-media-time-us")) {
+ int64_t mediaTimeUs;
+ AMessage::ItemData itemData = data->getEntryAt(i);
+ itemData.find(&mediaTimeUs);
- Mutex::Autolock _l(mCodec->mFrameRenderedCallbackLock);
- if (mCodec->mFrameRenderedCallback != NULL) {
+ int index = -1;
+ std::from_chars_result result = std::from_chars(
+ name.c_str(), name.c_str() + name.find("-"), index);
+ if (result.ec == std::errc() && 0 <= index && index < n) {
+ mediaTimesInUs[index] = mediaTimeUs;
+ } else {
+ std::error_code ec = std::make_error_code(result.ec);
+ ALOGE("Unexpected media time index: #%d with value %lldus (err=%d %s)",
+ index, (long long)mediaTimeUs, ec.value(), ec.message().c_str());
+ }
+ } else if (name.endsWith("-system-nano")) {
+ int64_t systemNano;
+ AMessage::ItemData itemData = data->getEntryAt(i);
+ itemData.find(&systemNano);
+
+ int index = -1;
+ std::from_chars_result result = std::from_chars(
+ name.c_str(), name.c_str() + name.find("-"), index);
+ if (result.ec == std::errc() && 0 <= index && index < n) {
+ systemTimesInNs[index] = systemNano;
+ } else {
+ std::error_code ec = std::make_error_code(result.ec);
+ ALOGE("Unexpected system time index: #%d with value %lldns (err=%d %s)",
+ index, (long long)systemNano, ec.value(), ec.message().c_str());
+ }
+ }
+ }
+
+ Mutex::Autolock _l(mCodec->mFrameRenderedCallbackLock);
+ if (mCodec->mFrameRenderedCallback != NULL) {
+ for (size_t i = 0; i < n; ++i) {
+ if (mediaTimesInUs[i] && systemTimesInNs[i]) {
mCodec->mFrameRenderedCallback(
mCodec,
mCodec->mFrameRenderedCallbackUserData,
- mediaTimeUs,
- systemNano);
+ mediaTimesInUs[i].value(),
+ systemTimesInNs[i].value());
+ } else {
+ break;
}
-
- index++;
}
}
break;
@@ -529,22 +562,31 @@
AMediaCodecOnAsyncNotifyCallback callback,
void *userdata) {
- Mutex::Autolock _l(mData->mAsyncCallbackLock);
-
- if (mData->mAsyncNotify == NULL) {
- mData->mAsyncNotify = new AMessage(kWhatAsyncNotify, mData->mHandler);
+ {
+ Mutex::Autolock _l(mData->mAsyncCallbackLock);
+ if (mData->mAsyncNotify == NULL) {
+ mData->mAsyncNotify = new AMessage(kWhatAsyncNotify, mData->mHandler);
+ }
+ // we set this ahead so that we can be ready
+ // to receive callbacks as soon as the next call is a
+ // success.
+ mData->mAsyncCallback = callback;
+ mData->mAsyncCallbackUserData = userdata;
}
// always call, codec may have been reset/re-configured since last call.
status_t err = mData->mCodec->setCallback(mData->mAsyncNotify);
if (err != OK) {
+ {
+ //The setup gone wrong. clean up the pointers.
+ Mutex::Autolock _l(mData->mAsyncCallbackLock);
+ mData->mAsyncCallback = {};
+ mData->mAsyncCallbackUserData = nullptr;
+ }
ALOGE("setAsyncNotifyCallback: err(%d), failed to set async callback", err);
return translate_error(err);
}
- mData->mAsyncCallback = callback;
- mData->mAsyncCallbackUserData = userdata;
-
return AMEDIA_OK;
}
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 59c1103..f4674de 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -183,7 +183,7 @@
AMediaDrmSessionId asid = {sessionId.data(), sessionId.size()};
int32_t dataSize = data.size();
const uint8_t *dataPtr = data.data();
- if (dataSize > 0) {
+ if (dataSize >= 0) {
(*mEventListener)(mObj, &asid, ndkEventType, 0, dataPtr, dataSize);
} else {
ALOGE("invalid event data size=%d", dataSize);
diff --git a/media/utils/MethodStatistics.cpp b/media/utils/MethodStatistics.cpp
index b179b20..086757b 100644
--- a/media/utils/MethodStatistics.cpp
+++ b/media/utils/MethodStatistics.cpp
@@ -22,7 +22,8 @@
std::shared_ptr<std::vector<std::string>>
getStatisticsClassesForModule(std::string_view moduleName) {
- static const std::map<std::string, std::shared_ptr<std::vector<std::string>>> m {
+ static const std::map<std::string, std::shared_ptr<std::vector<std::string>>,
+ std::less<> /* transparent comparator */> m {
{
METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL,
std::shared_ptr<std::vector<std::string>>(
@@ -34,13 +35,14 @@
})
},
};
- auto it = m.find({moduleName.begin(), moduleName.end()});
+ auto it = m.find(moduleName);
if (it == m.end()) return {};
return it->second;
}
static void addClassesToMap(const std::shared_ptr<std::vector<std::string>> &classNames,
- std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> &map) {
+ std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
+ std::less<> /* transparent comparator */> &map) {
if (classNames) {
for (const auto& className : *classNames) {
map.emplace(className, std::make_shared<MethodStatistics<std::string>>());
@@ -51,17 +53,18 @@
// singleton statistics for DeviceHalHidl StreamOutHalHidl StreamInHalHidl
std::shared_ptr<MethodStatistics<std::string>>
getStatisticsForClass(std::string_view className) {
- static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m =
+ static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>,
+ std::less<> /* transparent comparator */> m =
// copy elided initialization of map m.
[](){
- std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m;
+ std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>, std::less<>> m;
addClassesToMap(
getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL),
m);
return m;
}();
- auto it = m.find({className.begin(), className.end()});
+ auto it = m.find(className);
if (it == m.end()) return {};
return it->second;
}
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 0848eb3..e53d70f 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -21,6 +21,7 @@
#include <android-base/logging.h>
#include <audio_utils/clock.h>
#include <mediautils/EventLog.h>
+#include <mediautils/FixedString.h>
#include <mediautils/MethodStatistics.h>
#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
@@ -138,11 +139,11 @@
return getTimeCheckThread().toString();
}
-TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
+TimeCheck::TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
bool crashOnTimeout)
- : mTimeCheckHandler(new TimeCheckHandler{
- std::move(tag), std::move(onTimer), crashOnTimeout,
- std::chrono::system_clock::now(), gettid()})
+ : mTimeCheckHandler{ std::make_shared<TimeCheckHandler>(
+ tag, std::move(onTimer), crashOnTimeout,
+ std::chrono::system_clock::now(), gettid()) }
, mTimerHandle(timeoutMs == 0
? getTimeCheckThread().trackTask(mTimeCheckHandler->tag)
: getTimeCheckThread().scheduleTask(
@@ -231,14 +232,14 @@
mediautils::getStatisticsForClass(className);
if (!statistics) return {}; // empty TimeCheck.
return mediautils::TimeCheck(
- std::string(className).append("::").append(methodName),
- [ clazz = std::string(className), method = std::string(methodName),
+ FixedString62(className).append("::").append(methodName),
+ [ safeMethodName = FixedString30(methodName),
stats = std::move(statistics) ]
(bool timeout, float elapsedMs) {
if (timeout) {
; // ignored, there is no timeout value.
} else {
- stats->event(method, elapsedMs);
+ stats->event(safeMethodName.asStringView(), elapsedMs);
}
}, 0 /* timeoutMs */);
}
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
index 6de6b13..09783ed 100644
--- a/media/utils/TimerThread.cpp
+++ b/media/utils/TimerThread.cpp
@@ -31,17 +31,15 @@
extern std::string_view timeSuffix(std::string_view time1, std::string_view time2);
TimerThread::Handle TimerThread::scheduleTask(
- std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
+ std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
const auto now = std::chrono::system_clock::now();
- std::shared_ptr<const Request> request{
- new Request{ now, now + timeout, gettid(), std::move(tag) }};
+ auto request = std::make_shared<const Request>(now, now + timeout, gettid(), tag);
return mMonitorThread.add(std::move(request), std::move(func), timeout);
}
-TimerThread::Handle TimerThread::trackTask(std::string tag) {
+TimerThread::Handle TimerThread::trackTask(std::string_view tag) {
const auto now = std::chrono::system_clock::now();
- std::shared_ptr<const Request> request{
- new Request{ now, now, gettid(), std::move(tag) }};
+ auto request = std::make_shared<const Request>(now, now, gettid(), tag);
return mNoTimeoutMap.add(std::move(request));
}
@@ -106,10 +104,10 @@
//
/* static */
bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) {
- const size_t hidlPos = request->tag.find("Hidl");
+ const size_t hidlPos = request->tag.asStringView().find("Hidl");
if (hidlPos == std::string::npos) return false;
// should be a separator afterwards Hidl which indicates the string was in the class.
- const size_t separatorPos = request->tag.find("::", hidlPos);
+ const size_t separatorPos = request->tag.asStringView().find("::", hidlPos);
return separatorPos != std::string::npos;
}
diff --git a/media/utils/include/mediautils/FixedString.h b/media/utils/include/mediautils/FixedString.h
new file mode 100644
index 0000000..047aa82
--- /dev/null
+++ b/media/utils/include/mediautils/FixedString.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <string>
+#include <string_view>
+
+namespace android::mediautils {
+
+/*
+ * FixedString is a stack allocatable string buffer that supports
+ * simple appending of other strings and string_views.
+ *
+ * It is designed for no-malloc operation when std::string
+ * small buffer optimization is insufficient.
+ *
+ * To keep code small, use asStringView() for operations on this.
+ *
+ * Notes:
+ * 1) Appending beyond the internal buffer size results in truncation.
+ *
+ * Alternatives:
+ * 1) If you want a sharable copy-on-write string implementation,
+ * consider using the legacy android::String8().
+ * 2) Using std::string with a fixed stack allocator may suit your needs,
+ * but exception avoidance is tricky.
+ * 3) Using C++20 ranges https://en.cppreference.com/w/cpp/ranges if you don't
+ * need backing store. Be careful about allocation with ranges.
+ *
+ * Good small sizes are multiples of 16 minus 2, e.g. 14, 30, 46, 62.
+ *
+ * Implementation Notes:
+ * 1) No iterators or [] for FixedString - please convert to std::string_view.
+ * 2) For small N (e.g. less than 62), consider a change to always zero fill and
+ * potentially prevent always zero terminating (if one only does append).
+ *
+ * Possible arguments to create/append:
+ * 1) A FixedString.
+ * 2) A single char.
+ * 3) A char * pointer.
+ * 4) A std::string.
+ * 5) A std::string_view (or something convertible to it).
+ *
+ * Example:
+ *
+ * FixedString s1(std::string("a")); // ctor
+ * s1 << "bc" << 'd' << '\n'; // streaming append
+ * s1 += "hello"; // += append
+ * ASSERT_EQ(s1, "abcd\nhello");
+ */
+template <uint32_t N>
+struct FixedString
+{
+ // Find the best size type.
+ using strsize_t = std::conditional_t<(N > 255), uint32_t, uint8_t>;
+
+ // constructors
+ FixedString() { // override default
+ buffer_[0] = '\0';
+ }
+
+ FixedString(const FixedString& other) { // override default.
+ copyFrom<N>(other);
+ }
+
+ // The following constructor is not explicit to allow
+ // FixedString<8> s = "abcd";
+ template <typename ...Types>
+ FixedString(Types&&... args) {
+ append(std::forward<Types>(args)...);
+ }
+
+ // copy assign (copyFrom checks for equality and returns *this).
+ FixedString& operator=(const FixedString& other) { // override default.
+ return copyFrom<N>(other);
+ }
+
+ template <typename ...Types>
+ FixedString& operator=(Types&&... args) {
+ size_ = 0;
+ return append(std::forward<Types>(args)...);
+ }
+
+ // operator equals
+ bool operator==(const char *s) const {
+ return strncmp(c_str(), s, capacity() + 1) == 0;
+ }
+
+ bool operator==(std::string_view s) const {
+ return size() == s.size() && memcmp(data(), s.data(), size()) == 0;
+ }
+
+ // operator not-equals
+ template <typename T>
+ bool operator!=(const T& other) const {
+ return !operator==(other);
+ }
+
+ // operator +=
+ template <typename ...Types>
+ FixedString& operator+=(Types&&... args) {
+ return append(std::forward<Types>(args)...);
+ }
+
+ // conversion to std::string_view.
+ operator std::string_view() const {
+ return asStringView();
+ }
+
+ // basic observers
+ size_t buffer_offset() const { return offsetof(std::decay_t<decltype(*this)>, buffer_); }
+ static constexpr uint32_t capacity() { return N; }
+ uint32_t size() const { return size_; }
+ uint32_t remaining() const { return size_ >= N ? 0 : N - size_; }
+ bool empty() const { return size_ == 0; }
+ bool full() const { return size_ == N; } // when full, possible truncation risk.
+ char * data() { return buffer_; }
+ const char * data() const { return buffer_; }
+ const char * c_str() const { return buffer_; }
+
+ inline std::string_view asStringView() const {
+ return { buffer_, static_cast<size_t>(size_) };
+ }
+ inline std::string asString() const {
+ return { buffer_, static_cast<size_t>(size_) };
+ }
+
+ void clear() { size_ = 0; buffer_[0] = 0; }
+
+ // Implementation of append - using templates
+ // to guarantee precedence in the presence of ambiguity.
+ //
+ // Consider C++20 template overloading through constraints and concepts.
+ template <typename T>
+ FixedString& append(const T& t) {
+ using decayT = std::decay_t<T>;
+ if constexpr (is_specialization_v<decayT, FixedString>) {
+ // A FixedString<U>
+ if (size_ == 0) {
+ // optimization to erase everything.
+ return copyFrom(t);
+ } else {
+ return appendStringView({t.data(), t.size()});
+ }
+ } else if constexpr(std::is_same_v<decayT, char>) {
+ if (size_ < N) {
+ buffer_[size_++] = t;
+ buffer_[size_] = '\0';
+ }
+ return *this;
+ } else if constexpr(std::is_same_v<decayT, char *>) {
+ // Some char* ptr.
+ return appendString(t);
+ } else if constexpr (std::is_convertible_v<decayT, std::string_view>) {
+ // std::string_view, std::string, or some other convertible type.
+ return appendStringView(t);
+ } else /* constexpr */ {
+ static_assert(dependent_false_v<T>, "non-matching append type");
+ }
+ }
+
+ FixedString& appendStringView(std::string_view s) {
+ uint32_t total = std::min(static_cast<size_t>(N - size_), s.size());
+ memcpy(buffer_ + size_, s.data(), total);
+ size_ += total;
+ buffer_[size_] = '\0';
+ return *this;
+ }
+
+ FixedString& appendString(const char *s) {
+ // strncpy zero pads to the end,
+ // strlcpy returns total expected length,
+ // we don't have strncpy_s in Bionic,
+ // so we write our own here.
+ while (size_ < N && *s != '\0') {
+ buffer_[size_++] = *s++;
+ }
+ buffer_[size_] = '\0';
+ return *this;
+ }
+
+ // Copy initialize the struct.
+ // Note: We are POD but customize the copying for acceleration
+ // of moving small strings embedded in a large buffers.
+ template <uint32_t U>
+ FixedString& copyFrom(const FixedString<U>& other) {
+ if ((void*)this != (void*)&other) { // not a self-assignment
+ if (other.size() == 0) {
+ size_ = 0;
+ buffer_[0] = '\0';
+ return *this;
+ }
+ constexpr size_t kSizeToCopyWhole = 64;
+ if constexpr (N == U &&
+ sizeof(*this) == sizeof(other) &&
+ sizeof(*this) <= kSizeToCopyWhole) {
+ // As we have the same str size type, we can just
+ // memcpy with fixed size, which can be easily optimized.
+ memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
+ return *this;
+ }
+ if constexpr (std::is_same_v<strsize_t, typename FixedString<U>::strsize_t>) {
+ constexpr size_t kAlign = 8; // align to a multiple of 8.
+ static_assert((kAlign & (kAlign - 1)) == 0); // power of 2.
+ // we check any alignment issues.
+ if (buffer_offset() == other.buffer_offset() && other.size() <= capacity()) {
+ // improve on standard POD copying by reducing size.
+ const size_t mincpy = buffer_offset() + other.size() + 1 /* nul */;
+ const size_t maxcpy = std::min(sizeof(*this), sizeof(other));
+ const size_t cpysize = std::min(mincpy + kAlign - 1 & ~(kAlign - 1), maxcpy);
+ memcpy(static_cast<void*>(this), static_cast<const void*>(&other), cpysize);
+ return *this;
+ }
+ }
+ size_ = std::min(other.size(), capacity());
+ memcpy(buffer_, other.data(), size_);
+ buffer_[size_] = '\0'; // zero terminate.
+ }
+ return *this;
+ }
+
+private:
+ // Template helper methods
+
+ template <typename Test, template <uint32_t> class Ref>
+ struct is_specialization : std::false_type {};
+
+ template <template <uint32_t> class Ref, uint32_t UU>
+ struct is_specialization<Ref<UU>, Ref>: std::true_type {};
+
+ template <typename Test, template <uint32_t> class Ref>
+ static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
+
+ // For static assert(false) we need a template version to avoid early failure.
+ template <typename T>
+ static inline constexpr bool dependent_false_v = false;
+
+ // POD variables
+ strsize_t size_ = 0;
+ char buffer_[N + 1 /* allow zero termination */];
+};
+
+// Stream operator syntactic sugar.
+// Example:
+// s << 'b' << "c" << "d" << '\n';
+template <uint32_t N, typename ...Types>
+FixedString<N>& operator<<(FixedString<N>& fs, Types&&... args) {
+ return fs.append(std::forward<Types>(args)...);
+}
+
+// We do not use a default size for fixed string as changing
+// the default size would lead to different behavior - we want the
+// size to be explicitly known.
+
+// FixedString62 of 62 chars fits in one typical cache line.
+using FixedString62 = FixedString<62>;
+
+// Slightly smaller
+using FixedString30 = FixedString<30>;
+
+// Since we have added copy and assignment optimizations,
+// we are no longer trivially assignable and copyable.
+// But we check standard layout here to prevent inclusion of unacceptable members or virtuals.
+static_assert(std::is_standard_layout_v<FixedString62>);
+static_assert(std::is_standard_layout_v<FixedString30>);
+
+} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/MethodStatistics.h b/media/utils/include/mediautils/MethodStatistics.h
index 700fbaa..6d7e990 100644
--- a/media/utils/include/mediautils/MethodStatistics.h
+++ b/media/utils/include/mediautils/MethodStatistics.h
@@ -55,15 +55,23 @@
/**
* Adds a method event, typically execution time in ms.
*/
- void event(Code code, FloatType executeMs) {
+ template <typename C>
+ void event(C&& code, FloatType executeMs) {
std::lock_guard lg(mLock);
- mStatisticsMap[code].add(executeMs);
+ auto it = mStatisticsMap.lower_bound(code);
+ if (it != mStatisticsMap.end() && it->first == code) {
+ it->second.add(executeMs);
+ } else {
+ // StatsType ctor takes an optional array of data for initialization.
+ FloatType dataArray[1] = { executeMs };
+ mStatisticsMap.emplace_hint(it, std::forward<C>(code), dataArray);
+ }
}
/**
* Returns the name for the method code.
*/
- std::string getMethodForCode(Code code) const {
+ std::string getMethodForCode(const Code& code) const {
auto it = mMethodMap.find(code);
return it == mMethodMap.end() ? std::to_string((int)code) : it->second;
}
@@ -71,7 +79,7 @@
/**
* Returns the number of times the method was invoked by event().
*/
- size_t getMethodCount(Code code) const {
+ size_t getMethodCount(const Code& code) const {
std::lock_guard lg(mLock);
auto it = mStatisticsMap.find(code);
return it == mStatisticsMap.end() ? 0 : it->second.getN();
@@ -80,7 +88,7 @@
/**
* Returns the statistics object for the method.
*/
- StatsType getStatistics(Code code) const {
+ StatsType getStatistics(const Code& code) const {
std::lock_guard lg(mLock);
auto it = mStatisticsMap.find(code);
return it == mStatisticsMap.end() ? StatsType{} : it->second;
@@ -107,9 +115,10 @@
}
private:
- const std::map<Code, std::string> mMethodMap;
+ // Note: we use a transparent comparator std::less<> for heterogeneous key lookup.
+ const std::map<Code, std::string, std::less<>> mMethodMap;
mutable std::mutex mLock;
- std::map<Code, StatsType> mStatisticsMap GUARDED_BY(mLock);
+ std::map<Code, StatsType, std::less<>> mStatisticsMap GUARDED_BY(mLock);
};
// Managed Statistics support.
diff --git a/media/utils/include/mediautils/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h
index ef03aef..038e7dd 100644
--- a/media/utils/include/mediautils/TimeCheck.h
+++ b/media/utils/include/mediautils/TimeCheck.h
@@ -60,7 +60,7 @@
* the TimeCheck object is destroyed or leaves scope.
* \param crashOnTimeout true if the object issues an abort on timeout.
*/
- explicit TimeCheck(std::string tag, OnTimerFunc&& onTimer = {},
+ explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer = {},
uint32_t timeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true);
TimeCheck() = default;
@@ -79,7 +79,17 @@
// The usage here is const safe.
class TimeCheckHandler {
public:
- const std::string tag;
+ template <typename S, typename F>
+ TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout,
+ const std::chrono::system_clock::time_point& _startTime,
+ pid_t _tid)
+ : tag(std::forward<S>(_tag))
+ , onTimer(std::forward<F>(_onTimer))
+ , crashOnTimeout(_crashOnTimeout)
+ , startTime(_startTime)
+ , tid(_tid)
+ {}
+ const FixedString62 tag;
const OnTimerFunc onTimer;
const bool crashOnTimeout;
const std::chrono::system_clock::time_point startTime;
diff --git a/media/utils/include/mediautils/TimerThread.h b/media/utils/include/mediautils/TimerThread.h
index ffee602..349bffd 100644
--- a/media/utils/include/mediautils/TimerThread.h
+++ b/media/utils/include/mediautils/TimerThread.h
@@ -27,6 +27,8 @@
#include <android-base/thread_annotations.h>
+#include <mediautils/FixedString.h>
+
namespace android::mediautils {
/**
@@ -54,7 +56,7 @@
* \returns a handle that can be used for cancellation.
*/
Handle scheduleTask(
- std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout);
+ std::string_view tag, std::function<void()>&& func, std::chrono::milliseconds timeout);
/**
* Tracks a task that shows up on toString() until cancelled.
@@ -62,7 +64,7 @@
* \param tag string associated with the task.
* \returns a handle that can be used for cancellation.
*/
- Handle trackTask(std::string tag);
+ Handle trackTask(std::string_view tag);
/**
* Cancels a task previously scheduled with scheduleTask()
@@ -129,12 +131,22 @@
// To minimize movement of data, we pass around shared_ptrs to Requests.
// These are allocated and deallocated outside of the lock.
struct Request {
+ Request(const std::chrono::system_clock::time_point& _scheduled,
+ const std::chrono::system_clock::time_point& _deadline,
+ pid_t _tid,
+ std::string_view _tag)
+ : scheduled(_scheduled)
+ , deadline(_deadline)
+ , tid(_tid)
+ , tag(_tag)
+ {}
+
const std::chrono::system_clock::time_point scheduled;
const std::chrono::system_clock::time_point deadline; // deadline := scheduled + timeout
// if deadline == scheduled, no
// timeout, task not executed.
const pid_t tid;
- const std::string tag;
+ const FixedString62 tag;
std::string toString() const;
};
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 1024018..232cc4e 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -124,6 +124,27 @@
}
cc_test {
+ name: "mediautils_fixedstring_tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ shared_libs: [
+ "libaudioutils",
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+
+ srcs: [
+ "mediautils_fixedstring_tests.cpp",
+ ],
+}
+
+cc_test {
name: "mediautils_scopedstatistics_tests",
cflags: [
diff --git a/media/utils/tests/mediautils_fixedstring_tests.cpp b/media/utils/tests/mediautils_fixedstring_tests.cpp
new file mode 100644
index 0000000..7ab9a9f
--- /dev/null
+++ b/media/utils/tests/mediautils_fixedstring_tests.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "mediautils_fixedstring_tests"
+
+#include <mediautils/FixedString.h>
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android::mediautils;
+
+TEST(mediautils_fixedstring_tests, ctor) {
+ FixedString<8> s0("abcde");
+
+ ASSERT_FALSE(s0.empty());
+ ASSERT_EQ(8U, s0.capacity());
+
+ ASSERT_EQ(5U, s0.size());
+ ASSERT_EQ(3U, s0.remaining());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abcde"));
+
+ ASSERT_EQ(0, strcmp(s0.data(), "abcde"));
+
+ // overflow
+ FixedString<8> s1("abcdefghijk");
+ ASSERT_EQ(8U, s1.size());
+ ASSERT_TRUE(s1.full());
+ ASSERT_EQ(0U, s1.remaining());
+ ASSERT_EQ(0, strcmp(s1.c_str(), "abcdefgh"));
+
+ // overflow
+ FixedString<8> s2(std::string("abcdefghijk"));
+ ASSERT_TRUE(s2.full());
+
+ ASSERT_EQ(8U, s2.size());
+ ASSERT_EQ(0, strcmp(s2.c_str(), "abcdefgh"));
+
+ // complex
+ ASSERT_EQ(s1, s2);
+ ASSERT_EQ(FixedString<12>().append(s1), s2);
+ ASSERT_NE(s1, "bcd");
+
+ // string and stringview
+ ASSERT_EQ(s1.asString(), s1.asStringView());
+
+ FixedString30 s3;
+ s3 = std::string("abcd");
+ ASSERT_EQ(s3, "abcd");
+
+ s3.clear();
+ ASSERT_EQ(s3, "");
+ ASSERT_NE(s3, "abcd");
+ ASSERT_EQ(0U, s3.size());
+}
+
+TEST(mediautils_fixedstring_tests, append) {
+ FixedString<8> s0;
+ ASSERT_EQ(0U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), ""));
+ ASSERT_TRUE(s0.empty());
+ ASSERT_FALSE(s0.full());
+
+ s0.append("abc");
+ ASSERT_EQ(3U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abc"));
+
+ s0.append(std::string("d"));
+ ASSERT_EQ(4U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abcd"));
+
+ // overflow
+ s0.append("efghijk");
+ ASSERT_EQ(8U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), "abcdefgh"));
+ ASSERT_TRUE(s0.full());
+
+ // concatenated
+ ASSERT_EQ(FixedString62("abcd"),
+ FixedString<8>("ab").append("c").append(FixedString<8>("d")));
+ ASSERT_EQ(FixedString<12>("a").append(FixedString<12>("b")), "ab");
+}
+
+TEST(mediautils_fixedstring_tests, plus_equals) {
+ FixedString<8> s0;
+ ASSERT_EQ(0U, s0.size());
+ ASSERT_EQ(0, strcmp(s0.c_str(), ""));
+
+ s0 += "abc";
+ s0 += "def";
+ ASSERT_EQ(s0, "abcdef");
+}
+
+TEST(mediautils_fixedstring_tests, stream_operator) {
+ FixedString<8> s0('a');
+
+ s0 << 'b' << "c" << "d" << '\n';
+ ASSERT_EQ(s0, "abcd\n");
+}
+
+TEST(mediautils_fixedstring_tests, examples) {
+ FixedString30 s1(std::string("a"));
+ s1 << "bc" << 'd' << '\n';
+ s1 += "hello";
+
+ ASSERT_EQ(s1, "abcd\nhello");
+
+ FixedString30 s2;
+ for (const auto &c : s1.asStringView()) {
+ s2.append(c);
+ };
+ ASSERT_EQ(s1, s2);
+
+ FixedString30 s3(std::move(s1));
+}
+
+// Ensure type alias works fine as well.
+using FixedString1024 = FixedString<1024>;
+
+TEST(mediautils_fixedstring_tests, copy) {
+ FixedString1024 s0("abc");
+ FixedString62 s1(s0);
+
+ ASSERT_EQ(3U, s1.size());
+ ASSERT_EQ(0, strcmp(s1.c_str(), "abc"));
+ ASSERT_EQ(s0, s1);
+
+ FixedString<1024> s2(s1);
+ ASSERT_EQ(3U, s2.size());
+ ASSERT_EQ(0, strcmp(s2.c_str(), "abc"));
+ ASSERT_EQ(s2, "abc");
+ ASSERT_NE(s2, "def");
+ ASSERT_EQ(s2, std::string("abc"));
+ ASSERT_NE(s2, std::string("def"));
+ ASSERT_EQ(s1, s2);
+ ASSERT_EQ(s0, s2);
+ ASSERT_EQ(s2, FixedString62(FixedString1024("abc")));
+}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f7576f6..8ae06d0 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -226,6 +226,9 @@
BINDER_METHOD_ENTRY(getAAudioMixerBurstCount) \
BINDER_METHOD_ENTRY(getAAudioHardwareBurstMinUsec) \
BINDER_METHOD_ENTRY(setDeviceConnectedState) \
+BINDER_METHOD_ENTRY(setRequestedLatencyMode) \
+BINDER_METHOD_ENTRY(getSupportedLatencyModes) \
+
// singleton for Binder Method Statistics for IAudioFlinger
static auto& getIAudioFlingerStatistics() {
@@ -1583,6 +1586,32 @@
return NO_ERROR;
}
+status_t AudioFlinger::setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode) {
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == nullptr) {
+ return BAD_VALUE;
+ }
+ return thread->setRequestedLatencyMode(mode);
+}
+
+status_t AudioFlinger::getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes) {
+ if (output == AUDIO_IO_HANDLE_NONE) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == nullptr) {
+ return BAD_VALUE;
+ }
+ return thread->getSupportedLatencyModes(modes);
+}
+
status_t AudioFlinger::setStreamMute(audio_stream_type_t stream, bool muted)
{
// check calling permissions
@@ -2082,6 +2111,21 @@
}
}
+void AudioFlinger::onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) {
+ int32_t outputAidl = VALUE_OR_FATAL(legacy2aidl_audio_io_handle_t_int32_t(output));
+ std::vector<media::LatencyMode> modesAidl = VALUE_OR_FATAL(
+ convertContainer<std::vector<media::LatencyMode>>(modes,
+ legacy2aidl_audio_latency_mode_t_LatencyMode));
+
+ Mutex::Autolock _l(mClientLock);
+ size_t size = mNotificationClients.size();
+ for (size_t i = 0; i < size; i++) {
+ mNotificationClients.valueAt(i)->audioFlingerClient()
+ ->onSupportedLatencyModesChanged(outputAidl, modesAidl);
+ }
+}
+
// removeClient_l() must be called with AudioFlinger::mClientLock held
void AudioFlinger::removeClient_l(pid_t pid)
{
@@ -4476,6 +4520,8 @@
case TransactionCode::SET_RECORD_SILENCED:
case TransactionCode::AUDIO_POLICY_READY:
case TransactionCode::SET_DEVICE_CONNECTED_STATE:
+ case TransactionCode::SET_REQUESTED_LATENCY_MODE:
+ case TransactionCode::GET_SUPPORTED_LATENCY_MODES:
ALOGW("%s: transaction %d received from PID %d",
__func__, code, IPCThreadState::self()->getCallingPid());
// return status only for non void methods
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 8e4383c..fc4c807 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -293,6 +293,12 @@
virtual status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected);
+ virtual status_t setRequestedLatencyMode(
+ audio_io_handle_t output, audio_latency_mode_t mode);
+
+ virtual status_t getSupportedLatencyModes(audio_io_handle_t output,
+ std::vector<audio_latency_mode_t>* modes);
+
status_t onTransactWrapper(TransactionCode code, const Parcel& data, uint32_t flags,
const std::function<status_t()>& delegate) override;
@@ -764,6 +770,8 @@
void ioConfigChanged(audio_io_config_event_t event,
const sp<AudioIoDescriptor>& ioDesc,
pid_t pid = 0);
+ void onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes);
// Allocate an audio_unique_id_t.
// Specific types are audio_io_handle_t, audio_session_t, effect ID (int),
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3fa3e41..3d44aec 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -178,6 +178,11 @@
// Direct output thread minimum sleep time in idle or active(underrun) state
static const nsecs_t kDirectMinSleepTimeUs = 10000;
+// Minimum amount of time between checking to see if the timestamp is advancing
+// for underrun detection. If we check too frequently, we may not detect a
+// timestamp update and will falsely detect underrun.
+static const nsecs_t kMinimumTimeBetweenTimestampChecksNs = 150 /* ms */ * 1000;
+
// The universal constant for ubiquitous 20ms value. The value of 20ms seems to provide a good
// balance between power consumption and latency, and allows threads to be scheduled reliably
// by the CFS scheduler.
@@ -741,6 +746,12 @@
sendConfigEvent_l(configEvent);
}
+void AudioFlinger::ThreadBase::sendHalLatencyModesChangedEvent_l()
+{
+ sp<ConfigEvent> configEvent = sp<HalLatencyModesChangedEvent>::make();
+ sendConfigEvent_l(configEvent);
+}
+
// post condition: mConfigEvents.isEmpty()
void AudioFlinger::ThreadBase::processConfigEvents_l()
{
@@ -808,6 +819,10 @@
setCheckOutputStageEffects();
} break;
+ case CFG_EVENT_HAL_LATENCY_MODES_CHANGED: {
+ onHalLatencyModesChanged_l();
+ } break;
+
default:
ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType);
break;
@@ -2030,7 +2045,8 @@
mFastTrackAvailMask(((1 << FastMixerState::sMaxFastTracks) - 1) & ~1),
mHwSupportsPause(false), mHwPaused(false), mFlushPending(false),
mLeftVolFloat(-1.0), mRightVolFloat(-1.0),
- mDownStreamPatch{}
+ mDownStreamPatch{},
+ mIsTimestampAdvancing(kMinimumTimeBetweenTimestampChecksNs)
{
snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id);
mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mThreadName);
@@ -3136,10 +3152,7 @@
auto backInserter = std::back_inserter(metadata.tracks);
for (const sp<Track> &track : mActiveTracks) {
// No track is invalid as this is called after prepareTrack_l in the same critical section
- // Do not forward metadata for PatchTrack with unspecified stream type
- if (track->streamType() != AUDIO_STREAM_PATCH) {
- track->copyMetadataTo(backInserter);
- }
+ track->copyMetadataTo(backInserter);
}
sendMetadataToBackend_l(metadata);
}
@@ -3922,6 +3935,8 @@
// stop(), pause(), etc.), but the threadLoop is entitled to call audio
// data / buffer methods on tracks from activeTracks without the ThreadBase lock.
activeTracks.insert(activeTracks.end(), mActiveTracks.begin(), mActiveTracks.end());
+
+ setHalLatencyMode_l();
} // mLock scope ends
if (mBytesRemaining == 0) {
@@ -5021,6 +5036,7 @@
mCallbackThread->setDraining(mDrainSequence);
}
mHwPaused = false;
+ setHalLatencyMode_l();
}
void AudioFlinger::PlaybackThread::onAddNewTrack_l()
@@ -5894,18 +5910,35 @@
return trackCount;
}
-bool AudioFlinger::PlaybackThread::checkRunningTimestamp()
+bool AudioFlinger::PlaybackThread::IsTimestampAdvancing::check(AudioStreamOut * output)
{
+ // Check the timestamp to see if it's advancing once every 150ms. If we check too frequently, we
+ // could falsely detect that the frame position has stalled due to underrun because we haven't
+ // given the Audio HAL enough time to update.
+ const nsecs_t nowNs = systemTime();
+ if (nowNs - mPreviousNs < mMinimumTimeBetweenChecksNs) {
+ return mLatchedValue;
+ }
+ mPreviousNs = nowNs;
+ mLatchedValue = false;
+ // Determine if the presentation position is still advancing.
uint64_t position = 0;
struct timespec unused;
- const status_t ret = mOutput->getPresentationPosition(&position, &unused);
+ const status_t ret = output->getPresentationPosition(&position, &unused);
if (ret == NO_ERROR) {
- if (position != mLastCheckedTimestampPosition) {
- mLastCheckedTimestampPosition = position;
- return true;
+ if (position != mPreviousPosition) {
+ mPreviousPosition = position;
+ mLatchedValue = true;
}
}
- return false;
+ return mLatchedValue;
+}
+
+void AudioFlinger::PlaybackThread::IsTimestampAdvancing::clear()
+{
+ mLatchedValue = true;
+ mPreviousPosition = 0;
+ mPreviousNs = 0;
}
// isTrackAllowed_l() must be called with ThreadBase::mLock held
@@ -6340,9 +6373,9 @@
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
// Only consider last track started for mixer state control
+ bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput);
if (--(track->mRetryCount) <= 0) {
- const bool running = checkRunningTimestamp();
- if (running) { // still running, give us more time.
+ if (isTimestampAdvancing) { // HAL is still playing audio, give us more time.
track->mRetryCount = kMaxTrackRetriesOffload;
} else {
ALOGV("BUFFER TIMEOUT: remove track(%d) from active list", trackId);
@@ -6923,9 +6956,9 @@
} else {
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
+ bool isTimestampAdvancing = mIsTimestampAdvancing.check(mOutput);
if (--(track->mRetryCount) <= 0) {
- const bool running = checkRunningTimestamp();
- if (running) { // still running, give us more time.
+ if (isTimestampAdvancing) { // HAL is still playing audio, give us more time.
track->mRetryCount = kMaxTrackRetriesOffload;
} else {
ALOGV("OffloadThread: BUFFER TIMEOUT: remove track(%d) from active list",
@@ -7264,6 +7297,94 @@
{
}
+void AudioFlinger::SpatializerThread::onFirstRef() {
+ PlaybackThread::onFirstRef();
+
+ Mutex::Autolock _l(mLock);
+ status_t status = mOutput->stream->setLatencyModeCallback(this);
+ if (status != INVALID_OPERATION) {
+ updateHalSupportedLatencyModes_l();
+ }
+}
+
+status_t AudioFlinger::SpatializerThread::createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
+{
+ status_t status = MixerThread::createAudioPatch_l(patch, handle);
+ updateHalSupportedLatencyModes_l();
+ return status;
+}
+
+void AudioFlinger::SpatializerThread::updateHalSupportedLatencyModes_l() {
+ std::vector<audio_latency_mode_t> latencyModes;
+ if (mOutput->stream->getRecommendedLatencyModes(&latencyModes) != NO_ERROR) {
+ latencyModes.clear();
+ }
+ if (latencyModes != mSupportedLatencyModes) {
+ mSupportedLatencyModes.swap(latencyModes);
+ sendHalLatencyModesChangedEvent_l();
+ }
+}
+
+void AudioFlinger::SpatializerThread::onHalLatencyModesChanged_l() {
+ mAudioFlinger->onSupportedLatencyModesChanged(mId, mSupportedLatencyModes);
+}
+
+void AudioFlinger::SpatializerThread::setHalLatencyMode_l() {
+ // if mSupportedLatencyModes is empty, the HAL stream does not support
+ // latency mode control and we can exit.
+ if (mSupportedLatencyModes.empty()) {
+ return;
+ }
+ audio_latency_mode_t latencyMode = AUDIO_LATENCY_MODE_FREE;
+ if (mSupportedLatencyModes.size() == 1) {
+ // If the HAL only support one latency mode currently, confirm the choice
+ latencyMode = mSupportedLatencyModes[0];
+ } else if (mSupportedLatencyModes.size() > 1) {
+ // Request low latency if:
+ // - The low latency mode is requested by the spatializer controller
+ // (mRequestedLatencyMode = AUDIO_LATENCY_MODE_LOW)
+ // AND
+ // - At least one active track is spatialized
+ bool hasSpatializedActiveTrack = false;
+ for (const auto& track : mActiveTracks) {
+ if (track->isSpatialized()) {
+ hasSpatializedActiveTrack = true;
+ break;
+ }
+ }
+ if (hasSpatializedActiveTrack && mRequestedLatencyMode == AUDIO_LATENCY_MODE_LOW) {
+ latencyMode = AUDIO_LATENCY_MODE_LOW;
+ }
+ }
+
+ if (latencyMode != mSetLatencyMode) {
+ status_t status = mOutput->stream->setLatencyMode(latencyMode);
+ if (status == NO_ERROR) {
+ mSetLatencyMode = latencyMode;
+ }
+ }
+}
+
+status_t AudioFlinger::SpatializerThread::setRequestedLatencyMode(audio_latency_mode_t mode) {
+ if (mode != AUDIO_LATENCY_MODE_LOW && mode != AUDIO_LATENCY_MODE_FREE) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mLock);
+ mRequestedLatencyMode = mode;
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::SpatializerThread::getSupportedLatencyModes(
+ std::vector<audio_latency_mode_t>* modes) {
+ if (modes == nullptr) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mLock);
+ *modes = mSupportedLatencyModes;
+ return NO_ERROR;
+}
+
void AudioFlinger::SpatializerThread::checkOutputStageEffects()
{
bool hasVirtualizer = false;
@@ -7316,6 +7437,14 @@
}
}
+void AudioFlinger::SpatializerThread::onRecommendedLatencyModeChanged(
+ std::vector<audio_latency_mode_t> modes) {
+ Mutex::Autolock _l(mLock);
+ if (modes != mSupportedLatencyModes) {
+ mSupportedLatencyModes.swap(modes);
+ sendHalLatencyModesChangedEvent_l();
+ }
+}
// ----------------------------------------------------------------------------
// Record
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 074ae8f..ad5617d 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -55,7 +55,8 @@
CFG_EVENT_RELEASE_AUDIO_PATCH,
CFG_EVENT_UPDATE_OUT_DEVICE,
CFG_EVENT_RESIZE_BUFFER,
- CFG_EVENT_CHECK_OUTPUT_STAGE_EFFECTS
+ CFG_EVENT_CHECK_OUTPUT_STAGE_EFFECTS,
+ CFG_EVENT_HAL_LATENCY_MODES_CHANGED,
};
class ConfigEventData: public RefBase {
@@ -282,6 +283,15 @@
virtual ~CheckOutputStageEffectsEvent() {}
};
+ class HalLatencyModesChangedEvent : public ConfigEvent {
+ public:
+ HalLatencyModesChangedEvent() :
+ ConfigEvent(CFG_EVENT_HAL_LATENCY_MODES_CHANGED) {
+ }
+
+ virtual ~HalLatencyModesChangedEvent() {}
+ };
+
class PMDeathRecipient : public IBinder::DeathRecipient {
public:
@@ -353,6 +363,7 @@
void sendResizeBufferConfigEvent_l(int32_t maxSharedAudioHistoryMs);
void sendCheckOutputStageEffectsEvent();
void sendCheckOutputStageEffectsEvent_l();
+ void sendHalLatencyModesChangedEvent_l();
void processConfigEvents_l();
virtual void setCheckOutputStageEffects() {}
@@ -364,7 +375,7 @@
virtual void toAudioPortConfig(struct audio_port_config *config) = 0;
virtual void resizeInputBuffer_l(int32_t maxSharedAudioHistoryMs);
-
+ virtual void onHalLatencyModesChanged_l() {}
// see note at declaration of mStandby, mOutDevice and mInDevice
@@ -921,6 +932,8 @@
}
virtual void checkOutputStageEffects() {}
+ virtual void setHalLatencyMode_l() {}
+
void dumpInternals_l(int fd, const Vector<String16>& args) override;
void dumpTracks_l(int fd, const Vector<String16>& args) override;
@@ -1064,6 +1077,15 @@
bool hasMixer() const {
return mType == MIXER || mType == DUPLICATING || mType == SPATIALIZER;
}
+
+ virtual status_t setRequestedLatencyMode(
+ audio_latency_mode_t mode __unused) { return INVALID_OPERATION; }
+
+ virtual status_t getSupportedLatencyModes(
+ std::vector<audio_latency_mode_t>* modes __unused) {
+ return INVALID_OPERATION;
+ }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -1150,7 +1172,7 @@
volatile int32_t mSuspended;
int64_t mBytesWritten;
- int64_t mFramesWritten; // not reset on standby
+ std::atomic<int64_t> mFramesWritten; // not reset on standby
int64_t mLastFramesWritten = -1; // track changes in timestamp
// server frames written.
int64_t mSuspendedFrames; // not reset on standby
@@ -1364,6 +1386,7 @@
virtual bool hasFastMixer() const = 0;
virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const
{ FastTrackUnderruns dummy; return dummy; }
+ const std::atomic<int64_t>& framesWritten() const { return mFramesWritten; }
protected:
// accessed by both binder threads and within threadLoop(), lock on mutex needed
@@ -1381,13 +1404,38 @@
std::atomic_bool mCheckOutputStageEffects{};
- // A differential check on the timestamps to see if there is a change in the
- // timestamp frame position between the last call to checkRunningTimestamp.
- uint64_t mLastCheckedTimestampPosition = ~0LL;
- bool checkRunningTimestamp();
+ // Provides periodic checking for timestamp advancement for underrun detection.
+ class IsTimestampAdvancing {
+ public:
+ // The timestamp will not be checked any faster than the specified time.
+ IsTimestampAdvancing(nsecs_t minimumTimeBetweenChecksNs)
+ : mMinimumTimeBetweenChecksNs(minimumTimeBetweenChecksNs)
+ {
+ clear();
+ }
+ // Check if the presentation position has advanced in the last periodic time.
+ bool check(AudioStreamOut * output);
+ // Clear the internal state when the playback state changes for the output
+ // stream.
+ void clear();
+ private:
+ // The minimum time between timestamp checks.
+ const nsecs_t mMinimumTimeBetweenChecksNs;
+ // Add differential check on the timestamps to see if there is a change in the
+ // timestamp frame position between the last call to check.
+ uint64_t mPreviousPosition;
+ // The time at which the last check occurred, to ensure we don't check too
+ // frequently, giving the Audio HAL enough time to update its timestamps.
+ nsecs_t mPreviousNs;
+ // The valued is latched so we don't check timestamps too frequently.
+ bool mLatchedValue;
+ };
+ IsTimestampAdvancing mIsTimestampAdvancing;
- virtual void flushHw_l() { mLastCheckedTimestampPosition = ~0LL; }
+ virtual void flushHw_l() {
+ mIsTimestampAdvancing.clear();
+ }
};
class MixerThread : public PlaybackThread {
@@ -1682,7 +1730,8 @@
}
};
-class SpatializerThread : public MixerThread {
+class SpatializerThread : public MixerThread,
+ public StreamOutHalInterfaceLatencyModeCallback {
public:
SpatializerThread(const sp<AudioFlinger>& audioFlinger,
AudioStreamOut* output,
@@ -1693,10 +1742,35 @@
bool hasFastMixer() const override { return false; }
+ status_t createAudioPatch_l(const struct audio_patch *patch,
+ audio_patch_handle_t *handle) override;
+
+ // RefBase
+ virtual void onFirstRef();
+
+ // StreamOutHalInterfaceLatencyModeCallback
+ void onRecommendedLatencyModeChanged(std::vector<audio_latency_mode_t> modes) override;
+
+ status_t setRequestedLatencyMode(audio_latency_mode_t mode) override;
+ status_t getSupportedLatencyModes(std::vector<audio_latency_mode_t>* modes) override;
+
protected:
void checkOutputStageEffects() override;
+ void onHalLatencyModesChanged_l() override;
+ void setHalLatencyMode_l() override;
private:
+ void updateHalSupportedLatencyModes_l();
+
+ // Support low latency mode by default as unless explicitly indicated by the audio HAL
+ // we assume the audio path is compatible with the head tracking latency requirements
+ std::vector<audio_latency_mode_t> mSupportedLatencyModes = {AUDIO_LATENCY_MODE_LOW};
+ // default to invalid value to force first update to the audio HAL
+ audio_latency_mode_t mSetLatencyMode =
+ (audio_latency_mode_t)AUDIO_LATENCY_MODE_INVALID;
+ // Do not request a specific mode by default
+ audio_latency_mode_t mRequestedLatencyMode = AUDIO_LATENCY_MODE_FREE;
+
sp<EffectHandle> mFinalDownMixer;
};
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 2677ab3..20bfbb0 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -397,6 +397,8 @@
int64_t mLogStartFrames = 0; // Timestamp frames at start()
double mLogLatencyMs = 0.; // Track the last log latency
+ bool mLogForceVolumeUpdate = true; // force volume update to TrackMetrics.
+
TrackMetrics mTrackMetrics;
bool mServerLatencySupported = false;
diff --git a/services/audioflinger/TrackMetrics.h b/services/audioflinger/TrackMetrics.h
index 30d69ab..6fc70d6 100644
--- a/services/audioflinger/TrackMetrics.h
+++ b/services/audioflinger/TrackMetrics.h
@@ -64,7 +64,6 @@
AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, devices.c_str());
}
++mIntervalCount;
- mIntervalStartTimeNs = systemTime();
}
void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId,
@@ -90,11 +89,9 @@
// Called when we are removed from the Thread.
void logEndInterval() {
std::lock_guard l(mLock);
- if (mIntervalStartTimeNs != 0) {
- const int64_t elapsedTimeNs = systemTime() - mIntervalStartTimeNs;
- mIntervalStartTimeNs = 0;
- mCumulativeTimeNs += elapsedTimeNs;
- mDeviceTimeNs += elapsedTimeNs;
+ if (mLastVolumeChangeTimeNs != 0) {
+ logVolume_l(mVolume); // flush out the last volume.
+ mLastVolumeChangeTimeNs = 0;
}
}
@@ -133,20 +130,8 @@
// may be called multiple times during an interval
void logVolume(float volume) {
- const int64_t timeNs = systemTime();
std::lock_guard l(mLock);
- if (mStartVolumeTimeNs == 0) {
- mDeviceVolume = mVolume = volume;
- mLastVolumeChangeTimeNs = mStartVolumeTimeNs = timeNs;
- updateMinMaxVolume(0, mVolume);
- return;
- }
- const int64_t durationNs = timeNs - mLastVolumeChangeTimeNs;
- updateMinMaxVolume(durationNs, mVolume);
- mDeviceVolume = (mDeviceVolume * (mLastVolumeChangeTimeNs - mStartVolumeTimeNs) +
- mVolume * durationNs) / (timeNs - mStartVolumeTimeNs);
- mVolume = volume;
- mLastVolumeChangeTimeNs = timeNs;
+ logVolume_l(volume);
}
// Use absolute numbers returned by AudioTrackShared.
@@ -158,6 +143,7 @@
}
private:
+
// no lock required - all arguments and constants.
void deliverDeviceMetrics(const char *eventName, const char *devices) const {
mediametrics::LogItem(mMetricsId)
@@ -167,6 +153,23 @@
.record();
}
+ void logVolume_l(float volume) REQUIRES(mLock) {
+ const int64_t timeNs = systemTime();
+ const int64_t durationNs = mLastVolumeChangeTimeNs == 0
+ ? 0 : timeNs - mLastVolumeChangeTimeNs;
+ if (durationNs > 0) {
+ // See West's algorithm for weighted averages
+ // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+ mDeviceVolume += (mVolume - mDeviceVolume) * durationNs
+ / (durationNs + mDeviceTimeNs);
+ mDeviceTimeNs += durationNs;
+ mCumulativeTimeNs += durationNs;
+ }
+ updateMinMaxVolume(durationNs, mVolume); // always update.
+ mVolume = volume;
+ mLastVolumeChangeTimeNs = timeNs;
+ }
+
void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
if (mIntervalCount > 0) {
mediametrics::LogItem item(mMetricsId);
@@ -199,14 +202,12 @@
// mDevices is not reset by resetIntervalGroupMetrics.
mIntervalCount = 0;
- mIntervalStartTimeNs = 0;
// mCumulativeTimeNs is not reset by resetIntervalGroupMetrics.
mDeviceTimeNs = 0;
mVolume = 0.f;
mDeviceVolume = 0.f;
- mStartVolumeTimeNs = 0;
- mLastVolumeChangeTimeNs = 0;
+ mLastVolumeChangeTimeNs = 0; // last time volume logged, cleared on endInterval
mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
mMinVolumeDurationNs = 0;
@@ -230,14 +231,12 @@
// Number of intervals and playing time
int32_t mIntervalCount GUARDED_BY(mLock) = 0;
- int64_t mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
- int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0;
- int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0;
+ int64_t mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time.
+ int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0; // time on device.
// Average volume
- double mVolume GUARDED_BY(mLock) = 0.f;
- double mDeviceVolume GUARDED_BY(mLock) = 0.f;
- int64_t mStartVolumeTimeNs GUARDED_BY(mLock) = 0;
+ double mVolume GUARDED_BY(mLock) = 0.f; // last set volume.
+ double mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume.
int64_t mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0;
// Min/Max volume
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 6135020..95ca855 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1094,12 +1094,22 @@
__func__, mId, (int)mThreadIoHandle);
}
- // states to reset position info for non-offloaded/direct tracks
- if (!isOffloaded() && !isDirect()
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+
+ // states to reset position info for pcm tracks
+ if (audio_is_linear_pcm(mFormat)
&& (state == IDLE || state == STOPPED || state == FLUSHED)) {
mFrameMap.reset();
+
+ if (!isFastTrack() && (isDirect() || isOffloaded())) {
+ // Start point of track -> sink frame map. If the HAL returns a
+ // frame position smaller than the first written frame in
+ // updateTrackFrameInfo, the timestamp can be interpolated
+ // instead of using a larger value.
+ mFrameMap.push(mAudioTrackServerProxy->framesReleased(),
+ playbackThread->framesWritten());
+ }
}
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if (isFastTrack()) {
// refresh fast track underruns on start because that field is never cleared
// by the fast mixer; furthermore, the same track can be recycled, i.e. start
@@ -1123,6 +1133,7 @@
.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
mLogLatencyMs = 0.;
}
+ mLogForceVolumeUpdate = true; // at least one volume logged for metrics when starting.
if (status == NO_ERROR || status == ALREADY_EXISTS) {
// for streaming tracks, remove the buffer read stop limit.
@@ -1394,12 +1405,21 @@
if (mFinalVolume != volume) { // Compare to an epsilon if too many meaningless updates
mFinalVolume = volume;
setMetadataHasChanged();
- mTrackMetrics.logVolume(volume);
+ mLogForceVolumeUpdate = true;
+ }
+ if (mLogForceVolumeUpdate) {
+ mLogForceVolumeUpdate = false;
+ mTrackMetrics.logVolume(mFinalVolume);
}
}
void AudioFlinger::PlaybackThread::Track::copyMetadataTo(MetadataInserter& backInserter) const
{
+ // Do not forward metadata for PatchTrack with unspecified stream type
+ if (mStreamType == AUDIO_STREAM_PATCH) {
+ return;
+ }
+
playback_track_metadata_v7_t metadata;
metadata.base = {
.usage = mAttr.usage,
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index c2a20c6..bb1699e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -70,8 +70,9 @@
* @return OK if the request is valid
* otherwise if the request is not supported
*/
- status_t getOutputForAttr(const audio_attributes_t& attributes, uid_t uid,
- audio_output_flags_t flags,
+ status_t getOutputForAttr(const audio_attributes_t& attributes,
+ const audio_config_base_t& config,
+ uid_t uid, audio_output_flags_t flags,
sp<AudioPolicyMix> &primaryMix,
std::vector<sp<AudioPolicyMix>> *secondaryMixes);
@@ -126,6 +127,7 @@
enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
const audio_attributes_t& attributes,
+ const audio_config_base_t& config,
uid_t uid);
};
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 546f56b..551eab6 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -151,7 +151,7 @@
}
status_t AudioPolicyMixCollection::getOutputForAttr(
- const audio_attributes_t& attributes, uid_t uid,
+ const audio_attributes_t& attributes, const audio_config_base_t& config, uid_t uid,
audio_output_flags_t flags,
sp<AudioPolicyMix> &primaryMix,
std::vector<sp<AudioPolicyMix>> *secondaryMixes)
@@ -177,7 +177,7 @@
continue; // Primary output already found
}
- switch (mixMatch(policyMix.get(), i, attributes, uid)) {
+ switch (mixMatch(policyMix.get(), i, attributes, config, uid)) {
case MixMatchStatus::INVALID_MIX:
// The mix has contradictory rules, ignore it
// TODO: reject invalid mix at registration
@@ -202,7 +202,8 @@
}
AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
- const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes, uid_t uid) {
+ const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes,
+ const audio_config_base_t& config, uid_t uid) {
if (mix->mMixType == MIX_TYPE_PLAYERS) {
// Loopback render mixes are created from a public API and thus restricted
@@ -229,6 +230,14 @@
}
}
+ // Permit match only if requested format and mix format are PCM and can be format
+ // adapted by the mixer, or are the same (compressed) format.
+ if (!((audio_is_linear_pcm(config.format) && audio_is_linear_pcm(mix->mFormat.format)) ||
+ (config.format == mix->mFormat.format)) &&
+ config.format != AUDIO_CONFIG_BASE_INITIALIZER.format) {
+ return MixMatchStatus::NO_MATCH;
+ }
+
int userId = (int) multiuser_get_user_id(uid);
// TODO if adding more player rules (currently only 2), make rule handling "generic"
diff --git a/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml b/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml
index e7908eb..c453dea 100644
--- a/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml
+++ b/services/audiopolicy/config/bluetooth_with_le_audio_policy_configuration_7_0.xml
@@ -12,6 +12,7 @@
</mixPort>
<!-- Le Audio Audio Ports -->
<mixPort name="le audio output" role="source" />
+ <mixPort name="le audio broadcast output" role="source" />
<mixPort name="le audio input" role="sink">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="8000 16000 24000 32000 44100 48000"
@@ -47,6 +48,7 @@
<devicePort tagName="BLE Headset Out" type="AUDIO_DEVICE_OUT_BLE_HEADSET" role="sink"/>
<devicePort tagName="BLE Speaker Out" type="AUDIO_DEVICE_OUT_BLE_SPEAKER" role="sink"/>
<devicePort tagName="BLE Headset In" type="AUDIO_DEVICE_IN_BLE_HEADSET" role="source"/>
+ <devicePort tagName="BLE Broadcast Out" type="AUDIO_DEVICE_OUT_BLE_BROADCAST" role="sink"/>
</devicePorts>
<routes>
<route type="mix" sink="BT A2DP Out"
@@ -63,5 +65,7 @@
sources="BLE Headset In"/>
<route type="mix" sink="BLE Speaker Out"
sources="le audio output"/>
+ <route type="mix" sink="BLE Broadcast Out"
+ sources="le audio broadcast output"/>
</routes>
</module>
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index d4d514d..08d6453 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -195,6 +195,12 @@
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, }));
}
+ // If connected to a dock, never use the device speaker for calls
+ if (!availableOutputDevices.getDevicesFromTypes({AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET})
+ .isEmpty()) {
+ availableOutputDevices.remove(
+ availableOutputDevices.getDevicesFromTypes({AUDIO_DEVICE_OUT_SPEAKER}));
+ }
} break;
case STRATEGY_ACCESSIBILITY: {
// do not route accessibility prompts to a digital output currently configured with a
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 744609f..912d53a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1054,12 +1054,12 @@
if (curProfile->getFlags() != AUDIO_OUTPUT_FLAG_SPATIALIZER) {
continue;
}
- // reject profiles not corresponding to a device currently available
- DeviceVector supportedDevices = curProfile->getSupportedDevices();
- if (!mAvailableOutputDevices.containsAtLeastOne(supportedDevices)) {
- continue;
- }
if (!devices.empty()) {
+ // reject profiles not corresponding to a device currently available
+ DeviceVector supportedDevices = curProfile->getSupportedDevices();
+ if (!mAvailableOutputDevices.containsAtLeastOne(supportedDevices)) {
+ continue;
+ }
if (supportedDevices.getDevicesFromDeviceTypeAddrVec(devices).size()
!= devices.size()) {
continue;
@@ -1161,7 +1161,12 @@
// otherwise, fallback to the dynamic policies, if none match, query the engine.
// Secondary outputs are always found by dynamic policies as the engine do not support them
sp<AudioPolicyMix> primaryMix;
- status = mPolicyMixes.getOutputForAttr(*resultAttr, uid, *flags, primaryMix, secondaryMixes);
+ const audio_config_base_t clientConfig = {.sample_rate = config->sample_rate,
+ .channel_mask = config->channel_mask,
+ .format = config->format,
+ };
+ status = mPolicyMixes.getOutputForAttr(*resultAttr, clientConfig, uid, *flags, primaryMix,
+ secondaryMixes);
if (status != OK) {
return status;
}
@@ -1170,10 +1175,9 @@
bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && primaryMix != nullptr;
// FIXME: in case of RENDER policy, the output capabilities should be checked
- if ((usePrimaryOutputFromPolicyMixes
- || (secondaryMixes != nullptr && !secondaryMixes->empty()))
- && !audio_is_linear_pcm(config->format)) {
- ALOGD("%s: rejecting request as dynamic audio policy only support pcm", __func__);
+ if ((secondaryMixes != nullptr && !secondaryMixes->empty())
+ && !audio_is_linear_pcm(config->format)) {
+ ALOGD("%s: rejecting request as secondary mixes only support pcm", __func__);
return BAD_VALUE;
}
if (usePrimaryOutputFromPolicyMixes) {
@@ -1182,19 +1186,27 @@
primaryMix->mDeviceAddress,
AUDIO_FORMAT_DEFAULT);
sp<SwAudioOutputDescriptor> policyDesc = primaryMix->getOutput();
- if (deviceDesc != nullptr
- && (policyDesc == nullptr || (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT))) {
+ bool tryDirectForFlags = policyDesc == nullptr ||
+ (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT);
+ // if a direct output can be opened to deliver the track's multi-channel content to the
+ // output rather than being downmixed by the primary output, then use this direct
+ // output by by-passing the primary mix if possible, otherwise fall-through to primary
+ // mix.
+ bool tryDirectForChannelMask = policyDesc != nullptr
+ && (audio_channel_count_from_out_mask(policyDesc->getConfig().channel_mask) <
+ audio_channel_count_from_out_mask(config->channel_mask));
+ if (deviceDesc != nullptr && (tryDirectForFlags || tryDirectForChannelMask)) {
audio_io_handle_t newOutput;
status = openDirectOutput(
*stream, session, config,
(audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT),
DeviceVector(deviceDesc), &newOutput);
- if (status != NO_ERROR) {
- policyDesc = nullptr;
- } else {
+ if (status == NO_ERROR) {
policyDesc = mOutputs.valueFor(newOutput);
primaryMix->setOutput(policyDesc);
- }
+ } else if (tryDirectForFlags) {
+ policyDesc = nullptr;
+ } // otherwise use primary if available.
}
if (policyDesc != nullptr) {
policyDesc->mPolicyMix = primaryMix;
@@ -1868,7 +1880,8 @@
// (see b/200293124, the incorrect selection of AUDIO_OUTPUT_FLAG_VOIP_RX).
// 3: the output supporting the exact channel mask
// 4: the output with a higher channel count than requested
- // 5: the output with a higher sampling rate than requested
+ // 5: the output with the highest sampling rate if the requested sample rate is
+ // greater than default sampling rate
// 6: the output with the highest number of requested performance flags
// 7: the output with the bit depth the closest to the requested one
// 8: the primary output
@@ -1928,8 +1941,7 @@
}
// sampling rate match
- if (samplingRate > SAMPLE_RATE_HZ_DEFAULT &&
- samplingRate <= outputDesc->getSamplingRate()) {
+ if (samplingRate > SAMPLE_RATE_HZ_DEFAULT) {
currentMatchCriteria[4] = outputDesc->getSamplingRate();
}
@@ -5353,10 +5365,6 @@
}
}
- // The caller can have the devices criteria ignored by passing and empty vector, and
- // getSpatializerOutputProfile() will ignore the devices when looking for a match.
- // Otherwise an output profile supporting a spatializer effect that can be routed
- // to the specified devices must exist.
sp<IOProfile> profile =
getSpatializerOutputProfile(config, devices);
if (profile == nullptr) {
@@ -6330,8 +6338,8 @@
continue;
}
sp<AudioPolicyMix> primaryMix;
- status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
- client->flags(), primaryMix, nullptr);
+ status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->config(),
+ client->uid(), client->flags(), primaryMix, nullptr);
if (status != OK) {
continue;
}
@@ -6443,8 +6451,8 @@
for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
sp<AudioPolicyMix> primaryMix;
std::vector<sp<AudioPolicyMix>> secondaryMixes;
- status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
- client->flags(), primaryMix, &secondaryMixes);
+ status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->config(),
+ client->uid(), client->flags(), primaryMix, &secondaryMixes);
std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
for (auto &secondaryMix : secondaryMixes) {
sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
@@ -6702,8 +6710,8 @@
// check dynamic policies but only for primary descriptors (secondary not used for audible
// audio routing, only used for duplication for playback capture)
sp<AudioPolicyMix> policyMix;
- status_t status = mPolicyMixes.getOutputForAttr(attr, 0 /*uid unknown here*/,
- AUDIO_OUTPUT_FLAG_NONE, policyMix, nullptr /* secondaryMixes */);
+ status_t status = mPolicyMixes.getOutputForAttr(attr, AUDIO_CONFIG_BASE_INITIALIZER,
+ 0 /*uid unknown here*/, AUDIO_OUTPUT_FLAG_NONE, policyMix, nullptr);
if (status != OK) {
return status;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index db0ee15..87e6974 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -1099,6 +1099,18 @@
const audio_config_t *config,
const AudioDeviceTypeAddrVector &devices) const;
+
+ /**
+ * @brief Gets an IOProfile for a spatializer output with the best match with
+ * provided arguments.
+ * The caller can have the devices criteria ignored by passing and empty vector, and
+ * getSpatializerOutputProfile() will ignore the devices when looking for a match.
+ * Otherwise an output profile supporting a spatializer effect that can be routed
+ * to the specified devices must exist.
+ * @param config audio configuration describing the audio format, channels, sample rate...
+ * @param devices the sink audio device selected for playback
+ * @return an IOProfile that canbe used to open a spatializer output.
+ */
sp<IOProfile> getSpatializerOutputProfile(const audio_config_t *config,
const AudioDeviceTypeAddrVector &devices) const;
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index e7d945f..0c12991 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -516,9 +516,8 @@
void AudioPolicyService::doOnCheckSpatializer()
{
- Mutex::Autolock _l(mLock);
-
- ALOGI("%s mSpatializer %p level %d", __func__, mSpatializer.get(), (int)mSpatializer->getLevel());
+ ALOGV("%s mSpatializer %p level %d",
+ __func__, mSpatializer.get(), (int)mSpatializer->getLevel());
if (mSpatializer != nullptr) {
// Note: mSpatializer != nullptr => mAudioPolicyManager != nullptr
@@ -527,6 +526,8 @@
audio_io_handle_t newOutput;
const audio_attributes_t attr = attributes_initializer(AUDIO_USAGE_MEDIA);
audio_config_base_t config = mSpatializer->getAudioInConfig();
+
+ Mutex::Autolock _l(mLock);
status_t status =
mAudioPolicyManager->getSpatializerOutput(&config, &attr, &newOutput);
ALOGV("%s currentOutput %d newOutput %d channel_mask %#x",
@@ -538,21 +539,19 @@
mLock.unlock();
// It is OK to call detachOutput() is none is already attached.
mSpatializer->detachOutput();
- if (status != NO_ERROR || newOutput == AUDIO_IO_HANDLE_NONE) {
- mLock.lock();
- return;
+ if (status == NO_ERROR && newOutput != AUDIO_IO_HANDLE_NONE) {
+ status = mSpatializer->attachOutput(newOutput, numActiveTracks);
}
- status = mSpatializer->attachOutput(newOutput, numActiveTracks);
mLock.lock();
if (status != NO_ERROR) {
mAudioPolicyManager->releaseSpatializerOutput(newOutput);
}
} else if (mSpatializer->getLevel() == media::SpatializationLevel::NONE
&& mSpatializer->getOutput() != AUDIO_IO_HANDLE_NONE) {
- mLock.unlock();
audio_io_handle_t output = mSpatializer->detachOutput();
- mLock.lock();
+
if (output != AUDIO_IO_HANDLE_NONE) {
+ Mutex::Autolock _l(mLock);
mAudioPolicyManager->releaseSpatializerOutput(output);
}
}
@@ -581,19 +580,16 @@
void AudioPolicyService::doOnUpdateActiveSpatializerTracks()
{
- sp<Spatializer> spatializer;
+ if (mSpatializer == nullptr) {
+ return;
+ }
+ audio_io_handle_t output = mSpatializer->getOutput();
size_t activeClients;
{
Mutex::Autolock _l(mLock);
- if (mSpatializer == nullptr) {
- return;
- }
- spatializer = mSpatializer;
- activeClients = countActiveClientsOnOutput_l(mSpatializer->getOutput());
+ activeClients = countActiveClientsOnOutput_l(output);
}
- if (spatializer != nullptr) {
- spatializer->updateActiveTracks(activeClients);
- }
+ mSpatializer->updateActiveTracks(activeClients);
}
status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
@@ -1215,6 +1211,14 @@
dumpReleaseLock(mLock, locked);
+ if (mSpatializer != nullptr) {
+ std::string dumpString = mSpatializer->toString(1 /* level */);
+ write(fd, dumpString.c_str(), dumpString.size());
+ } else {
+ String8 spatializerPtr = String8::format("Spatializer no supportted on this device\n");
+ write(fd, spatializerPtr.c_str(), spatializerPtr.size());
+ }
+
{
std::string timeCheckStats = getIAudioPolicyServiceStatistics().dump();
dprintf(fd, "\nIAudioPolicyService binder call profile\n");
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 3a08cf8..a87d871 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -1060,6 +1060,7 @@
CaptureStateNotifier mCaptureStateNotifier;
+ // created in onFirstRef() and never cleared: does not need to be guarded by mLock
sp<Spatializer> mSpatializer;
void *mLibraryHandle = nullptr;
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index c199a76..b6b90ff 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -15,11 +15,12 @@
** limitations under the License.
*/
-
+#include <string>
#define LOG_TAG "Spatializer"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
@@ -58,11 +59,13 @@
if (!_tmp.ok()) return aidl_utils::binderStatusFromStatusT(_tmp.error()); \
std::move(_tmp.value()); })
-audio_channel_mask_t getMaxChannelMask(std::vector<audio_channel_mask_t> masks) {
+static audio_channel_mask_t getMaxChannelMask(
+ const std::vector<audio_channel_mask_t>& masks, size_t channelLimit = SIZE_MAX) {
uint32_t maxCount = 0;
audio_channel_mask_t maxMask = AUDIO_CHANNEL_NONE;
for (auto mask : masks) {
const size_t count = audio_channel_count_from_out_mask(mask);
+ if (count > channelLimit) continue; // ignore masks greater than channelLimit
if (count > maxCount) {
maxMask = mask;
maxCount = count;
@@ -84,6 +87,7 @@
kWhatOnFramesProcessed, // AudioEffect::EVENT_FRAMES_PROCESSED
kWhatOnHeadToStagePose, // SpatializerPoseController::Listener::onHeadToStagePose
kWhatOnActualModeChange, // SpatializerPoseController::Listener::onActualModeChange
+ kWhatOnLatencyModesChanged, // Spatializer::onSupportedLatencyModesChanged
};
static constexpr const char *kNumFramesKey = "numFrames";
static constexpr const char *kModeKey = "mode";
@@ -93,15 +97,27 @@
static constexpr const char *kRotation0Key = "rotation0";
static constexpr const char *kRotation1Key = "rotation1";
static constexpr const char *kRotation2Key = "rotation2";
+ static constexpr const char *kLatencyModesKey = "latencyModes";
+
+ class LatencyModes : public RefBase {
+ public:
+ LatencyModes(audio_io_handle_t output,
+ const std::vector<audio_latency_mode_t>& latencyModes)
+ : mOutput(output), mLatencyModes(latencyModes) {}
+ ~LatencyModes() = default;
+
+ audio_io_handle_t mOutput;
+ std::vector<audio_latency_mode_t> mLatencyModes;
+ };
void onMessageReceived(const sp<AMessage> &msg) override {
+ sp<Spatializer> spatializer = mSpatializer.promote();
+ if (spatializer == nullptr) {
+ ALOGW("%s: Cannot promote spatializer", __func__);
+ return;
+ }
switch (msg->what()) {
case kWhatOnFramesProcessed: {
- sp<Spatializer> spatializer = mSpatializer.promote();
- if (spatializer == nullptr) {
- ALOGW("%s: Cannot promote spatializer", __func__);
- return;
- }
int numFrames;
if (!msg->findInt32(kNumFramesKey, &numFrames)) {
ALOGE("%s: Cannot find num frames!", __func__);
@@ -112,11 +128,6 @@
}
} break;
case kWhatOnHeadToStagePose: {
- sp<Spatializer> spatializer = mSpatializer.promote();
- if (spatializer == nullptr) {
- ALOGW("%s: Cannot promote spatializer", __func__);
- return;
- }
std::vector<float> headToStage(sHeadPoseKeys.size());
for (size_t i = 0 ; i < sHeadPoseKeys.size(); i++) {
if (!msg->findFloat(sHeadPoseKeys[i], &headToStage[i])) {
@@ -127,18 +138,25 @@
spatializer->onHeadToStagePoseMsg(headToStage);
} break;
case kWhatOnActualModeChange: {
- sp<Spatializer> spatializer = mSpatializer.promote();
- if (spatializer == nullptr) {
- ALOGW("%s: Cannot promote spatializer", __func__);
- return;
- }
int mode;
- if (!msg->findInt32(EngineCallbackHandler::kModeKey, &mode)) {
+ if (!msg->findInt32(kModeKey, &mode)) {
ALOGE("%s: Cannot find actualMode!", __func__);
return;
}
spatializer->onActualModeChangeMsg(static_cast<HeadTrackingMode>(mode));
} break;
+
+ case kWhatOnLatencyModesChanged: {
+ sp<RefBase> object;
+ if (!msg->findObject(kLatencyModesKey, &object)) {
+ ALOGE("%s: Cannot find latency modes!", __func__);
+ return;
+ }
+ sp<LatencyModes> latencyModes = static_cast<LatencyModes*>(object.get());
+ spatializer->onSupportedLatencyModesChangedMsg(
+ latencyModes->mOutput, std::move(latencyModes->mLatencyModes));
+ } break;
+
default:
LOG_ALWAYS_FATAL("Invalid callback message %d", msg->what());
}
@@ -157,6 +175,41 @@
};
// ---------------------------------------------------------------------------
+
+// Convert recorded sensor data to string with level indentation.
+std::string Spatializer::HeadToStagePoseRecorder::toString(unsigned level) const {
+ std::string prefixSpace(level, ' ');
+ return mPoseRecordLog.dumpToString((prefixSpace + " ").c_str(), Spatializer::mMaxLocalLogLine);
+}
+
+// Compute sensor data, record into local log when it is time.
+void Spatializer::HeadToStagePoseRecorder::record(const std::vector<float>& headToStage) {
+ if (headToStage.size() != mPoseVectorSize) return;
+
+ if (mNumOfSampleSinceLastRecord++ == 0) {
+ mFirstSampleTimestamp = std::chrono::steady_clock::now();
+ }
+ // if it's time, do record and reset.
+ if (shouldRecordLog()) {
+ poseSumToAverage();
+ mPoseRecordLog.log(
+ "mean: %s, min: %s, max %s, calculated %d samples in %0.4f second(s)",
+ Spatializer::toString<double>(mPoseRadianSum, true /* radianToDegree */).c_str(),
+ Spatializer::toString<float>(mMinPoseAngle, true /* radianToDegree */).c_str(),
+ Spatializer::toString<float>(mMaxPoseAngle, true /* radianToDegree */).c_str(),
+ mNumOfSampleSinceLastRecord, mNumOfSecondsSinceLastRecord.count());
+ resetRecord();
+ }
+ // update stream average.
+ for (int i = 0; i < mPoseVectorSize; i++) {
+ mPoseRadianSum[i] += headToStage[i];
+ mMaxPoseAngle[i] = std::max(mMaxPoseAngle[i], headToStage[i]);
+ mMinPoseAngle[i] = std::min(mMinPoseAngle[i], headToStage[i]);
+ }
+ return;
+}
+
+// ---------------------------------------------------------------------------
sp<Spatializer> Spatializer::create(SpatializerPolicyCallback *callback) {
sp<Spatializer> spatializer;
@@ -176,17 +229,22 @@
ALOG_ASSERT(!descriptors.empty(),
"%s getDescriptors() returned no error but empty list", __func__);
- //TODO: get supported spatialization modes from FX engine or descriptor
-
+ // TODO: get supported spatialization modes from FX engine or descriptor
sp<EffectHalInterface> effect;
status = effectsFactoryHal->createEffect(&descriptors[0].uuid, AUDIO_SESSION_OUTPUT_STAGE,
AUDIO_IO_HANDLE_NONE, AUDIO_PORT_HANDLE_NONE, &effect);
- ALOGI("%s FX create status %d effect %p", __func__, status, effect.get());
+ ALOGI("%s FX create status %d effect ID %" PRId64, __func__, status,
+ effect ? effect->effectId() : 0);
if (status == NO_ERROR && effect != nullptr) {
spatializer = new Spatializer(descriptors[0], callback);
if (spatializer->loadEngineConfiguration(effect) != NO_ERROR) {
spatializer.clear();
+ ALOGW("%s loadEngine error: %d effect Id %" PRId64,
+ __func__, status, effect ? effect->effectId() : 0);
+ } else {
+ spatializer->mLocalLog.log("%s with effect Id %" PRId64, __func__,
+ effect ? effect->effectId() : 0);
}
}
@@ -221,6 +279,16 @@
mHandler.clear();
}
+static std::string channelMaskVectorToString(
+ const std::vector<audio_channel_mask_t>& masks) {
+ std::stringstream ss;
+ for (const auto &mask : masks) {
+ if (ss.tellp() != 0) ss << "|";
+ ss << mask;
+ }
+ return ss.str();
+}
+
status_t Spatializer::loadEngineConfiguration(sp<EffectHalInterface> effect) {
ALOGV("%s", __func__);
@@ -268,6 +336,7 @@
ALOGW("%s: cannot get SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES", __func__);
return status;
}
+
for (const auto spatializationMode : spatializationModes) {
if (!aidl_utils::isValidEnum(spatializationMode)) {
ALOGW("%s: ignoring spatializationMode:%d", __func__, (int)spatializationMode);
@@ -309,7 +378,7 @@
}
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE)
- .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)getMaxChannelMask(mChannelMasks))
+ .set(AMEDIAMETRICS_PROP_CHANNELMASKS, channelMaskVectorToString(mChannelMasks))
.set(AMEDIAMETRICS_PROP_LEVELS, aidl_utils::enumsToString(mLevels))
.set(AMEDIAMETRICS_PROP_MODES, aidl_utils::enumsToString(mSpatializationModes))
.set(AMEDIAMETRICS_PROP_HEADTRACKINGMODES, aidl_utils::enumsToString(mHeadTrackingModes))
@@ -323,7 +392,7 @@
std::lock_guard lock(mLock);
audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
// For now use highest supported channel count
- config.channel_mask = getMaxChannelMask(mChannelMasks);
+ config.channel_mask = getMaxChannelMask(mChannelMasks, FCC_LIMIT);
return config;
}
@@ -334,6 +403,17 @@
return BAD_VALUE;
}
+ if (mSpatializerCallback != nullptr) {
+ if (IInterface::asBinder(callback) == IInterface::asBinder(mSpatializerCallback)) {
+ ALOGW("%s: Registering callback %p again",
+ __func__, mSpatializerCallback.get());
+ return NO_ERROR;
+ }
+ ALOGE("%s: Already one client registered with callback %p",
+ __func__, mSpatializerCallback.get());
+ return INVALID_OPERATION;
+ }
+
sp<IBinder> binder = IInterface::asBinder(callback);
status_t status = binder->linkToDeath(this);
if (status == NO_ERROR) {
@@ -366,7 +446,8 @@
}
Status Spatializer::setLevel(SpatializationLevel level) {
- ALOGV("%s level %d", __func__, (int)level);
+ ALOGV("%s level %s", __func__, media::toString(level).c_str());
+ mLocalLog.log("%s with %s", __func__, media::toString(level).c_str());
if (level != SpatializationLevel::NONE
&& std::find(mLevels.begin(), mLevels.end(), level) == mLevels.end()) {
return binderStatusFromStatusT(BAD_VALUE);
@@ -426,11 +507,12 @@
}
Status Spatializer::setDesiredHeadTrackingMode(SpatializerHeadTrackingMode mode) {
- ALOGV("%s mode %d", __func__, (int)mode);
+ ALOGV("%s mode %s", __func__, media::toString(mode).c_str());
if (!mSupportsHeadTracking) {
return binderStatusFromStatusT(INVALID_OPERATION);
}
+ mLocalLog.log("%s with %s", __func__, media::toString(mode).c_str());
std::lock_guard lock(mLock);
switch (mode) {
case SpatializerHeadTrackingMode::OTHER:
@@ -485,6 +567,7 @@
}
std::lock_guard lock(mLock);
if (mPoseController != nullptr) {
+ mLocalLog.log("%s with screenToStage %s", __func__, toString<float>(screenToStage).c_str());
mPoseController->setScreenToStagePose(maybePose.value());
}
return Status::ok();
@@ -520,6 +603,7 @@
}
std::lock_guard lock(mLock);
if (mHeadSensor != sensorHandle) {
+ mLocalLog.log("%s with 0x%08x", __func__, sensorHandle);
mHeadSensor = sensorHandle;
checkPoseController_l();
checkSensorsState_l();
@@ -534,6 +618,7 @@
}
std::lock_guard lock(mLock);
if (mScreenSensor != sensorHandle) {
+ mLocalLog.log("%s with 0x%08x", __func__, sensorHandle);
mScreenSensor = sensorHandle;
// TODO: consider a new method setHeadAndScreenSensor()
// because we generally set both at the same time.
@@ -550,6 +635,7 @@
}
std::lock_guard lock(mLock);
mDisplayOrientation = physicalToLogicalAngle;
+ mLocalLog.log("%s with %f", __func__, physicalToLogicalAngle);
if (mPoseController != nullptr) {
mPoseController->setDisplayOrientation(mDisplayOrientation);
}
@@ -564,6 +650,7 @@
std::lock_guard lock(mLock);
ALOGV("%s hingeAngle %f", __func__, hingeAngle);
if (mEngine != nullptr) {
+ mLocalLog.log("%s with %f", __func__, hingeAngle);
setEffectParameter_l(SPATIALIZER_PARAM_HINGE_ANGLE, std::vector<float>{hingeAngle});
}
return Status::ok();
@@ -650,6 +737,8 @@
callback = mHeadTrackingCallback;
if (mEngine != nullptr) {
setEffectParameter_l(SPATIALIZER_PARAM_HEAD_TO_STAGE, headToStage);
+ mPoseRecorder.record(headToStage);
+ mPoseDurableRecorder.record(headToStage);
}
}
@@ -659,7 +748,9 @@
}
void Spatializer::onActualModeChange(HeadTrackingMode mode) {
- ALOGV("%s(%d)", __func__, (int)mode);
+ std::string modeStr = SpatializerPoseController::toString(mode);
+ ALOGV("%s(%s)", __func__, modeStr.c_str());
+ mLocalLog.log("%s with %s", __func__, modeStr.c_str());
sp<AMessage> msg =
new AMessage(EngineCallbackHandler::kWhatOnActualModeChange, mHandler);
msg->setInt32(EngineCallbackHandler::kModeKey, static_cast<int>(mode));
@@ -695,6 +786,9 @@
std::vector<SpatializerHeadTrackingMode>{spatializerMode});
}
callback = mHeadTrackingCallback;
+ mLocalLog.log("%s: %s, spatializerMode %s", __func__,
+ SpatializerPoseController::toString(mode).c_str(),
+ media::toString(spatializerMode).c_str());
}
if (callback != nullptr) {
callback->onHeadTrackingModeChanged(spatializerMode);
@@ -708,13 +802,17 @@
{
std::lock_guard lock(mLock);
ALOGV("%s output %d mOutput %d", __func__, (int)output, (int)mOutput);
+ mLocalLog.log("%s with output %d tracks %zu (mOutput %d)", __func__, (int)output,
+ numActiveTracks, (int)mOutput);
if (mOutput != AUDIO_IO_HANDLE_NONE) {
LOG_ALWAYS_FATAL_IF(mEngine == nullptr, "%s output set without FX engine", __func__);
// remove FX instance
mEngine->setEnabled(false);
mEngine.clear();
mPoseController.reset();
+ AudioSystem::removeSupportedLatencyModesCallback(this);
}
+
// create FX instance on output
AttributionSourceState attributionSource = AttributionSourceState();
mEngine = new AudioEffect(attributionSource);
@@ -730,6 +828,13 @@
outputChanged = mOutput != output;
mOutput = output;
mNumActiveTracks = numActiveTracks;
+ AudioSystem::addSupportedLatencyModesCallback(this);
+
+ std::vector<audio_latency_mode_t> latencyModes;
+ status = AudioSystem::getSupportedLatencyModes(mOutput, &latencyModes);
+ if (status == OK) {
+ mSupportedLatencyModes = latencyModes;
+ }
checkEngineState_l();
if (mSupportsHeadTracking) {
@@ -752,6 +857,7 @@
{
std::lock_guard lock(mLock);
+ mLocalLog.log("%s with output %d tracks %zu", __func__, (int)mOutput, mNumActiveTracks);
ALOGV("%s mOutput %d", __func__, (int)mOutput);
if (mOutput == AUDIO_IO_HANDLE_NONE) {
return output;
@@ -759,6 +865,7 @@
// remove FX instance
mEngine->setEnabled(false);
mEngine.clear();
+ AudioSystem::removeSupportedLatencyModesCallback(this);
output = mOutput;
mOutput = AUDIO_IO_HANDLE_NONE;
mPoseController.reset();
@@ -771,9 +878,31 @@
return output;
}
+void Spatializer::onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) {
+ ALOGV("%s output %d num modes %zu", __func__, (int)output, modes.size());
+ sp<AMessage> msg =
+ new AMessage(EngineCallbackHandler::kWhatOnLatencyModesChanged, mHandler);
+ msg->setObject(EngineCallbackHandler::kLatencyModesKey,
+ sp<EngineCallbackHandler::LatencyModes>::make(output, modes));
+ msg->post();
+}
+
+void Spatializer::onSupportedLatencyModesChangedMsg(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes) {
+ std::lock_guard lock(mLock);
+ ALOGV("%s output %d mOutput %d num modes %zu",
+ __func__, (int)output, (int)mOutput, modes.size());
+ if (output == mOutput) {
+ mSupportedLatencyModes = std::move(modes);
+ checkSensorsState_l();
+ }
+}
+
void Spatializer::updateActiveTracks(size_t numActiveTracks) {
std::lock_guard lock(mLock);
if (mNumActiveTracks != numActiveTracks) {
+ mLocalLog.log("%s from %zu to %zu", __func__, mNumActiveTracks, numActiveTracks);
mNumActiveTracks = numActiveTracks;
checkEngineState_l();
checkSensorsState_l();
@@ -781,17 +910,25 @@
}
void Spatializer::checkSensorsState_l() {
+ audio_latency_mode_t requestedLatencyMode = AUDIO_LATENCY_MODE_FREE;
+ bool lowLatencySupported = mSupportedLatencyModes.empty()
+ || (std::find(mSupportedLatencyModes.begin(), mSupportedLatencyModes.end(),
+ AUDIO_LATENCY_MODE_LOW) != mSupportedLatencyModes.end());
if (mSupportsHeadTracking && mPoseController != nullptr) {
- if (mNumActiveTracks > 0 && mLevel != SpatializationLevel::NONE
+ if (lowLatencySupported && mNumActiveTracks > 0 && mLevel != SpatializationLevel::NONE
&& mDesiredHeadTrackingMode != HeadTrackingMode::STATIC
&& mHeadSensor != SpatializerPoseController::INVALID_SENSOR) {
mPoseController->setHeadSensor(mHeadSensor);
mPoseController->setScreenSensor(mScreenSensor);
+ requestedLatencyMode = AUDIO_LATENCY_MODE_LOW;
} else {
mPoseController->setHeadSensor(SpatializerPoseController::INVALID_SENSOR);
mPoseController->setScreenSensor(SpatializerPoseController::INVALID_SENSOR);
}
}
+ if (mOutput != AUDIO_IO_HANDLE_NONE) {
+ AudioSystem::setRequestedLatencyMode(mOutput, requestedLatencyMode);
+ }
}
void Spatializer::checkEngineState_l() {
@@ -861,4 +998,80 @@
msg->post();
}
+std::string Spatializer::toString(unsigned level) const {
+ std::string prefixSpace;
+ prefixSpace.append(level, ' ');
+ std::string ss = prefixSpace + "Spatializer:\n";
+ bool needUnlock = false;
+
+ prefixSpace += ' ';
+ if (!mLock.try_lock()) {
+ // dumpsys even try_lock failed, information dump can be useful although may not accurate
+ ss.append(prefixSpace).append("try_lock failed, dumpsys below maybe INACCURATE!\n");
+ } else {
+ needUnlock = true;
+ }
+
+ // Spatializer class information.
+ // 1. Capabilities (mLevels, mHeadTrackingModes, mSpatializationModes, mChannelMasks, etc)
+ ss.append(prefixSpace).append("Supported levels: [");
+ for (auto& level : mLevels) {
+ base::StringAppendF(&ss, " %s", media::toString(level).c_str());
+ }
+ base::StringAppendF(&ss, "], mLevel: %s", media::toString(mLevel).c_str());
+
+ base::StringAppendF(&ss, "\n%smHeadTrackingModes: [", prefixSpace.c_str());
+ for (auto& mode : mHeadTrackingModes) {
+ base::StringAppendF(&ss, " %s", media::toString(mode).c_str());
+ }
+ base::StringAppendF(&ss, "], Desired: %s, Actual %s\n",
+ SpatializerPoseController::toString(mDesiredHeadTrackingMode).c_str(),
+ media::toString(mActualHeadTrackingMode).c_str());
+
+ base::StringAppendF(&ss, "%smSpatializationModes: [", prefixSpace.c_str());
+ for (auto& mode : mSpatializationModes) {
+ base::StringAppendF(&ss, " %s", media::toString(mode).c_str());
+ }
+ ss += "]\n";
+
+ base::StringAppendF(&ss, "%smChannelMasks: ", prefixSpace.c_str());
+ for (auto& mask : mChannelMasks) {
+ base::StringAppendF(&ss, "%s", audio_channel_out_mask_to_string(mask));
+ }
+ base::StringAppendF(&ss, "\n%smSupportsHeadTracking: %s\n", prefixSpace.c_str(),
+ mSupportsHeadTracking ? "true" : "false");
+ // 2. Settings (Output, tracks)
+ base::StringAppendF(&ss, "%smNumActiveTracks: %zu\n", prefixSpace.c_str(), mNumActiveTracks);
+ base::StringAppendF(&ss, "%sOutputStreamHandle: %d\n", prefixSpace.c_str(), (int)mOutput);
+
+ // 3. Sensors, Effect information.
+ base::StringAppendF(&ss, "%sHeadSensorHandle: 0x%08x\n", prefixSpace.c_str(), mHeadSensor);
+ base::StringAppendF(&ss, "%sScreenSensorHandle: 0x%08x\n", prefixSpace.c_str(), mScreenSensor);
+ base::StringAppendF(&ss, "%sEffectHandle: %p\n", prefixSpace.c_str(), mEngine.get());
+ base::StringAppendF(&ss, "%sDisplayOrientation: %f\n", prefixSpace.c_str(),
+ mDisplayOrientation);
+
+ ss.append(prefixSpace + "CommandLog:\n");
+ ss += mLocalLog.dumpToString((prefixSpace + " ").c_str(), mMaxLocalLogLine);
+
+ // PostController dump.
+ if (mPoseController != nullptr) {
+ ss += mPoseController->toString(level + 1);
+ ss.append(prefixSpace +
+ "Sensor data format - [rx, ry, rz, vx, vy, vz] (units-degree, "
+ "r-transform, v-angular velocity, x-pitch, y-roll, z-yaw):\n");
+ ss.append(prefixSpace + "PerMinuteHistory:\n");
+ ss += mPoseDurableRecorder.toString(level + 1);
+ ss.append(prefixSpace + "PerSecondHistory:\n");
+ ss += mPoseRecorder.toString(level + 1);
+ } else {
+ ss.append(prefixSpace).append("SpatializerPoseController not exist\n");
+ }
+
+ if (needUnlock) {
+ mLock.unlock();
+ }
+ return ss;
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index 29f4b08..c9071d4 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -17,15 +17,19 @@
#ifndef ANDROID_MEDIA_SPATIALIZER_H
#define ANDROID_MEDIA_SPATIALIZER_H
+#include <android-base/stringprintf.h>
#include <android/media/BnEffect.h>
#include <android/media/BnSpatializer.h>
#include <android/media/SpatializationLevel.h>
#include <android/media/SpatializationMode.h>
#include <android/media/SpatializerHeadTrackingMode.h>
+#include <audio_utils/SimpleLog.h>
+#include <math.h>
+#include <media/AudioEffect.h>
#include <media/audiohal/EffectHalInterface.h>
#include <media/stagefright/foundation/ALooper.h>
-#include <media/AudioEffect.h>
#include <system/audio_effects/effect_spatializer.h>
+#include <string>
#include "SpatializerPoseController.h"
@@ -85,7 +89,8 @@
*/
class Spatializer : public media::BnSpatializer,
public IBinder::DeathRecipient,
- private SpatializerPoseController::Listener {
+ private SpatializerPoseController::Listener,
+ public virtual AudioSystem::SupportedLatencyModesCallback {
public:
static sp<Spatializer> create(SpatializerPolicyCallback *callback);
@@ -122,6 +127,10 @@
/** IBinder::DeathRecipient. Listen to the death of the INativeSpatializerCallback. */
virtual void binderDied(const wp<IBinder>& who);
+ /** SupportedLatencyModesCallback */
+ void onSupportedLatencyModesChanged(
+ audio_io_handle_t output, const std::vector<audio_latency_mode_t>& modes) override;
+
/** Registers a INativeSpatializerCallback when a client is attached to this Spatializer
* by audio policy service.
*/
@@ -150,6 +159,45 @@
void calculateHeadPose();
+ /** Convert fields in Spatializer and sub-modules to a string. Disable thread-safety-analysis
+ * here because we want to dump mutex guarded members even try_lock failed to provide as much
+ * information as possible for debugging purpose. */
+ std::string toString(unsigned level) const NO_THREAD_SAFETY_ANALYSIS;
+
+ static std::string toString(audio_latency_mode_t mode) {
+ switch (mode) {
+ case AUDIO_LATENCY_MODE_FREE:
+ return "LATENCY_MODE_FREE";
+ case AUDIO_LATENCY_MODE_LOW:
+ return "LATENCY_MODE_LOW";
+ }
+ return "EnumNotImplemented";
+ };
+
+ /**
+ * Format head to stage vector to a string, [0.00, 0.00, 0.00, -1.29, -0.50, 15.27].
+ */
+ template <typename T>
+ static std::string toString(const std::vector<T>& vec, bool radianToDegree = false) {
+ if (vec.size() == 0) {
+ return "[]";
+ }
+
+ std::string ss = "[";
+ for (auto f = vec.begin(); f != vec.end(); ++f) {
+ if (f != vec.begin()) {
+ ss .append(", ");
+ }
+ if (radianToDegree) {
+ base::StringAppendF(&ss, "%0.2f", HeadToStagePoseRecorder::getDegreeWithRadian(*f));
+ } else {
+ base::StringAppendF(&ss, "%f", *f);
+ }
+ }
+ ss.append("]");
+ return ss;
+ };
+
private:
Spatializer(effect_descriptor_t engineDescriptor,
SpatializerPolicyCallback *callback);
@@ -162,6 +210,8 @@
void onHeadToStagePoseMsg(const std::vector<float>& headToStage);
void onActualModeChangeMsg(media::HeadTrackingMode mode);
+ void onSupportedLatencyModesChangedMsg(
+ audio_io_handle_t output, std::vector<audio_latency_mode_t>&& modes);
static constexpr int kMaxEffectParamValues = 10;
/**
@@ -354,10 +404,105 @@
sp<EngineCallbackHandler> mHandler;
size_t mNumActiveTracks GUARDED_BY(mLock) = 0;
+ std::vector<audio_latency_mode_t> mSupportedLatencyModes GUARDED_BY(mLock);
- static const std::vector<const char *> sHeadPoseKeys;
-};
+ static const std::vector<const char*> sHeadPoseKeys;
+ // Local log for command messages.
+ static constexpr int mMaxLocalLogLine = 10;
+ SimpleLog mLocalLog{mMaxLocalLogLine};
+
+ /**
+ * @brief Calculate and record sensor data.
+ * Dump to local log with max/average pose angle every mPoseRecordThreshold.
+ */
+ class HeadToStagePoseRecorder {
+ public:
+ HeadToStagePoseRecorder(std::chrono::duration<double> threshold, int maxLogLine)
+ : mPoseRecordThreshold(threshold), mPoseRecordLog(maxLogLine) {
+ resetRecord();
+ }
+
+ /** Convert recorded sensor data to string with level indentation */
+ std::string toString(unsigned level) const;
+
+ /**
+ * @brief Calculate sensor data, record into local log when it is time.
+ *
+ * @param headToStage The vector from Pose3f::toVector().
+ */
+ void record(const std::vector<float>& headToStage);
+
+ static constexpr float getDegreeWithRadian(const float radian) {
+ float radianToDegreeRatio = (180 / PI);
+ return (radian * radianToDegreeRatio);
+ }
+
+ private:
+ static constexpr float PI = M_PI;
+ /**
+ * Pose recorder time threshold to record sensor data in local log.
+ * Sensor data will be recorded into log at least every mPoseRecordThreshold.
+ */
+ std::chrono::duration<double> mPoseRecordThreshold;
+ // Number of seconds pass since last record.
+ std::chrono::duration<double> mNumOfSecondsSinceLastRecord;
+ /**
+ * According to frameworks/av/media/libheadtracking/include/media/Pose.h
+ * "The vector will have exactly 6 elements, where the first three are a translation vector
+ * and the last three are a rotation vector."
+ */
+ static constexpr size_t mPoseVectorSize = 6;
+ /**
+ * Timestamp of last sensor data record in local log.
+ */
+ std::chrono::time_point<std::chrono::steady_clock> mFirstSampleTimestamp;
+ /**
+ * Number of sensor samples received since last record, sample rate is ~100Hz which produce
+ * ~6k samples/minute.
+ */
+ uint32_t mNumOfSampleSinceLastRecord = 0;
+ /* The sum of pose angle represented by radian since last dump, div
+ * mNumOfSampleSinceLastRecord to get arithmetic mean. Largest possible value: 2PI * 100Hz *
+ * mPoseRecordThreshold.
+ */
+ std::vector<double> mPoseRadianSum;
+ std::vector<float> mMaxPoseAngle;
+ std::vector<float> mMinPoseAngle;
+ // Local log for history sensor data.
+ SimpleLog mPoseRecordLog{mMaxLocalLogLine};
+
+ bool shouldRecordLog() {
+ mNumOfSecondsSinceLastRecord = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::steady_clock::now() - mFirstSampleTimestamp);
+ return mNumOfSecondsSinceLastRecord >= mPoseRecordThreshold;
+ }
+
+ void resetRecord() {
+ mPoseRadianSum.assign(mPoseVectorSize, 0);
+ mMaxPoseAngle.assign(mPoseVectorSize, -PI);
+ mMinPoseAngle.assign(mPoseVectorSize, PI);
+ mNumOfSampleSinceLastRecord = 0;
+ mNumOfSecondsSinceLastRecord = std::chrono::seconds(0);
+ }
+
+ // Add each sample to sum and only calculate when record.
+ void poseSumToAverage() {
+ if (mNumOfSampleSinceLastRecord == 0) return;
+ for (auto& p : mPoseRadianSum) {
+ const float reciprocal = 1.f / mNumOfSampleSinceLastRecord;
+ p *= reciprocal;
+ }
+ }
+ }; // HeadToStagePoseRecorder
+
+ // Record one log line per second (up to mMaxLocalLogLine) to capture most recent sensor data.
+ HeadToStagePoseRecorder mPoseRecorder GUARDED_BY(mLock) =
+ HeadToStagePoseRecorder(std::chrono::seconds(1), mMaxLocalLogLine);
+ // Record one log line per minute (up to mMaxLocalLogLine) to capture durable sensor data.
+ HeadToStagePoseRecorder mPoseDurableRecorder GUARDED_BY(mLock) =
+ HeadToStagePoseRecorder(std::chrono::minutes(1), mMaxLocalLogLine);
+}; // Spatializer
}; // namespace android
diff --git a/services/audiopolicy/service/SpatializerPoseController.cpp b/services/audiopolicy/service/SpatializerPoseController.cpp
index 304d44a..97ddd4e 100644
--- a/services/audiopolicy/service/SpatializerPoseController.cpp
+++ b/services/audiopolicy/service/SpatializerPoseController.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
#include "SpatializerPoseController.h"
+#include <android-base/stringprintf.h>
+#include <chrono>
+#include <cstdint>
+#include <string>
#define LOG_TAG "SpatializerPoseController"
//#define LOG_NDEBUG 0
@@ -61,13 +65,13 @@
// Screen is considered to be unstable (not still) if it has moved significantly within the last
// time window of this duration.
-constexpr auto kScreenStillnessWindowDuration = 3s;
+constexpr auto kScreenStillnessWindowDuration = 750ms;
// Screen is considered to have moved significantly if translated by this much (in meter, approx).
constexpr float kScreenStillnessTranslationThreshold = 0.1f;
// Screen is considered to have moved significantly if rotated by this much (in radians, approx).
-constexpr float kScreenStillnessRotationThreshold = 7.0f / 180 * M_PI;
+constexpr float kScreenStillnessRotationThreshold = 15.0f / 180 * M_PI;
// Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
// what we use for pose filtering.
@@ -100,7 +104,10 @@
.screenStillnessRotationalThreshold = kScreenStillnessRotationThreshold,
})),
mPoseProvider(SensorPoseProvider::create("headtracker", this)),
- mThread([this, maxUpdatePeriod] {
+ mThread([this, maxUpdatePeriod] { // It's important that mThread is initialized after
+ // everything else because it runs a member
+ // function that may use any member
+ // of this class.
while (true) {
Pose3f headToStage;
std::optional<HeadTrackingMode> modeIfChanged;
@@ -288,4 +295,58 @@
}
}
+std::string SpatializerPoseController::toString(unsigned level) const {
+ std::string prefixSpace;
+ prefixSpace.append(level, ' ');
+ std::string ss = prefixSpace + "SpatializerPoseController:\n";
+ bool needUnlock = false;
+
+ prefixSpace += ' ';
+ auto now = std::chrono::steady_clock::now();
+ if (!mMutex.try_lock_until(now + media::kSpatializerDumpSysTimeOutInSecond)) {
+ ss.append(prefixSpace).append("try_lock failed, dumpsys maybe INACCURATE!\n");
+ } else {
+ needUnlock = true;
+ }
+
+ ss += prefixSpace;
+ if (mHeadSensor == media::SensorPoseProvider::INVALID_HANDLE) {
+ ss.append("HeadSensor: INVALID\n");
+ } else {
+ base::StringAppendF(&ss, "HeadSensor: 0x%08x\n", mHeadSensor);
+ }
+
+ ss += prefixSpace;
+ if (mScreenSensor == media::SensorPoseProvider::INVALID_HANDLE) {
+ ss += "ScreenSensor: INVALID\n";
+ } else {
+ base::StringAppendF(&ss, "ScreenSensor: 0x%08x\n", mScreenSensor);
+ }
+
+ ss += prefixSpace;
+ if (mActualMode.has_value()) {
+ base::StringAppendF(&ss, "ActualMode: %s", toString(mActualMode.value()).c_str());
+ } else {
+ ss += "ActualMode NOTEXIST\n";
+ }
+
+ if (mProcessor) {
+ ss += mProcessor->toString_l(level + 1);
+ } else {
+ ss.append(prefixSpace.c_str()).append("HeadTrackingProcessor not exist\n");
+ }
+
+ if (mPoseProvider) {
+ ss += mPoseProvider->toString(level + 1);
+ } else {
+ ss.append(prefixSpace.c_str()).append("SensorPoseProvider not exist\n");
+ }
+
+ if (needUnlock) {
+ mMutex.unlock();
+ }
+ // TODO: 233092747 add history sensor info with SimpleLog.
+ return ss;
+}
+
} // namespace android
diff --git a/services/audiopolicy/service/SpatializerPoseController.h b/services/audiopolicy/service/SpatializerPoseController.h
index 2c6d79a..546eba0 100644
--- a/services/audiopolicy/service/SpatializerPoseController.h
+++ b/services/audiopolicy/service/SpatializerPoseController.h
@@ -113,8 +113,23 @@
*/
void waitUntilCalculated();
+ // convert fields to a printable string
+ std::string toString(unsigned level) const;
+
+ static std::string toString(media::HeadTrackingMode mode) {
+ switch (mode) {
+ case media::HeadTrackingMode::STATIC:
+ return "STATIC";
+ case media::HeadTrackingMode::WORLD_RELATIVE:
+ return "WORLD_RELATIVE";
+ case media::HeadTrackingMode::SCREEN_RELATIVE:
+ return "SCREEN_RELATIVE";
+ }
+ return "EnumNotImplemented";
+ };
+
private:
- mutable std::mutex mMutex;
+ mutable std::timed_mutex mMutex;
Listener* const mListener;
const std::chrono::microseconds mSensorPeriod;
// Order matters for the following two members to ensure correct destruction.
@@ -123,12 +138,15 @@
int32_t mHeadSensor = media::SensorPoseProvider::INVALID_HANDLE;
int32_t mScreenSensor = media::SensorPoseProvider::INVALID_HANDLE;
std::optional<media::HeadTrackingMode> mActualMode;
- std::thread mThread;
- std::condition_variable mCondVar;
+ std::condition_variable_any mCondVar;
bool mShouldCalculate = true;
bool mShouldExit = false;
bool mCalculated = false;
+ // It's important that mThread is the last variable in this class
+ // since we starts mThread in initializer list
+ std::thread mThread;
+
void onPose(int64_t timestamp, int32_t sensor, const media::Pose3f& pose,
const std::optional<media::Twist3f>& twist, bool isNewReference) override;
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 5429176..43b1a2a 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -148,6 +148,8 @@
std::unique_ptr<AudioPolicyManagerTestClient> mClient;
std::unique_ptr<AudioPolicyTestManager> mManager;
+
+ const uint32_t k48000SamplingRate = 48000;
};
void AudioPolicyManagerTest::SetUp() {
@@ -414,11 +416,11 @@
AudioPolicyConfig& config = mManager->getConfig();
mMsdOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_BUS);
sp<AudioProfile> pcmOutputProfile = new AudioProfile(
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
sp<AudioProfile> ac3OutputProfile = new AudioProfile(
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000);
+ AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, k48000SamplingRate);
sp<AudioProfile> iec958OutputProfile = new AudioProfile(
- AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
mMsdOutputDevice->addAudioProfile(pcmOutputProfile);
mMsdOutputDevice->addAudioProfile(ac3OutputProfile);
mMsdOutputDevice->addAudioProfile(iec958OutputProfile);
@@ -473,7 +475,7 @@
// Add a profile with another encoding to the default device to test routing
// of streams that are not supported by MSD.
sp<AudioProfile> dtsOutputProfile = new AudioProfile(
- AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000);
+ AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, k48000SamplingRate);
config.getDefaultOutputDevice()->addAudioProfile(dtsOutputProfile);
sp<OutputProfile> primaryEncodedOutputProfile = new OutputProfile("encoded");
primaryEncodedOutputProfile->addAudioProfile(dtsOutputProfile);
@@ -491,7 +493,7 @@
// Add HDMI input device with IEC60958 profile for HDMI in -> MSD patching.
mHdmiInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_HDMI);
sp<AudioProfile> iec958InputProfile = new AudioProfile(
- AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_IN_STEREO, 48000);
+ AUDIO_FORMAT_IEC60958, AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate);
mHdmiInputDevice->addAudioProfile(iec958InputProfile);
config.addDevice(mHdmiInputDevice);
sp<InputProfile> hdmiInputProfile = new InputProfile("hdmi input");
@@ -556,8 +558,8 @@
TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
}
@@ -566,7 +568,7 @@
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
}
@@ -574,13 +576,13 @@
TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, k48000SamplingRate);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
}
@@ -588,8 +590,8 @@
TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
@@ -600,9 +602,8 @@
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- nullptr /*output*/, &portId);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, nullptr /*output*/, &portId);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -612,9 +613,8 @@
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
- nullptr /*output*/, &portId);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, nullptr /*output*/, &portId);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(-static_cast<int>(mExpectedAudioPatchCount), patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
@@ -623,8 +623,8 @@
{
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- getOutputForAttr(&selectedDeviceId,
- AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
@@ -653,8 +653,8 @@
ASSERT_EQ(AUDIO_FORMAT_IEC60958, patch->mPatch.sinks[0].format);
ASSERT_EQ(AUDIO_CHANNEL_IN_STEREO, patch->mPatch.sources[0].channel_mask);
ASSERT_EQ(AUDIO_CHANNEL_OUT_STEREO, patch->mPatch.sinks[0].channel_mask);
- ASSERT_EQ(48000, patch->mPatch.sources[0].sample_rate);
- ASSERT_EQ(48000, patch->mPatch.sinks[0].sample_rate);
+ ASSERT_EQ(k48000SamplingRate, patch->mPatch.sources[0].sample_rate);
+ ASSERT_EQ(k48000SamplingRate, patch->mPatch.sinks[0].sample_rate);
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
@@ -996,7 +996,7 @@
clearPolicyMix();
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig,
std::vector<PolicyMixTuple>());
@@ -1035,7 +1035,7 @@
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig,
std::vector<PolicyMixTuple>());
@@ -1273,7 +1273,7 @@
audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "", audioConfig, std::vector<PolicyMixTuple>());
ASSERT_EQ(INVALID_OPERATION, ret);
@@ -1307,7 +1307,7 @@
audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig, mUsageRules);
ASSERT_EQ(NO_ERROR, ret);
@@ -1323,7 +1323,7 @@
std::string tags = "addr=" + mMixAddress;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
getInputForAttr(attr, mTracker->getRiid(), &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE, &mPortId);
+ AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &mPortId);
ASSERT_EQ(NO_ERROR, mManager->startInput(mPortId));
ASSERT_EQ(extractionPort.id, selectedDeviceId);
@@ -1350,8 +1350,8 @@
audio_port_handle_t playbackRoutedPortId = AUDIO_PORT_HANDLE_NONE;
getOutputForAttr(&playbackRoutedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE,
- nullptr /*output*/, nullptr /*portId*/, attr);
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, nullptr /*portId*/,
+ attr);
if (std::find_if(begin(mUsageRules), end(mUsageRules), [&usage](const auto &usageRule) {
return (std::get<0>(usageRule) == usage) &&
(std::get<2>(usageRule) == RULE_MATCH_ATTRIBUTE_USAGE);}) != end(mUsageRules) ||
@@ -1493,7 +1493,7 @@
audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
audioConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO;
audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- audioConfig.sample_rate = 48000;
+ audioConfig.sample_rate = k48000SamplingRate;
status_t ret = addPolicyMix(MIX_TYPE_RECORDERS, MIX_ROUTE_FLAG_LOOP_BACK,
AUDIO_DEVICE_IN_REMOTE_SUBMIX, mMixAddress, audioConfig, mSourceRules);
ASSERT_EQ(NO_ERROR, ret);
@@ -1509,7 +1509,7 @@
std::string tags = std::string("addr=") + mMixAddress;
strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, &mPortId, attr);
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE, nullptr /*output*/, &mPortId, attr);
ASSERT_EQ(NO_ERROR, mManager->startOutput(mPortId));
ASSERT_EQ(injectionPort.id, getDeviceIdFromPatch(mClient->getLastAddedPatch()));
@@ -1537,7 +1537,7 @@
audio_port_handle_t captureRoutedPortId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
getInputForAttr(attr, mTracker->getRiid(), &captureRoutedPortId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE, &portId);
+ AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &portId);
if (std::find_if(begin(mSourceRules), end(mSourceRules), [&source](const auto &sourceRule) {
return (std::get<1>(sourceRule) == source) &&
(std::get<2>(sourceRule) == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET);})
@@ -1687,11 +1687,11 @@
// Try start input or output according to the device type
if (audio_is_output_devices(type)) {
getOutputForAttr(&routedPortId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
- 48000 /*sampleRate*/, AUDIO_OUTPUT_FLAG_NONE);
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_NONE);
} else if (audio_is_input_device(type)) {
RecordingActivityTracker tracker;
getInputForAttr({}, tracker.getRiid(), &routedPortId, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_STEREO, 48000 /*sampleRate*/, AUDIO_INPUT_FLAG_NONE);
+ AUDIO_CHANNEL_IN_STEREO, k48000SamplingRate, AUDIO_INPUT_FLAG_NONE);
}
ASSERT_EQ(devicePort.id, routedPortId);
@@ -1715,6 +1715,57 @@
)
);
+class AudioPolicyManagerCarTest : public AudioPolicyManagerTestDynamicPolicy {
+protected:
+ std::string getConfigFile() override { return sCarConfig; }
+
+ static const std::string sCarConfig;
+};
+
+const std::string AudioPolicyManagerCarTest::sCarConfig =
+ AudioPolicyManagerCarTest::sExecutableDir + "test_car_ap_atmos_offload_configuration.xml";
+
+TEST_F(AudioPolicyManagerCarTest, InitSuccess) {
+ // SetUp must finish with no assertions.
+}
+
+TEST_F(AudioPolicyManagerCarTest, Dump) {
+ dumpToLog();
+}
+
+TEST_F(AudioPolicyManagerCarTest, GetOutputForAttrAtmosOutputAfterRegisteringPolicyMix) {
+ status_t ret;
+ audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
+ const std::string kTestBusMediaOutput = "bus0_media_out";
+ ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+ AUDIO_DEVICE_OUT_BUS, kTestBusMediaOutput, audioConfig, std::vector<PolicyMixTuple>());
+ ASSERT_EQ(NO_ERROR, ret);
+
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t output;
+ audio_port_handle_t portId;
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_E_AC3_JOC, AUDIO_CHANNEL_OUT_5POINT1,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, &output, &portId);
+ ASSERT_NE(AUDIO_PORT_HANDLE_NONE, selectedDeviceId);
+ sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
+ ASSERT_NE(nullptr, outDesc.get());
+ ASSERT_EQ(AUDIO_FORMAT_E_AC3_JOC, outDesc->getFormat());
+ ASSERT_EQ(AUDIO_CHANNEL_OUT_5POINT1, outDesc->getChannelMask());
+ ASSERT_EQ(k48000SamplingRate, outDesc->getSamplingRate());
+
+ selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ output = AUDIO_IO_HANDLE_NONE;
+ portId = AUDIO_PORT_HANDLE_NONE;
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_7POINT1POINT4,
+ k48000SamplingRate, AUDIO_OUTPUT_FLAG_DIRECT, &output, &portId);
+ ASSERT_NE(AUDIO_PORT_HANDLE_NONE, selectedDeviceId);
+ outDesc = mManager->getOutputs().valueFor(output);
+ ASSERT_NE(nullptr, outDesc.get());
+ ASSERT_EQ(AUDIO_FORMAT_PCM_16_BIT, outDesc->getFormat());
+ ASSERT_EQ(AUDIO_CHANNEL_OUT_7POINT1POINT4, outDesc->getChannelMask());
+ ASSERT_EQ(k48000SamplingRate, outDesc->getSamplingRate());
+}
+
class AudioPolicyManagerTVTest : public AudioPolicyManagerTestWithConfigurationFile {
protected:
std::string getConfigFile() override { return sTvConfig; }
@@ -1735,8 +1786,8 @@
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t output;
audio_port_handle_t portId;
- getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000,
- flags, &output, &portId);
+ getOutputForAttr(&selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ k48000SamplingRate, flags, &output, &portId);
sp<SwAudioOutputDescriptor> outDesc = mManager->getOutputs().valueFor(output);
ASSERT_NE(nullptr, outDesc.get());
audio_port_v7 port = {};
diff --git a/services/audiopolicy/tests/resources/Android.bp b/services/audiopolicy/tests/resources/Android.bp
index ff4d568..5e71210 100644
--- a/services/audiopolicy/tests/resources/Android.bp
+++ b/services/audiopolicy/tests/resources/Android.bp
@@ -12,6 +12,7 @@
srcs: [
"test_audio_policy_configuration.xml",
"test_audio_policy_primary_only_configuration.xml",
+ "test_car_ap_atmos_offload_configuration.xml",
"test_invalid_audio_policy_configuration.xml",
"test_tv_apm_configuration.xml",
"test_settop_box_surround_configuration.xml",
diff --git a/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
new file mode 100644
index 0000000..d131ed8
--- /dev/null
+++ b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
@@ -0,0 +1,308 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <globalConfiguration speaker_drc_enabled="true"/>
+
+ <modules>
+ <!-- Primary Audio HAL -->
+ <module name="primary" halVersion="3.0">
+ <attachedDevices>
+ <!-- One bus per context -->
+ <item>bus0_media_out</item>
+ <item>bus1_navigation_out</item>
+ <item>bus2_voice_command_out</item>
+ <item>bus3_call_ring_out</item>
+ <item>bus4_call_out</item>
+ <item>bus5_alarm_out</item>
+ <item>bus6_notification_out</item>
+ <item>bus7_system_sound_out</item>
+ <!-- names with _audio_zone_# are used for defined an emulator rear seat audio zone
+ where each number # is the zone id number -->
+ <item>bus100_audio_zone_1</item>
+ <item>bus200_audio_zone_2</item>
+ <item>Built-In Mic</item>
+ <item>Built-In Back Mic</item>
+ <item>Echo-Reference Mic</item>
+ <item>FM Tuner</item>
+ <item>Tone Generator 0</item>
+ <item>Tone Generator 1</item>
+ </attachedDevices>
+ <defaultOutputDevice>bus0_media_out</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="mixport_bus0_media_out" role="source"
+ flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus0_media_out_atmos" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" format="AUDIO_FORMAT_E_AC3_JOC"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1"/>
+ </mixPort>
+ <mixPort name="mixport_bus0_media_out_atmos_pcm" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+ </mixPort>
+ <mixPort name="mixport_bus1_navigation_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus2_voice_command_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus3_call_ring_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus4_call_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus5_alarm_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus6_notification_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus7_system_sound_out" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus100_audio_zone_1" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_bus200_audio_zone_2" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </mixPort>
+ <mixPort name="mixport_tuner0" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_input_bus_tone_zone_0" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ <mixPort name="mixport_input_bus_tone_zone_1" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus0_media_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <profile name="" format="AUDIO_FORMAT_E_AC3_JOC"
+ samplingRates="48000"
+ channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_5POINT1"/>
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus1_navigation_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus1_navigation_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus2_voice_command_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus2_voice_command_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus3_call_ring_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus3_call_ring_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus4_call_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus4_call_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus5_alarm_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus5_alarm_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus6_notification_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus6_notification_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus7_system_sound_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus7_system_sound_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus100_audio_zone_1" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus100_audio_zone_1">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="bus200_audio_zone_2" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus200_audio_zone_2">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC"
+ role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="Echo-Reference Mic" type="AUDIO_DEVICE_IN_ECHO_REFERENCE"
+ role="source">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+ channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+ </devicePort>
+ <devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source"
+ address="tuner0">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="Tone Generator 0" type="AUDIO_DEVICE_IN_BUS" role="source"
+ address="input_bus_tone_zone_0">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ <devicePort tagName="Tone Generator 1" type="AUDIO_DEVICE_IN_BUS" role="source"
+ address="input_bus_tone_zone_1">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
+ </devicePorts>
+ <!-- route declaration, i.e. list all available sources for a given sink -->
+ <routes>
+ <route type="mix" sink="bus0_media_out"
+ sources="mixport_bus0_media_out,mixport_bus0_media_out_atmos,mixport_bus0_media_out_atmos_pcm"/>
+ <route type="mix" sink="bus1_navigation_out" sources="mixport_bus1_navigation_out"/>
+ <route type="mix" sink="bus2_voice_command_out"
+ sources="mixport_bus2_voice_command_out"/>
+ <route type="mix" sink="bus3_call_ring_out" sources="mixport_bus3_call_ring_out"/>
+ <route type="mix" sink="bus4_call_out" sources="mixport_bus4_call_out"/>
+ <route type="mix" sink="bus5_alarm_out" sources="mixport_bus5_alarm_out"/>
+ <route type="mix" sink="bus6_notification_out"
+ sources="mixport_bus6_notification_out"/>
+ <route type="mix" sink="bus7_system_sound_out"
+ sources="mixport_bus7_system_sound_out"/>
+ <route type="mix" sink="bus100_audio_zone_1" sources="mixport_bus100_audio_zone_1"/>
+ <route type="mix" sink="bus200_audio_zone_2" sources="mixport_bus200_audio_zone_2"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Mic,Built-In Back Mic,Echo-Reference Mic"/>
+ <route type="mix" sink="mixport_tuner0" sources="FM Tuner"/>
+ <route type="mix" sink="mixport_input_bus_tone_zone_0" sources="Tone Generator 0"/>
+ <route type="mix" sink="mixport_input_bus_tone_zone_1" sources="Tone Generator 1"/>
+ </routes>
+ </module>
+ </modules>
+</audioPolicyConfiguration>
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 99e3691..12593ff 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -240,6 +240,35 @@
"sharing_requested",
};
+static constexpr const char * HeadTrackerDeviceEnabledFields[] {
+ "mediametrics_headtrackerdeviceenabled_reported",
+ "type",
+ "event",
+ "enabled",
+};
+
+static constexpr const char * HeadTrackerDeviceSupportedFields[] {
+ "mediametrics_headtrackerdevicesupported_reported",
+ "type",
+ "event",
+ "supported",
+};
+
+static constexpr const char * SpatializerCapabilitiesFields[] {
+ "mediametrics_spatializer_reported",
+ "head_tracking_modes",
+ "spatializer_levels",
+ "spatializer_modes",
+ "channel_masks",
+};
+
+static constexpr const char * SpatializerDeviceEnabledFields[] {
+ "mediametrics_spatializerdeviceenabled_reported",
+ "type",
+ "event",
+ "enabled",
+};
+
/**
* printFields is a helper method that prints the fields and corresponding values
* in a human readable style.
@@ -459,6 +488,15 @@
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
mAudioPowerUsage.checkCreatePatch(item);
}));
+
+ // Handle Spatializer - these keys are prefixed by "audio.spatializer."
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER "*." AMEDIAMETRICS_PROP_EVENT,
+ std::monostate{}, /* match any event */
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mSpatializer.onEvent(item);
+ }));
}
AudioAnalytics::~AudioAnalytics()
@@ -1525,5 +1563,173 @@
return { s, n };
}
+// Classifies the setting event for statsd (use generated statsd enums.proto constants).
+static int32_t classifySettingEvent(bool isSetAlready, bool withinBoot) {
+ if (isSetAlready) {
+ return util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__EVENT__SPATIALIZER_SETTING_EVENT_NORMAL;
+ }
+ if (withinBoot) {
+ return util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__EVENT__SPATIALIZER_SETTING_EVENT_BOOT;
+ }
+ return util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__EVENT__SPATIALIZER_SETTING_EVENT_FIRST;
+}
+
+void AudioAnalytics::Spatializer::onEvent(
+ const std::shared_ptr<const android::mediametrics::Item> &item)
+{
+ const auto key = item->getKey();
+
+ if (!startsWith(key, AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER)) return;
+
+ const std::string suffix =
+ key.substr(std::size(AMEDIAMETRICS_KEY_PREFIX_AUDIO_SPATIALIZER) - 1);
+
+ std::string eventStr; // optional - find the actual event string.
+ (void)item->get(AMEDIAMETRICS_PROP_EVENT, &eventStr);
+
+ const size_t delim = suffix.find('.'); // note could use split.
+ if (delim == suffix.npos) {
+ // on create with suffix == "0" for the first spatializer effect.
+
+ std::string headTrackingModes;
+ (void)item->get(AMEDIAMETRICS_PROP_HEADTRACKINGMODES, &headTrackingModes);
+
+ std::string levels;
+ (void)item->get(AMEDIAMETRICS_PROP_LEVELS, &levels);
+
+ std::string modes;
+ (void)item->get(AMEDIAMETRICS_PROP_MODES, &modes);
+
+ std::string channelMasks;
+ (void)item->get(AMEDIAMETRICS_PROP_CHANNELMASKS, &channelMasks);
+
+ LOG(LOG_LEVEL) << "key:" << key
+ << " headTrackingModes:" << headTrackingModes
+ << " levels:" << levels
+ << " modes:" << modes
+ << " channelMasks:" << channelMasks
+ ;
+
+ const std::vector<int32_t> headTrackingModesVector =
+ types::vectorFromMap(headTrackingModes, types::getHeadTrackingModeMap());
+ const std::vector<int32_t> levelsVector =
+ types::vectorFromMap(levels, types::getSpatializerLevelMap());
+ const std::vector<int32_t> modesVector =
+ types::vectorFromMap(modes, types::getSpatializerModeMap());
+ const std::vector<int64_t> channelMasksVector =
+ types::channelMaskVectorFromString(channelMasks);
+
+ const auto [ result, str ] = sendToStatsd(SpatializerCapabilitiesFields,
+ CONDITION(android::util::MEDIAMETRICS_SPATIALIZERCAPABILITIES_REPORTED)
+ , headTrackingModesVector
+ , levelsVector
+ , modesVector
+ , channelMasksVector
+ );
+
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_SPATIALIZERCAPABILITIES_REPORTED, str);
+
+ std::lock_guard lg(mLock);
+ if (mFirstCreateTimeNs == 0) {
+ // Only update the create time once to prevent audioserver restart
+ // from looking like a boot.
+ mFirstCreateTimeNs = item->getTimestamp();
+ }
+ mSimpleLog.log("%s suffix: %s item: %s",
+ __func__, suffix.c_str(), item->toString().c_str());
+ } else {
+ std::string subtype = suffix.substr(0, delim);
+ if (subtype != "device") return; // not a device.
+
+ const std::string deviceType = suffix.substr(std::size("device.") - 1);
+
+ const int32_t deviceTypeStatsd =
+ types::lookup<types::AUDIO_DEVICE_INFO_TYPE, int32_t>(deviceType);
+
+ std::string address;
+ (void)item->get(AMEDIAMETRICS_PROP_ADDRESS, &address);
+ std::string enabled;
+ (void)item->get(AMEDIAMETRICS_PROP_ENABLED, &enabled);
+ std::string hasHeadTracker;
+ (void)item->get(AMEDIAMETRICS_PROP_HASHEADTRACKER, &hasHeadTracker);
+ std::string headTrackerEnabled;
+ (void)item->get(AMEDIAMETRICS_PROP_HEADTRACKERENABLED, &headTrackerEnabled);
+
+ std::lock_guard lg(mLock);
+
+ // Validate from our cached state
+
+ // Our deviceKey takes the device type and appends the address if any.
+ // This distinguishes different wireless devices for the purposes of tracking.
+ std::string deviceKey(deviceType);
+ deviceKey.append("_").append(address);
+ DeviceState& deviceState = mDeviceStateMap[deviceKey];
+
+ // check whether the settings event is within a certain time of spatializer creation.
+ const bool withinBoot =
+ item->getTimestamp() - mFirstCreateTimeNs < kBootDurationThreshold;
+
+ if (!enabled.empty()) {
+ if (enabled != deviceState.enabled) {
+ const int32_t settingEventStatsd =
+ classifySettingEvent(!deviceState.enabled.empty(), withinBoot);
+ deviceState.enabled = enabled;
+ const bool enabledStatsd = enabled == "true";
+ const auto [ result, str ] = sendToStatsd(SpatializerDeviceEnabledFields,
+ CONDITION(android::util::MEDIAMETRICS_SPATIALIZERDEVICEENABLED_REPORTED)
+ , deviceTypeStatsd
+ , settingEventStatsd
+ , enabledStatsd
+ );
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_SPATIALIZERDEVICEENABLED_REPORTED, str);
+ }
+ }
+ if (!hasHeadTracker.empty()) {
+ if (hasHeadTracker != deviceState.hasHeadTracker) {
+ const int32_t settingEventStatsd =
+ classifySettingEvent(!deviceState.hasHeadTracker.empty(), withinBoot);
+ deviceState.hasHeadTracker = hasHeadTracker;
+ const bool supportedStatsd = hasHeadTracker == "true";
+ const auto [ result, str ] = sendToStatsd(HeadTrackerDeviceSupportedFields,
+ CONDITION(android::util::MEDIAMETRICS_HEADTRACKERDEVICESUPPORTED_REPORTED)
+ , deviceTypeStatsd
+ , settingEventStatsd
+ , supportedStatsd
+ );
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_HEADTRACKERDEVICESUPPORTED_REPORTED, str);
+ }
+ }
+ if (!headTrackerEnabled.empty()) {
+ if (headTrackerEnabled != deviceState.headTrackerEnabled) {
+ const int32_t settingEventStatsd =
+ classifySettingEvent(!deviceState.headTrackerEnabled.empty(), withinBoot);
+ deviceState.headTrackerEnabled = headTrackerEnabled;
+ const bool enabledStatsd = headTrackerEnabled == "true";
+ const auto [ result, str ] = sendToStatsd(HeadTrackerDeviceEnabledFields,
+ CONDITION(android::util::MEDIAMETRICS_HEADTRACKERDEVICEENABLED_REPORTED)
+ , deviceTypeStatsd
+ , settingEventStatsd
+ , enabledStatsd
+ );
+ mAudioAnalytics.mStatsdLog->log(
+ android::util::MEDIAMETRICS_HEADTRACKERDEVICEENABLED_REPORTED, str);
+ }
+ }
+ mSimpleLog.log("%s deviceKey: %s item: %s",
+ __func__, deviceKey.c_str(), item->toString().c_str());
+ }
+}
+
+std::pair<std::string, int32_t> AudioAnalytics::Spatializer::dump(
+ int32_t lines, const char *prefix) const
+{
+ std::lock_guard lg(mLock);
+ std::string s = mSimpleLog.dumpToString(prefix == nullptr ? "" : prefix, lines);
+ size_t n = std::count(s.begin(), s.end(), '\n');
+ return { s, n };
+}
} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
index 6e24a58..d2b4aab 100644
--- a/services/mediametrics/AudioTypes.cpp
+++ b/services/mediametrics/AudioTypes.cpp
@@ -135,6 +135,94 @@
return map;
}
+// A map for the Java AudioDeviceInfo types to internal (native) output devices.
+const std::unordered_map<std::string, int32_t>& getAudioDeviceOutCompactMap() {
+ // DO NOT MODIFY VALUES (OK to add new ones).
+ static std::unordered_map<std::string, int32_t> map{
+ // should "unknown" go to AUDIO_DEVICE_NONE?
+ {"earpiece", AUDIO_DEVICE_OUT_EARPIECE},
+ {"speaker", AUDIO_DEVICE_OUT_SPEAKER},
+ {"headset", AUDIO_DEVICE_OUT_WIRED_HEADSET},
+ {"headphone", AUDIO_DEVICE_OUT_WIRED_HEADPHONE},
+ {"bt_sco", AUDIO_DEVICE_OUT_BLUETOOTH_SCO},
+ {"bt_sco_hs", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET},
+ {"bt_sco_carkit", AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT},
+ {"bt_a2dp", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP},
+ {"bt_a2dp_hp", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES},
+ {"bt_a2dp_spk", AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER},
+ {"aux_digital", AUDIO_DEVICE_OUT_AUX_DIGITAL},
+ {"hdmi", AUDIO_DEVICE_OUT_HDMI},
+ {"analog_dock", AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET},
+ {"digital_dock", AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET},
+ {"usb_accessory", AUDIO_DEVICE_OUT_USB_ACCESSORY},
+ {"usb_device", AUDIO_DEVICE_OUT_USB_DEVICE},
+ {"remote_submix", AUDIO_DEVICE_OUT_REMOTE_SUBMIX},
+ {"telephony_tx", AUDIO_DEVICE_OUT_TELEPHONY_TX},
+ {"line", AUDIO_DEVICE_OUT_LINE},
+ {"hdmi_arc", AUDIO_DEVICE_OUT_HDMI_ARC},
+ {"hdmi_earc", AUDIO_DEVICE_OUT_HDMI_EARC},
+ {"spdif", AUDIO_DEVICE_OUT_SPDIF},
+ {"fm_transmitter", AUDIO_DEVICE_OUT_FM},
+ {"aux_line", AUDIO_DEVICE_OUT_AUX_LINE},
+ {"speaker_safe", AUDIO_DEVICE_OUT_SPEAKER_SAFE},
+ {"ip", AUDIO_DEVICE_OUT_IP},
+ {"bus", AUDIO_DEVICE_OUT_BUS},
+ {"proxy", AUDIO_DEVICE_OUT_PROXY},
+ {"usb_headset", AUDIO_DEVICE_OUT_USB_HEADSET},
+ {"hearing_aid_out", AUDIO_DEVICE_OUT_HEARING_AID},
+ {"echo_canceller", AUDIO_DEVICE_OUT_ECHO_CANCELLER},
+ // default does not exist
+ {"ble_headset", AUDIO_DEVICE_OUT_BLE_HEADSET},
+ {"ble_speaker", AUDIO_DEVICE_OUT_BLE_SPEAKER},
+ {"ble_broadcast", AUDIO_DEVICE_OUT_BLE_BROADCAST},
+ };
+ return map;
+}
+
+// A map for the Java AudioDeviceInfo types.
+// This uses generated statsd enums.proto constants.
+const std::unordered_map<std::string, int32_t>& getAudioDeviceInfoTypeMap() {
+ // DO NOT MODIFY VALUES (OK to add new ones).
+ static std::unordered_map<std::string, int32_t> map{
+ {"unknown", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_UNKNOWN},
+ {"earpiece", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUILTIN_EARPIECE},
+ {"speaker", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUILTIN_SPEAKER},
+ {"headset", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_WIRED_HEADSET},
+ {"headphone", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_WIRED_HEADPHONES}, // sic
+ {"bt_sco", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_SCO},
+ {"bt_sco_hs", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_SCO},
+ {"bt_sco_carkit", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_SCO},
+ {"bt_a2dp", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_A2DP},
+ {"bt_a2dp_hp", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_A2DP},
+ {"bt_a2dp_spk", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLUETOOTH_A2DP},
+ {"aux_digital", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI},
+ {"hdmi", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI},
+ {"analog_dock", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_DOCK},
+ {"digital_dock", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_DOCK},
+ {"usb_accessory", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_USB_ACCESSORY},
+ {"usb_device", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_USB_DEVICE},
+ {"usb_headset", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_USB_HEADSET},
+ {"remote_submix", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_REMOTE_SUBMIX},
+ {"telephony_tx", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_TELEPHONY},
+ {"line", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_LINE_ANALOG},
+ {"hdmi_arc", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI_ARC},
+ {"hdmi_earc", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HDMI_EARC},
+ {"spdif", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_LINE_DIGITAL},
+ {"fm_transmitter", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_FM},
+ {"aux_line", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_AUX_LINE},
+ {"speaker_safe", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUILTIN_SPEAKER_SAFE},
+ {"ip", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_IP},
+ {"bus", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BUS},
+ {"proxy", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_UNKNOWN /* AUDIO_DEVICE_INFO_TYPE_PROXY */},
+ {"hearing_aid_out", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_HEARING_AID},
+ {"echo_canceller", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_ECHO_REFERENCE}, // sic
+ {"ble_headset", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLE_HEADSET},
+ {"ble_speaker", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLE_SPEAKER},
+ {"ble_broadcast", util::MEDIAMETRICS_SPATIALIZER_DEVICE_ENABLED_REPORTED__TYPE__AUDIO_DEVICE_INFO_TYPE_BLE_BROADCAST},
+ };
+ return map;
+}
+
const std::unordered_map<std::string, int32_t>& getAudioThreadTypeMap() {
// DO NOT MODIFY VALUES (OK to add new ones).
// This may be found in frameworks/av/services/audioflinger/Threads.h
@@ -197,6 +285,41 @@
return map;
}
+const std::unordered_map<std::string, int32_t>& getHeadTrackingModeMap() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // frameworks/base/media/java/android/media/Spatializer.java
+ // frameworks/av/media/libaudioclient/aidl/android/media/SpatializerHeadTrackingMode.aidl
+ static std::unordered_map<std::string, int32_t> map {
+ {"OTHER", 0},
+ {"DISABLED", -1},
+ {"RELATIVE_WORLD", 1},
+ {"RELATIVE_SCREEN", 2},
+ };
+ return map;
+}
+
+const std::unordered_map<std::string, int32_t>& getSpatializerLevelMap() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // frameworks/base/media/java/android/media/Spatializer.java
+ // frameworks/av/media/libaudioclient/aidl/android/media/SpatializerHeadTrackingMode.aidl
+ static std::unordered_map<std::string, int32_t> map {
+ {"NONE", 0},
+ {"SPATIALIZER_MULTICHANNEL", 1},
+ {"SPATIALIZER_MCHAN_BED_PLUS_OBJECTS", 2},
+ };
+ return map;
+}
+
+const std::unordered_map<std::string, int32_t>& getSpatializerModeMap() {
+ // DO NOT MODIFY VALUES(OK to add new ones).
+ // frameworks/av/media/libaudioclient/aidl/android/media/SpatializationMode.aidl
+ static std::unordered_map<std::string, int32_t> map {
+ {"SPATIALIZER_BINAURAL", 0},
+ {"SPATIALIZER_TRANSAURAL", 1},
+ };
+ return map;
+}
+
const std::unordered_map<std::string, int32_t>& getStatusMap() {
// DO NOT MODIFY VALUES(OK to add new ones).
static std::unordered_map<std::string, int32_t> map {
@@ -286,6 +409,35 @@
return value;
}
+std::vector<int32_t> vectorFromMap(
+ const std::string &str, const std::unordered_map<std::string, int32_t>& map)
+{
+ std::vector<int32_t> v;
+
+ if (str.empty()) return v;
+
+ const auto result = stringutils::split(str, "|");
+ for (const auto &s : result) {
+ auto it = map.find(s);
+ if (it == map.end()) continue;
+ v.push_back(it->second);
+ }
+ return v;
+}
+
+std::vector<int64_t> channelMaskVectorFromString(const std::string &s)
+{
+ std::vector<int64_t> v;
+
+ const auto result = stringutils::split(s, "|");
+ for (const auto &mask : result) {
+ // 0 if undetected or if actually 0.
+ int64_t int64Mask = strtoll(mask.c_str(), nullptr, 0);
+ v.push_back(int64Mask);
+ }
+ return v;
+}
+
template <>
int32_t lookup<CONTENT_TYPE>(const std::string &contentType)
{
@@ -441,6 +593,17 @@
}
template <>
+int32_t lookup<AUDIO_DEVICE_INFO_TYPE>(const std::string& audioDeviceInfoType)
+{
+ auto& map = getAudioDeviceInfoTypeMap();
+ auto it = map.find(audioDeviceInfoType);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
int32_t lookup<CALLER_NAME>(const std::string &callerName)
{
auto& map = getAudioCallerNameMap();
@@ -463,6 +626,39 @@
}
template <>
+int32_t lookup<HEAD_TRACKING_MODE>(const std::string& headTrackingMode)
+{
+ auto& map = getHeadTrackingModeMap();
+ auto it = map.find(headTrackingMode);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
+int32_t lookup<SPATIALIZER_LEVEL>(const std::string& spatializerLevel)
+{
+ auto& map = getSpatializerLevelMap();
+ auto it = map.find(spatializerLevel);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
+int32_t lookup<SPATIALIZER_MODE>(const std::string& spatializerMode)
+{
+ auto& map = getSpatializerModeMap();
+ auto it = map.find(spatializerMode);
+ if (it == map.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+template <>
int32_t lookup<STATUS>(const std::string &status)
{
auto& map = getStatusMap();
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index ff16b9e..8a7e8a9 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -336,6 +336,15 @@
result << "-- some lines may be truncated --\n";
}
+ const int32_t spatializerLinesToDump = all ? INT32_MAX : 15;
+ result << "\nSpatializer Message Log:";
+ const auto [ spatializerDumpString, spatializerLines ] =
+ mAudioAnalytics.dumpSpatializer(spatializerLinesToDump);
+ result << "\n" << spatializerDumpString;
+ if (spatializerLines == spatializerLinesToDump) {
+ result << "-- some lines may be truncated --\n";
+ }
+
result << "\nLogSessionId:\n"
<< mediametrics::ValidateId::get()->dump();
diff --git a/services/mediametrics/include/mediametricsservice/AudioAnalytics.h b/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
index 5ee8c30..7000ba8 100644
--- a/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
+++ b/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
@@ -92,6 +92,15 @@
return mHealth.dump(lines);
}
+ /**
+ * Returns a pair consisting of the dump string and the number of lines in the string.
+ *
+ * Spatializer dump.
+ */
+ std::pair<std::string, int32_t> dumpSpatializer(int32_t lines = INT32_MAX) const {
+ return mSpatializer.dump(lines);
+ }
+
void clear() {
// underlying state is locked.
mPreviousAnalyticsState->clear();
@@ -317,6 +326,36 @@
SimpleLog mSimpleLog GUARDED_BY(mLock) {64};
} mHealth{*this};
+ // Spatializer is a nested class that tracks related messages.
+ class Spatializer {
+ public:
+ explicit Spatializer(AudioAnalytics &audioAnalytics)
+ : mAudioAnalytics(audioAnalytics) {}
+
+ // an item that starts with "audio.spatializer"
+ void onEvent(const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ std::pair<std::string, int32_t> dump(
+ int32_t lines = INT32_MAX, const char *prefix = nullptr) const;
+
+ private:
+
+ // Current device state as strings:
+ // "" means unknown, "true" or "false".
+ struct DeviceState {
+ std::string enabled;
+ std::string hasHeadTracker;
+ std::string headTrackerEnabled;
+ };
+
+ AudioAnalytics& mAudioAnalytics;
+ static constexpr int64_t kBootDurationThreshold = 120 /* seconds */ * 1e9;
+ mutable std::mutex mLock;
+ int64_t mFirstCreateTimeNs GUARDED_BY(mLock) = 0;
+ std::map<std::string, DeviceState> mDeviceStateMap GUARDED_BY(mLock);
+ SimpleLog mSimpleLog GUARDED_BY(mLock) {64};
+ } mSpatializer{*this};
+
AudioPowerUsage mAudioPowerUsage;
};
diff --git a/services/mediametrics/include/mediametricsservice/AudioTypes.h b/services/mediametrics/include/mediametricsservice/AudioTypes.h
index 5dbff9b..b5fe28b 100644
--- a/services/mediametrics/include/mediametricsservice/AudioTypes.h
+++ b/services/mediametrics/include/mediametricsservice/AudioTypes.h
@@ -29,6 +29,14 @@
const std::unordered_map<std::string, int64_t>& getAudioDeviceOutMap();
const std::unordered_map<std::string, int32_t>& getAudioThreadTypeMap();
const std::unordered_map<std::string, int32_t>& getAudioTrackTraitsMap();
+const std::unordered_map<std::string, int32_t>& getHeadTrackingModeMap();
+const std::unordered_map<std::string, int32_t>& getSpatializerLevelMap();
+const std::unordered_map<std::string, int32_t>& getSpatializerModeMap();
+
+std::vector<int32_t> vectorFromMap(
+ const std::string &str, const std::unordered_map<std::string, int32_t>& map);
+
+std::vector<int64_t> channelMaskVectorFromString(const std::string &s);
// Enumeration for the device connection results.
enum DeviceConnectionResult : int32_t {
@@ -47,14 +55,18 @@
AAUDIO_DIRECTION,
AAUDIO_PERFORMANCE_MODE,
AAUDIO_SHARING_MODE,
+ AUDIO_DEVICE_INFO_TYPE,
CALLER_NAME,
CONTENT_TYPE,
ENCODING,
+ HEAD_TRACKING_MODE,
INPUT_DEVICE, // int64_t
INPUT_FLAG,
OUTPUT_DEVICE, // int64_t
OUTPUT_FLAG,
SOURCE_TYPE,
+ SPATIALIZER_LEVEL,
+ SPATIALIZER_MODE,
STATUS,
STREAM_TYPE,
THREAD_TYPE,
diff --git a/services/mediametrics/include/mediametricsservice/MediaMetricsService.h b/services/mediametrics/include/mediametricsservice/MediaMetricsService.h
index 8d0b1cf..3ec5ac7 100644
--- a/services/mediametrics/include/mediametricsservice/MediaMetricsService.h
+++ b/services/mediametrics/include/mediametricsservice/MediaMetricsService.h
@@ -125,7 +125,7 @@
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
// mStatsdLog is locked internally (thread-safe) and shows the last atoms logged
- static constexpr size_t STATSD_LOG_LINES_MAX = 30; // recent log lines to keep
+ static constexpr size_t STATSD_LOG_LINES_MAX = 48; // recent log lines to keep
static constexpr size_t STATSD_LOG_LINES_DUMP = 4; // normal amount of lines to dump
const std::shared_ptr<mediametrics::StatsdLog> mStatsdLog{
std::make_shared<mediametrics::StatsdLog>(STATSD_LOG_LINES_MAX)};
diff --git a/services/mediametrics/include/mediametricsservice/StringUtils.h b/services/mediametrics/include/mediametricsservice/StringUtils.h
index a56f5b8..a91d37b 100644
--- a/services/mediametrics/include/mediametricsservice/StringUtils.h
+++ b/services/mediametrics/include/mediametricsservice/StringUtils.h
@@ -23,6 +23,19 @@
namespace android::mediametrics::stringutils {
+// Define a way of printing a vector - this
+// is used for proto repeated arguments.
+template <typename T>
+inline std::ostream & operator<< (std::ostream& s,
+ std::vector<T> const& v) {
+ s << "{ ";
+ for (const auto& e : v) {
+ s << e << " ";
+ }
+ s << "}";
+ return s;
+}
+
/**
* fieldPrint is a helper method that logs to a stringstream a sequence of
* field names (in a fixed size array) together with a variable number of arg parameters.