Merge "AudioFlinger: remove false underruns if we retry for data"
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 5d0f68e..ad1ccbc 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -262,6 +262,17 @@
void CryptoHal::clearHeapBase(int32_t seqNum) {
Mutex::Autolock autoLock(mLock);
+ /*
+ * Clear the remote shared memory mapping by setting the shared
+ * buffer base to a null hidl_memory.
+ *
+ * TODO: Add a releaseSharedBuffer method in a future DRM HAL
+ * API version to make this explicit.
+ */
+ uint32_t bufferId = mHeapBases.valueFor(seqNum).getBufferId();
+ Return<void> hResult = mPlugin->setSharedBufferBase(hidl_memory(), bufferId);
+ ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed");
+
mHeapBases.removeItem(seqNum);
}
diff --git a/media/extractors/mp4/ItemTable.cpp b/media/extractors/mp4/ItemTable.cpp
index b6787af..ca9deab 100644
--- a/media/extractors/mp4/ItemTable.cpp
+++ b/media/extractors/mp4/ItemTable.cpp
@@ -506,7 +506,7 @@
ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
if (!derivedImage.dimgRefs.empty()) {
- ALOGW("dimgRefs if not clean!");
+ ALOGW("dimgRefs not clean!");
}
derivedImage.dimgRefs.appendVector(mRefs);
@@ -1490,6 +1490,17 @@
const ImageItem *image = &mItemIdToItemMap[itemIndex];
+ ssize_t tileItemIndex = -1;
+ if (image->isGrid()) {
+ if (image->dimgRefs.empty()) {
+ return NULL;
+ }
+ tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
+ if (tileItemIndex < 0) {
+ return NULL;
+ }
+ }
+
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
@@ -1530,10 +1541,6 @@
}
if (image->isGrid()) {
- ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
- if (tileItemIndex < 0) {
- return NULL;
- }
meta->setInt32(kKeyGridRows, image->rows);
meta->setInt32(kKeyGridCols, image->columns);
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 2df37a8..6146c0e 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -49,6 +49,7 @@
"libaudiomanager",
"libmedia_helper",
"libmediametrics",
+ "libmediautils",
],
export_shared_lib_headers: ["libbinder"],
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 00af7e8..37c62a8 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -24,10 +24,8 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
-#include <cutils/multiuser.h>
#include <media/TimeCheck.h>
-#include <private/android_filesystem_config.h>
-
+#include <mediautils/ServiceUtilities.h>
#include "IAudioFlinger.h"
namespace android {
@@ -912,7 +910,7 @@
case SET_MIC_MUTE:
case SET_LOW_RAM_DEVICE:
case SYSTEM_READY: {
- if (multiuser_get_app_id(IPCThreadState::self()->getCallingUid()) >= AID_APP_START) {
+ if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index a1236e7..316105c 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -24,11 +24,10 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
-#include <cutils/multiuser.h>
#include <media/AudioEffect.h>
#include <media/IAudioPolicyService.h>
#include <media/TimeCheck.h>
-#include <private/android_filesystem_config.h>
+#include <mediautils/ServiceUtilities.h>
#include <system/audio.h>
namespace android {
@@ -936,7 +935,7 @@
case STOP_AUDIO_SOURCE:
case GET_SURROUND_FORMATS:
case SET_SURROUND_FORMAT_ENABLED: {
- if (multiuser_get_app_id(IPCThreadState::self()->getCallingUid()) >= AID_APP_START) {
+ if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
__func__, code, IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index aa39443..f6f817a 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -192,7 +192,6 @@
// always recompute for both channel masks even if only one has changed.
const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask);
const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask);
- const bool mixerChannelCountChanged = track->mMixerChannelCount != mixerChannelCount;
ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX)
&& trackChannelCount
@@ -213,7 +212,7 @@
// do it after downmix since track format may change!
track->prepareForReformat();
- if (track->mResampler.get() != nullptr && mixerChannelCountChanged) {
+ if (track->mResampler.get() != nullptr) {
// resampler channels may have changed.
const uint32_t resetToSampleRate = track->sampleRate;
track->mResampler.reset(nullptr);
diff --git a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
index 55383eb..0b883f1 100644
--- a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
+++ b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
@@ -51,7 +51,7 @@
{0x7261676f, 0x6d75, 0x7369, 0x6364, {0x28, 0xe2, 0xfd, 0x3a, 0xc3, 0x9e}}, // type
{0xe0e6539b, 0x1781, 0x7261, 0x676f, {0x6d, 0x75, 0x73, 0x69, 0x63, 0x40}}, // uuid
EFFECT_CONTROL_API_VERSION,
- (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST | EFFECT_FLAG_VOLUME_CTRL),
0, // TODO
1,
"Dynamics Processing",
@@ -367,6 +367,76 @@
return 0;
}
+//helper function
+bool DP_checkSizesInt(uint32_t paramSize, uint32_t valueSize, uint32_t expectedParams,
+ uint32_t expectedValues) {
+ if (paramSize < expectedParams * sizeof(int32_t)) {
+ ALOGE("Invalid paramSize: %u expected %u", paramSize,
+ (uint32_t)(expectedParams * sizeof(int32_t)));
+ return false;
+ }
+ if (valueSize < expectedValues * sizeof(int32_t)) {
+ ALOGE("Invalid valueSize %u expected %u", valueSize,
+ (uint32_t)(expectedValues * sizeof(int32_t)));
+ return false;
+ }
+ return true;
+}
+
+static dp_fx::DPChannel* DP_getChannel(DynamicsProcessingContext *pContext,
+ int32_t channel) {
+ if (pContext->mPDynamics == NULL) {
+ return NULL;
+ }
+ dp_fx::DPChannel *pChannel = pContext->mPDynamics->getChannel(channel);
+ ALOGE_IF(pChannel == NULL, "DPChannel NULL. invalid channel %d", channel);
+ return pChannel;
+}
+
+static dp_fx::DPEq* DP_getEq(DynamicsProcessingContext *pContext, int32_t channel,
+ int32_t eqType) {
+ dp_fx::DPChannel *pChannel = DP_getChannel(pContext, channel);
+ if (pChannel == NULL) {
+ return NULL;
+ }
+ dp_fx::DPEq *pEq = (eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() :
+ (eqType == DP_PARAM_POST_EQ ? pChannel->getPostEq() : NULL));
+ ALOGE_IF(pEq == NULL,"DPEq NULL invalid eq");
+ return pEq;
+}
+
+static dp_fx::DPEqBand* DP_getEqBand(DynamicsProcessingContext *pContext, int32_t channel,
+ int32_t eqType, int32_t band) {
+ dp_fx::DPEq *pEq = DP_getEq(pContext, channel, eqType);
+ if (pEq == NULL) {
+ return NULL;
+ }
+ dp_fx::DPEqBand *pEqBand = pEq->getBand(band);
+ ALOGE_IF(pEqBand == NULL, "DPEqBand NULL. invalid band %d", band);
+ return pEqBand;
+}
+
+static dp_fx::DPMbc* DP_getMbc(DynamicsProcessingContext *pContext, int32_t channel) {
+ dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
+ if (pChannel == NULL) {
+ return NULL;
+ }
+ dp_fx::DPMbc *pMbc = pChannel->getMbc();
+ ALOGE_IF(pMbc == NULL, "DPMbc NULL invalid MBC");
+ return pMbc;
+}
+
+static dp_fx::DPMbcBand* DP_getMbcBand(DynamicsProcessingContext *pContext, int32_t channel,
+ int32_t band) {
+ dp_fx::DPMbc *pMbc = DP_getMbc(pContext, channel);
+ if (pMbc == NULL) {
+ return NULL;
+ }
+ dp_fx::DPMbcBand *pMbcBand = pMbc->getBand(band);
+ ALOGE_IF(pMbcBand == NULL, "pMbcBand NULL. invalid band %d", band);
+ return pMbcBand;
+}
+
int DP_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
void *pCmdData, uint32_t *replySize, void *pReplyData) {
@@ -483,8 +553,49 @@
p->data + voffset);
break;
}
+ case EFFECT_CMD_SET_VOLUME: {
+ ALOGV("EFFECT_CMD_SET_VOLUME");
+ // if pReplyData is NULL, VOL_CTRL is delegated to another effect
+ if (pReplyData == NULL || replySize == NULL || *replySize < ((int)sizeof(int32_t) * 2)) {
+ ALOGV("no VOLUME data to return");
+ break;
+ }
+ if (pCmdData == NULL || cmdSize < ((int)sizeof(uint32_t) * 2)) {
+ ALOGE("\tLVM_ERROR : DynamicsProcessing EFFECT_CMD_SET_VOLUME ERROR");
+ return -EINVAL;
+ }
+
+ const int32_t unityGain = 1 << 24;
+ //channel count
+ int32_t channelCount = (int32_t)audio_channel_count_from_out_mask(
+ pContext->mConfig.inputCfg.channels);
+ for (int32_t ch = 0; ch < channelCount; ch++) {
+
+ dp_fx::DPChannel * pChannel = DP_getChannel(pContext, ch);
+ if (pChannel == NULL) {
+ ALOGE("%s EFFECT_CMD_SET_VOLUME invalid channel %d", __func__, ch);
+ return -EINVAL;
+ break;
+ }
+
+ int32_t offset = ch;
+ if (ch > 1) {
+ // FIXME: limited to 2 unique channels. If more channels present, use value for
+ // first channel
+ offset = 0;
+ }
+ const float gain = (float)*((uint32_t *)pCmdData + offset) / unityGain;
+ const float gainDb = linearToDb(gain);
+ ALOGVV("%s EFFECT_CMD_SET_VOLUME channel %d, engine outputlevel %f (%0.2f dB)",
+ __func__, ch, gain, gainDb);
+ pChannel->setOutputGain(gainDb);
+ }
+
+ const int32_t volRet[2] = {unityGain, unityGain}; // Apply no volume before effect.
+ memcpy(pReplyData, volRet, sizeof(volRet));
+ break;
+ }
case EFFECT_CMD_SET_DEVICE:
- case EFFECT_CMD_SET_VOLUME:
case EFFECT_CMD_SET_AUDIO_MODE:
break;
@@ -523,76 +634,6 @@
return 0;
}
-//helper function
-bool DP_checkSizesInt(uint32_t paramSize, uint32_t valueSize, uint32_t expectedParams,
- uint32_t expectedValues) {
- if (paramSize < expectedParams * sizeof(int32_t)) {
- ALOGE("Invalid paramSize: %u expected %u", paramSize,
- (uint32_t) (expectedParams * sizeof(int32_t)));
- return false;
- }
- if (valueSize < expectedValues * sizeof(int32_t)) {
- ALOGE("Invalid valueSize %u expected %u", valueSize,
- (uint32_t)(expectedValues * sizeof(int32_t)));
- return false;
- }
- return true;
-}
-
-static dp_fx::DPChannel* DP_getChannel(DynamicsProcessingContext *pContext,
- int32_t channel) {
- if (pContext->mPDynamics == NULL) {
- return NULL;
- }
- dp_fx::DPChannel *pChannel = pContext->mPDynamics->getChannel(channel);
- ALOGE_IF(pChannel == NULL, "DPChannel NULL. invalid channel %d", channel);
- return pChannel;
-}
-
-static dp_fx::DPEq* DP_getEq(DynamicsProcessingContext *pContext, int32_t channel,
- int32_t eqType) {
- dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
- if (pChannel == NULL) {
- return NULL;
- }
- dp_fx::DPEq *pEq = (eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() :
- (eqType == DP_PARAM_POST_EQ ? pChannel->getPostEq() : NULL));
- ALOGE_IF(pEq == NULL,"DPEq NULL invalid eq");
- return pEq;
-}
-
-static dp_fx::DPEqBand* DP_getEqBand(DynamicsProcessingContext *pContext, int32_t channel,
- int32_t eqType, int32_t band) {
- dp_fx::DPEq *pEq = DP_getEq(pContext, channel, eqType);
- if (pEq == NULL) {
- return NULL;
- }
- dp_fx::DPEqBand *pEqBand = pEq->getBand(band);
- ALOGE_IF(pEqBand == NULL, "DPEqBand NULL. invalid band %d", band);
- return pEqBand;
-}
-
-static dp_fx::DPMbc* DP_getMbc(DynamicsProcessingContext *pContext, int32_t channel) {
- dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
- if (pChannel == NULL) {
- return NULL;
- }
- dp_fx::DPMbc *pMbc = pChannel->getMbc();
- ALOGE_IF(pMbc == NULL, "DPMbc NULL invalid MBC");
- return pMbc;
-}
-
-static dp_fx::DPMbcBand* DP_getMbcBand(DynamicsProcessingContext *pContext, int32_t channel,
- int32_t band) {
- dp_fx::DPMbc *pMbc = DP_getMbc(pContext, channel);
- if (pMbc == NULL) {
- return NULL;
- }
- dp_fx::DPMbcBand *pMbcBand = pMbc->getBand(band);
- ALOGE_IF(pMbcBand == NULL, "pMbcBand NULL. invalid band %d", band);
- return pMbcBand;
-}
-
int DP_getParameter(DynamicsProcessingContext *pContext,
uint32_t paramSize,
void *pParam,
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.cpp b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
index 8b79991..ac758e0 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.cpp
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
@@ -174,8 +174,8 @@
}
//----
-DPChannel::DPChannel() : mInitialized(false), mInputGainDb(0), mPreEqInUse(false), mMbcInUse(false),
- mPostEqInUse(false), mLimiterInUse(false) {
+DPChannel::DPChannel() : mInitialized(false), mInputGainDb(0), mOutputGainDb(0),
+ mPreEqInUse(false), mMbcInUse(false), mPostEqInUse(false), mLimiterInUse(false) {
}
void DPChannel::init(float inputGain, bool preEqInUse, uint32_t preEqBandCount,
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.h b/media/libeffects/dynamicsproc/dsp/DPBase.h
index 355f64b..e74f91d 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.h
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.h
@@ -272,6 +272,16 @@
mInputGainDb = gain;
}
+ float getOutputGain() const {
+ if (!mInitialized) {
+ return 0;
+ }
+ return mOutputGainDb;
+ }
+ void setOutputGain(float gain) {
+ mOutputGainDb = gain;
+ }
+
DPEq* getPreEq();
DPMbc* getMbc();
DPEq* getPostEq();
@@ -281,6 +291,7 @@
private:
bool mInitialized;
float mInputGainDb;
+ float mOutputGainDb;
DPEq mPreEq;
DPMbc mMbc;
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
index 59195fc..d06fd70 100644
--- a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
@@ -29,7 +29,7 @@
#define CIRCULAR_BUFFER_UPSAMPLE 4 //4 times buffer size
-static constexpr float MIN_ENVELOPE = 0.000001f;
+static constexpr float MIN_ENVELOPE = 1e-6f; //-120 dB
//helper functionS
static inline bool isPowerOf2(unsigned long n) {
return (n & (n - 1)) == 0;
@@ -53,14 +53,6 @@
#define IS_CHANGED(c, a, b) { c |= !compareEquality(a,b); \
(a) = (b); }
-float dBtoLinear(float valueDb) {
- return pow (10, valueDb / 20.0);
-}
-
-float linearToDb(float value) {
- return 20 * log10(value);
-}
-
//ChannelBuffers helper
void ChannelBuffer::initBuffers(unsigned int blockSize, unsigned int overlapSize,
unsigned int halfFftSize, unsigned int samplingRate, DPBase &dpBase) {
@@ -74,7 +66,7 @@
cBOutput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE);
//fill input with half block size...
- for (unsigned int k = 0; k < mBlockSize/2; k++) {
+ for (unsigned int k = 0; k < mBlockSize/2; k++) {
cBInput.write(0);
}
@@ -94,12 +86,14 @@
mMbcBands.size(), mPostEqBands.size());
DPChannel *pChannel = dpBase.getChannel(0);
- if (pChannel != NULL) {
+ if (pChannel != nullptr) {
mPreEqInUse = pChannel->getPreEq()->isInUse();
mMbcInUse = pChannel->getMbc()->isInUse();
mPostEqInUse = pChannel->getPostEq()->isInUse();
mLimiterInUse = pChannel->getLimiter()->isInUse();
}
+
+ mLimiterParams.linkGroup = -1; //no group.
}
void ChannelBuffer::computeBinStartStop(BandParams &bp, size_t binStart) {
@@ -108,8 +102,35 @@
bp.binStop = (int)(0.5 + bp.freqCutoffHz * mBlockSize / mSamplingRate);
}
-//== DPFrequency
+//== LinkedLimiters Helper
+void LinkedLimiters::reset() {
+ mGroupsMap.clear();
+}
+void LinkedLimiters::update(int32_t group, int index) {
+ mGroupsMap[group].push_back(index);
+}
+
+void LinkedLimiters::remove(int index) {
+ //check all groups and if index is found, remove it.
+ //if group is empty afterwards, remove it.
+ for (auto it = mGroupsMap.begin(); it != mGroupsMap.end(); ) {
+ for (auto itIndex = it->second.begin(); itIndex != it->second.end(); ) {
+ if (*itIndex == index) {
+ itIndex = it->second.erase(itIndex);
+ } else {
+ ++itIndex;
+ }
+ }
+ if (it->second.size() == 0) {
+ it = mGroupsMap.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+//== DPFrequency
void DPFrequency::reset() {
}
@@ -147,14 +168,25 @@
mSamplingRate, *this);
}
- //dsp
+ //effective number of frames processed per second
+ mBlocksPerSecond = (float)mSamplingRate / (mBlockSize - mOverlapSize);
+
fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize);
+
+ //compute window rms for energy compensation
+ mWindowRms = 0;
+ for (size_t i = 0; i < mVWindow.size(); i++) {
+ mWindowRms += mVWindow[i] * mVWindow[i];
+ }
+
+ //Making sure window rms is not zero.
+ mWindowRms = std::max(sqrt(mWindowRms / mVWindow.size()), MIN_ENVELOPE);
}
void DPFrequency::updateParameters(ChannelBuffer &cb, int channelIndex) {
DPChannel *pChannel = getChannel(channelIndex);
- if (pChannel == NULL) {
+ if (pChannel == nullptr) {
ALOGE("Error: updateParameters null DPChannel %d", channelIndex);
return;
}
@@ -166,7 +198,7 @@
//===EqPre
if (cb.mPreEqInUse) {
DPEq *pPreEq = pChannel->getPreEq();
- if (pPreEq == NULL) {
+ if (pPreEq == nullptr) {
ALOGE("Error: updateParameters null PreEq for channel: %d", channelIndex);
return;
}
@@ -174,7 +206,7 @@
if (cb.mPreEqEnabled) {
for (unsigned int b = 0; b < getPreEqBandCount(); b++) {
DPEqBand *pEqBand = pPreEq->getBand(b);
- if (pEqBand == NULL) {
+ if (pEqBand == nullptr) {
ALOGE("Error: updateParameters null PreEqBand for band %d", b);
return; //failed.
}
@@ -222,7 +254,7 @@
bool changed = false;
DPEq *pPostEq = pChannel->getPostEq();
- if (pPostEq == NULL) {
+ if (pPostEq == nullptr) {
ALOGE("Error: updateParameters null postEq for channel: %d", channelIndex);
return; //failed.
}
@@ -230,7 +262,7 @@
if (cb.mPostEqEnabled) {
for (unsigned int b = 0; b < getPostEqBandCount(); b++) {
DPEqBand *pEqBand = pPostEq->getBand(b);
- if (pEqBand == NULL) {
+ if (pEqBand == nullptr) {
ALOGE("Error: updateParameters PostEqBand NULL for band %d", b);
return; //failed.
}
@@ -265,7 +297,7 @@
//===MBC
if (cb.mMbcInUse) {
DPMbc *pMbc = pChannel->getMbc();
- if (pMbc == NULL) {
+ if (pMbc == nullptr) {
ALOGE("Error: updateParameters Mbc NULL for channel: %d", channelIndex);
return;
}
@@ -274,7 +306,7 @@
bool changed = false;
for (unsigned int b = 0; b < getMbcBandCount(); b++) {
DPMbcBand *pMbcBand = pMbc->getBand(b);
- if (pMbcBand == NULL) {
+ if (pMbcBand == nullptr) {
ALOGE("Error: updateParameters MbcBand NULL for band %d", b);
return; //failed.
}
@@ -307,11 +339,38 @@
cb.computeBinStartStop(*pMbcBandParams, binNext);
binNext = pMbcBandParams->binStop + 1;
}
-
}
-
}
}
+
+ //===Limiter
+ if (cb.mLimiterInUse) {
+ bool changed = false;
+ DPLimiter *pLimiter = pChannel->getLimiter();
+ if (pLimiter == nullptr) {
+ ALOGE("Error: updateParameters Limiter NULL for channel: %d", channelIndex);
+ return;
+ }
+ cb.mLimiterEnabled = pLimiter->isEnabled();
+ if (cb.mLimiterEnabled) {
+ IS_CHANGED(changed, cb.mLimiterParams.linkGroup ,
+ (int32_t)pLimiter->getLinkGroup());
+ cb.mLimiterParams.attackTimeMs = pLimiter->getAttackTime();
+ cb.mLimiterParams.releaseTimeMs = pLimiter->getReleaseTime();
+ cb.mLimiterParams.ratio = pLimiter->getRatio();
+ cb.mLimiterParams.thresholdDb = pLimiter->getThreshold();
+ cb.mLimiterParams.postGainDb = pLimiter->getPostGain();
+ }
+
+ if (changed) {
+ ALOGV("limiter changed, recomputing linkGroups for %d", channelIndex);
+ mLinkedLimiters.remove(channelIndex); //in case it was already there.
+ mLinkedLimiters.update(cb.mLimiterParams.linkGroup, channelIndex);
+ }
+ }
+
+ //=== Output Gain
+ cb.outputGainDb = pChannel->getOutputGain();
}
size_t DPFrequency::processSamples(const float *in, float *out, size_t samples) {
@@ -336,12 +395,8 @@
}
}
- //TODO: lookahead limiters
- //TODO: apply linked limiters to all channels.
- //**Process each Channel
- for (int ch = 0; ch < channelCount; ch++) {
- processMono(mChannelBuffers[ch]);
- }
+ //**process all channelBuffers
+ processChannelBuffers(mChannelBuffers);
//** estimate how much data is available in ALL channels
size_t available = mChannelBuffers[0].cBOutput.availableToRead();
@@ -370,62 +425,78 @@
return samples;
}
-size_t DPFrequency::processMono(ChannelBuffer &cb) {
-
+size_t DPFrequency::processChannelBuffers(CBufferVector &channelBuffers) {
+ const int channelCount = channelBuffers.size();
size_t processedSamples = 0;
+ size_t processFrames = mBlockSize - mOverlapSize;
- size_t available = cb.cBInput.availableToRead();
- while (available >= mBlockSize - mOverlapSize) {
-
- //move tail of previous
- for (unsigned int k = 0; k < mOverlapSize; ++k) {
- cb.input[k] = cb.input[mBlockSize - mOverlapSize + k];
- }
-
- //read new available data
- for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
- cb.input[mOverlapSize + k] = cb.cBInput.read();
- }
-
- //## Actual process
- processOneVector(cb.output, cb.input, cb);
- //##End of Process
-
- //mix tail (and capture new tail
- for (unsigned int k = 0; k < mOverlapSize; k++) {
- cb.output[k] += cb.outTail[k];
- cb.outTail[k] = cb.output[mBlockSize - mOverlapSize + k]; //new tail
- }
-
- //output data
- for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
- cb.cBOutput.write(cb.output[k]);
- }
-
- available = cb.cBInput.availableToRead();
+ size_t available = channelBuffers[0].cBInput.availableToRead();
+ for (int ch = 1; ch < channelCount; ch++) {
+ available = std::min(available, channelBuffers[ch].cBInput.availableToRead());
}
+ while (available >= processFrames) {
+ //First pass
+ for (int ch = 0; ch < channelCount; ch++) {
+ ChannelBuffer * pCb = &channelBuffers[ch];
+ //move tail of previous
+ std::copy(pCb->input.begin() + processFrames,
+ pCb->input.end(),
+ pCb->input.begin());
+
+ //read new available data
+ for (unsigned int k = 0; k < processFrames; k++) {
+ pCb->input[mOverlapSize + k] = pCb->cBInput.read();
+ }
+ //first stages: fft, preEq, mbc, postEq and start of Limiter
+ processedSamples += processFirstStages(*pCb);
+ }
+
+ //**compute linked limiters and update levels if needed
+ processLinkedLimiters(channelBuffers);
+
+ //final pass.
+ for (int ch = 0; ch < channelCount; ch++) {
+ ChannelBuffer * pCb = &channelBuffers[ch];
+
+ //linked limiter and ifft
+ processLastStages(*pCb);
+
+ //mix tail (and capture new tail
+ for (unsigned int k = 0; k < mOverlapSize; k++) {
+ pCb->output[k] += pCb->outTail[k];
+ pCb->outTail[k] = pCb->output[processFrames + k]; //new tail
+ }
+
+ //output data
+ for (unsigned int k = 0; k < processFrames; k++) {
+ pCb->cBOutput.write(pCb->output[k]);
+ }
+ }
+ available -= processFrames;
+ }
return processedSamples;
}
-
-size_t DPFrequency::processOneVector(FloatVec & output, FloatVec & input,
- ChannelBuffer &cb) {
+size_t DPFrequency::processFirstStages(ChannelBuffer &cb) {
//##apply window
Eigen::Map<Eigen::VectorXf> eWindow(&mVWindow[0], mVWindow.size());
- Eigen::Map<Eigen::VectorXf> eInput(&input[0], input.size());
+ Eigen::Map<Eigen::VectorXf> eInput(&cb.input[0], cb.input.size());
Eigen::VectorXf eWin = eInput.cwiseProduct(eWindow); //apply window
- //##fft //TODO: refactor frequency transformations away from other stages.
- mFftServer.fwd(mComplexTemp, eWin);
+ //##fft
+ //Note: we are using eigen with the default scaling, which ensures that
+ // IFFT( FFT(x) ) = x.
+ // TODO: optimize by using the noscale option, and compensate with dB scale offsets
+ mFftServer.fwd(cb.complexTemp, eWin);
- size_t cSize = mComplexTemp.size();
+ size_t cSize = cb.complexTemp.size();
size_t maxBin = std::min(cSize/2, mHalfFFTSize);
//== EqPre (always runs)
for (size_t k = 0; k < maxBin; k++) {
- mComplexTemp[k] *= cb.mPreEqFactorVector[k];
+ cb.complexTemp[k] *= cb.mPreEqFactorVector[k];
}
//== MBC
@@ -439,62 +510,68 @@
float preGainSquared = preGainFactor * preGainFactor;
for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
- float fReal = mComplexTemp[k].real();
- float fImag = mComplexTemp[k].imag();
- float fSquare = (fReal * fReal + fImag * fImag) * preGainSquared;
-
- fEnergySum += fSquare;
+ fEnergySum += std::norm(cb.complexTemp[k]) * preGainSquared; //mag squared
}
- fEnergySum = sqrt(fEnergySum /2.0);
+ //Eigen FFT is full spectrum, even if the source was real data.
+ // Each half spectrum has half the energy. This is taken into account with the * 2
+ // factor in the energy computations.
+ // energy = sqrt(sum_components_squared) number_points
+ // in here, the fEnergySum is duplicated to account for the second half spectrum,
+ // and the windowRms is used to normalize by the expected energy reduction
+ // caused by the window used (expected for steady state signals)
+ fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms);
+
+ // updates computed per frame advance.
float fTheta = 0.0;
- float fFAtt = pMbcBandParams->attackTimeMs;
- float fFRel = pMbcBandParams->releaseTimeMs;
-
- float fUpdatesPerSecond = 10; //TODO: compute from framerate
-
+ float fFAttSec = pMbcBandParams->attackTimeMs / 1000; //in seconds
+ float fFRelSec = pMbcBandParams->releaseTimeMs / 1000; //in seconds
if (fEnergySum > pMbcBandParams->previousEnvelope) {
- fTheta = exp(-1.0 / (fFAtt * fUpdatesPerSecond));
+ fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond));
} else {
- fTheta = exp(-1.0 / (fFRel * fUpdatesPerSecond));
+ fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond));
}
float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope;
-
//preserve for next iteration
pMbcBandParams->previousEnvelope = fEnv;
- float fThreshold = dBtoLinear(pMbcBandParams->thresholdDb);
- float fNoiseGateThreshold = dBtoLinear(pMbcBandParams->noiseGateThresholdDb);
-
- float fNewFactor = 1.0;
-
- if (fEnv > fThreshold) {
- float fDbAbove = linearToDb(fThreshold / fEnv);
- float fDbTarget = fDbAbove / pMbcBandParams->ratio;
- float fDbChange = fDbAbove - fDbTarget;
- fNewFactor = dBtoLinear(fDbChange);
- } else if (fEnv < fNoiseGateThreshold) {
- if (fEnv < MIN_ENVELOPE) {
- fEnv = MIN_ENVELOPE;
- }
- float fDbBelow = linearToDb(fNoiseGateThreshold / fEnv);
- float fDbTarget = fDbBelow / pMbcBandParams->expanderRatio;
- float fDbChange = fDbBelow - fDbTarget;
- fNewFactor = dBtoLinear(fDbChange);
+ if (fEnv < MIN_ENVELOPE) {
+ fEnv = MIN_ENVELOPE;
}
+ const float envDb = linearToDb(fEnv);
+ float newLevelDb = envDb;
+ //using shorter variables for code clarity
+ const float thresholdDb = pMbcBandParams->thresholdDb;
+ const float ratio = pMbcBandParams->ratio;
+ const float kneeWidthDbHalf = pMbcBandParams->kneeWidthDb / 2;
+ const float noiseGateThresholdDb = pMbcBandParams->noiseGateThresholdDb;
+ const float expanderRatio = pMbcBandParams->expanderRatio;
+
+ //find segment
+ if (envDb > thresholdDb + kneeWidthDbHalf) {
+ //compression segment
+ newLevelDb = envDb + ((1 / ratio) - 1) * (envDb - thresholdDb);
+ } else if (envDb > thresholdDb - kneeWidthDbHalf) {
+ //knee-compression segment
+ float temp = (envDb - thresholdDb + kneeWidthDbHalf);
+ newLevelDb = envDb + ((1 / ratio) - 1) *
+ temp * temp / (kneeWidthDbHalf * 4);
+ } else if (envDb < noiseGateThresholdDb) {
+ //expander segment
+ newLevelDb = noiseGateThresholdDb -
+ expanderRatio * (noiseGateThresholdDb - envDb);
+ }
+
+ float newFactor = dBtoLinear(newLevelDb - envDb);
//apply post gain.
- fNewFactor *= dBtoLinear(pMbcBandParams->gainPostDb);
-
- if (fNewFactor < 0) {
- fNewFactor = 0;
- }
+ newFactor *= dBtoLinear(pMbcBandParams->gainPostDb);
//apply to this band
for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
- mComplexTemp[k] *= fNewFactor;
+ cb.complexTemp[k] *= newFactor;
}
} //end per band process
@@ -504,14 +581,94 @@
//== EqPost
if (cb.mPostEqInUse && cb.mPostEqEnabled) {
for (size_t k = 0; k < maxBin; k++) {
- mComplexTemp[k] *= cb.mPostEqFactorVector[k];
+ cb.complexTemp[k] *= cb.mPostEqFactorVector[k];
+ }
+ }
+
+ //== Limiter. First Pass
+ if (cb.mLimiterInUse && cb.mLimiterEnabled) {
+ float fEnergySum = 0;
+ for (size_t k = 0; k < maxBin; k++) {
+ fEnergySum += std::norm(cb.complexTemp[k]);
+ }
+
+ //see explanation above for energy computation logic
+ fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms);
+ float fTheta = 0.0;
+ float fFAttSec = cb.mLimiterParams.attackTimeMs / 1000; //in seconds
+ float fFRelSec = cb.mLimiterParams.releaseTimeMs / 1000; //in seconds
+
+ if (fEnergySum > cb.mLimiterParams.previousEnvelope) {
+ fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond));
+ } else {
+ fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond));
+ }
+
+ float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * cb.mLimiterParams.previousEnvelope;
+ //preserve for next iteration
+ cb.mLimiterParams.previousEnvelope = fEnv;
+
+ const float envDb = linearToDb(fEnv);
+ float newFactorDb = 0;
+ //using shorter variables for code clarity
+ const float thresholdDb = cb.mLimiterParams.thresholdDb;
+ const float ratio = cb.mLimiterParams.ratio;
+
+ if (envDb > thresholdDb) {
+ //limiter segment
+ newFactorDb = ((1 / ratio) - 1) * (envDb - thresholdDb);
+ }
+
+ float newFactor = dBtoLinear(newFactorDb);
+
+ cb.mLimiterParams.newFactor = newFactor;
+
+ } //end Limiter
+ return mBlockSize;
+}
+
+void DPFrequency::processLinkedLimiters(CBufferVector &channelBuffers) {
+
+ const int channelCount = channelBuffers.size();
+ for (auto &groupPair : mLinkedLimiters.mGroupsMap) {
+ float minFactor = 1.0;
+ //estimate minfactor for all linked
+ for(int index : groupPair.second) {
+ if (index >= 0 && index < channelCount) {
+ minFactor = std::min(channelBuffers[index].mLimiterParams.newFactor, minFactor);
+ }
+ }
+ //apply minFactor
+ for(int index : groupPair.second) {
+ if (index >= 0 && index < channelCount) {
+ channelBuffers[index].mLimiterParams.linkFactor = minFactor;
+ }
+ }
+ }
+}
+
+size_t DPFrequency::processLastStages(ChannelBuffer &cb) {
+
+ float outputGainFactor = dBtoLinear(cb.outputGainDb);
+ //== Limiter. last Pass
+ if (cb.mLimiterInUse && cb.mLimiterEnabled) {
+ //compute factor, with post-gain
+ float factor = cb.mLimiterParams.linkFactor * dBtoLinear(cb.mLimiterParams.postGainDb);
+ outputGainFactor *= factor;
+ }
+
+ //apply to all if != 1.0
+ if (!compareEquality(outputGainFactor, 1.0f)) {
+ size_t cSize = cb.complexTemp.size();
+ size_t maxBin = std::min(cSize/2, mHalfFFTSize);
+ for (size_t k = 0; k < maxBin; k++) {
+ cb.complexTemp[k] *= outputGainFactor;
}
}
//##ifft directly to output.
- Eigen::Map<Eigen::VectorXf> eOutput(&output[0], output.size());
- mFftServer.inv(eOutput, mComplexTemp);
-
+ Eigen::Map<Eigen::VectorXf> eOutput(&cb.output[0], cb.output.size());
+ mFftServer.inv(eOutput, cb.complexTemp);
return mBlockSize;
}
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.h b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
index 9919142..be8771d 100644
--- a/media/libeffects/dynamicsproc/dsp/DPFrequency.h
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
@@ -39,8 +39,11 @@
FloatVec output; // time domain temp vector for output
FloatVec outTail; // time domain temp vector for output tail (for overlap-add method)
+ Eigen::VectorXcf complexTemp; // complex temp vector for frequency domain operations
+
//Current parameters
float inputGainDb;
+ float outputGainDb;
struct BandParams {
bool enabled;
float freqCutoffHz;
@@ -64,6 +67,19 @@
//Historic values
float previousEnvelope;
};
+ struct LimiterParams {
+ int32_t linkGroup;
+ float attackTimeMs;
+ float releaseTimeMs;
+ float ratio;
+ float thresholdDb;
+ float postGainDb;
+
+ //Historic values
+ float previousEnvelope;
+ float newFactor;
+ float linkFactor;
+ };
bool mPreEqInUse;
bool mPreEqEnabled;
@@ -79,6 +95,7 @@
bool mLimiterInUse;
bool mLimiterEnabled;
+ LimiterParams mLimiterParams;
FloatVec mPreEqFactorVector; // temp pre-computed vector to shape spectrum at preEQ stage
FloatVec mPostEqFactorVector; // temp pre-computed vector to shape spectrum at postEQ stage
@@ -91,6 +108,18 @@
};
+using CBufferVector = std::vector<ChannelBuffer>;
+
+using GroupsMap = std::map<int32_t, IntVec>;
+
+class LinkedLimiters {
+public:
+ void reset();
+ void update(int32_t group, int index);
+ void remove(int index);
+ GroupsMap mGroupsMap;
+};
+
class DPFrequency : public DPBase {
public:
virtual size_t processSamples(const float *in, float *out, size_t samples);
@@ -104,16 +133,25 @@
size_t processMono(ChannelBuffer &cb);
size_t processOneVector(FloatVec &output, FloatVec &input, ChannelBuffer &cb);
+ size_t processChannelBuffers(CBufferVector &channelBuffers);
+ size_t processFirstStages(ChannelBuffer &cb);
+ size_t processLastStages(ChannelBuffer &cb);
+ void processLinkedLimiters(CBufferVector &channelBuffers);
+
size_t mBlockSize;
size_t mHalfFFTSize;
size_t mOverlapSize;
size_t mSamplingRate;
- std::vector<ChannelBuffer> mChannelBuffers;
+ float mBlocksPerSecond;
+
+ CBufferVector mChannelBuffers;
+
+ LinkedLimiters mLinkedLimiters;
//dsp
FloatVec mVWindow; //window class.
- Eigen::VectorXcf mComplexTemp;
+ float mWindowRms;
Eigen::FFT<float> mFftServer;
};
diff --git a/media/libeffects/dynamicsproc/dsp/RDsp.h b/media/libeffects/dynamicsproc/dsp/RDsp.h
index 1048442..cfa1305 100644
--- a/media/libeffects/dynamicsproc/dsp/RDsp.h
+++ b/media/libeffects/dynamicsproc/dsp/RDsp.h
@@ -20,10 +20,25 @@
#include <complex>
#include <log/log.h>
#include <vector>
+#include <map>
using FloatVec = std::vector<float>;
+using IntVec = std::vector<int>;
using ComplexVec = std::vector<std::complex<float>>;
// =======
+// Helper Functions
+// =======
+template <class T>
+static T dBtoLinear(T valueDb) {
+ return pow (10, valueDb / 20.0);
+}
+
+template <class T>
+static T linearToDb(T value) {
+ return 20 * log10(value);
+}
+
+// =======
// DSP window creation
// =======
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index ba7003a..8ff267f 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -32,6 +32,9 @@
"libaudioclient_headers",
"libaudio_system_headers",
],
+ export_header_lib_headers: [
+ "libmedia_headers",
+ ],
clang: true,
}
diff --git a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
index f1b7806..6ad2441 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaDataBase.h
@@ -70,6 +70,7 @@
kKeyWantsNALFragments = 'NALf',
kKeyIsSyncFrame = 'sync', // int32_t (bool)
kKeyIsCodecConfig = 'conf', // int32_t (bool)
+ kKeyIsMuxerData = 'muxd', // int32_t (bool)
kKeyTime = 'time', // int64_t (usecs)
kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs)
kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp)
@@ -220,6 +221,7 @@
kKeyFrameCount = 'nfrm', // int32_t, total number of frame in video track
kKeyExifOffset = 'exof', // int64_t, Exif data offset
kKeyExifSize = 'exsz', // int64_t, Exif data size
+ kKeyIsExif = 'exif', // bool (int32_t) buffer contains exif data block
};
enum {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index cbc3015..23d66bb 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -934,7 +934,11 @@
sp<MetaData> meta = mSources.itemAt(trackIndex)->getFormat();
if (meta == NULL) {
ALOGE("no metadata for track %zu", trackIndex);
- return NULL;
+ format->setInt32("type", MEDIA_TRACK_TYPE_UNKNOWN);
+ format->setString("mime", "application/octet-stream");
+ format->setString("language", "und");
+
+ return format;
}
const char *mime;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index 0402fca..fb12360 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -155,7 +155,9 @@
break;
default:
ALOGE("Unknown track type: %d", track.mTrackType);
- return NULL;
+ format->setInt32("type", MEDIA_TRACK_TYPE_UNKNOWN);
+ format->setString("mime", "application/octet-stream");
+ return format;
}
// For CEA-608 CC1, field 0 channel 0
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 2a08f62..69cd82e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -745,6 +745,7 @@
sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
reply->setSize("buffer-ix", index);
reply->setInt32("generation", mBufferGeneration);
+ reply->setSize("size", size);
if (eos) {
ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
@@ -1127,6 +1128,7 @@
int32_t render;
size_t bufferIx;
int32_t eos;
+ size_t size;
CHECK(msg->findSize("buffer-ix", &bufferIx));
if (!mIsAudio) {
@@ -1146,7 +1148,10 @@
CHECK(msg->findInt64("timestampNs", ×tampNs));
err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
} else {
- mNumOutputFramesDropped += !mIsAudio;
+ if (!msg->findInt32("eos", &eos) || !eos ||
+ !msg->findSize("size", &size) || size) {
+ mNumOutputFramesDropped += !mIsAudio;
+ }
err = mCodec->releaseOutputBuffer(bufferIx);
}
if (err != OK) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2abea9e..bf5ae2c 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -5322,13 +5322,13 @@
convertCodecColorAspectsToPlatformAspects(aspects, &range, &standard, &transfer);
// if some aspects are unspecified, use dataspace fields
- if (range != 0) {
+ if (range == 0) {
range = (dataSpace & HAL_DATASPACE_RANGE_MASK) >> HAL_DATASPACE_RANGE_SHIFT;
}
- if (standard != 0) {
+ if (standard == 0) {
standard = (dataSpace & HAL_DATASPACE_STANDARD_MASK) >> HAL_DATASPACE_STANDARD_SHIFT;
}
- if (transfer != 0) {
+ if (transfer == 0) {
transfer = (dataSpace & HAL_DATASPACE_TRANSFER_MASK) >> HAL_DATASPACE_TRANSFER_SHIFT;
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 550a99c..6ff3d78 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -83,6 +83,9 @@
static const char kMetaKey_TemporalLayerCount[] = "com.android.video.temporal_layers_count";
static const int kTimestampDebugCount = 10;
+static const int kItemIdBase = 10000;
+static const char kExifHeader[] = {'E', 'x', 'i', 'f', '\0', '\0'};
+static const int32_t kTiffHeaderOffset = htonl(sizeof(kExifHeader));
static const uint8_t kMandatoryHevcNalUnitTypes[3] = {
kHevcNalUnitTypeVps,
@@ -112,7 +115,7 @@
int64_t getDurationUs() const;
int64_t getEstimatedTrackSizeBytes() const;
- int32_t getMetaSizeIncrease() const;
+ int32_t getMetaSizeIncrease(int32_t angle, int32_t trackCount) const;
void writeTrackHeader(bool use32BitOffset = true);
int64_t getMinCttsOffsetTimeUs();
void bufferChunk(int64_t timestampUs);
@@ -122,8 +125,10 @@
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic; }
+ bool isExifData(const MediaBufferBase *buffer) const;
void addChunkOffset(off64_t offset);
- void addItemOffsetAndSize(off64_t offset, size_t size);
+ void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif);
+ void flushItemRefs();
int32_t getTrackId() const { return mTrackId; }
status_t dump(int fd, const Vector<String16>& args) const;
static const char *getFourCCForMime(const char *mime);
@@ -355,7 +360,9 @@
int32_t mRotation;
Vector<uint16_t> mProperties;
- Vector<uint16_t> mDimgRefs;
+ ItemRefs mDimgRefs;
+ ItemRefs mCdscRefs;
+ uint16_t mImageItemId;
int32_t mIsPrimary;
int32_t mWidth, mHeight;
int32_t mTileWidth, mTileHeight;
@@ -499,6 +506,7 @@
mPrimaryItemId = 0;
mAssociationEntryCount = 0;
mNumGrids = 0;
+ mHasRefs = false;
// Following variables only need to be set for the first recording session.
// And they will stay the same for all the recording sessions.
@@ -680,7 +688,12 @@
#endif
}
-int64_t MPEG4Writer::estimateFileLevelMetaSize() {
+int64_t MPEG4Writer::estimateFileLevelMetaSize(MetaData *params) {
+ int32_t rotation;
+ if (!params || !params->findInt32(kKeyRotation, &rotation)) {
+ rotation = 0;
+ }
+
// base meta size
int64_t metaSize = 12 // meta fullbox header
+ 33 // hdlr box
@@ -695,7 +708,7 @@
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
if ((*it)->isHeic()) {
- metaSize += (*it)->getMetaSizeIncrease();
+ metaSize += (*it)->getMetaSizeIncrease(rotation, mTracks.size());
}
}
@@ -900,7 +913,7 @@
if (mInMemoryCacheSize == 0) {
int32_t bitRate = -1;
if (mHasFileLevelMeta) {
- mInMemoryCacheSize += estimateFileLevelMetaSize();
+ mInMemoryCacheSize += estimateFileLevelMetaSize(param);
}
if (mHasMoovBox) {
if (param) {
@@ -1344,12 +1357,17 @@
}
off64_t MPEG4Writer::addSample_l(
- MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten) {
+ MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten) {
off64_t old_offset = mOffset;
if (usePrefix) {
addMultipleLengthPrefixedSamples_l(buffer);
} else {
+ if (isExif) {
+ ::write(mFd, &kTiffHeaderOffset, 4); // exif_tiff_header_offset field
+ mOffset += 4;
+ }
+
::write(mFd,
(const uint8_t *)buffer->data() + buffer->range_offset(),
buffer->range_length());
@@ -1767,6 +1785,9 @@
mReachedEOS(false),
mStartTimestampUs(-1),
mRotation(0),
+ mDimgRefs("dimg"),
+ mCdscRefs("cdsc"),
+ mImageItemId(0),
mIsPrimary(0),
mWidth(0),
mHeight(0),
@@ -1933,6 +1954,13 @@
return OK;
}
+bool MPEG4Writer::Track::isExifData(const MediaBufferBase *buffer) const {
+ return mIsHeic
+ && (buffer->range_length() > sizeof(kExifHeader))
+ && !memcmp((uint8_t *)buffer->data() + buffer->range_offset(),
+ kExifHeader, sizeof(kExifHeader));
+}
+
void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
CHECK(!mIsHeic);
if (mOwner->use32BitFileOffset()) {
@@ -1943,7 +1971,7 @@
}
}
-void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size) {
+void MPEG4Writer::Track::addItemOffsetAndSize(off64_t offset, size_t size, bool isExif) {
CHECK(mIsHeic);
if (offset > UINT32_MAX || size > UINT32_MAX) {
@@ -1954,6 +1982,18 @@
if (mIsMalformed) {
return;
}
+
+ if (isExif) {
+ mCdscRefs.value.push_back(mOwner->addItem_l({
+ .itemType = "Exif",
+ .isPrimary = false,
+ .isHidden = false,
+ .offset = (uint32_t)offset,
+ .size = (uint32_t)size,
+ }));
+ return;
+ }
+
if (mTileIndex >= mNumTiles) {
ALOGW("Ignoring excess tiles!");
return;
@@ -1968,7 +2008,7 @@
default: break; // don't set if invalid
}
- bool hasGrid = (mNumTiles > 1);
+ bool hasGrid = (mTileWidth > 0);
if (mProperties.empty()) {
mProperties.push_back(mOwner->addProperty_l({
@@ -1990,18 +2030,16 @@
}
}
- uint16_t itemId = mOwner->addItem_l({
- .itemType = "hvc1",
- .isPrimary = hasGrid ? false : (mIsPrimary != 0),
- .isHidden = hasGrid,
- .offset = (uint32_t)offset,
- .size = (uint32_t)size,
- .properties = mProperties,
- });
-
mTileIndex++;
if (hasGrid) {
- mDimgRefs.push_back(itemId);
+ mDimgRefs.value.push_back(mOwner->addItem_l({
+ .itemType = "hvc1",
+ .isPrimary = false,
+ .isHidden = true,
+ .offset = (uint32_t)offset,
+ .size = (uint32_t)size,
+ .properties = mProperties,
+ }));
if (mTileIndex == mNumTiles) {
mProperties.clear();
@@ -2016,7 +2054,7 @@
.rotation = heifRotation,
}));
}
- mOwner->addItem_l({
+ mImageItemId = mOwner->addItem_l({
.itemType = "grid",
.isPrimary = (mIsPrimary != 0),
.isHidden = false,
@@ -2025,9 +2063,31 @@
.width = (uint32_t)mWidth,
.height = (uint32_t)mHeight,
.properties = mProperties,
- .dimgRefs = mDimgRefs,
});
}
+ } else {
+ mImageItemId = mOwner->addItem_l({
+ .itemType = "hvc1",
+ .isPrimary = (mIsPrimary != 0),
+ .isHidden = false,
+ .offset = (uint32_t)offset,
+ .size = (uint32_t)size,
+ .properties = mProperties,
+ });
+ }
+}
+
+// Flush out the item refs for this track. Note that it must be called after the
+// writer thread has stopped, because there might be pending items in the last
+// few chunks written by the writer thread (as opposed to the track). In particular,
+// it affects the 'dimg' refs for tiled image, as we only have the refs after the
+// last tile sample is written.
+void MPEG4Writer::Track::flushItemRefs() {
+ CHECK(mIsHeic);
+
+ if (mImageItemId > 0) {
+ mOwner->addRefs_l(mImageItemId, mDimgRefs);
+ mOwner->addRefs_l(mImageItemId, mCdscRefs);
}
}
@@ -2174,15 +2234,20 @@
chunk->mTimeStampUs, chunk->mTrack->getTrackType());
int32_t isFirstSample = true;
- bool usePrefix = chunk->mTrack->usePrefix();
while (!chunk->mSamples.empty()) {
List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
+ int32_t isExif;
+ if (!(*it)->meta_data().findInt32(kKeyIsExif, &isExif)) {
+ isExif = 0;
+ }
+ bool usePrefix = chunk->mTrack->usePrefix() && !isExif;
+
size_t bytesWritten;
- off64_t offset = addSample_l(*it, usePrefix, &bytesWritten);
+ off64_t offset = addSample_l(*it, usePrefix, isExif, &bytesWritten);
if (chunk->mTrack->isHeic()) {
- chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten);
+ chunk->mTrack->addItemOffsetAndSize(offset, bytesWritten, isExif);
} else if (isFirstSample) {
chunk->mTrack->addChunkOffset(offset);
isFirstSample = false;
@@ -2904,6 +2969,19 @@
break;
}
+ bool isExif = false;
+ int32_t isMuxerData;
+ if (buffer->meta_data().findInt32(kKeyIsMuxerData, &isMuxerData) && isMuxerData) {
+ // We only support one type of muxer data, which is Exif data block.
+ isExif = isExifData(buffer);
+ if (!isExif) {
+ ALOGW("Ignoring bad Exif data block");
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+ }
+
++nActualFrames;
// Make a deep copy of the MediaBuffer and Metadata and release
@@ -2916,10 +2994,15 @@
buffer->release();
buffer = NULL;
- if (usePrefix()) StripStartcode(copy);
+ if (isExif) {
+ copy->meta_data().setInt32(kKeyIsExif, 1);
+ }
+ bool usePrefix = this->usePrefix() && !isExif;
+
+ if (usePrefix) StripStartcode(copy);
size_t sampleSize = copy->range_length();
- if (usePrefix()) {
+ if (usePrefix) {
if (mOwner->useNalLengthFour()) {
sampleSize += 4;
} else {
@@ -3185,10 +3268,10 @@
}
if (!hasMultipleTracks) {
size_t bytesWritten;
- off64_t offset = mOwner->addSample_l(copy, usePrefix(), &bytesWritten);
+ off64_t offset = mOwner->addSample_l(copy, usePrefix, isExif, &bytesWritten);
if (mIsHeic) {
- addItemOffsetAndSize(offset, bytesWritten);
+ addItemOffsetAndSize(offset, bytesWritten, isExif);
} else {
uint32_t count = (mOwner->use32BitFileOffset()
? mStcoTableEntries->count()
@@ -3450,10 +3533,12 @@
return mEstimatedTrackSizeBytes;
}
-int32_t MPEG4Writer::Track::getMetaSizeIncrease() const {
+int32_t MPEG4Writer::Track::getMetaSizeIncrease(
+ int32_t angle, int32_t trackCount) const {
CHECK(mIsHeic);
- int32_t grid = (mNumTiles > 1);
+ int32_t grid = (mTileWidth > 0);
+ int32_t rotate = (angle > 0);
// Note that the rotation angle is in the file meta, and we don't have
// it until start, so here the calculation has to assume rotation.
@@ -3461,25 +3546,34 @@
// increase to ipco
int32_t increase = 20 * (grid + 1) // 'ispe' property
+ (8 + mCodecSpecificDataSize) // 'hvcC' property
- + 9; // 'irot' property (worst case)
+ ;
+
+ if (rotate) {
+ increase += 9; // 'irot' property (worst case)
+ }
// increase to iref and idat
if (grid) {
- increase += (8 + 2 + 2 + mNumTiles * 2) // 'dimg' in iref
- + 12; // ImageGrid in 'idat' (worst case)
+ increase += (12 + mNumTiles * 2) // 'dimg' in iref
+ + 12; // ImageGrid in 'idat' (worst case)
}
- // increase to iloc, iinf and ipma
- increase += (16 // increase to 'iloc'
- + 21 // increase to 'iinf'
- + (3 + 2 * 2)) // increase to 'ipma' (worst case, 2 props x 2 bytes)
- * (mNumTiles + grid);
+ increase += (12 + 2); // 'cdsc' in iref
- // adjust to ipma:
- // if rotation is present and only one tile, it could ref 3 properties
- if (!grid) {
- increase += 2;
- }
+ // increase to iloc, iinf
+ increase += (16 // increase to 'iloc'
+ + 21) // increase to 'iinf'
+ * (mNumTiles + grid + 1); // "+1" is for 'Exif'
+
+ // When total # of properties is > 127, the properties id becomes 2-byte.
+ // We write 4 properties at most for each image (2x'ispe', 1x'hvcC', 1x'irot').
+ // Set the threshold to be 30.
+ int32_t propBytes = trackCount > 30 ? 2 : 1;
+
+ // increase to ipma
+ increase += (3 + 2 * propBytes) * mNumTiles // 'ispe' + 'hvcC'
+ + grid * (3 + propBytes) // 'ispe' for grid
+ + rotate * propBytes; // 'irot' (either on grid or tile)
return increase;
}
@@ -4239,7 +4333,7 @@
writeInt16((uint16_t)itemCount);
for (size_t i = 0; i < itemCount; i++) {
writeInfeBox(mItems[i].itemId, mItems[i].itemType,
- mItems[i].isHidden ? 1 : 0);
+ (mItems[i].isImage() && mItems[i].isHidden) ? 1 : 0);
}
endBox();
@@ -4274,21 +4368,21 @@
writeInt32(0); // Version = 0, Flags = 0
{
for (size_t i = 0; i < mItems.size(); i++) {
- if (!mItems[i].isGrid()) {
- continue;
+ for (size_t r = 0; r < mItems[i].refsList.size(); r++) {
+ const ItemRefs &refs = mItems[i].refsList[r];
+ beginBox(refs.key);
+ writeInt16(mItems[i].itemId);
+ size_t refCount = refs.value.size();
+ if (refCount > 65535) {
+ ALOGW("too many entries in %s", refs.key);
+ refCount = 65535;
+ }
+ writeInt16((uint16_t)refCount);
+ for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
+ writeInt16(refs.value[refIndex]);
+ }
+ endBox();
}
- beginBox("dimg");
- writeInt16(mItems[i].itemId);
- size_t refCount = mItems[i].dimgRefs.size();
- if (refCount > 65535) {
- ALOGW("too many entries in dimg");
- refCount = 65535;
- }
- writeInt16((uint16_t)refCount);
- for (size_t refIndex = 0; refIndex < refCount; refIndex++) {
- writeInt16(mItems[i].dimgRefs[refIndex]);
- }
- endBox();
}
}
endBox();
@@ -4384,32 +4478,45 @@
}
void MPEG4Writer::writeFileLevelMetaBox() {
- if (mItems.empty()) {
- ALOGE("no valid item was found");
- return;
- }
-
// patch up the mPrimaryItemId and count items with prop associations
uint16_t firstVisibleItemId = 0;
+ uint16_t firstImageItemId = 0;
for (size_t index = 0; index < mItems.size(); index++) {
+ if (!mItems[index].isImage()) continue;
+
if (mItems[index].isPrimary) {
mPrimaryItemId = mItems[index].itemId;
- } else if (!firstVisibleItemId && !mItems[index].isHidden) {
+ }
+ if (!firstImageItemId) {
+ firstImageItemId = mItems[index].itemId;
+ }
+ if (!firstVisibleItemId && !mItems[index].isHidden) {
firstVisibleItemId = mItems[index].itemId;
}
-
if (!mItems[index].properties.empty()) {
mAssociationEntryCount++;
}
}
+ if (!firstImageItemId) {
+ ALOGE("no valid image was found");
+ return;
+ }
+
if (mPrimaryItemId == 0) {
if (firstVisibleItemId > 0) {
- ALOGW("didn't find primary, using first visible item");
+ ALOGW("didn't find primary, using first visible image");
mPrimaryItemId = firstVisibleItemId;
} else {
- ALOGW("no primary and no visible item, using first item");
- mPrimaryItemId = mItems[0].itemId;
+ ALOGW("no primary and no visible item, using first image");
+ mPrimaryItemId = firstImageItemId;
+ }
+ }
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ if ((*it)->isHeic()) {
+ (*it)->flushItemRefs();
}
}
@@ -4422,6 +4529,8 @@
writeIprpBox();
if (mNumGrids > 0) {
writeIdatBox();
+ }
+ if (mHasRefs) {
writeIrefBox();
}
endBox();
@@ -4445,8 +4554,8 @@
size_t index = mItems.size();
mItems.push_back(info);
- // make the item id start at 10000
- mItems.editItemAt(index).itemId = index + 10000;
+ // make the item id start at kItemIdBase
+ mItems.editItemAt(index).itemId = index + kItemIdBase;
#if (LOG_NDEBUG==0)
if (!info.properties.empty()) {
@@ -4464,6 +4573,20 @@
return mItems[index].itemId;
}
+void MPEG4Writer::addRefs_l(uint16_t itemId, const ItemRefs &refs) {
+ if (refs.value.empty()) {
+ return;
+ }
+ if (itemId < kItemIdBase) {
+ ALOGW("itemId shouldn't be smaller than kItemIdBase");
+ return;
+ }
+
+ size_t index = itemId - kItemIdBase;
+ mItems.editItemAt(index).refsList.push_back(refs);
+ mHasRefs = true;
+}
+
/*
* Geodata is stored according to ISO-6709 standard.
*/
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 06a49d0..8436591 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -854,8 +854,7 @@
//static
sp<CodecBase> MediaCodec::GetCodecBase(const AString &name) {
- static bool ccodecEnabled = property_get_bool("debug.stagefright.ccodec", false);
- if (ccodecEnabled && name.startsWithIgnoreCase("c2.")) {
+ if (name.startsWithIgnoreCase("c2.")) {
return CreateCCodec();
} else if (name.startsWithIgnoreCase("omx.")) {
// at this time only ACodec specifies a mime type.
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 9a33168..8598a49 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -93,9 +93,7 @@
std::vector<MediaCodecListBuilderBase *> GetBuilders() {
std::vector<MediaCodecListBuilderBase *> builders {&sOmxInfoBuilder};
- if (property_get_bool("debug.stagefright.ccodec", false)) {
- builders.push_back(GetCodec2InfoBuilder());
- }
+ builders.push_back(GetCodec2InfoBuilder());
return builders;
}
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 23e543d..98f59b5 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -190,6 +190,10 @@
sampleMetaData.setInt32(kKeyIsSyncFrame, true);
}
+ if (flags & MediaCodec::BUFFER_FLAG_MUXER_DATA) {
+ sampleMetaData.setInt32(kKeyIsMuxerData, 1);
+ }
+
sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
// This pushBuffer will wait until the mediaBuffer is consumed.
return currentTrack->pushBuffer(mediaBuffer);
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 43304aa..df66ac6 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -197,6 +197,7 @@
CHECK(mNumItems < kMaxNumItems);
i = mNumItems++;
item = &mItems[i];
+ item->mType = kTypeInt32;
item->setName(name, len);
}
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 7b41362..f18940d 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -132,7 +132,7 @@
status_t startTracks(MetaData *params);
size_t numTracks();
int64_t estimateMoovBoxSize(int32_t bitRate);
- int64_t estimateFileLevelMetaSize();
+ int64_t estimateFileLevelMetaSize(MetaData *params);
void writeCachedBoxToFile(const char *type);
struct Chunk {
@@ -167,8 +167,10 @@
Condition mChunkReadyCondition; // Signal that chunks are available
// HEIF writing
+ typedef key_value_pair_t< const char *, Vector<uint16_t> > ItemRefs;
typedef struct _ItemInfo {
bool isGrid() const { return !strcmp("grid", itemType); }
+ bool isImage() const { return !strcmp("hvc1", itemType) || isGrid(); }
const char *itemType;
uint16_t itemId;
bool isPrimary;
@@ -188,7 +190,7 @@
};
};
Vector<uint16_t> properties;
- Vector<uint16_t> dimgRefs;
+ Vector<ItemRefs> refsList;
} ItemInfo;
typedef struct _ItemProperty {
@@ -204,6 +206,7 @@
uint32_t mPrimaryItemId;
uint32_t mAssociationEntryCount;
uint32_t mNumGrids;
+ bool mHasRefs;
Vector<ItemInfo> mItems;
Vector<ItemProperty> mProperties;
@@ -252,11 +255,12 @@
void initInternal(int fd, bool isFirstSession);
// Acquire lock before calling these methods
- off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, size_t *bytesWritten);
+ off64_t addSample_l(MediaBuffer *buffer, bool usePrefix, bool isExif, size_t *bytesWritten);
void addLengthPrefixedSample_l(MediaBuffer *buffer);
void addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
uint16_t addProperty_l(const ItemProperty &);
uint16_t addItem_l(const ItemInfo &);
+ void addRefs_l(uint16_t itemId, const ItemRefs &);
bool exceedsFileSizeLimit();
bool use32BitFileOffset() const;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 67808f1..ad02004 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -61,9 +61,11 @@
};
enum BufferFlags {
- BUFFER_FLAG_SYNCFRAME = 1,
- BUFFER_FLAG_CODECCONFIG = 2,
- BUFFER_FLAG_EOS = 4,
+ BUFFER_FLAG_SYNCFRAME = 1,
+ BUFFER_FLAG_CODECCONFIG = 2,
+ BUFFER_FLAG_EOS = 4,
+ BUFFER_FLAG_PARTIAL_FRAME = 8,
+ BUFFER_FLAG_MUXER_DATA = 16,
};
enum {
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index c8da34d..de8e46a 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -25,6 +25,7 @@
],
shared_libs: [
"libbinder",
+ "libcutils",
"liblog",
"libutils",
"libmemunreachable",
diff --git a/media/utils/ServiceUtilities.cpp b/media/utils/ServiceUtilities.cpp
index c4a4374..6a90bea 100644
--- a/media/utils/ServiceUtilities.cpp
+++ b/media/utils/ServiceUtilities.cpp
@@ -18,7 +18,6 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
-#include <private/android_filesystem_config.h>
#include "mediautils/ServiceUtilities.h"
/* When performing permission checks we do not use permission cache for
@@ -32,24 +31,6 @@
static const String16 sAndroidPermissionRecordAudio("android.permission.RECORD_AUDIO");
-// Not valid until initialized by AudioFlinger constructor. It would have to be
-// re-initialized if the process containing AudioFlinger service forks (which it doesn't).
-// This is often used to validate binder interface calls within audioserver
-// (e.g. AudioPolicyManager to AudioFlinger).
-pid_t getpid_cached;
-
-// A trusted calling UID may specify the client UID as part of a binder interface call.
-// otherwise the calling UID must be equal to the client UID.
-bool isTrustedCallingUid(uid_t uid) {
- switch (uid) {
- case AID_MEDIA:
- case AID_AUDIOSERVER:
- return true;
- default:
- return false;
- }
-}
-
static String16 resolveCallingPackage(PermissionController& permissionController,
const String16& opPackageName, uid_t uid) {
if (opPackageName.size() > 0) {
@@ -71,16 +52,11 @@
return packages[0];
}
-static inline bool isAudioServerOrRoot(uid_t uid) {
- // AID_ROOT is OK for command-line tests. Native unforked audioserver always OK.
- return uid == AID_ROOT || uid == AID_AUDIOSERVER ;
-}
-
static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,
uid_t uid, bool start) {
// Okay to not track in app ops as audio server is us and if
// device is rooted security model is considered compromised.
- if (isAudioServerOrRoot(uid)) return true;
+ if (isAudioServerOrRootUid(uid)) return true;
// We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
// may open a record track on behalf of a client. Note that pid may be a tid.
@@ -127,7 +103,7 @@
void finishRecording(const String16& opPackageName, uid_t uid) {
// Okay to not track in app ops as audio server is us and if
// device is rooted security model is considered compromised.
- if (isAudioServerOrRoot(uid)) return;
+ if (isAudioServerOrRootUid(uid)) return;
PermissionController permissionController;
String16 resolvedOpPackageName = resolveCallingPackage(
@@ -142,7 +118,7 @@
}
bool captureAudioOutputAllowed(pid_t pid, uid_t uid) {
- if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+ if (isAudioServerOrRootUid(uid)) return true;
static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid);
if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
@@ -163,7 +139,8 @@
}
bool settingsAllowed() {
- if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+ // given this is a permission check, could this be isAudioServerOrRootUid()?
+ if (isAudioServerUid(IPCThreadState::self()->getCallingUid())) return true;
static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
bool ok = PermissionCache::checkCallingPermission(sAudioSettings);
@@ -180,7 +157,6 @@
}
bool dumpAllowed() {
- // don't optimize for same pid, since mediaserver never dumps itself
static const String16 sDump("android.permission.DUMP");
// IMPORTANT: Use PermissionCache - not a runtime permission and may not change.
bool ok = PermissionCache::checkCallingPermission(sDump);
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index 8ead410..2bdba5e 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -17,13 +17,49 @@
#include <unistd.h>
#include <binder/PermissionController.h>
+#include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
namespace android {
// Audio permission utilities
-extern pid_t getpid_cached;
-bool isTrustedCallingUid(uid_t uid);
+// Used for calls that should originate from system services.
+// We allow that some services might have separate processes to
+// handle multiple users, e.g. u10_system, u10_bluetooth, u10_radio.
+static inline bool isServiceUid(uid_t uid) {
+ return multiuser_get_app_id(uid) < AID_APP_START;
+}
+
+// Used for calls that should originate from audioserver.
+static inline bool isAudioServerUid(uid_t uid) {
+ return uid == AID_AUDIOSERVER;
+}
+
+// Used for some permission checks.
+// AID_ROOT is OK for command-line tests. Native audioserver always OK.
+static inline bool isAudioServerOrRootUid(uid_t uid) {
+ return uid == AID_AUDIOSERVER || uid == AID_ROOT;
+}
+
+// Used for calls that should come from system server or internal.
+// Note: system server is multiprocess for multiple users. audioserver is not.
+static inline bool isAudioServerOrSystemServerUid(uid_t uid) {
+ return multiuser_get_app_id(uid) == AID_SYSTEM || uid == AID_AUDIOSERVER;
+}
+
+// Mediaserver may forward the client PID and UID as part of a binder interface call;
+// otherwise the calling UID must be equal to the client UID.
+static inline bool isAudioServerOrMediaServerUid(uid_t uid) {
+ switch (uid) {
+ case AID_MEDIA:
+ case AID_AUDIOSERVER:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid);
bool startRecording(const String16& opPackageName, pid_t pid, uid_t uid);
void finishRecording(const String16& opPackageName, uid_t uid);
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 78a62ca..c0aa477 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -13,7 +13,8 @@
PatchPanel.cpp \
StateQueue.cpp \
BufLog.cpp \
- TypedLogger.cpp
+ TypedLogger.cpp \
+ NBAIO_Tee.cpp \
LOCAL_C_INCLUDES := \
frameworks/av/services/audiopolicy \
@@ -41,6 +42,7 @@
LOCAL_STATIC_LIBRARIES := \
libcpustats \
+ libsndfile \
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f165c31..13477f3 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -28,7 +28,6 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-#include <cutils/multiuser.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <binder/Parcel.h>
@@ -47,6 +46,7 @@
#include <system/audio.h>
#include "AudioFlinger.h"
+#include "NBAIO_Tee.h"
#include <media/AudioResamplerPublic.h>
@@ -55,7 +55,6 @@
#include <system/audio_effects/effect_aec.h>
#include <audio_utils/primitives.h>
-#include <audio_utils/string.h>
#include <powermanager/PowerManager.h>
@@ -100,17 +99,6 @@
uint32_t AudioFlinger::mScreenState;
-
-#ifdef TEE_SINK
-bool AudioFlinger::mTeeSinkInputEnabled = false;
-bool AudioFlinger::mTeeSinkOutputEnabled = false;
-bool AudioFlinger::mTeeSinkTrackEnabled = false;
-
-size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault;
-size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
-size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
-#endif
-
// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
// we define a minimum time during which a global effect is considered enabled.
static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
@@ -169,7 +157,6 @@
mNextUniqueIds[use] = AUDIO_UNIQUE_ID_USE_MAX;
}
- getpid_cached = getpid();
const bool doLog = property_get_bool("ro.test_harness", false);
if (doLog) {
mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters",
@@ -186,27 +173,6 @@
mEffectsFactoryHal = EffectsFactoryHalInterface::create();
mMediaLogNotifier->run("MediaLogNotifier");
-
-#ifdef TEE_SINK
- char value[PROPERTY_VALUE_MAX];
- (void) property_get("ro.debuggable", value, "0");
- int debuggable = atoi(value);
- int teeEnabled = 0;
- if (debuggable) {
- (void) property_get("af.tee", value, "0");
- teeEnabled = atoi(value);
- }
- // FIXME symbolic constants here
- if (teeEnabled & 1) {
- mTeeSinkInputEnabled = true;
- }
- if (teeEnabled & 2) {
- mTeeSinkOutputEnabled = true;
- }
- if (teeEnabled & 4) {
- mTeeSinkTrackEnabled = true;
- }
-#endif
}
void AudioFlinger::onFirstRef()
@@ -535,19 +501,16 @@
mPatchPanel.dump(fd);
-#ifdef TEE_SINK
- // dump the serially shared record tee sink
- if (mRecordTeeSource != 0) {
- dumpTee(fd, mRecordTeeSource, AUDIO_IO_HANDLE_NONE, 'C');
- }
-#endif
-
BUFLOG_RESET;
if (locked) {
mLock.unlock();
}
+#ifdef TEE_SINK
+ // NBAIO_Tee dump is safe to call outside of AF lock.
+ NBAIO_Tee::dumpAll(fd, "_DUMP");
+#endif
// append a copy of media.log here by forwarding fd to it, but don't attempt
// to lookup the service if it's not running, as it will block for a second
if (sMediaLogServiceAsBinder != 0) {
@@ -667,7 +630,7 @@
bool updatePid = (input.clientInfo.clientPid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
uid_t clientUid = input.clientInfo.clientUid;
- if (!isTrustedCallingUid(callingUid)) {
+ if (!isAudioServerOrMediaServerUid(callingUid)) {
ALOGW_IF(clientUid != callingUid,
"%s uid %d tried to pass itself off as %d",
__FUNCTION__, callingUid, clientUid);
@@ -1079,9 +1042,9 @@
ALOGW("checkStreamType() invalid stream %d", stream);
return BAD_VALUE;
}
- pid_t caller = IPCThreadState::self()->getCallingPid();
- if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && caller != getpid_cached) {
- ALOGW("checkStreamType() pid %d cannot use internal stream type %d", caller, stream);
+ const uid_t callerUid = IPCThreadState::self()->getCallingUid();
+ if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT && !isAudioServerUid(callerUid)) {
+ ALOGW("checkStreamType() uid %d cannot use internal stream type %d", callerUid, stream);
return PERMISSION_DENIED;
}
@@ -1201,9 +1164,8 @@
String8(AudioParameter::keyStreamSupportedSamplingRates),
};
- // multiuser friendly app ID check for requests coming from audioserver
- if (multiuser_get_app_id(callingUid) == AID_AUDIOSERVER) {
- return;
+ if (isAudioServerUid(callingUid)) {
+ return; // no need to filter if audioserver.
}
AudioParameter param = AudioParameter(keyValuePairs);
@@ -1637,7 +1599,7 @@
bool updatePid = (input.clientInfo.clientPid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
uid_t clientUid = input.clientInfo.clientUid;
- if (!isTrustedCallingUid(callingUid)) {
+ if (!isAudioServerOrMediaServerUid(callingUid)) {
ALOGW_IF(clientUid != callingUid,
"%s uid %d tried to pass itself off as %d",
__FUNCTION__, callingUid, clientUid);
@@ -1886,7 +1848,7 @@
status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice, int64_t totalMemory)
{
uid_t uid = IPCThreadState::self()->getCallingUid();
- if (uid != AID_SYSTEM) {
+ if (!isAudioServerOrSystemServerUid(uid)) {
return PERMISSION_DENIED;
}
Mutex::Autolock _l(mLock);
@@ -2430,55 +2392,6 @@
thread.get());
return thread;
} else {
-#ifdef TEE_SINK
- // Try to re-use most recently used Pipe to archive a copy of input for dumpsys,
- // or (re-)create if current Pipe is idle and does not match the new format
- sp<NBAIO_Sink> teeSink;
- enum {
- TEE_SINK_NO, // don't copy input
- TEE_SINK_NEW, // copy input using a new pipe
- TEE_SINK_OLD, // copy input using an existing pipe
- } kind;
- NBAIO_Format format = Format_from_SR_C(halconfig.sample_rate,
- audio_channel_count_from_in_mask(halconfig.channel_mask), halconfig.format);
- if (!mTeeSinkInputEnabled) {
- kind = TEE_SINK_NO;
- } else if (!Format_isValid(format)) {
- kind = TEE_SINK_NO;
- } else if (mRecordTeeSink == 0) {
- kind = TEE_SINK_NEW;
- } else if (mRecordTeeSink->getStrongCount() != 1) {
- kind = TEE_SINK_NO;
- } else if (Format_isEqual(format, mRecordTeeSink->format())) {
- kind = TEE_SINK_OLD;
- } else {
- kind = TEE_SINK_NEW;
- }
- switch (kind) {
- case TEE_SINK_NEW: {
- Pipe *pipe = new Pipe(mTeeSinkInputFrames, format);
- size_t numCounterOffers = 0;
- const NBAIO_Format offers[1] = {format};
- ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- PipeReader *pipeReader = new PipeReader(*pipe);
- numCounterOffers = 0;
- index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mRecordTeeSink = pipe;
- mRecordTeeSource = pipeReader;
- teeSink = pipe;
- }
- break;
- case TEE_SINK_OLD:
- teeSink = mRecordTeeSink;
- break;
- case TEE_SINK_NO:
- default:
- break;
- }
-#endif
-
// Start record thread
// RecordThread requires both input and output device indication to forward to audio
// pre processing modules
@@ -2488,9 +2401,6 @@
primaryOutputDevice_l(),
devices,
mSystemReady
-#ifdef TEE_SINK
- , teeSink
-#endif
);
mRecordThreads.add(*input, thread);
ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get());
@@ -2628,7 +2538,8 @@
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
ALOGV("acquiring %d from %d, for %d", audioSession, caller, pid);
- if (pid != -1 && (caller == getpid_cached)) {
+ const uid_t callerUid = IPCThreadState::self()->getCallingUid();
+ if (pid != -1 && isAudioServerUid(callerUid)) { // check must match releaseAudioSessionId()
caller = pid;
}
@@ -2662,7 +2573,8 @@
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
ALOGV("releasing %d from %d for %d", audioSession, caller, pid);
- if (pid != -1 && (caller == getpid_cached)) {
+ const uid_t callerUid = IPCThreadState::self()->getCallingUid();
+ if (pid != -1 && isAudioServerUid(callerUid)) { // check must match acquireAudioSessionId()
caller = pid;
}
size_t num = mAudioSessionRefs.size();
@@ -2679,9 +2591,10 @@
return;
}
}
- // If the caller is mediaserver it is likely that the session being released was acquired
+ // If the caller is audioserver it is likely that the session being released was acquired
// on behalf of a process not in notification clients and we ignore the warning.
- ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller);
+ ALOGW_IF(!isAudioServerUid(callerUid),
+ "session id %d not found for pid %d", audioSession, caller);
}
bool AudioFlinger::isSessionAcquired_l(audio_session_t audioSession)
@@ -2989,7 +2902,7 @@
effect_descriptor_t desc;
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (pid == -1 || !isTrustedCallingUid(callingUid)) {
+ if (pid == -1 || !isAudioServerOrMediaServerUid(callingUid)) {
const pid_t callingPid = IPCThreadState::self()->getCallingPid();
ALOGW_IF(pid != -1 && pid != callingPid,
"%s uid %d pid %d tried to pass itself off as pid %d",
@@ -3012,8 +2925,8 @@
}
// Session AUDIO_SESSION_OUTPUT_STAGE is reserved for output stage effects
- // that can only be created by audio policy manager (running in same process)
- if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && getpid_cached != pid) {
+ // that can only be created by audio policy manager
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE && !isAudioServerUid(callingUid)) {
lStatus = PERMISSION_DENIED;
goto Exit;
}
@@ -3393,136 +3306,6 @@
}
-struct Entry {
-#define TEE_MAX_FILENAME 32 // %Y%m%d%H%M%S_%d.wav = 4+2+2+2+2+2+1+1+4+1 = 21
- char mFileName[TEE_MAX_FILENAME];
-};
-
-int comparEntry(const void *p1, const void *p2)
-{
- return strcmp(((const Entry *) p1)->mFileName, ((const Entry *) p2)->mFileName);
-}
-
-#ifdef TEE_SINK
-void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix)
-{
- NBAIO_Source *teeSource = source.get();
- if (teeSource != NULL) {
- // .wav rotation
- // There is a benign race condition if 2 threads call this simultaneously.
- // They would both traverse the directory, but the result would simply be
- // failures at unlink() which are ignored. It's also unlikely since
- // normally dumpsys is only done by bugreport or from the command line.
- char teePath[PATH_MAX] = "/data/misc/audioserver";
- size_t teePathLen = strlen(teePath);
- DIR *dir = opendir(teePath);
- teePath[teePathLen++] = '/';
- if (dir != NULL) {
-#define TEE_MAX_SORT 20 // number of entries to sort
-#define TEE_MAX_KEEP 10 // number of entries to keep
- struct Entry entries[TEE_MAX_SORT];
- size_t entryCount = 0;
- while (entryCount < TEE_MAX_SORT) {
- errno = 0; // clear errno before readdir() to track potential errors.
- const struct dirent *result = readdir(dir);
- if (result == nullptr) {
- ALOGW_IF(errno != 0, "tee readdir() failure %s", strerror(errno));
- break;
- }
- // ignore non .wav file entries
- const size_t nameLen = strlen(result->d_name);
- if (nameLen <= 4 || nameLen >= TEE_MAX_FILENAME ||
- strcmp(&result->d_name[nameLen - 4], ".wav")) {
- continue;
- }
- (void)audio_utils_strlcpy(entries[entryCount++].mFileName, result->d_name);
- }
- (void) closedir(dir);
- if (entryCount > TEE_MAX_KEEP) {
- qsort(entries, entryCount, sizeof(Entry), comparEntry);
- for (size_t i = 0; i < entryCount - TEE_MAX_KEEP; ++i) {
- strcpy(&teePath[teePathLen], entries[i].mFileName);
- (void) unlink(teePath);
- }
- }
- } else {
- if (fd >= 0) {
- dprintf(fd, "unable to rotate tees in %.*s: %s\n", (int) teePathLen, teePath,
- strerror(errno));
- }
- }
- char teeTime[16];
- struct timeval tv;
- gettimeofday(&tv, NULL);
- struct tm tm;
- localtime_r(&tv.tv_sec, &tm);
- strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
- snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d_%c.wav", teeTime, id,
- suffix);
- // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
- int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
- if (teeFd >= 0) {
- // FIXME use libsndfile
- char wavHeader[44];
- memcpy(wavHeader,
- "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0",
- sizeof(wavHeader));
- NBAIO_Format format = teeSource->format();
- unsigned channelCount = Format_channelCount(format);
- uint32_t sampleRate = Format_sampleRate(format);
- size_t frameSize = Format_frameSize(format);
- wavHeader[22] = channelCount; // number of channels
- wavHeader[24] = sampleRate; // sample rate
- wavHeader[25] = sampleRate >> 8;
- wavHeader[32] = frameSize; // block alignment
- wavHeader[33] = frameSize >> 8;
- write(teeFd, wavHeader, sizeof(wavHeader));
- size_t total = 0;
- bool firstRead = true;
-#define TEE_SINK_READ 1024 // frames per I/O operation
- void *buffer = malloc(TEE_SINK_READ * frameSize);
- for (;;) {
- size_t count = TEE_SINK_READ;
- ssize_t actual = teeSource->read(buffer, count);
- bool wasFirstRead = firstRead;
- firstRead = false;
- if (actual <= 0) {
- if (actual == (ssize_t) OVERRUN && wasFirstRead) {
- continue;
- }
- break;
- }
- ALOG_ASSERT(actual <= (ssize_t)count);
- write(teeFd, buffer, actual * frameSize);
- total += actual;
- }
- free(buffer);
- lseek(teeFd, (off_t) 4, SEEK_SET);
- uint32_t temp = 44 + total * frameSize - 8;
- // FIXME not big-endian safe
- write(teeFd, &temp, sizeof(temp));
- lseek(teeFd, (off_t) 40, SEEK_SET);
- temp = total * frameSize;
- // FIXME not big-endian safe
- write(teeFd, &temp, sizeof(temp));
- close(teeFd);
- // TODO Should create file with temporary name and then rename to final if non-empty.
- if (total > 0) {
- if (fd >= 0) {
- dprintf(fd, "tee copied to %s\n", teePath);
- }
- } else {
- unlink(teePath);
- }
- } else {
- if (fd >= 0) {
- dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
- }
- }
- }
-}
-#endif
-
// ----------------------------------------------------------------------------
status_t AudioFlinger::onTransact(
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 7cfe542..a59c13e 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -71,6 +71,7 @@
#include "AudioStreamOut.h"
#include "SpdifStreamOut.h"
#include "AudioHwDevice.h"
+#include "NBAIO_Tee.h"
#include <powermanager/IPowerManager.h>
@@ -800,35 +801,7 @@
void filterReservedParameters(String8& keyValuePairs, uid_t callingUid);
-#ifdef TEE_SINK
- // all record threads serially share a common tee sink, which is re-created on format change
- sp<NBAIO_Sink> mRecordTeeSink;
- sp<NBAIO_Source> mRecordTeeSource;
-#endif
-
public:
-
-#ifdef TEE_SINK
- // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
- static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id, char suffix);
-
- // whether tee sink is enabled by property
- static bool mTeeSinkInputEnabled;
- static bool mTeeSinkOutputEnabled;
- static bool mTeeSinkTrackEnabled;
-
- // runtime configured size of each tee sink pipe, in frames
- static size_t mTeeSinkInputFrames;
- static size_t mTeeSinkOutputFrames;
- static size_t mTeeSinkTrackFrames;
-
- // compile-time default size of tee sink pipes, in frames
- // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
- static const size_t kTeeSinkInputFramesDefault = 0x200000;
- static const size_t kTeeSinkOutputFramesDefault = 0x200000;
- static const size_t kTeeSinkTrackFramesDefault = 0x200000;
-#endif
-
// These methods read variables atomically without mLock,
// though the variables are updated with mLock.
bool isLowRamDevice() const { return mIsLowRamDevice; }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f89c6db..786c4af 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1815,7 +1815,7 @@
bool locked = mCblk != NULL && AudioFlinger::dumpTryLock(mCblk->lock);
snprintf(buffer, size, "\t\t\t%5d %5d %3s %3s %5u %5u\n",
- (mClient == 0) ? getpid_cached : mClient->pid(),
+ (mClient == 0) ? getpid() : mClient->pid(),
mPriority,
mHasControl ? "yes" : "no",
locked ? "yes" : "no",
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 79bb9fe..7a02963 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -47,7 +47,8 @@
/*static*/ const FastMixerState FastMixer::sInitial;
-FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"),
+FastMixer::FastMixer(audio_io_handle_t parentIoHandle)
+ : FastThread("cycle_ms", "load_us"),
// mFastTrackNames
// mGenerations
mOutputSink(NULL),
@@ -66,8 +67,11 @@
mTotalNativeFramesWritten(0),
// timestamp
mNativeFramesWrittenButNotPresented(0), // the = 0 is to silence the compiler
- mMasterMono(false)
+ mMasterMono(false),
+ mThreadIoHandle(parentIoHandle)
{
+ (void)mThreadIoHandle; // prevent unused warning, see C++17 [[maybe_unused]]
+
// FIXME pass sInitial as parameter to base class constructor, and make it static local
mPrevious = &sInitial;
mCurrent = &sInitial;
@@ -220,6 +224,10 @@
previousTrackMask = 0;
mFastTracksGen = current->mFastTracksGen - 1;
dumpState->mFrameCount = frameCount;
+#ifdef TEE_SINK
+ mTee.set(mFormat, NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD);
+ mTee.setId(std::string("_") + std::to_string(mThreadIoHandle) + "_F");
+#endif
} else {
previousTrackMask = previous->mTrackMask;
}
@@ -446,10 +454,9 @@
frameCount * Format_channelCount(mFormat));
}
// if non-NULL, then duplicate write() to this non-blocking sink
- NBAIO_Sink* teeSink;
- if ((teeSink = current->mTeeSink) != NULL) {
- (void) teeSink->write(buffer, frameCount);
- }
+#ifdef TEE_SINK
+ mTee.write(buffer, frameCount);
+#endif
// FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
// but this code should be modified to handle both non-blocking and blocking sinks
dumpState->mWriteSequence++;
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 235d23f..1c86d9a 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -22,6 +22,7 @@
#include "StateQueue.h"
#include "FastMixerState.h"
#include "FastMixerDumpState.h"
+#include "NBAIO_Tee.h"
namespace android {
@@ -32,7 +33,9 @@
class FastMixer : public FastThread {
public:
- FastMixer();
+ /** FastMixer constructor takes as param the parent MixerThread's io handle (id)
+ for purposes of identification. */
+ explicit FastMixer(audio_io_handle_t threadIoHandle);
virtual ~FastMixer();
FastMixerStateQueue* sq();
@@ -87,6 +90,11 @@
// accessed without lock between multiple threads.
std::atomic_bool mMasterMono;
std::atomic_int_fast64_t mBoottimeOffset;
+
+ const audio_io_handle_t mThreadIoHandle; // parent thread id for debugging purposes
+#ifdef TEE_SINK
+ NBAIO_Tee mTee;
+#endif
}; // class FastMixer
} // namespace android
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 36d8eef..b98842d 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -35,7 +35,7 @@
FastMixerState::FastMixerState() : FastThreadState(),
// mFastTracks
mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0),
- mFrameCount(0), mTeeSink(NULL)
+ mFrameCount(0)
{
int ok = pthread_once(&sMaxFastTracksOnce, sMaxFastTracksInit);
if (ok != 0) {
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 2be1e91..c7fcbd8 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -77,9 +77,6 @@
WRITE = 0x10, // write to output sink
MIX_WRITE = 0x18; // mix tracks and write to output sink
- // This might be a one-time configuration rather than per-state
- NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink
-
// never returns NULL; asserts if command is invalid
static const char *commandToString(Command command);
diff --git a/services/audioflinger/NBAIO_Tee.cpp b/services/audioflinger/NBAIO_Tee.cpp
new file mode 100644
index 0000000..53083d5
--- /dev/null
+++ b/services/audioflinger/NBAIO_Tee.cpp
@@ -0,0 +1,517 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NBAIO_Tee"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include <deque>
+#include <dirent.h>
+#include <future>
+#include <list>
+#include <vector>
+
+#include <audio_utils/format.h>
+#include <audio_utils/sndfile.h>
+#include <media/nbaio/PipeReader.h>
+
+#include "Configuration.h"
+#include "NBAIO_Tee.h"
+
+// Enabled with TEE_SINK in Configuration.h
+#ifdef TEE_SINK
+
+namespace android {
+
+/*
+ Tee filenames generated as follows:
+
+ "aftee_Date_ThreadId_C_reason.wav" RecordThread
+ "aftee_Date_ThreadId_M_reason.wav" MixerThread (Normal)
+ "aftee_Date_ThreadId_F_reason.wav" MixerThread (Fast)
+ "aftee_Date_ThreadId_TrackId_R_reason.wav" RecordTrack
+ "aftee_Date_ThreadId_TrackId_TrackName_T_reason.wav" PlaybackTrack
+
+ where Date = YYYYmmdd_HHMMSS_MSEC
+
+ where Reason = [ DTOR | DUMP | REMOVE ]
+
+ Examples:
+ aftee_20180424_153811_038_13_57_2_T_REMOVE.wav
+ aftee_20180424_153811_218_13_57_2_T_REMOVE.wav
+ aftee_20180424_153811_378_13_57_2_T_REMOVE.wav
+ aftee_20180424_153825_147_62_C_DUMP.wav
+ aftee_20180424_153825_148_62_59_R_DUMP.wav
+ aftee_20180424_153825_149_13_F_DUMP.wav
+ aftee_20180424_153842_125_62_59_R_REMOVE.wav
+ aftee_20180424_153842_168_62_C_DTOR.wav
+*/
+
+static constexpr char DEFAULT_PREFIX[] = "aftee_";
+static constexpr char DEFAULT_DIRECTORY[] = "/data/misc/audioserver";
+static constexpr size_t DEFAULT_THREADPOOL_SIZE = 8;
+
+/** AudioFileHandler manages temporary audio wav files with a least recently created
+ retention policy.
+
+ The temporary filenames are systematically generated. A common filename prefix,
+ storage directory, and concurrency pool are passed in on creating the object.
+
+ Temporary files are created by "create", which returns a filename generated by
+
+ prefix + 14 char date + suffix
+
+ TODO Move to audio_utils.
+ TODO Avoid pointing two AudioFileHandlers to the same directory and prefix
+ as we don't have a prefix specific lock file. */
+
+class AudioFileHandler {
+public:
+
+ AudioFileHandler(const std::string &prefix, const std::string &directory, size_t pool)
+ : mThreadPool(pool)
+ , mPrefix(prefix)
+ {
+ (void)setDirectory(directory);
+ }
+
+ /** returns filename of created audio file, else empty string on failure. */
+ std::string create(
+ std::function<ssize_t /* frames_read */
+ (void * /* buffer */, size_t /* size_in_frames */)> reader,
+ uint32_t sampleRate,
+ uint32_t channelCount,
+ audio_format_t format,
+ const std::string &suffix);
+
+private:
+ /** sets the current directory. this is currently private to avoid confusion
+ when changing while pending operations are occurring (it's okay, but
+ weakly synchronized). */
+ status_t setDirectory(const std::string &directory);
+
+ /** cleans current directory and returns the directory name done. */
+ status_t clean(std::string *dir = nullptr);
+
+ /** creates an audio file from a reader functor passed in. */
+ status_t createInternal(
+ std::function<ssize_t /* frames_read */
+ (void * /* buffer */, size_t /* size_in_frames */)> reader,
+ uint32_t sampleRate,
+ uint32_t channelCount,
+ audio_format_t format,
+ const std::string &filename);
+
+ static bool isDirectoryValid(const std::string &directory) {
+ return directory.size() > 0 && directory[0] == '/';
+ }
+
+ std::string generateFilename(const std::string &suffix) const {
+ char fileTime[sizeof("YYYYmmdd_HHMMSS_\0")];
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ struct tm tm;
+ localtime_r(&tv.tv_sec, &tm);
+ LOG_ALWAYS_FATAL_IF(strftime(fileTime, sizeof(fileTime), "%Y%m%d_%H%M%S_", &tm) == 0,
+ "incorrect fileTime buffer");
+ char msec[4];
+ (void)snprintf(msec, sizeof(msec), "%03d", (int)(tv.tv_usec / 1000));
+ return mPrefix + fileTime + msec + suffix + ".wav";
+ }
+
+ bool isManagedFilename(const char *name) {
+ constexpr size_t FILENAME_LEN_DATE = 4 + 2 + 2 // %Y%m%d%
+ + 1 + 2 + 2 + 2 // _H%M%S
+ + 1 + 3; //_MSEC
+ const size_t prefixLen = mPrefix.size();
+ const size_t nameLen = strlen(name);
+
+ // reject on size, prefix, and .wav
+ if (nameLen < prefixLen + FILENAME_LEN_DATE + 4 /* .wav */
+ || strncmp(name, mPrefix.c_str(), prefixLen) != 0
+ || strcmp(name + nameLen - 4, ".wav") != 0) {
+ return false;
+ }
+
+ // validate date portion
+ const char *date = name + prefixLen;
+ return std::all_of(date, date + 8, isdigit)
+ && date[8] == '_'
+ && std::all_of(date + 9, date + 15, isdigit)
+ && date[15] == '_'
+ && std::all_of(date + 16, date + 19, isdigit);
+ }
+
+ // yet another ThreadPool implementation.
+ class ThreadPool {
+ public:
+ ThreadPool(size_t size)
+ : mThreadPoolSize(size)
+ { }
+
+ /** launches task "name" with associated function "func".
+ if the threadpool is exhausted, it will launch on calling function */
+ status_t launch(const std::string &name, std::function<status_t()> func);
+
+ private:
+ std::mutex mLock;
+ std::list<std::pair<
+ std::string, std::future<status_t>>> mFutures; // GUARDED_BY(mLock)
+
+ const size_t mThreadPoolSize;
+ } mThreadPool;
+
+ const std::string mPrefix;
+ std::mutex mLock;
+ std::string mDirectory; // GUARDED_BY(mLock)
+ std::deque<std::string> mFiles; // GUARDED_BY(mLock) sorted list of files by creation time
+
+ static constexpr size_t FRAMES_PER_READ = 1024;
+ static constexpr size_t MAX_FILES_READ = 1024;
+ static constexpr size_t MAX_FILES_KEEP = 32;
+};
+
+/* static */
+void NBAIO_Tee::NBAIO_TeeImpl::dumpTee(
+ int fd, const NBAIO_SinkSource &sinkSource, const std::string &suffix)
+{
+ // Singleton. Constructed thread-safe on first call, never destroyed.
+ static AudioFileHandler audioFileHandler(
+ DEFAULT_PREFIX, DEFAULT_DIRECTORY, DEFAULT_THREADPOOL_SIZE);
+
+ auto &source = sinkSource.second;
+ if (source.get() == nullptr) {
+ return;
+ }
+
+ const NBAIO_Format format = source->format();
+ bool firstRead = true;
+ std::string filename = audioFileHandler.create(
+ // this functor must not hold references to stack
+ [firstRead, sinkSource] (void *buffer, size_t frames) mutable {
+ auto &source = sinkSource.second;
+ ssize_t actualRead = source->read(buffer, frames);
+ if (actualRead == (ssize_t)OVERRUN && firstRead) {
+ // recheck once
+ actualRead = source->read(buffer, frames);
+ }
+ firstRead = false;
+ return actualRead;
+ },
+ Format_sampleRate(format),
+ Format_channelCount(format),
+ format.mFormat,
+ suffix);
+
+ if (fd >= 0 && filename.size() > 0) {
+ dprintf(fd, "tee wrote to %s\n", filename.c_str());
+ }
+}
+
+/* static */
+NBAIO_Tee::NBAIO_TeeImpl::NBAIO_SinkSource NBAIO_Tee::NBAIO_TeeImpl::makeSinkSource(
+ const NBAIO_Format &format, size_t frames, bool *enabled)
+{
+ if (Format_isValid(format) && audio_is_linear_pcm(format.mFormat)) {
+ Pipe *pipe = new Pipe(frames, format);
+ size_t numCounterOffers = 0;
+ const NBAIO_Format offers[1] = {format};
+ ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+ if (index != 0) {
+ ALOGW("pipe failure to negotiate: %zd", index);
+ goto exit;
+ }
+ PipeReader *pipeReader = new PipeReader(*pipe);
+ numCounterOffers = 0;
+ index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+ if (index != 0) {
+ ALOGW("pipeReader failure to negotiate: %zd", index);
+ goto exit;
+ }
+ if (enabled != nullptr) *enabled = true;
+ return {pipe, pipeReader};
+ }
+exit:
+ if (enabled != nullptr) *enabled = false;
+ return {nullptr, nullptr};
+}
+
+std::string AudioFileHandler::create(
+ std::function<ssize_t /* frames_read */
+ (void * /* buffer */, size_t /* size_in_frames */)> reader,
+ uint32_t sampleRate,
+ uint32_t channelCount,
+ audio_format_t format,
+ const std::string &suffix)
+{
+ const std::string filename = generateFilename(suffix);
+
+ if (mThreadPool.launch(std::string("create ") + filename,
+ [=]() { return createInternal(reader, sampleRate, channelCount, format, filename); })
+ == NO_ERROR) {
+ return filename;
+ }
+ return "";
+}
+
+status_t AudioFileHandler::setDirectory(const std::string &directory)
+{
+ if (!isDirectoryValid(directory)) return BAD_VALUE;
+
+ // TODO: consider using std::filesystem in C++17
+ DIR *dir = opendir(directory.c_str());
+
+ if (dir == nullptr) {
+ ALOGW("%s: cannot open directory %s", __func__, directory.c_str());
+ return BAD_VALUE;
+ }
+
+ size_t toRemove = 0;
+ decltype(mFiles) files;
+
+ while (files.size() < MAX_FILES_READ) {
+ errno = 0;
+ const struct dirent *result = readdir(dir);
+ if (result == nullptr) {
+ ALOGW_IF(errno != 0, "%s: readdir failure %s", __func__, strerror(errno));
+ break;
+ }
+ // is it a managed filename?
+ if (!isManagedFilename(result->d_name)) {
+ continue;
+ }
+ files.emplace_back(result->d_name);
+ }
+ (void)closedir(dir);
+
+ // OPTIMIZATION: we don't need to stat each file, the filenames names are
+ // already (roughly) ordered by creation date. we use std::deque instead
+ // of std::set for faster insertion and sorting times.
+
+ if (files.size() > MAX_FILES_KEEP) {
+ // removed files can use a partition (no need to do a full sort).
+ toRemove = files.size() - MAX_FILES_KEEP;
+ std::nth_element(files.begin(), files.begin() + toRemove - 1, files.end());
+ }
+
+ // kept files must be sorted.
+ std::sort(files.begin() + toRemove, files.end());
+
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+
+ mDirectory = directory;
+ mFiles = std::move(files);
+ }
+
+ if (toRemove > 0) { // launch a clean in background.
+ (void)mThreadPool.launch(
+ std::string("cleaning ") + directory, [this]() { return clean(); });
+ }
+ return NO_ERROR;
+}
+
+status_t AudioFileHandler::clean(std::string *directory)
+{
+ std::vector<std::string> filesToRemove;
+ std::string dir;
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+
+ if (!isDirectoryValid(mDirectory)) return NO_INIT;
+
+ dir = mDirectory;
+ if (mFiles.size() > MAX_FILES_KEEP) {
+ size_t toRemove = mFiles.size() - MAX_FILES_KEEP;
+
+ // use move and erase to efficiently transfer std::string
+ std::move(mFiles.begin(),
+ mFiles.begin() + toRemove,
+ std::back_inserter(filesToRemove));
+ mFiles.erase(mFiles.begin(), mFiles.begin() + toRemove);
+ }
+ }
+
+ std::string dirp = dir + "/";
+ // remove files outside of lock for better concurrency.
+ for (const auto &file : filesToRemove) {
+ (void)unlink((dirp + file).c_str());
+ }
+
+ // return the directory if requested.
+ if (directory != nullptr) {
+ *directory = dir;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioFileHandler::ThreadPool::launch(
+ const std::string &name, std::function<status_t()> func)
+{
+ if (mThreadPoolSize > 1) {
+ std::lock_guard<std::mutex> _l(mLock);
+ if (mFutures.size() >= mThreadPoolSize) {
+ for (auto it = mFutures.begin(); it != mFutures.end();) {
+ const std::string &filename = it->first;
+ std::future<status_t> &future = it->second;
+ if (!future.valid() ||
+ future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
+ ALOGV("%s: future %s ready", __func__, filename.c_str());
+ it = mFutures.erase(it);
+ } else {
+ ALOGV("%s: future %s not ready", __func__, filename.c_str());
+ ++it;
+ }
+ }
+ }
+ if (mFutures.size() < mThreadPoolSize) {
+ ALOGV("%s: deferred calling %s", __func__, name.c_str());
+ mFutures.emplace_back(name, std::async(std::launch::async, func));
+ return NO_ERROR;
+ }
+ }
+ ALOGV("%s: immediate calling %s", __func__, name.c_str());
+ return func();
+}
+
+status_t AudioFileHandler::createInternal(
+ std::function<ssize_t /* frames_read */
+ (void * /* buffer */, size_t /* size_in_frames */)> reader,
+ uint32_t sampleRate,
+ uint32_t channelCount,
+ audio_format_t format,
+ const std::string &filename)
+{
+ // Attempt to choose the best matching file format.
+ // We can choose any sf_format
+ // but writeFormat must be one of 16, 32, float
+ // due to sf_writef compatibility.
+ int sf_format;
+ audio_format_t writeFormat;
+ switch (format) {
+ case AUDIO_FORMAT_PCM_8_BIT:
+ case AUDIO_FORMAT_PCM_16_BIT:
+ sf_format = SF_FORMAT_PCM_16;
+ writeFormat = AUDIO_FORMAT_PCM_16_BIT;
+ ALOGV("%s: %s using PCM_16 for format %#x", __func__, filename.c_str(), format);
+ break;
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_PCM_32_BIT:
+ sf_format = SF_FORMAT_PCM_32;
+ writeFormat = AUDIO_FORMAT_PCM_32_BIT;
+ ALOGV("%s: %s using PCM_32 for format %#x", __func__, filename.c_str(), format);
+ break;
+ case AUDIO_FORMAT_PCM_FLOAT:
+ sf_format = SF_FORMAT_FLOAT;
+ writeFormat = AUDIO_FORMAT_PCM_FLOAT;
+ ALOGV("%s: %s using PCM_FLOAT for format %#x", __func__, filename.c_str(), format);
+ break;
+ default:
+ // TODO:
+ // handle audio_has_proportional_frames() formats.
+ // handle compressed formats as single byte files.
+ return BAD_VALUE;
+ }
+
+ std::string directory;
+ status_t status = clean(&directory);
+ if (status != NO_ERROR) return status;
+ std::string dirPrefix = directory + "/";
+
+ const std::string path = dirPrefix + filename;
+
+ /* const */ SF_INFO info = {
+ .frames = 0,
+ .samplerate = (int)sampleRate,
+ .channels = (int)channelCount,
+ .format = SF_FORMAT_WAV | sf_format,
+ };
+ SNDFILE *sf = sf_open(path.c_str(), SFM_WRITE, &info);
+ if (sf == nullptr) {
+ return INVALID_OPERATION;
+ }
+
+ size_t total = 0;
+ void *buffer = malloc(FRAMES_PER_READ * std::max(
+ channelCount * audio_bytes_per_sample(writeFormat), //output framesize
+ channelCount * audio_bytes_per_sample(format))); // input framesize
+ if (buffer == nullptr) {
+ sf_close(sf);
+ return NO_MEMORY;
+ }
+
+ for (;;) {
+ const ssize_t actualRead = reader(buffer, FRAMES_PER_READ);
+ if (actualRead <= 0) {
+ break;
+ }
+
+ // Convert input format to writeFormat as needed.
+ if (format != writeFormat) {
+ memcpy_by_audio_format(
+ buffer, writeFormat, buffer, format, actualRead * info.channels);
+ }
+
+ ssize_t reallyWritten;
+ switch (writeFormat) {
+ case AUDIO_FORMAT_PCM_16_BIT:
+ reallyWritten = sf_writef_short(sf, (const int16_t *)buffer, actualRead);
+ break;
+ case AUDIO_FORMAT_PCM_32_BIT:
+ reallyWritten = sf_writef_int(sf, (const int32_t *)buffer, actualRead);
+ break;
+ case AUDIO_FORMAT_PCM_FLOAT:
+ reallyWritten = sf_writef_float(sf, (const float *)buffer, actualRead);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("%s: %s writeFormat: %#x", __func__, filename.c_str(), writeFormat);
+ break;
+ }
+
+ if (reallyWritten < 0) {
+ ALOGW("%s: %s write error: %zd", __func__, filename.c_str(), reallyWritten);
+ break;
+ }
+ total += reallyWritten;
+ if (reallyWritten < actualRead) {
+ ALOGW("%s: %s write short count: %zd < %zd",
+ __func__, filename.c_str(), reallyWritten, actualRead);
+ break;
+ }
+ }
+ sf_close(sf);
+ free(buffer);
+ if (total == 0) {
+ (void)unlink(path.c_str());
+ return NOT_ENOUGH_DATA;
+ }
+
+ // Success: add our name to managed files.
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ // weak synchronization - only update mFiles if the directory hasn't changed.
+ if (mDirectory == directory) {
+ mFiles.emplace_back(filename); // add to the end to preserve sort.
+ }
+ }
+ return NO_ERROR; // return full path
+}
+
+} // namespace android
+
+#endif // TEE_SINK
diff --git a/services/audioflinger/NBAIO_Tee.h b/services/audioflinger/NBAIO_Tee.h
new file mode 100644
index 0000000..fed8cc8
--- /dev/null
+++ b/services/audioflinger/NBAIO_Tee.h
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+
+// Enabled with TEE_SINK in Configuration.h
+#ifndef ANDROID_NBAIO_TEE_H
+#define ANDROID_NBAIO_TEE_H
+
+#ifdef TEE_SINK
+
+#include <atomic>
+#include <mutex>
+#include <set>
+
+#include <cutils/properties.h>
+#include <media/nbaio/NBAIO.h>
+
+namespace android {
+
+/**
+ * The NBAIO_Tee uses the NBAIO Pipe and PipeReader for nonblocking
+ * data collection, for eventual dump to log files.
+ * See https://source.android.com/devices/audio/debugging for how to
+ * enable by ro.debuggable and af.tee properties.
+ *
+ * The write() into the NBAIO_Tee is therefore nonblocking,
+ * but changing NBAIO_Tee formats with set() cannot be done during a write();
+ * usually the caller already implements this mutual exclusion.
+ *
+ * All other calls except set() vs write() may occur at any time.
+ *
+ * dump() disruption is minimized to the caller since system calls are executed
+ * in an asynchronous thread (when possible).
+ *
+ * Currently the NBAIO_Tee is "hardwired" for AudioFlinger support.
+ *
+ * Some AudioFlinger specific notes:
+ *
+ * 1) Tees capture only linear PCM data.
+ * 2) Tees without any data written are considered empty and do not generate
+ * any output files.
+ * 2) Once a Tee dumps data, it is considered "emptied" and new data
+ * needs to be written before another Tee file is generated.
+ * 3) Tee file format is
+ * WAV integer PCM 16 bit for AUDIO_FORMAT_PCM_8_BIT, AUDIO_FORMAT_PCM_16_BIT.
+ * WAV integer PCM 32 bit for AUDIO_FORMAT_PCM_8_24_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED
+ * AUDIO_FORMAT_PCM_32_BIT.
+ * WAV float PCM 32 bit for AUDIO_FORMAT_PCM_FLOAT.
+ *
+ * Input_Thread:
+ * 1) Capture buffer is teed when read from the HAL, before resampling for the AudioRecord
+ * client.
+ *
+ * Output_Thread:
+ * 1) MixerThreads will tee at the FastMixer output (if it has one) or at the
+ * NormalMixer output (if no FastMixer).
+ * 2) DuplicatingThreads do not tee any mixed data. Apply a tee on the downstream OutputTrack
+ * or on the upstream playback Tracks.
+ * 3) DirectThreads and OffloadThreads do not tee any data. The upstream track
+ * (if linear PCM format) may be teed to discover data.
+ * 4) MmapThreads are not supported.
+ *
+ * Tracks:
+ * 1) RecordTracks and playback Tracks tee as data is being written to or
+ * read from the shared client-server track buffer by the associated Threads.
+ * 2) The mechanism is on the AudioBufferProvider release() so large static Track
+ * playback may not show any Tee data depending on when it is released.
+ * 3) When a track becomes inactive, the Thread will trigger a dump.
+ */
+
+class NBAIO_Tee {
+public:
+ /* TEE_FLAG is used in set() and must match the flags for the af.tee property
+ given in https://source.android.com/devices/audio/debugging
+ */
+ enum TEE_FLAG {
+ TEE_FLAG_NONE = 0,
+ TEE_FLAG_INPUT_THREAD = (1 << 0), // treat as a Tee for input (Capture) Threads
+ TEE_FLAG_OUTPUT_THREAD = (1 << 1), // treat as a Tee for output (Playback) Threads
+ TEE_FLAG_TRACK = (1 << 2), // treat as a Tee for tracks (Record and Playback)
+ };
+
+ NBAIO_Tee()
+ : mTee(std::make_shared<NBAIO_TeeImpl>())
+ {
+ getRunningTees().add(mTee);
+ }
+
+ ~NBAIO_Tee() {
+ getRunningTees().remove(mTee);
+ dump(-1, "_DTOR"); // log any data remaining in Tee.
+ }
+
+ /**
+ * \brief set is used for deferred configuration of Tee.
+ *
+ * May be called anytime except concurrently with write().
+ *
+ * \param format NBAIO_Format used to open NBAIO pipes
+ * \param flags (https://source.android.com/devices/audio/debugging)
+ * - TEE_FLAG_NONE to bypass af.tee property checks (default);
+ * - TEE_FLAG_INPUT_THREAD to check af.tee if input thread logging set;
+ * - TEE_FLAG_OUTPUT_THREAD to check af.tee if output thread logging set;
+ * - TEE_FLAG_TRACK to check af.tee if track logging set.
+ * \param frames number of frames to open the NBAIO pipe (set to 0 to use default).
+ *
+ * \return
+ * - NO_ERROR on success (or format unchanged)
+ * - BAD_VALUE if format or flags invalid.
+ * - PERMISSION_DENIED if flags not allowed by af.tee
+ */
+
+ status_t set(const NBAIO_Format &format,
+ TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
+ return mTee->set(format, flags, frames);
+ }
+
+ status_t set(uint32_t sampleRate, uint32_t channelCount, audio_format_t format,
+ TEE_FLAG flags = TEE_FLAG_NONE, size_t frames = 0) const {
+ return mTee->set(Format_from_SR_C(sampleRate, channelCount, format), flags, frames);
+ }
+
+ /**
+ * \brief write data to the tee.
+ *
+ * This call is lock free (as shared pointer and NBAIO is lock free);
+ * may be called simultaneous to all methods except set().
+ *
+ * \param buffer to write to pipe.
+ * \param frameCount in frames as specified by the format passed to set()
+ */
+
+ void write(const void *buffer, size_t frameCount) const {
+ mTee->write(buffer, frameCount);
+ }
+
+ /** sets Tee id string which identifies the generated file (should be unique). */
+ void setId(const std::string &id) const {
+ mTee->setId(id);
+ }
+
+ /**
+ * \brief dump the audio content written to the Tee.
+ *
+ * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
+ * \param reason string suffix to append to the generated file.
+ */
+ void dump(int fd, const std::string &reason = "") const {
+ mTee->dump(fd, reason);
+ }
+
+ /**
+ * \brief dump all Tees currently alive.
+ *
+ * \param fd file descriptor to write dumped filename for logging, use -1 to ignore.
+ * \param reason string suffix to append to the generated file.
+ */
+ static void dumpAll(int fd, const std::string &reason = "") {
+ getRunningTees().dump(fd, reason);
+ }
+
+private:
+
+ /** The underlying implementation of the Tee - the lifetime is through
+ a shared pointer so destruction of the NBAIO_Tee container may proceed
+ even though dumping is occurring. */
+ class NBAIO_TeeImpl {
+ public:
+ status_t set(const NBAIO_Format &format, TEE_FLAG flags, size_t frames) {
+ static const int teeConfig = property_get_bool("ro.debuggable", false)
+ ? property_get_int32("af.tee", 0) : 0;
+
+ // check the type of Tee
+ const TEE_FLAG type = TEE_FLAG(
+ flags & (TEE_FLAG_INPUT_THREAD | TEE_FLAG_OUTPUT_THREAD | TEE_FLAG_TRACK));
+
+ // parameter flags can't select multiple types.
+ if (__builtin_popcount(type) > 1) {
+ return BAD_VALUE;
+ }
+
+ // if type is set, we check to see if it is permitted by configuration.
+ if (type != 0 && (type & teeConfig) == 0) {
+ return PERMISSION_DENIED;
+ }
+
+ // determine number of frames for Tee
+ if (frames == 0) {
+ // TODO: consider varying frame count based on type.
+ frames = DEFAULT_TEE_FRAMES;
+ }
+
+ // TODO: should we check minimum number of frames?
+
+ // don't do anything if format and frames are the same.
+ if (Format_isEqual(format, mFormat) && frames == mFrames) {
+ return NO_ERROR;
+ }
+
+ bool enabled = false;
+ auto sinksource = makeSinkSource(format, frames, &enabled);
+
+ // enabled is set if makeSinkSource is successful.
+ // Note: as mentioned in NBAIO_Tee::set(), don't call set() while write() is
+ // ongoing.
+ if (enabled) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mFlags = flags;
+ mFormat = format; // could get this from the Sink.
+ mFrames = frames;
+ mSinkSource = std::move(sinksource);
+ mEnabled.store(true);
+ return NO_ERROR;
+ }
+ return BAD_VALUE;
+ }
+
+ void setId(const std::string &id) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mId = id;
+ }
+
+ void dump(int fd, const std::string &reason) {
+ if (!mDataReady.exchange(false)) return;
+ std::string suffix;
+ NBAIO_SinkSource sinkSource;
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ suffix = mId + reason;
+ sinkSource = mSinkSource;
+ }
+ dumpTee(fd, sinkSource, suffix);
+ }
+
+ void write(const void *buffer, size_t frameCount) {
+ if (!mEnabled.load() || frameCount == 0) return;
+ (void)mSinkSource.first->write(buffer, frameCount);
+ mDataReady.store(true);
+ }
+
+ private:
+ // TRICKY: We need to keep the NBAIO_Sink and NBAIO_Source both alive at the same time
+ // because PipeReader holds a naked reference (not a strong or weak pointer) to Pipe.
+ using NBAIO_SinkSource = std::pair<sp<NBAIO_Sink>, sp<NBAIO_Source>>;
+
+ static void dumpTee(int fd, const NBAIO_SinkSource& sinkSource, const std::string& suffix);
+
+ static NBAIO_SinkSource makeSinkSource(
+ const NBAIO_Format &format, size_t frames, bool *enabled);
+
+ // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
+ static constexpr size_t DEFAULT_TEE_FRAMES = 0x200000;
+
+ // atomic status checking
+ std::atomic<bool> mEnabled{false};
+ std::atomic<bool> mDataReady{false};
+
+ // locked dump information
+ mutable std::mutex mLock;
+ std::string mId; // GUARDED_BY(mLock)
+ TEE_FLAG mFlags = TEE_FLAG_NONE; // GUARDED_BY(mLock)
+ NBAIO_Format mFormat = Format_Invalid; // GUARDED_BY(mLock)
+ size_t mFrames = 0; // GUARDED_BY(mLock)
+ NBAIO_SinkSource mSinkSource; // GUARDED_BY(mLock)
+ };
+
+ /** RunningTees tracks current running tees for dump purposes.
+ It is implemented to have minimal locked regions, to be transparent to the caller. */
+ class RunningTees {
+ public:
+ void add(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
+ std::lock_guard<std::mutex> _l(mLock);
+ ALOGW_IF(!mTees.emplace(tee).second,
+ "%s: %p already exists in mTees", __func__, tee.get());
+ }
+
+ void remove(const std::shared_ptr<NBAIO_TeeImpl> &tee) {
+ std::lock_guard<std::mutex> _l(mLock);
+ ALOGW_IF(mTees.erase(tee) != 1,
+ "%s: %p doesn't exist in mTees", __func__, tee.get());
+ }
+
+ void dump(int fd, const std::string &reason) {
+ std::vector<std::shared_ptr<NBAIO_TeeImpl>> tees; // safe snapshot of tees
+ {
+ std::lock_guard<std::mutex> _l(mLock);
+ tees.insert(tees.end(), mTees.begin(), mTees.end());
+ }
+ for (const auto &tee : tees) {
+ tee->dump(fd, reason);
+ }
+ }
+
+ private:
+ std::mutex mLock;
+ std::set<std::shared_ptr<NBAIO_TeeImpl>> mTees; // GUARDED_BY(mLock)
+ };
+
+ // singleton
+ static RunningTees &getRunningTees() {
+ static RunningTees runningTees;
+ return runningTees;
+ }
+
+ // The NBAIO TeeImpl may have lifetime longer than NBAIO_Tee if
+ // RunningTees::dump() is being called simultaneous to ~NBAIO_Tee().
+ // This is allowed for maximum concurrency.
+ const std::shared_ptr<NBAIO_TeeImpl> mTee;
+}; // NBAIO_Tee
+
+} // namespace android
+
+#endif // TEE_SINK
+#endif // !ANDROID_NBAIO_TEE_H
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index a78be99..ff9444d 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -56,6 +56,12 @@
LOG_ALWAYS_FATAL_IF(mName >= 0 && name >= 0,
"%s both old name %d and new name %d are valid", __func__, mName, name);
mName = name;
+#ifdef TEE_SINK
+ mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+ + "_" + std::to_string(mId)
+ + "_" + std::to_string(mName)
+ + "_T");
+#endif
}
virtual uint32_t sampleRate() const;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8bf5337..90e2e8e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1579,6 +1579,9 @@
--mBatteryCounter[track->uid()].second;
// mLatestActiveTrack is not cleared even if is the same as track.
mHasChanged = true;
+#ifdef TEE_SINK
+ track->dumpTee(-1 /* fd */, "_REMOVE");
+#endif
return index;
}
@@ -2853,6 +2856,9 @@
ATRACE_END();
if (framesWritten > 0) {
bytesWritten = framesWritten * mFrameSize;
+#ifdef TEE_SINK
+ mTee.write((char *)mSinkBuffer + offset, framesWritten);
+#endif
} else {
bytesWritten = framesWritten;
}
@@ -3866,9 +3872,7 @@
// create a MonoPipe to connect our submix to FastMixer
NBAIO_Format format = mOutputSink->format();
-#ifdef TEE_SINK
- NBAIO_Format origformat = format;
-#endif
+
// adjust format to match that of the Fast Mixer
ALOGV("format changed from %#x to %#x", format.mFormat, fastMixerFormat);
format.mFormat = fastMixerFormat;
@@ -3880,7 +3884,7 @@
MonoPipe *monoPipe = new MonoPipe(mNormalFrameCount * 4, format, true /*writeCanBlock*/);
const NBAIO_Format offers[1] = {format};
size_t numCounterOffers = 0;
-#if !LOG_NDEBUG || defined(TEE_SINK)
+#if !LOG_NDEBUG
ssize_t index =
#else
(void)
@@ -3891,25 +3895,8 @@
(monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
mPipeSink = monoPipe;
-#ifdef TEE_SINK
- if (mTeeSinkOutputEnabled) {
- // create a Pipe to archive a copy of FastMixer's output for dumpsys
- Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, origformat);
- const NBAIO_Format offers2[1] = {origformat};
- numCounterOffers = 0;
- index = teeSink->negotiate(offers2, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mTeeSink = teeSink;
- PipeReader *teeSource = new PipeReader(*teeSink);
- numCounterOffers = 0;
- index = teeSource->negotiate(offers2, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mTeeSource = teeSource;
- }
-#endif
-
// create fast mixer and configure it initially with just one fast track for our submix
- mFastMixer = new FastMixer();
+ mFastMixer = new FastMixer(mId);
FastMixerStateQueue *sq = mFastMixer->sq();
#ifdef STATE_QUEUE_DUMP
sq->setObserverDump(&mStateQueueObserverDump);
@@ -3935,9 +3922,6 @@
state->mColdFutexAddr = &mFastMixerFutex;
state->mColdGen++;
state->mDumpState = &mFastMixerDumpState;
-#ifdef TEE_SINK
- state->mTeeSink = mTeeSink.get();
-#endif
mFastMixerNBLogWriter = audioFlinger->newWriter_l(kFastMixerLogSize, "FastMixer");
state->mNBLogWriter = mFastMixerNBLogWriter.get();
sq->end();
@@ -3946,7 +3930,7 @@
// start the fast mixer
mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
pid_t tid = mFastMixer->getTid();
- sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/);
+ sendPrioConfigEvent(getpid(), tid, kPriorityFastMixer, false /*forApp*/);
stream()->setHalThreadPriority(kPriorityFastMixer);
#ifdef AUDIO_WATCHDOG
@@ -3955,9 +3939,14 @@
mAudioWatchdog->setDump(&mAudioWatchdogDump);
mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
tid = mAudioWatchdog->getTid();
- sendPrioConfigEvent(getpid_cached, tid, kPriorityFastMixer, false /*forApp*/);
+ sendPrioConfigEvent(getpid(), tid, kPriorityFastMixer, false /*forApp*/);
#endif
-
+ } else {
+#ifdef TEE_SINK
+ // Only use the MixerThread tee if there is no FastMixer.
+ mTee.set(mOutputSink->format(), NBAIO_Tee::TEE_FLAG_OUTPUT_THREAD);
+ mTee.setId(std::string("_") + std::to_string(mId) + "_M");
+#endif
}
switch (kUseFastMixer) {
@@ -5096,12 +5085,6 @@
} else {
dprintf(fd, " No FastMixer\n");
}
-
-#ifdef TEE_SINK
- // Write the tee output to a .wav file
- dumpTee(fd, mTeeSource, mId, 'M');
-#endif
-
}
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
@@ -6267,9 +6250,6 @@
audio_devices_t outDevice,
audio_devices_t inDevice,
bool systemReady
-#ifdef TEE_SINK
- , const sp<NBAIO_Sink>& teeSink
-#endif
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
mInput(input),
@@ -6277,9 +6257,6 @@
mRsmpInBuffer(NULL),
// mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l()
mRsmpInRear(0)
-#ifdef TEE_SINK
- , mTeeSink(teeSink)
-#endif
, mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize,
"RecordThreadRO", MemoryHeapBase::READ_ONLY))
// mFastCapture below
@@ -6394,7 +6371,7 @@
// start the fast capture
mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO);
pid_t tid = mFastCapture->getTid();
- sendPrioConfigEvent(getpid_cached, tid, kPriorityFastCapture, false /*forApp*/);
+ sendPrioConfigEvent(getpid(), tid, kPriorityFastCapture, false /*forApp*/);
stream()->setHalThreadPriority(kPriorityFastCapture);
#ifdef AUDIO_WATCHDOG
// FIXME
@@ -6402,6 +6379,10 @@
mFastTrackAvail = true;
}
+#ifdef TEE_SINK
+ mTee.set(mInputSource->format(), NBAIO_Tee::TEE_FLAG_INPUT_THREAD);
+ mTee.setId(std::string("_") + std::to_string(mId) + "_C");
+#endif
failed: ;
// FIXME mNormalSource
@@ -6744,9 +6725,9 @@
}
ALOG_ASSERT(framesRead > 0);
- if (mTeeSink != 0) {
- (void) mTeeSink->write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
- }
+#ifdef TEE_SINK
+ (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
+#endif
// If destination is non-contiguous, we now correct for reading past end of buffer.
{
size_t part1 = mRsmpInFramesP2 - rear;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 5e5e948..f18294b 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -497,6 +497,9 @@
// we must not wait for async write callback in the thread loop before evaluating it
bool mSignalPending;
+#ifdef TEE_SINK
+ NBAIO_Tee mTee;
+#endif
// ActiveTracks is a sorted vector of track type T representing the
// active tracks of threadLoop() to be considered by the locked prepare portion.
// ActiveTracks should be accessed with the ThreadBase lock held.
@@ -1054,11 +1057,6 @@
sp<NBAIO_Sink> mPipeSink;
// The current sink for the normal mixer to write it's (sub)mix, mOutputSink or mPipeSink
sp<NBAIO_Sink> mNormalSink;
-#ifdef TEE_SINK
- // For dumpsys
- sp<NBAIO_Sink> mTeeSink;
- sp<NBAIO_Source> mTeeSource;
-#endif
uint32_t mScreenState; // cached copy of gScreenState
// TODO: add comment and adjust size as needed
static const size_t kFastMixerLogSize = 8 * 1024;
@@ -1374,9 +1372,6 @@
audio_devices_t outDevice,
audio_devices_t inDevice,
bool systemReady
-#ifdef TEE_SINK
- , const sp<NBAIO_Sink>& teeSink
-#endif
);
virtual ~RecordThread();
@@ -1506,8 +1501,6 @@
int32_t mRsmpInRear; // last filled frame + 1
// For dumpsys
- const sp<NBAIO_Sink> mTeeSink;
-
const sp<MemoryDealer> mReadOnlyHeap;
// one-time initialization, no locks required
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index ccfb69f..9f17c3c 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -100,6 +100,12 @@
audio_attributes_t attributes() const { return mAttr; }
+#ifdef TEE_SINK
+ void dumpTee(int fd, const std::string &reason) const {
+ mTee.dump(fd, reason);
+ }
+#endif
+
protected:
DISALLOW_COPY_AND_ASSIGN(TrackBase);
@@ -208,8 +214,9 @@
const bool mIsOut;
sp<ServerProxy> mServerProxy;
const int mId;
- sp<NBAIO_Sink> mTeeSink;
- sp<NBAIO_Source> mTeeSource;
+#ifdef TEE_SINK
+ NBAIO_Tee mTee;
+#endif
bool mTerminated;
track_type mType; // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ...
audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index fc8f34b..824dac7 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -102,7 +102,7 @@
mIsInvalid(false)
{
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (!isTrustedCallingUid(callingUid) || clientUid == AUDIO_UID_INVALID) {
+ if (!isAudioServerOrMediaServerUid(callingUid) || clientUid == AUDIO_UID_INVALID) {
ALOGW_IF(clientUid != AUDIO_UID_INVALID && clientUid != callingUid,
"%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, clientUid);
clientUid = callingUid;
@@ -210,22 +210,7 @@
mBufferSize = bufferSize;
#ifdef TEE_SINK
- if (mTeeSinkTrackEnabled) {
- NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount, mFormat);
- if (Format_isValid(pipeFormat)) {
- Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat);
- size_t numCounterOffers = 0;
- const NBAIO_Format offers[1] = {pipeFormat};
- ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- PipeReader *pipeReader = new PipeReader(*pipe);
- numCounterOffers = 0;
- index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mTeeSink = pipe;
- mTeeSource = pipeReader;
- }
- }
+ mTee.set(sampleRate, mChannelCount, format, NBAIO_Tee::TEE_FLAG_TRACK);
#endif
}
@@ -244,9 +229,6 @@
AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
-#ifdef TEE_SINK
- dumpTee(-1, mTeeSource, mId, 'T');
-#endif
// delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
mServerProxy.clear();
if (mCblk != NULL) {
@@ -274,9 +256,7 @@
void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
#ifdef TEE_SINK
- if (mTeeSink != 0) {
- (void) mTeeSink->write(buffer->raw, buffer->frameCount);
- }
+ mTee.write(buffer->raw, buffer->frameCount);
#endif
ServerProxy::Buffer buf;
@@ -454,6 +434,12 @@
thread->mFastTrackAvailMask &= ~(1 << i);
}
mName = TRACK_NAME_PENDING;
+
+#ifdef TEE_SINK
+ mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+ + "_" + std::to_string(mId) +
+ + "_PEND_T");
+#endif
}
AudioFlinger::PlaybackThread::Track::~Track()
@@ -599,7 +585,7 @@
"%08X %6zu%c %6zu %c %9u%c %7u "
"%08zX %08zX\n",
active ? "yes" : "no",
- (mClient == 0) ? getpid_cached : mClient->pid(),
+ (mClient == 0) ? getpid() : mClient->pid(),
mSessionId,
getTrackStateString(),
mCblk->mFlags,
@@ -1509,7 +1495,7 @@
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
buffer, bufferSize, nullptr /* sharedBuffer */,
- AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH),
+ AUDIO_SESSION_NONE, AID_AUDIOSERVER, flags, TYPE_PATCH),
mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true))
{
uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) /
@@ -1695,6 +1681,11 @@
ALOG_ASSERT(thread->mFastTrackAvail);
thread->mFastTrackAvail = false;
}
+#ifdef TEE_SINK
+ mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+ + "_" + std::to_string(mId)
+ + "_R");
+#endif
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -1795,7 +1786,7 @@
"%08X %6zu %3c\n",
isFastTrack() ? 'F' : ' ',
active ? "yes" : "no",
- (mClient == 0) ? getpid_cached : mClient->pid(),
+ (mClient == 0) ? getpid() : mClient->pid(),
mSessionId,
getTrackStateString(),
mCblk->mFlags,
@@ -1875,7 +1866,8 @@
: RecordTrack(recordThread, NULL,
audio_attributes_t{} /* currently unused for patch track */,
sampleRate, format, channelMask, frameCount,
- buffer, bufferSize, AUDIO_SESSION_NONE, getuid(), flags, TYPE_PATCH),
+ buffer, bufferSize, AUDIO_SESSION_NONE, AID_AUDIOSERVER,
+ flags, TYPE_PATCH),
mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true))
{
uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) /
diff --git a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
index fac6cbe..32b4440 100644
--- a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
+++ b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
@@ -24,6 +24,7 @@
namespace android {
class DeviceDescriptor;
+class DeviceVector;
class SessionRoute : public RefBase
{
@@ -98,7 +99,8 @@
int decRouteActivity(audio_session_t session);
bool getAndClearRouteChanged(audio_session_t session); // also clears the changed flag
void log(const char* caption);
-
+ audio_devices_t getActiveDeviceForStream(audio_stream_type_t streamType,
+ const DeviceVector& availableDevices);
// Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the
// source argument.
// Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index d85562e..fc868d3 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -398,10 +398,9 @@
status_t AudioPortConfig::applyAudioPortConfig(const struct audio_port_config *config,
struct audio_port_config *backupConfig)
{
- struct audio_port_config localBackupConfig;
+ struct audio_port_config localBackupConfig = { .config_mask = config->config_mask };
status_t status = NO_ERROR;
- localBackupConfig.config_mask = config->config_mask;
toAudioPortConfig(&localBackupConfig);
sp<AudioPort> audioport = getAudioPort();
diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
index 8edd4d1..d34214b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
@@ -82,7 +82,7 @@
void SessionRouteMap::log(const char* caption)
{
ALOGI("%s ----", caption);
- for(size_t index = 0; index < size(); index++) {
+ for (size_t index = 0; index < size(); index++) {
valueAt(index)->log(" ");
}
}
@@ -119,4 +119,22 @@
}
}
+audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType,
+ const DeviceVector& availableDevices)
+{
+ audio_devices_t device = AUDIO_DEVICE_NONE;
+
+ for (size_t index = 0; index < size(); index++) {
+ sp<SessionRoute> route = valueAt(index);
+ if (streamType == route->mStreamType && route->isActiveOrChanged()
+ && route->mDeviceDescriptor != 0) {
+ device = route->mDeviceDescriptor->type();
+ if (!availableDevices.getDevicesFromType(device).isEmpty()) {
+ break;
+ }
+ }
+ }
+ return device;
+}
+
} // namespace android
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 23c020d..3e13e50 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -453,6 +453,12 @@
}
// Use both Bluetooth SCO and phone default output when ringing in normal mode
if (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) {
+ if ((strategy == STRATEGY_SONIFICATION) &&
+ (device & AUDIO_DEVICE_OUT_SPEAKER) &&
+ (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+ device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+ device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+ }
if (device2 != AUDIO_DEVICE_NONE) {
device |= device2;
break;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 6999ba5..418604a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -39,6 +39,7 @@
#include <media/AudioParameter.h>
#include <media/AudioPolicyHelper.h>
#include <media/PatchBuilder.h>
+#include <private/android_filesystem_config.h>
#include <soundtrigger/SoundTrigger.h>
#include <system/audio.h>
#include <audio_policy_conf.h>
@@ -1150,7 +1151,9 @@
}
} else if (mOutputRoutes.getAndClearRouteChanged(session)) {
newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
- checkStrategyRoute(getStrategy(stream), output);
+ if (newDevice != outputDesc->device()) {
+ checkStrategyRoute(getStrategy(stream), output);
+ }
} else {
newDevice = AUDIO_DEVICE_NONE;
}
@@ -1406,6 +1409,7 @@
(newDevice != desc->device())) {
audio_devices_t newDevice2 = getNewOutputDevice(desc, false /*fromCache*/);
bool force = desc->device() != newDevice2;
+
setOutputDevice(desc,
newDevice2,
force,
@@ -3184,10 +3188,10 @@
return BAD_VALUE;
}
- struct audio_port_config backupConfig;
+ struct audio_port_config backupConfig = {};
status_t status = audioPortConfig->applyAudioPortConfig(config, &backupConfig);
if (status == NO_ERROR) {
- struct audio_port_config newConfig;
+ struct audio_port_config newConfig = {};
audioPortConfig->toAudioPortConfig(&newConfig, config);
status = mpClientInterface->setAudioPortConfig(&newConfig, 0);
}
@@ -3335,7 +3339,7 @@
sp<AudioSourceDescriptor> sourceDesc =
new AudioSourceDescriptor(srcDeviceDesc, attributes, uid);
- struct audio_patch dummyPatch;
+ struct audio_patch dummyPatch = {};
sp<AudioPatch> patchDesc = new AudioPatch(&dummyPatch, uid);
sourceDesc->mPatchDesc = patchDesc;
@@ -3865,7 +3869,7 @@
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface,
bool /*forTesting*/)
:
- mUidCached(getuid()),
+ mUidCached(AID_AUDIOSERVER), // no need to call getuid(), there's only one of us running.
mpClientInterface(clientInterface),
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mA2dpSuspended(false),
@@ -4794,6 +4798,20 @@
}
}
+ // Check if an explicit routing request exists for an active stream on this output and
+ // use it in priority before any other rule
+ for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
+ if (outputDesc->isStreamActive((audio_stream_type_t)stream)) {
+ audio_devices_t forcedDevice =
+ mOutputRoutes.getActiveDeviceForStream(
+ (audio_stream_type_t)stream, mAvailableOutputDevices);
+
+ if (forcedDevice != AUDIO_DEVICE_NONE) {
+ return forcedDevice;
+ }
+ }
+ }
+
// check the following by order of priority to request a routing change if necessary:
// 1: the strategy enforced audible is active and enforced on the output:
// use device for strategy enforced audible
@@ -5001,19 +5019,16 @@
audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
bool fromCache)
{
- // Routing
- // see if we have an explicit route
- // scan the whole RouteMap, for each entry, convert the stream type to a strategy
- // (getStrategy(stream)).
- // if the strategy from the stream type in the RouteMap is the same as the argument above,
- // and activity count is non-zero and the device in the route descriptor is available
- // then select this device.
- for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
- sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
- routing_strategy routeStrategy = getStrategy(route->mStreamType);
- if ((routeStrategy == strategy) && route->isActiveOrChanged() &&
- (mAvailableOutputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
- return route->mDeviceDescriptor->type();
+ // Check if an explicit routing request exists for a stream type corresponding to the
+ // specified strategy and use it in priority over default routing rules.
+ for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
+ if (getStrategy((audio_stream_type_t)stream) == strategy) {
+ audio_devices_t forcedDevice =
+ mOutputRoutes.getActiveDeviceForStream(
+ (audio_stream_type_t)stream, mAvailableOutputDevices);
+ if (forcedDevice != AUDIO_DEVICE_NONE) {
+ return forcedDevice;
+ }
}
}
@@ -5515,7 +5530,10 @@
}
float volumeDb = computeVolume(stream, index, device);
- if (outputDesc->isFixedVolume(device)) {
+ if (outputDesc->isFixedVolume(device) ||
+ // Force VoIP volume to max for bluetooth SCO
+ ((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) &&
+ (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
volumeDb = 0.0f;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index c814ff9..008e1ca 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -540,7 +540,7 @@
static bool streamsMatchForvolume(audio_stream_type_t stream1,
audio_stream_type_t stream2);
- uid_t mUidCached;
+ const uid_t mUidCached; // AID_AUDIOSERVER
AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
sp<SwAudioOutputDescriptor> mPrimaryOutput; // primary output descriptor
// list of descriptors for outputs currently opened
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index de30f40..4d3093f 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -183,7 +183,7 @@
Mutex::Autolock _l(mLock);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (!isTrustedCallingUid(callingUid) || uid == (uid_t)-1) {
+ if (!isAudioServerOrMediaServerUid(callingUid) || uid == (uid_t)-1) {
ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
"%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
uid = callingUid;
@@ -320,7 +320,7 @@
bool updatePid = (pid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
- if (!isTrustedCallingUid(callingUid)) {
+ if (!isAudioServerOrMediaServerUid(callingUid)) {
ALOGW_IF(uid != (uid_t)-1 && uid != callingUid,
"%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, uid);
uid = callingUid;
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 5d25ea8..1379223 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -26,7 +26,6 @@
#include <sys/time.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
-#include <cutils/multiuser.h>
#include <cutils/properties.h>
#include <binder/IPCThreadState.h>
#include <binder/ActivityManager.h>
@@ -43,8 +42,6 @@
#include <system/audio.h>
#include <system/audio_policy.h>
-#include <private/android_filesystem_config.h>
-
namespace android {
static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n";
@@ -275,7 +272,7 @@
void AudioPolicyService::NotificationClient::onDynamicPolicyMixStateUpdate(
const String8& regId, int32_t state)
{
- if (mAudioPolicyServiceClient != 0 && multiuser_get_app_id(mUid) < AID_APP_START) {
+ if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) {
mAudioPolicyServiceClient->onDynamicPolicyMixStateUpdate(regId, state);
}
}
@@ -285,7 +282,7 @@
const audio_config_base_t *clientConfig, const audio_config_base_t *deviceConfig,
audio_patch_handle_t patchHandle)
{
- if (mAudioPolicyServiceClient != 0 && multiuser_get_app_id(mUid) < AID_APP_START) {
+ if (mAudioPolicyServiceClient != 0 && isServiceUid(mUid)) {
mAudioPolicyServiceClient->onRecordingConfigurationUpdate(event, clientInfo,
clientConfig, deviceConfig, patchHandle);
}
@@ -577,10 +574,6 @@
updateUidCache(uid, false, true);
}
-bool AudioPolicyService::UidPolicy::isServiceUid(uid_t uid) const {
- return multiuser_get_app_id(uid) < AID_APP_START;
-}
-
void AudioPolicyService::UidPolicy::notifyService(uid_t uid, bool active) {
sp<AudioPolicyService> service = mService.promote();
if (service != nullptr) {
@@ -1212,6 +1205,7 @@
patch = ((CreateAudioPatchData *)command->mParam.get())->mPatch;
} else {
handle = ((ReleaseAudioPatchData *)command->mParam.get())->mHandle;
+ memset(&patch, 0, sizeof(patch));
}
audio_patch_handle_t handle2;
struct audio_patch patch2;
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 407d7a5..02019a2 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -293,7 +293,6 @@
void removeOverrideUid(uid_t uid) { updateOverrideUid(uid, false, false); }
private:
- bool isServiceUid(uid_t uid) const;
void notifyService(uid_t uid, bool active);
void updateOverrideUid(uid_t uid, bool active, bool insert);
void updateUidCache(uid_t uid, bool active, bool insert);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 9acbf0a..d9bcba3 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -77,7 +77,8 @@
mNextShutterFrameNumber(0),
mNextReprocessShutterFrameNumber(0),
mListener(NULL),
- mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID)
+ mVendorTagId(CAMERA_METADATA_INVALID_VENDOR_ID),
+ mLastTemplateId(-1)
{
ATRACE_CALL();
camera3_callback_ops::notify = &sNotify;
@@ -1597,6 +1598,18 @@
Mutex::Autolock il(mInterfaceLock);
Mutex::Autolock l(mLock);
+ // In case the client doesn't include any session parameter, try a
+ // speculative configuration using the values from the last cached
+ // default request.
+ if (sessionParams.isEmpty() &&
+ ((mLastTemplateId > 0) && (mLastTemplateId < CAMERA3_TEMPLATE_COUNT)) &&
+ (!mRequestTemplateCache[mLastTemplateId].isEmpty())) {
+ ALOGV("%s: Speculative session param configuration with template id: %d", __func__,
+ mLastTemplateId);
+ return filterParamsAndConfigureLocked(mRequestTemplateCache[mLastTemplateId],
+ operatingMode);
+ }
+
return filterParamsAndConfigureLocked(sessionParams, operatingMode);
}
@@ -1673,6 +1686,7 @@
if (!mRequestTemplateCache[templateId].isEmpty()) {
*request = mRequestTemplateCache[templateId];
+ mLastTemplateId = templateId;
return OK;
}
}
@@ -1697,6 +1711,7 @@
mRequestTemplateCache[templateId].acquire(rawRequest);
*request = mRequestTemplateCache[templateId];
+ mLastTemplateId = templateId;
}
return OK;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 5372407..35f799d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -1191,6 +1191,9 @@
metadata_vendor_id_t mVendorTagId;
+ // Cached last requested template id
+ int mLastTemplateId;
+
/**
* Static callback forwarding methods from HAL to instance
*/
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index f4d5a18..59ac636 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -212,7 +212,11 @@
SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__,
maxConsumerBuffers, mMaxHalBuffers);
- size_t totalBufferCount = maxConsumerBuffers + mMaxHalBuffers;
+ // The output slot count requirement can change depending on the current amount
+ // of outputs and incoming buffer consumption rate. To avoid any issues with
+ // insufficient slots, set their count to the maximum supported. The output
+ // surface buffer allocation is disabled so no real buffers will get allocated.
+ size_t totalBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
res = native_window_set_buffer_count(outputQueue.get(),
totalBufferCount);
if (res != OK) {
diff --git a/services/medialog/Android.bp b/services/medialog/Android.bp
index 29e6dfc..ca96f62 100644
--- a/services/medialog/Android.bp
+++ b/services/medialog/Android.bp
@@ -9,7 +9,9 @@
shared_libs: [
"libaudioutils",
"libbinder",
+ "libcutils",
"liblog",
+ "libmediautils",
"libnbaio",
"libnblog",
"libutils",
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index 1be5544..e58dff7 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -21,7 +21,7 @@
#include <utils/Log.h>
#include <binder/PermissionCache.h>
#include <media/nblog/NBLog.h>
-#include <private/android_filesystem_config.h>
+#include <mediautils/ServiceUtilities.h>
#include "MediaLogService.h"
namespace android {
@@ -53,7 +53,7 @@
void MediaLogService::registerWriter(const sp<IMemory>& shared, size_t size, const char *name)
{
- if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0 ||
+ if (!isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid()) || shared == 0 ||
size < kMinSize || size > kMaxSize || name == NULL ||
shared->size() < NBLog::Timeline::sharedSize(size)) {
return;
@@ -67,7 +67,7 @@
void MediaLogService::unregisterWriter(const sp<IMemory>& shared)
{
- if (IPCThreadState::self()->getCallingUid() != AID_AUDIOSERVER || shared == 0) {
+ if (!isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid()) || shared == 0) {
return;
}
Mutex::Autolock _l(mLock);
@@ -95,10 +95,8 @@
status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused)
{
- // FIXME merge with similar but not identical code at services/audioflinger/ServiceUtilities.cpp
- static const String16 sDump("android.permission.DUMP");
- if (!(IPCThreadState::self()->getCallingUid() == AID_AUDIOSERVER ||
- PermissionCache::checkCallingPermission(sDump))) {
+ if (!(isAudioServerOrMediaServerUid(IPCThreadState::self()->getCallingUid())
+ || dumpAllowed())) {
dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());