Merge "Add flag for enabling stereo channel spatialization" into main
diff --git a/services/audioflinger/IAfThread.h b/services/audioflinger/IAfThread.h
index 7084be9..46a67e8 100644
--- a/services/audioflinger/IAfThread.h
+++ b/services/audioflinger/IAfThread.h
@@ -386,6 +386,12 @@
const effect_uuid_t* type, bool suspend, audio_session_t sessionId)
REQUIRES(mutex()) = 0;
+ // Wait while the Thread is busy. This is done to ensure that
+ // the Thread is not busy releasing the Tracks, during which the Thread mutex
+ // may be temporarily unlocked. Some Track methods will use this method to
+ // avoid races.
+ virtual void waitWhileThreadBusy_l(audio_utils::unique_lock& ul)
+ REQUIRES(mutex()) = 0;
// Dynamic cast to derived interface
virtual sp<IAfDirectOutputThread> asIAfDirectOutputThread() { return nullptr; }
virtual sp<IAfDuplicatingThread> asIAfDuplicatingThread() { return nullptr; }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 18bb173..cec2033 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2849,6 +2849,8 @@
// effectively get the latency it requested.
if (track->isExternalTrack()) {
IAfTrackBase::track_state state = track->state();
+ // Because the track is not on the ActiveTracks,
+ // at this point, only the TrackHandle will be adding the track.
mutex().unlock();
status = AudioSystem::startOutput(track->portId());
mutex().lock();
@@ -2929,7 +2931,12 @@
track->setResetDone(false);
track->resetPresentationComplete();
+
+ // Do not release the ThreadBase mutex after the track is added to mActiveTracks unless
+ // all key changes are complete. It is possible that the threadLoop will begin
+ // processing the added track immediately after the ThreadBase mutex is released.
mActiveTracks.add(track);
+
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
track->sessionId());
@@ -4704,8 +4711,12 @@
void PlaybackThread::removeTracks_l(const Vector<sp<IAfTrack>>& tracksToRemove)
NO_THREAD_SAFETY_ANALYSIS // release and re-acquire mutex()
{
+ if (tracksToRemove.empty()) return;
+
+ // Block all incoming TrackHandle requests until we are finished with the release.
+ setThreadBusy_l(true);
+
for (const auto& track : tracksToRemove) {
- mActiveTracks.remove(track);
ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId());
sp<IAfEffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
@@ -4713,17 +4724,16 @@
__func__, track->id(), chain.get(), track->sessionId());
chain->decActiveTrackCnt();
}
+
// If an external client track, inform APM we're no longer active, and remove if needed.
- // We do this under lock so that the state is consistent if the Track is destroyed.
+ // Since the track is active, we do it here instead of TrackBase::destroy().
if (track->isExternalTrack()) {
+ mutex().unlock();
AudioSystem::stopOutput(track->portId());
if (track->isTerminated()) {
AudioSystem::releaseOutput(track->portId());
}
- }
- if (track->isTerminated()) {
- // remove from our tracks vector
- removeTrack_l(track);
+ mutex().lock();
}
if (mHapticChannelCount > 0 &&
((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
@@ -4740,7 +4750,24 @@
chain->setHapticIntensity_l(track->id(), os::HapticScale::MUTE);
}
}
+
+ // Under lock, the track is removed from the active tracks list.
+ //
+ // Once the track is no longer active, the TrackHandle may directly
+ // modify it as the threadLoop() is no longer responsible for its maintenance.
+ // Do not modify the track from threadLoop after the mutex is unlocked
+ // if it is not active.
+ mActiveTracks.remove(track);
+
+ if (track->isTerminated()) {
+ // remove from our tracks vector
+ removeTrack_l(track);
+ }
}
+
+ // Allow incoming TrackHandle requests. We still hold the mutex,
+ // so pending TrackHandle requests will occur after we unlock it.
+ setThreadBusy_l(false);
}
status_t PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp)
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index ea994a5..8491e43 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -599,6 +599,35 @@
// check if some effects must be suspended when an effect chain is added
void checkSuspendOnAddEffectChain_l(const sp<IAfEffectChain>& chain) REQUIRES(mutex());
+ /**
+ * waitWhileThreadBusy_l() serves as a mutex gate, which does not allow
+ * progress beyond the method while the PlaybackThread is busy (see setThreadBusy_l()).
+ * During the wait, the ThreadBase_Mutex is temporarily unlocked.
+ *
+ * This implementation uses a condition variable. Alternative methods to gate
+ * the thread may use a second mutex (i.e. entry based on scoped_lock(mutex, gating_mutex)),
+ * but those have less flexibility and more lock order issues.
+ *
+ * Current usage by Track::destroy(), Track::start(), Track::stop(), Track::pause(),
+ * and Track::flush() block this way, and the primary caller is through TrackHandle
+ * with no other mutexes held.
+ *
+ * Special tracks like PatchTrack and OutputTrack may also hold the another thread's
+ * ThreadBase_Mutex during this time. No other mutex is held.
+ */
+
+ void waitWhileThreadBusy_l(audio_utils::unique_lock& ul) final REQUIRES(mutex()) {
+ // the wait returns immediately if the predicate is satisfied.
+ mThreadBusyCv.wait(ul, [&]{ return mThreadBusy == false;});
+ }
+
+ void setThreadBusy_l(bool busy) REQUIRES(mutex()) {
+ if (busy == mThreadBusy) return;
+ mThreadBusy = busy;
+ if (busy == true) return; // no need to wake threads if we become busy.
+ mThreadBusyCv.notify_all();
+ }
+
// sends the metadata of the active tracks to the HAL
struct MetadataUpdate {
std::vector<playback_track_metadata_v7_t> playbackMetadataUpdate;
@@ -641,6 +670,13 @@
ThreadMetrics mThreadMetrics;
const bool mIsOut;
+ // mThreadBusy is checked under the ThreadBase_Mutex to ensure that
+ // TrackHandle operations do not proceed while the ThreadBase is busy
+ // with the track. mThreadBusy is only true if the track is active.
+ //
+ bool mThreadBusy = false; // GUARDED_BY(ThreadBase_Mutex) but read in lambda.
+ audio_utils::condition_variable mThreadBusyCv;
+
// updated by PlaybackThread::readOutputParameters_l() or
// RecordThread::readInputParameters_l()
uint32_t mSampleRate;
@@ -839,7 +875,7 @@
SimpleLog mLocalLog; // locked internally
-private:
+ private:
void dumpBase_l(int fd, const Vector<String16>& args) REQUIRES(mutex());
void dumpEffectChains_l(int fd, const Vector<String16>& args) REQUIRES(mutex());
};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index fe582eb..d279af1 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -890,12 +890,17 @@
bool wasActive = false;
const sp<IAfThreadBase> thread = mThread.promote();
if (thread != 0) {
- audio_utils::lock_guard _l(thread->mutex());
+ audio_utils::unique_lock ul(thread->mutex());
+ thread->waitWhileThreadBusy_l(ul);
+
auto* const playbackThread = thread->asIAfPlaybackThread().get();
wasActive = playbackThread->destroyTrack_l(this);
forEachTeePatchTrack_l([](const auto& patchTrack) { patchTrack->destroy(); });
}
if (isExternalTrack() && !wasActive) {
+ // If the track is not active, the TrackHandle is responsible for
+ // releasing the port id, not the ThreadBase::threadLoop().
+ // At this point, there is no concurrency issue as the track is going away.
AudioSystem::releaseOutput(mPortId);
}
}
@@ -1181,7 +1186,9 @@
return PERMISSION_DENIED;
}
}
- audio_utils::lock_guard _lth(thread->mutex());
+ audio_utils::unique_lock ul(thread->mutex());
+ thread->waitWhileThreadBusy_l(ul);
+
track_state state = mState;
// here the track could be either new, or restarted
// in both cases "unstop" the track
@@ -1306,7 +1313,9 @@
ALOGV("%s(%d): calling pid %d", __func__, mId, IPCThreadState::self()->getCallingPid());
const sp<IAfThreadBase> thread = mThread.promote();
if (thread != 0) {
- audio_utils::lock_guard _l(thread->mutex());
+ audio_utils::unique_lock ul(thread->mutex());
+ thread->waitWhileThreadBusy_l(ul);
+
track_state state = mState;
if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) {
// If the track is not active (PAUSED and buffers full), flush buffers
@@ -1341,7 +1350,9 @@
ALOGV("%s(%d): calling pid %d", __func__, mId, IPCThreadState::self()->getCallingPid());
const sp<IAfThreadBase> thread = mThread.promote();
if (thread != 0) {
- audio_utils::lock_guard _l(thread->mutex());
+ audio_utils::unique_lock ul(thread->mutex());
+ thread->waitWhileThreadBusy_l(ul);
+
auto* const playbackThread = thread->asIAfPlaybackThread().get();
switch (mState) {
case STOPPING_1:
@@ -1378,7 +1389,9 @@
ALOGV("%s(%d)", __func__, mId);
const sp<IAfThreadBase> thread = mThread.promote();
if (thread != 0) {
- audio_utils::lock_guard _l(thread->mutex());
+ audio_utils::unique_lock ul(thread->mutex());
+ thread->waitWhileThreadBusy_l(ul);
+
auto* const playbackThread = thread->asIAfPlaybackThread().get();
// Flush the ring buffer now if the track is not active in the PlaybackThread.
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 13b70e5..7c70877 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -364,7 +364,7 @@
void dump(String8 *dst, int spaces, const char* extraInfo = nullptr) const override;
virtual DeviceVector devices() const;
- void setDevices(const DeviceVector &devices) { mDevices = devices; }
+ void setDevices(const DeviceVector &devices);
bool sharesHwModuleWith(const sp<SwAudioOutputDescriptor>& outputDesc);
virtual DeviceVector supportedDevices() const;
virtual bool devicesSupportEncodedFormats(const DeviceTypeSet& deviceTypes);
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index 6c130fd..c502fc2 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -93,6 +93,8 @@
void setEncapsulationInfoFromHal(AudioPolicyClientInterface *clientInterface);
+ void setPreferredConfig(const audio_config_base_t * preferredConfig);
+
void dump(String8 *dst, int spaces, bool verbose = true) const;
private:
@@ -107,6 +109,7 @@
audio_format_t mCurrentEncodedFormat;
bool mIsDynamic = false;
std::string mDeclaredAddress; // Original device address
+ std::optional<audio_config_base_t> mPreferredConfig;
};
class DeviceVector : public SortedVector<sp<DeviceDescriptor> >
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index d027564..6537a00 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -778,6 +778,19 @@
}
}
+void SwAudioOutputDescriptor::setDevices(const android::DeviceVector &devices) {
+ if ((mFlags & AUDIO_OUTPUT_FLAG_BIT_PERFECT) == AUDIO_OUTPUT_FLAG_BIT_PERFECT) {
+ for (auto device : mDevices) {
+ device->setPreferredConfig(nullptr);
+ }
+ auto config = getConfig();
+ for (auto device : devices) {
+ device->setPreferredConfig(&config);
+ }
+ }
+ mDevices = devices;
+}
+
// HwAudioOutputDescriptor implementation
HwAudioOutputDescriptor::HwAudioOutputDescriptor(const sp<SourceClientDescriptor>& source,
AudioPolicyClientInterface *clientInterface)
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index fe25693..9f7b8fc 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -132,6 +132,20 @@
{
DeviceDescriptorBase::toAudioPortConfig(dstConfig, srcConfig);
dstConfig->ext.device.hw_module = getModuleHandle();
+ if (mPreferredConfig.has_value()) {
+ if (mPreferredConfig->format != AUDIO_FORMAT_DEFAULT) {
+ dstConfig->config_mask |= AUDIO_PORT_CONFIG_FORMAT;
+ dstConfig->format = mPreferredConfig->format;
+ }
+ if (mPreferredConfig->sample_rate != 0) {
+ dstConfig->config_mask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
+ dstConfig->sample_rate = mPreferredConfig->sample_rate;
+ }
+ if (mPreferredConfig->channel_mask != AUDIO_CHANNEL_NONE) {
+ dstConfig->config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
+ dstConfig->channel_mask = mPreferredConfig->channel_mask;
+ }
+ }
}
void DeviceDescriptor::toAudioPort(struct audio_port *port) const
@@ -183,6 +197,14 @@
}
}
+void DeviceDescriptor::setPreferredConfig(const audio_config_base_t* preferredConfig) {
+ if (preferredConfig == nullptr) {
+ mPreferredConfig.reset();
+ } else {
+ mPreferredConfig = *preferredConfig;
+ }
+}
+
void DeviceDescriptor::dump(String8 *dst, int spaces, bool verbose) const
{
String8 extraInfo;
@@ -193,6 +215,13 @@
std::string descBaseDumpStr;
DeviceDescriptorBase::dump(&descBaseDumpStr, spaces, extraInfo.c_str(), verbose);
dst->append(descBaseDumpStr.c_str());
+
+ if (mPreferredConfig.has_value()) {
+ dst->append(base::StringPrintf(
+ "%*sPreferred Config: format=%#x, channelMask=%#x, sampleRate=%u\n",
+ spaces, "", mPreferredConfig.value().format, mPreferredConfig.value().channel_mask,
+ mPreferredConfig.value().sample_rate).c_str());
+ }
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 3bffcd8..7e11204 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -4448,8 +4448,8 @@
if (!mAvailableOutputDevices.containsAtLeastOne(curProfile->getSupportedDevices())) {
continue;
}
- if ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
- != AUDIO_OUTPUT_FLAG_NONE) {
+ if (offloadPossible && ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
+ != AUDIO_OUTPUT_FLAG_NONE)) {
if ((directMode & AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED)
!= AUDIO_DIRECT_NOT_SUPPORTED) {
// Already reports offload gapless supported. No need to report offload support.
@@ -6751,6 +6751,12 @@
closingOutput->stop();
}
closingOutput->close();
+ if ((closingOutput->getFlags().output & AUDIO_OUTPUT_FLAG_BIT_PERFECT)
+ == AUDIO_OUTPUT_FLAG_BIT_PERFECT) {
+ for (const auto device : closingOutput->devices()) {
+ device->setPreferredConfig(nullptr);
+ }
+ }
removeOutput(output);
mPreviousOutputs = mOutputs;
@@ -8365,6 +8371,12 @@
ALOGE("%s failed to open output %d", __func__, status);
return nullptr;
}
+ if ((flags & AUDIO_OUTPUT_FLAG_BIT_PERFECT) == AUDIO_OUTPUT_FLAG_BIT_PERFECT) {
+ auto portConfig = desc->getConfig();
+ for (const auto& device : devices) {
+ device->setPreferredConfig(&portConfig);
+ }
+ }
// Here is where the out_set_parameters() for card & device gets called
sp<DeviceDescriptor> device = devices.getDeviceForOpening();
diff --git a/services/camera/virtualcamera/TEST_MAPPING b/services/camera/virtualcamera/TEST_MAPPING
index 66c5e52..25fca73 100644
--- a/services/camera/virtualcamera/TEST_MAPPING
+++ b/services/camera/virtualcamera/TEST_MAPPING
@@ -2,9 +2,7 @@
"presubmit" : [
{
"name": "virtual_camera_tests"
- }
- ],
- "postsubmit": [
+ },
{
"name": "CtsVirtualDevicesCameraTestCases",
"options": [
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
index 630a436..7dc445b 100644
--- a/services/mediametrics/AudioPowerUsage.cpp
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -549,7 +549,7 @@
int slot = 1;
std::stringstream ss;
- ss << "AudioPowerUsage:\n";
+ ss << "AudioPowerUsage interval " << mIntervalHours << " hours:\n";
for (const auto &item : mItems) {
if (slot >= limit - 1) {
ss << "-- AudioPowerUsage may be truncated!\n";
diff --git a/services/mediametrics/include/mediametricsservice/TimedAction.h b/services/mediametrics/include/mediametricsservice/TimedAction.h
index c7ef585..e5d7df0 100644
--- a/services/mediametrics/include/mediametricsservice/TimedAction.h
+++ b/services/mediametrics/include/mediametricsservice/TimedAction.h
@@ -25,6 +25,12 @@
namespace android::mediametrics {
class TimedAction {
+ // Use system_clock instead of steady_clock to include suspend time.
+ using TimerClock = class std::chrono::system_clock;
+
+ // Define granularity of wakeup to prevent delayed events if
+ // device is suspended.
+ static constexpr auto kWakeupInterval = std::chrono::minutes(15);
public:
TimedAction() : mThread{[this](){threadLoop();}} {}
@@ -35,7 +41,7 @@
// TODO: return a handle for cancelling the action?
template <typename T> // T is in units of std::chrono::duration.
void postIn(const T& time, std::function<void()> f) {
- postAt(std::chrono::steady_clock::now() + time, f);
+ postAt(TimerClock::now() + time, f);
}
template <typename T> // T is in units of std::chrono::time_point
@@ -75,16 +81,21 @@
void threadLoop() NO_THREAD_SAFETY_ANALYSIS { // thread safety doesn't cover unique_lock
std::unique_lock l(mLock);
while (!mQuit) {
- auto sleepUntilTime = std::chrono::time_point<std::chrono::steady_clock>::max();
+ auto sleepUntilTime = std::chrono::time_point<TimerClock>::max();
if (!mMap.empty()) {
sleepUntilTime = mMap.begin()->first;
- if (sleepUntilTime <= std::chrono::steady_clock::now()) {
+ const auto now = TimerClock::now();
+ if (sleepUntilTime <= now) {
auto node = mMap.extract(mMap.begin()); // removes from mMap.
l.unlock();
node.mapped()();
l.lock();
continue;
}
+ // Bionic uses CLOCK_MONOTONIC for its pthread_mutex regardless
+ // of REALTIME specification, use kWakeupInterval to ensure minimum
+ // granularity if suspended.
+ sleepUntilTime = std::min(sleepUntilTime, now + kWakeupInterval);
}
mCondition.wait_until(l, sleepUntilTime);
}
@@ -93,7 +104,7 @@
mutable std::mutex mLock;
std::condition_variable mCondition GUARDED_BY(mLock);
bool mQuit GUARDED_BY(mLock) = false;
- std::multimap<std::chrono::time_point<std::chrono::steady_clock>, std::function<void()>>
+ std::multimap<std::chrono::time_point<TimerClock>, std::function<void()>>
mMap GUARDED_BY(mLock); // multiple functions could execute at the same time.
// needs to be initialized after the variables above, done in constructor initializer list.