Merge "Update doc for aaudio data callback."
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index b6f8552..0d156a5 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2198,6 +2198,10 @@
* <a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#turnOnTorchWithStrengthLevel">CameraManager#turnOnTorchWithStrengthLevel</a>.
* If this value is equal to 1, flashlight brightness control is not supported.
* The value for this key will be null for devices with no flash unit.</p>
+ * <p>The maximum value is guaranteed to be safe to use for an indefinite duration in
+ * terms of device flashlight lifespan, but may be too bright for comfort for many
+ * use cases. Use the default torch brightness value to avoid problems with an
+ * over-bright flashlight.</p>
*/
ACAMERA_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL = // int32
ACAMERA_FLASH_INFO_START + 2,
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp
index 8b46d3f..28cceca 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.cpp
+++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp
@@ -392,9 +392,9 @@
static C2R PictureQuantizationSetter(bool mayBlock,
C2P<C2StreamPictureQuantizationTuning::output> &me) {
(void)mayBlock;
- (void)me;
- // TODO: refactor with same algorithm in the SetQp()
+ // these are the ones we're going to set, so want them to default
+ // to the DEFAULT values for the codec
int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN;
int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX;
@@ -419,13 +419,14 @@
ALOGV("PictureQuantizationSetter(entry): i %d-%d p %d-%d b %d-%d",
iMin, iMax, pMin, pMax, bMin, bMax);
- // ensure we have legal values
- iMax = std::clamp(iMax, CODEC_QP_MIN, CODEC_QP_MAX);
- iMin = std::clamp(iMin, CODEC_QP_MIN, CODEC_QP_MAX);
- pMax = std::clamp(pMax, CODEC_QP_MIN, CODEC_QP_MAX);
- pMin = std::clamp(pMin, CODEC_QP_MIN, CODEC_QP_MAX);
- bMax = std::clamp(bMax, CODEC_QP_MIN, CODEC_QP_MAX);
- bMin = std::clamp(bMin, CODEC_QP_MIN, CODEC_QP_MAX);
+ // min is clamped to [AVC_QP_MIN, max] to avoid error
+ // cases where layer.min > layer.max
+ iMax = std::clamp(iMax, AVC_QP_MIN, AVC_QP_MAX);
+ iMin = std::clamp(iMin, AVC_QP_MIN, iMax);
+ pMax = std::clamp(pMax, AVC_QP_MIN, AVC_QP_MAX);
+ pMin = std::clamp(pMin, AVC_QP_MIN, pMax);
+ bMax = std::clamp(bMax, AVC_QP_MIN, AVC_QP_MAX);
+ bMin = std::clamp(bMin, AVC_QP_MIN, bMax);
// put them back into the structure
for (size_t i = 0; i < me.v.flexCount(); ++i) {
@@ -819,7 +820,8 @@
s_qp_ip.e_cmd = IVE_CMD_VIDEO_CTL;
s_qp_ip.e_sub_cmd = IVE_CMD_CTL_SET_QP;
- // TODO: refactor with same algorithm in the PictureQuantizationSetter()
+ // we resolved out-of-bound and unspecified values in PictureQuantizationSetter()
+ // so we can start with defaults that are overridden as needed.
int32_t iMin = DEFAULT_I_QP_MIN, pMin = DEFAULT_P_QP_MIN, bMin = DEFAULT_B_QP_MIN;
int32_t iMax = DEFAULT_I_QP_MAX, pMax = DEFAULT_P_QP_MAX, bMax = DEFAULT_B_QP_MAX;
diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h
index 293867d..cde6604 100644
--- a/media/codec2/components/avc/C2SoftAvcEnc.h
+++ b/media/codec2/components/avc/C2SoftAvcEnc.h
@@ -103,8 +103,8 @@
/** limits as specified by h264
* (QP_MIN==4 is actually a limitation of this SW codec, not the H.264 standard)
**/
-#define CODEC_QP_MIN 4
-#define CODEC_QP_MAX 51
+#define AVC_QP_MIN 4
+#define AVC_QP_MAX 51
#define MIN(a, b) ((a) < (b))? (a) : (b)
diff --git a/media/codec2/fuzzer/C2Fuzzer.cpp b/media/codec2/fuzzer/C2Fuzzer.cpp
index e469d8b..d6793e0 100644
--- a/media/codec2/fuzzer/C2Fuzzer.cpp
+++ b/media/codec2/fuzzer/C2Fuzzer.cpp
@@ -246,7 +246,8 @@
bufferSource->parse();
c2_status_t status = C2_OK;
size_t numFrames = 0;
- while (!bufferSource->isEos()) {
+ int32_t iterationCount = 0;
+ while (!bufferSource->isEos() && ++iterationCount <= kMaxIterations) {
uint8_t* frame = nullptr;
size_t frameSize = 0;
FrameData frameData = bufferSource->getFrame();
diff --git a/media/codec2/fuzzer/C2Fuzzer.h b/media/codec2/fuzzer/C2Fuzzer.h
index da76885..4e3e3ea 100644
--- a/media/codec2/fuzzer/C2Fuzzer.h
+++ b/media/codec2/fuzzer/C2Fuzzer.h
@@ -37,6 +37,7 @@
#define C2FUZZER_ALIGN(_sz, _align) (((_sz) + ((_align)-1)) & ~((_align)-1))
constexpr std::chrono::milliseconds kC2FuzzerTimeOut = 5000ms;
+constexpr int32_t kMaxIterations = 100;
constexpr int32_t kNumberOfC2WorkItems = 8;
constexpr uint32_t kWidthOfVideo = 3840;
constexpr uint32_t kHeightOfVideo = 2160;
diff --git a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
index bd7ec0d..327717b 100644
--- a/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioEncTest.cpp
@@ -372,9 +372,8 @@
ULock l(queueLock);
flushedIndices.emplace_back(frameID);
}
- char* data = (char*)malloc(bytesCount);
- ASSERT_NE(data, nullptr);
- eleStream.read(data, bytesCount);
+ std::vector<char> eleData(bytesCount);
+ eleStream.read(eleData.data(), bytesCount);
// if we have reached at the end of input stream, signal eos
if (eleStream.gcount() < bytesCount) {
bytesCount = eleStream.gcount();
@@ -396,12 +395,11 @@
ASSERT_EQ(0u, view.offset());
ASSERT_EQ((size_t)bytesCount, view.size());
- memcpy(view.base(), data, bytesCount);
+ memcpy(view.base(), eleData.data(), bytesCount);
work->input.buffers.clear();
work->input.buffers.emplace_back(new LinearBuffer(block));
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
- free(data);
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 67873fa..117d9ca 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -214,10 +214,8 @@
calc_md5_cksum(uPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_u_chksum);
calc_md5_cksum(vPlane, uvStride, cropWidth / 2, cropHeight / 2, au1_v_chksum);
} else if (bitDepth == 8 && layoutType == C2PlanarLayout::TYPE_YUV && colInc == 2) {
- uint8_t* cbPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
- uint8_t* crPlane = (uint8_t*)malloc(cropWidth * cropHeight / 4);
- ASSERT_NE(cbPlane, nullptr);
- ASSERT_NE(crPlane, nullptr);
+ std::vector<uint8_t> cbPlane(cropWidth * cropHeight / 4);
+ std::vector<uint8_t> crPlane(cropWidth * cropHeight / 4);
size_t count = 0;
for (size_t k = 0; k < (cropHeight / 2); k++) {
for (size_t l = 0; l < (cropWidth); l = l + 2) {
@@ -227,10 +225,10 @@
}
}
calc_md5_cksum(yPlane, yStride, cropWidth, cropHeight, au1_y_chksum);
- calc_md5_cksum(cbPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_u_chksum);
- calc_md5_cksum(crPlane, cropWidth / 2, cropWidth / 2, cropHeight / 2, au1_v_chksum);
- free(cbPlane);
- free(crPlane);
+ calc_md5_cksum(cbPlane.data(), cropWidth / 2, cropWidth / 2, cropHeight / 2,
+ au1_u_chksum);
+ calc_md5_cksum(crPlane.data(), cropWidth / 2, cropWidth / 2, cropHeight / 2,
+ au1_v_chksum);
} else {
mMd5Enable = false;
ALOGV("Disabling MD5 chksm flag");
diff --git a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
index 6c8ce3c..c77eb22 100644
--- a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
+++ b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
@@ -666,6 +666,7 @@
grallocHandle, GraphicBuffer::CLONE_HANDLE,
width, height, format, 1, usage, stride);
+ native_handle_delete(grallocHandle);
std::shared_ptr<C2GraphicBlock> dstBlock;
C2BlockPool::local_id_t poolId = mIntf->getPoolId();
std::shared_ptr<C2BlockPool> pool;
@@ -684,6 +685,7 @@
grallocHandle, GraphicBuffer::CLONE_HANDLE,
width, height, format, 1, usage, stride);
+ native_handle_delete(grallocHandle);
Rect sourceCrop(0, 0, width, height);
renderengine::DisplaySettings clientCompositionDisplay;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 296d7ed..43bdb21 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();
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..876c96d 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -843,6 +843,10 @@
}
}
sp<ABuffer> aBuffer(alloc(align(width, 16) * align(height, 16) * bpp / 8));
+ if (aBuffer == nullptr) {
+ ALOGD("%s: failed to allocate buffer", __func__);
+ return nullptr;
+ }
return new ConstGraphicBlockBuffer(
format,
aBuffer,
@@ -998,9 +1002,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 +1162,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 +1174,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/libaaudio/include/aaudio/AAudioTesting.h b/media/libaaudio/include/aaudio/AAudioTesting.h
index 0f2d7a2..01d97b6 100644
--- a/media/libaaudio/include/aaudio/AAudioTesting.h
+++ b/media/libaaudio/include/aaudio/AAudioTesting.h
@@ -87,7 +87,7 @@
* @note This is only for testing. Do not use this in an application.
* It may change or be removed at any time.
*
- * @return true if the stream uses ther MMAP data path
+ * @return true if the stream uses the MMAP data path
*/
AAUDIO_API bool AAudioStream_isMMapUsed(AAudioStream* stream);
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index ffbb32f..b18a569 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -29,6 +29,7 @@
#include <audio_utils/clock.h>
#include <audio_utils/primitives.h>
#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
#include <media/AudioTrack.h>
#include <utils/Log.h>
#include <private/media/AudioTrackShared.h>
@@ -42,7 +43,9 @@
#define WAIT_PERIOD_MS 10
#define WAIT_STREAM_END_TIMEOUT_SEC 120
+
static const int kMaxLoopCountNotifications = 32;
+static constexpr char kAudioServiceName[] = "audio";
using ::android::aidl_utils::statusTFromBinderStatus;
using ::android::base::StringPrintf;
@@ -450,7 +453,7 @@
std::string errorMessage;
// Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set.
ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
- "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
+ "flags %#x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
__func__,
streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
sessionId, transferType, attributionSource.uid, attributionSource.pid);
@@ -1289,6 +1292,10 @@
legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(playbackRate))));
if (status == NO_ERROR) {
mPlaybackRate = playbackRate;
+ } else if (status == INVALID_OPERATION
+ && playbackRate.mSpeed == 1.0f && mPlaybackRate.mPitch == 1.0f) {
+ mPlaybackRate = playbackRate;
+ return NO_ERROR;
}
return status;
}
@@ -1945,6 +1952,9 @@
}
mPortId = output.portId;
+ // notify the upper layers about the new portId
+ triggerPortIdUpdate_l();
+
// We retain a copy of the I/O handle, but don't own the reference
mOutput = output.outputId;
mRefreshRemaining = true;
@@ -3507,12 +3517,34 @@
if (mPlayerIId == playerIId) return;
mPlayerIId = playerIId;
+ triggerPortIdUpdate_l();
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYERIID)
.set(AMEDIAMETRICS_PROP_PLAYERIID, playerIId)
.record();
}
+void AudioTrack::triggerPortIdUpdate_l() {
+ if (mAudioManager == nullptr) {
+ // use checkService() to avoid blocking if audio service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16(kAudioServiceName));
+ if (binder == nullptr) {
+ ALOGE("%s(%d): binding to audio service failed.",
+ __func__,
+ mPlayerIId);
+ return;
+ }
+
+ mAudioManager = interface_cast<IAudioManager>(binder);
+ }
+
+ // first time when the track is created we do not have a valid piid
+ if (mPlayerIId != PLAYER_PIID_INVALID) {
+ mAudioManager->playerEvent(mPlayerIId, PLAYER_UPDATE_PORT_ID, mPortId);
+ }
+}
+
status_t AudioTrack::addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback)
{
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 32576c2..e873607 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_AUDIOTRACK_H
#define ANDROID_AUDIOTRACK_H
+#include <audiomanager/IAudioManager.h>
#include <binder/IMemory.h>
#include <cutils/sched_policy.h>
#include <media/AudioSystem.h>
@@ -1145,6 +1146,8 @@
void setAudioTrackCallback(const sp<media::IAudioTrackCallback>& callback) {
mAudioTrackCallback->setAudioTrackCallback(callback);
}
+ private:
+ void triggerPortIdUpdate_l();
protected:
/* copying audio tracks is not allowed */
@@ -1419,6 +1422,9 @@
*/
int mPlayerIId = -1; // AudioManager.h PLAYER_PIID_INVALID
+ /** Interface for interacting with the AudioService. */
+ sp<IAudioManager> mAudioManager;
+
/**
* mLogSessionId is a string identifying this AudioTrack for the metrics service.
* It may be unique or shared with other objects. An empty string means the
diff --git a/media/libmedia/tests/codeclist/Android.bp b/media/libmedia/tests/codeclist/Android.bp
index 2ed3126..d4494f6 100644
--- a/media/libmedia/tests/codeclist/Android.bp
+++ b/media/libmedia/tests/codeclist/Android.bp
@@ -25,7 +25,7 @@
cc_test {
name: "CodecListTest",
- test_suites: ["device-tests", "mts"],
+ test_suites: ["device-tests", "mts-media"],
gtest: true,
// Support multilib variants (using different suffix per sub-architecture), which is needed on
diff --git a/media/libmediametrics/libmediametrics.map.txt b/media/libmediametrics/libmediametrics.map.txt
index c46281a..f37af64 100644
--- a/media/libmediametrics/libmediametrics.map.txt
+++ b/media/libmediametrics/libmediametrics.map.txt
@@ -1,29 +1,29 @@
LIBMEDIAMETRICS_1 {
global:
- mediametrics_addDouble; # apex
- mediametrics_addInt32; # apex
- mediametrics_addInt64; # apex
- mediametrics_addRate; # apex
- mediametrics_count; # apex
- mediametrics_create; # apex
- mediametrics_delete; # apex
- mediametrics_freeCString; # apex
- mediametrics_getAttributes; # apex
- mediametrics_getCString; # apex
- mediametrics_getDouble; # apex
- mediametrics_getInt32; # apex
- mediametrics_getInt64; # apex
- mediametrics_getKey; # apex
- mediametrics_getRate; # apex
- mediametrics_isEnabled; # apex
- mediametrics_readable; # apex
- mediametrics_selfRecord; # apex
- mediametrics_setCString; # apex
- mediametrics_setDouble; # apex
- mediametrics_setInt32; # apex
- mediametrics_setInt64; # apex
- mediametrics_setRate; # apex
- mediametrics_setUid; # apex
+ mediametrics_addDouble; # systemapi
+ mediametrics_addInt32; # systemapi
+ mediametrics_addInt64; # systemapi
+ mediametrics_addRate; # systemapi
+ mediametrics_count; # systemapi
+ mediametrics_create; # systemapi
+ mediametrics_delete; # systemapi
+ mediametrics_freeCString; # systemapi
+ mediametrics_getAttributes; # systemapi
+ mediametrics_getCString; # systemapi
+ mediametrics_getDouble; # systemapi
+ mediametrics_getInt32; # systemapi
+ mediametrics_getInt64; # systemapi
+ mediametrics_getKey; # systemapi
+ mediametrics_getRate; # systemapi
+ mediametrics_isEnabled; # systemapi
+ mediametrics_readable; # systemapi
+ mediametrics_selfRecord; # systemapi
+ mediametrics_setCString; # systemapi
+ mediametrics_setDouble; # systemapi
+ mediametrics_setInt32; # systemapi
+ mediametrics_setInt64; # systemapi
+ mediametrics_setRate; # systemapi
+ mediametrics_setUid; # systemapi
local:
*;
};
diff --git a/media/libstagefright/DataConverter.cpp b/media/libstagefright/DataConverter.cpp
index 52be054..b53ac77 100644
--- a/media/libstagefright/DataConverter.cpp
+++ b/media/libstagefright/DataConverter.cpp
@@ -24,6 +24,10 @@
#include <media/MediaCodecBuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
+#include <system/audio.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
+
namespace android {
@@ -81,12 +85,38 @@
return numSamples * mTargetSampleSize;
}
+static audio_format_t getAudioFormat(AudioEncoding e) {
+ audio_format_t format = AUDIO_FORMAT_INVALID;
+ switch (e) {
+ case kAudioEncodingPcm16bit:
+ format = AUDIO_FORMAT_PCM_16_BIT;
+ break;
+ case kAudioEncodingPcm8bit:
+ format = AUDIO_FORMAT_PCM_8_BIT;
+ break;
+ case kAudioEncodingPcmFloat:
+ format = AUDIO_FORMAT_PCM_FLOAT;
+ break;
+ case kAudioEncodingPcm24bitPacked:
+ format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ break;
+ case kAudioEncodingPcm32bit:
+ format = AUDIO_FORMAT_PCM_32_BIT;
+ break;
+ default:
+ ALOGE("Invalid AudioEncoding %d", e);
+ }
+ return format;
+}
static size_t getAudioSampleSize(AudioEncoding e) {
switch (e) {
- case kAudioEncodingPcm16bit: return 2;
- case kAudioEncodingPcm8bit: return 1;
- case kAudioEncodingPcmFloat: return 4;
+ case kAudioEncodingPcm16bit:
+ case kAudioEncodingPcm8bit:
+ case kAudioEncodingPcmFloat:
+ case kAudioEncodingPcm24bitPacked:
+ case kAudioEncodingPcm32bit:
+ return audio_bytes_per_sample(getAudioFormat(e));
default: return 0;
}
}
@@ -116,7 +146,15 @@
} else if (mTo == kAudioEncodingPcmFloat && mFrom == kAudioEncodingPcm16bit) {
memcpy_to_float_from_i16((float*)tgt->base(), (const int16_t*)src->data(), src->size() / 2);
} else {
- return INVALID_OPERATION;
+ audio_format_t srcFormat = getAudioFormat(mFrom);
+ audio_format_t dstFormat = getAudioFormat(mTo);
+
+ if ((srcFormat == AUDIO_FORMAT_INVALID) || (dstFormat == AUDIO_FORMAT_INVALID))
+ return INVALID_OPERATION;
+
+ size_t frames = src->size() / audio_bytes_per_sample(srcFormat);
+ memcpy_by_audio_format((void*)tgt->base(), dstFormat, (void*)src->data(),
+ srcFormat, frames);
}
return OK;
}
diff --git a/media/libstagefright/renderfright/tests/RenderEngineTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
index 2697ff4..3a67cc2 100644
--- a/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
+++ b/media/libstagefright/renderfright/tests/RenderEngineTest.cpp
@@ -60,7 +60,7 @@
}
static sp<GraphicBuffer> allocateDefaultBuffer() {
- return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
+ return sp<GraphicBuffer>::make(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
HAL_PIXEL_FORMAT_RGBA_8888, 1,
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER,
@@ -69,7 +69,7 @@
// Allocates a 1x1 buffer to fill with a solid color
static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) {
- return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ return sp<GraphicBuffer>::make(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1,
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_TEXTURE,
"input");
@@ -275,7 +275,7 @@
renderengine::DisplaySettings settings;
std::vector<const renderengine::LayerSettings*> layers;
// Meaningless buffer since we don't do any drawing
- sp<GraphicBuffer> buffer = new GraphicBuffer();
+ sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
invokeDraw(settings, layers, buffer);
}
diff --git a/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
index 97c7442..b82586b 100644
--- a/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
+++ b/media/libstagefright/renderfright/tests/RenderEngineThreadedTest.cpp
@@ -70,7 +70,7 @@
}
TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
- sp<GraphicBuffer> buf = new GraphicBuffer();
+ sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
.WillOnce(Return(NO_ERROR));
status_t result = mThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
@@ -83,7 +83,7 @@
}
TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
- sp<GraphicBuffer> buf = new GraphicBuffer();
+ sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
mThreadedRE->cacheExternalTextureBuffer(buf);
}
@@ -198,7 +198,7 @@
TEST_F(RenderEngineThreadedTest, drawLayers) {
renderengine::DisplaySettings settings;
std::vector<const renderengine::LayerSettings*> layers;
- sp<GraphicBuffer> buffer = new GraphicBuffer();
+ sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
base::unique_fd bufferFence;
base::unique_fd drawFence;
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index 2f516d5..ddf797c 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -618,13 +618,14 @@
int32_t AAVCAssembler::pickStartSeq(const Queue *queue,
uint32_t first, int64_t play, int64_t jit) {
+ CHECK(!queue->empty());
// pick the first sequence number has the start bit.
sp<ABuffer> buffer = *(queue->begin());
int32_t firstSeqNo = buffer->int32Data();
// This only works for FU-A type & non-start sequence
- unsigned nalType = buffer->data()[0] & 0x1f;
- if (nalType != 28 || buffer->data()[1] & 0x80) {
+ int32_t nalType = buffer->size() >= 1 ? buffer->data()[0] & 0x1f : -1;
+ if (nalType != 28 || (buffer->size() >= 2 && buffer->data()[1] & 0x80)) {
return firstSeqNo;
}
@@ -634,7 +635,7 @@
if (rtpTime + jit >= play) {
break;
}
- if ((data[1] & 0x80)) {
+ if (it->size() >= 2 && (data[1] & 0x80)) {
const int32_t seqNo = it->int32Data();
ALOGE("finding [HEAD] pkt. \t Seq# (%d ~ )[%d", firstSeqNo, seqNo);
firstSeqNo = seqNo;
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 6fcf119..d917772 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -985,7 +985,7 @@
int type = storage->getType();
if (type == MTP_STORAGE_REMOVABLE_RAM) {
- std::string str = android::base::Trim((const char*)name);
+ std::string str = android::base::Trim(name);
name.set(str.c_str());
}
ALOGV("name: %s format: 0x%04X (%s)\n", (const char*)name, format,
diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h
index 4cec58a..30d1bc1 100644
--- a/media/mtp/MtpStringBuffer.h
+++ b/media/mtp/MtpStringBuffer.h
@@ -20,6 +20,7 @@
#include <log/log.h>
#include <stdint.h>
#include <string>
+#include <string_view>
// Max Character number of a MTP String
#define MTP_STRING_MAX_CHARACTER_NUMBER 255
@@ -55,6 +56,7 @@
inline int size() const { return mString.length(); }
inline operator const char*() const { return mString.c_str(); }
+ operator std::string_view() const { return mString; }
};
inline void MtpStringBuffer::append(const char* other) {
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 38e422d..7495082 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -529,22 +529,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 f0e3c1e..602cada 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -132,6 +132,20 @@
}
cc_test {
+ name: "mediautils_fixedstring_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ shared_libs: [
+ "libaudioutils",
+ ],
+
+ srcs: [
+ "mediautils_fixedstring_tests.cpp",
+ ],
+}
+
+cc_test {
name: "mediautils_scopedstatistics_tests",
defaults: ["libmediautils_tests_defaults"],
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/Android.bp b/services/audioflinger/Android.bp
index a08879e..420ae52 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -71,6 +71,7 @@
"liblog",
"libbinder",
"libaudioclient",
+ "libaudiomanager",
"libmedialogservice",
"libmediametrics",
"libmediautils",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 7a9027c..a68f63e 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -54,7 +54,7 @@
#include <cutils/properties.h>
#include <system/audio.h>
-#include <audiomanager/AudioManager.h>
+#include <audiomanager/IAudioManager.h>
#include "AudioFlinger.h"
#include "NBAIO_Tee.h"
@@ -114,11 +114,12 @@
using media::audio::common::AudioMMapPolicyType;
using android::content::AttributionSourceState;
-static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
-static const char kHardwareLockedString[] = "Hardware lock is taken\n";
-static const char kClientLockedString[] = "Client lock is taken\n";
-static const char kNoEffectsFactory[] = "Effects Factory is absent\n";
+static constexpr char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
+static constexpr char kHardwareLockedString[] = "Hardware lock is taken\n";
+static constexpr char kClientLockedString[] = "Client lock is taken\n";
+static constexpr char kNoEffectsFactory[] = "Effects Factory is absent\n";
+static constexpr char kAudioServiceName[] = "audio";
nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
@@ -2722,9 +2723,28 @@
ThreadBase *thread = (ThreadBase *)mMmapThreads.valueAt(i).get();
thread->systemReady();
}
+
+ // Java services are ready, so we can create a reference to AudioService
+ getOrCreateAudioManager();
+
return NO_ERROR;
}
+sp<IAudioManager> AudioFlinger::getOrCreateAudioManager()
+{
+ if (mAudioManager.load() == nullptr) {
+ // use checkService() to avoid blocking
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16(kAudioServiceName));
+ if (binder != nullptr) {
+ mAudioManager = interface_cast<IAudioManager>(binder);
+ } else {
+ ALOGE("%s(): binding to audio service failed.", __func__);
+ }
+ }
+ return mAudioManager.load();
+}
+
status_t AudioFlinger::getMicrophones(std::vector<media::MicrophoneInfo> *microphones)
{
AutoMutex lock(mHardwareLock);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index fc4c807..08594e2 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -117,6 +117,7 @@
class DevicesFactoryHalInterface;
class EffectsFactoryHalInterface;
class FastMixer;
+class IAudioManager;
class PassthruBufferProvider;
class RecordBufferConverter;
class ServerProxy;
@@ -988,6 +989,8 @@
size_t rejectedKVPSize, const String8& rejectedKVPs,
uid_t callingUid);
+ sp<IAudioManager> getOrCreateAudioManager();
+
public:
// These methods read variables atomically without mLock,
// though the variables are updated with mLock.
@@ -1029,6 +1032,9 @@
std::vector<media::audio::common::AudioMMapPolicyInfo>> mPolicyInfos;
int32_t mAAudioBurstsPerBuffer = 0;
int32_t mAAudioHwBurstMinMicros = 0;
+
+ /** Interface for interacting with the AudioService. */
+ mediautils::atomic_sp<IAudioManager> mAudioManager;
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 6a138bb..e52d1ee 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -204,6 +204,12 @@
float getSpeed() const { return mSpeed; }
bool isSpatialized() const override { return mIsSpatialized; }
+ /**
+ * Updates the mute state and notifies the audio service. Call this only when holding player
+ * thread lock.
+ */
+ void processMuteEvent_l(const sp<IAudioManager>& audioManager, mute_state_t muteState);
+
protected:
// for numerous
friend class PlaybackThread;
@@ -354,6 +360,11 @@
TeePatches mTeePatches;
const float mSpeed;
const bool mIsSpatialized;
+
+ // TODO: replace PersistableBundle with own struct
+ // access these two variables only when holding player thread lock.
+ std::unique_ptr<os::PersistableBundle> mMuteEventExtras;
+ mute_state_t mMuteState;
}; // end of Track
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e127431..77e2421 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -31,6 +31,7 @@
#include <sys/syscall.h>
#include <cutils/bitops.h>
#include <cutils/properties.h>
+#include <binder/PersistableBundle.h>
#include <media/AudioContainers.h>
#include <media/AudioDeviceTypeAddr.h>
#include <media/AudioParameter.h>
@@ -178,6 +179,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.
@@ -2040,7 +2046,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);
@@ -5369,6 +5376,12 @@
volume = masterVolume * mStreamTypes[track->streamType()].volume;
}
+ track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted()});
+
handleVoipVolume_l(&volume);
// cache the combined master volume and stream type volume for fast mixer; this
@@ -5533,6 +5546,12 @@
v = 0;
}
+ track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+ /*muteState=*/{masterVolume == 0.f,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted()});
+
handleVoipVolume_l(&v);
if (track->isPausing()) {
@@ -5910,18 +5929,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
@@ -6146,6 +6182,12 @@
right *= v * mMasterBalanceRight;
}
+ track->processMuteEvent_l(mAudioFlinger->getOrCreateAudioManager(),
+ /*muteState=*/{mMasterMute,
+ mStreamTypes[track->streamType()].volume == 0.f,
+ mStreamTypes[track->streamType()].mute,
+ track->isPlaybackRestricted()});
+
if (lastTrack) {
track->setFinalVolume((left + right) / 2.f);
if (left != mLeftVolFloat || right != mRightVolFloat) {
@@ -6356,9 +6398,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);
@@ -6939,9 +6981,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",
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index fc37bd2..b477d65 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -624,7 +624,7 @@
virtual void dumpTracks_l(int fd __unused, const Vector<String16>& args __unused) { }
- friend class AudioFlinger; // for mEffectChains
+ friend class AudioFlinger; // for mEffectChains and mAudioManager
const type_t mType;
@@ -1177,7 +1177,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
@@ -1391,6 +1391,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
@@ -1408,13 +1409,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 {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 2dfa1b2..3f9c306 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
@@ -1484,6 +1494,39 @@
}
}
+// must be called with player thread lock held
+void AudioFlinger::PlaybackThread::Track::processMuteEvent_l(const sp<
+ IAudioManager>& audioManager, mute_state_t muteState)
+{
+ if (mMuteState == muteState) {
+ // mute state did not change, do nothing
+ return;
+ }
+
+ status_t result = UNKNOWN_ERROR;
+ if (audioManager && mPortId != AUDIO_PORT_HANDLE_NONE) {
+ if (mMuteEventExtras == nullptr) {
+ mMuteEventExtras = std::make_unique<os::PersistableBundle>();
+ }
+ mMuteEventExtras->putInt(String16(kExtraPlayerEventMuteKey),
+ static_cast<int>(muteState));
+
+ result = audioManager->portEvent(mPortId,
+ PLAYER_UPDATE_MUTED,
+ mMuteEventExtras);
+ }
+
+ if (result == OK) {
+ mMuteState = muteState;
+ } else {
+ ALOGW("%s(%d): cannot process mute state for port ID %d, status error %d",
+ __func__,
+ id(),
+ mPortId,
+ result);
+ }
+}
+
status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
{
if (!isOffloaded() && !isDirect()) {
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
index b036e12..60b16ac 100644
--- a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -25,11 +25,11 @@
const engineConfig::ProductStrategies gOrderedStrategies = {
{"STRATEGY_PHONE",
{
- {"phone", AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",
+ {AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}},
},
- {"sco", AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",
+ {AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO,
""}},
}
@@ -37,11 +37,11 @@
},
{"STRATEGY_SONIFICATION",
{
- {"ring", AUDIO_STREAM_RING, "AUDIO_STREAM_RING",
+ {AUDIO_STREAM_RING, "AUDIO_STREAM_RING",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
},
- {"alarm", AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",
+ {AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}},
}
@@ -49,7 +49,7 @@
},
{"STRATEGY_ENFORCED_AUDIBLE",
{
- {"", AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE",
+ {AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}}
}
@@ -57,7 +57,7 @@
},
{"STRATEGY_ACCESSIBILITY",
{
- {"", AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY",
+ {AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
}
@@ -65,7 +65,7 @@
},
{"STRATEGY_SONIFICATION_RESPECTFUL",
{
- {"", AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION",
+ {AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""},
@@ -77,11 +77,11 @@
},
{"STRATEGY_MEDIA",
{
- {"assistant", AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
+ {AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT",
{{AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
},
- {"music", AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
+ {AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""},
@@ -95,7 +95,7 @@
AUDIO_FLAG_NONE, ""}
},
},
- {"system", AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM",
+ {AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}
}
@@ -103,7 +103,7 @@
},
{"STRATEGY_DTMF",
{
- {"", AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF",
+ {AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}
@@ -113,7 +113,7 @@
},
{"STRATEGY_CALL_ASSISTANT",
{
- {"", AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT",
+ {AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_CALL_ASSISTANT, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}}
}
@@ -121,7 +121,7 @@
},
{"STRATEGY_TRANSMITTED_THROUGH_SPEAKER",
{
- {"", AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
+ {AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS",
{
{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_BEACON, ""},
@@ -140,7 +140,7 @@
const engineConfig::ProductStrategies gOrderedSystemStrategies = {
{"rerouting",
{
- {"", AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
+ {AUDIO_STREAM_REROUTING, "AUDIO_STREAM_REROUTING",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VIRTUAL_SOURCE, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}}
}
@@ -148,7 +148,7 @@
},
{"patch",
{
- {"", AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
+ {AUDIO_STREAM_PATCH, "AUDIO_STREAM_PATCH",
{{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
AUDIO_FLAG_NONE, ""}}
}
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index 2ebb7df..8036eea 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -35,7 +35,6 @@
using StreamVector = std::vector<audio_stream_type_t>;
struct AttributesGroup {
- std::string name;
audio_stream_type_t stream;
std::string volumeGroup;
AttributesVector attributesVect;
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 6f560d5..ac117f0 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -57,7 +57,6 @@
static constexpr const char *collectionTag = "AttributesGroups";
struct Attributes {
- static constexpr const char *name = "name";
static constexpr const char *streamType = "streamType";
static constexpr const char *volumeGroup = "volumeGroup";
};
@@ -313,12 +312,6 @@
status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
Collection &attributesGroup)
{
- std::string name = getXmlAttribute(child, Attributes::name);
- if (name.empty()) {
- ALOGV("AttributesGroupTraits No attribute %s found", Attributes::name);
- }
- ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
-
std::string volumeGroup = getXmlAttribute(child, Attributes::volumeGroup);
if (volumeGroup.empty()) {
ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::volumeGroup);
@@ -339,7 +332,7 @@
AttributesVector attributesVect;
deserializeAttributesCollection(doc, child, attributesVect);
- attributesGroup.push_back({name, streamType, volumeGroup, attributesVect});
+ attributesGroup.push_back({streamType, volumeGroup, attributesVect});
return NO_ERROR;
}
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 08d6453..d67e6c4 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -78,7 +78,7 @@
case AUDIO_POLICY_FORCE_FOR_COMMUNICATION:
if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO &&
config != AUDIO_POLICY_FORCE_NONE) {
- ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
+ ALOGW("setForceUse() invalid config %d for COMMUNICATION", config);
return BAD_VALUE;
}
break;
@@ -88,14 +88,14 @@
config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_NO_BT_A2DP && config != AUDIO_POLICY_FORCE_SPEAKER ) {
- ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
+ ALOGW("setForceUse() invalid config %d for MEDIA", config);
return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_RECORD:
if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
config != AUDIO_POLICY_FORCE_NONE) {
- ALOGW("setForceUse() invalid config %d for FOR_RECORD", config);
+ ALOGW("setForceUse() invalid config %d for RECORD", config);
return BAD_VALUE;
}
break;
@@ -105,19 +105,22 @@
config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY &&
config != AUDIO_POLICY_FORCE_ANALOG_DOCK &&
config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) {
- ALOGW("setForceUse() invalid config %d for FOR_DOCK", config);
+ ALOGW("setForceUse() invalid config %d for DOCK", config);
+ return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_SYSTEM:
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
- ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config);
+ ALOGW("setForceUse() invalid config %d for SYSTEM", config);
+ return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO:
if (config != AUDIO_POLICY_FORCE_NONE &&
config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) {
ALOGW("setForceUse() invalid config %d for HDMI_SYSTEM_AUDIO", config);
+ return BAD_VALUE;
}
break;
case AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND:
@@ -131,13 +134,13 @@
break;
case AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING:
if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_NONE) {
- ALOGW("setForceUse() invalid config %d for FOR_VIBRATE_RINGING", config);
+ ALOGW("setForceUse() invalid config %d for VIBRATE_RINGING", config);
return BAD_VALUE;
}
break;
default:
ALOGW("setForceUse() invalid usage %d", usage);
- break; // TODO return BAD_VALUE?
+ return BAD_VALUE;
}
return EngineBase::setForceUse(usage, config);
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index dea329a..c21c6a2 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -7850,6 +7850,9 @@
addOutput(output, desc);
+ sp<DeviceDescriptor> speaker = mAvailableOutputDevices.getDevice(
+ AUDIO_DEVICE_OUT_SPEAKER, String8(""), AUDIO_FORMAT_DEFAULT);
+
if (audio_is_remote_submix_device(deviceType) && address != "0") {
sp<AudioPolicyMix> policyMix;
if (mPolicyMixes.getAudioPolicyMix(deviceType, address, policyMix) == NO_ERROR) {
@@ -7860,13 +7863,13 @@
address.string());
}
- } else if (hasPrimaryOutput() && profile->getModule()
- != mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)
+ } else if (hasPrimaryOutput() && speaker != nullptr
+ && mPrimaryOutput->supportsDevice(speaker) && !desc->supportsDevice(speaker)
&& ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) {
// no duplicated output for:
// - direct outputs
// - outputs used by dynamic policy mixes
- // - outputs opened on the primary HW module
+ // - outputs that supports SPEAKER while the primary output does not.
audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
//TODO: configure audio effect output stage here
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index b24ab14..20c7a5d 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -84,6 +84,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 +94,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 +125,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 +135,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());
}
@@ -784,9 +799,21 @@
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 = modes;
+ mSupportedLatencyModes = std::move(modes);
checkSensorsState_l();
}
}
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index 4aebcea..bc95fe4 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -168,6 +168,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;
/**
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 6f43270..f926b88 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -105,11 +105,6 @@
TClientBase::mCameraIdStr.string());
status_t res;
- // Verify ops permissions
- res = TClientBase::startCameraOps();
- if (res != OK) {
- return res;
- }
IPCTransport providerTransport = IPCTransport::INVALID;
res = providerPtr->getCameraIdIPCTransport(TClientBase::mCameraIdStr.string(),
&providerTransport);
@@ -145,6 +140,14 @@
return res;
}
+ // Verify ops permissions
+ res = TClientBase::startCameraOps();
+ if (res != OK) {
+ TClientBase::finishCameraOps();
+ mDevice.clear();
+ return res;
+ }
+
wp<NotificationListener> weakThis(this);
res = mDevice->setNotifyCallback(weakThis);
if (res != OK) {