AudioFlinger: Associate audio time with client uid
Group active track operations for add and remove together.
PlaybackThread now uses strong pointer for active tracks.
Test: Play Music, Youtube, Batterystats, CTS AudioTrack
Bug: 32361950
Change-Id: Ica96e2b84b9dfa5766571e24de49401a81a30e9d
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4ae5a02..737d21e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -647,8 +647,7 @@
mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
// mName will be set by concrete (non-virtual) subclass
mDeathRecipient(new PMDeathRecipient(this)),
- mSystemReady(systemReady),
- mNotifiedBatteryStart(false)
+ mSystemReady(systemReady)
{
memset(&mPatch, 0, sizeof(struct audio_patch));
}
@@ -989,10 +988,10 @@
}
}
-void AudioFlinger::ThreadBase::acquireWakeLock(int uid)
+void AudioFlinger::ThreadBase::acquireWakeLock()
{
Mutex::Autolock _l(mLock);
- acquireWakeLock_l(uid);
+ acquireWakeLock_l();
}
String16 AudioFlinger::ThreadBase::getWakeLockTag()
@@ -1014,37 +1013,23 @@
}
}
-void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid)
+void AudioFlinger::ThreadBase::acquireWakeLock_l()
{
getPowerManager_l();
if (mPowerManager != 0) {
sp<IBinder> binder = new BBinder();
- status_t status;
- if (uid >= 0) {
- status = mPowerManager->acquireWakeLockWithUid(POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder,
- getWakeLockTag(),
- String16("audioserver"),
- uid,
- true /* FIXME force oneway contrary to .aidl */);
- } else {
- status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ // Uses AID_AUDIOSERVER for wakelock. updateWakeLockUids_l() updates with client uids.
+ status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
binder,
getWakeLockTag(),
String16("audioserver"),
true /* FIXME force oneway contrary to .aidl */);
- }
if (status == NO_ERROR) {
mWakeLockToken = binder;
}
ALOGV("acquireWakeLock_l() %s status %d", mThreadName, status);
}
- if (!mNotifiedBatteryStart) {
- // TODO: call this function for each track when it becomes active.
- BatteryNotifier::getInstance().noteStartAudio(AID_AUDIOSERVER);
- mNotifiedBatteryStart = true;
- }
gBoottime.acquire(mWakeLockToken);
mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME] =
gBoottime.getBoottimeOffset();
@@ -1067,17 +1052,6 @@
}
mWakeLockToken.clear();
}
-
- if (mNotifiedBatteryStart) {
- // TODO: call this function for each track when it becomes inactive.
- BatteryNotifier::getInstance().noteStopAudio(AID_AUDIOSERVER);
- mNotifiedBatteryStart = false;
- }
-}
-
-void AudioFlinger::ThreadBase::updateWakeLockUids(const SortedVector<int> &uids) {
- Mutex::Autolock _l(mLock);
- updateWakeLockUids_l(uids);
}
void AudioFlinger::ThreadBase::getPowerManager_l() {
@@ -1096,6 +1070,15 @@
void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uids) {
getPowerManager_l();
+
+#if !LOG_NDEBUG
+ std::stringstream s;
+ for (int uid : uids) {
+ s << uid << " ";
+ }
+ ALOGD("updateWakeLockUids_l %s uids:%s", mThreadName, s.str().c_str());
+#endif
+
if (mWakeLockToken == NULL) { // token may be NULL if AudioFlinger::systemReady() not called.
if (mSystemReady) {
ALOGE("no wake lock to update, but system ready!");
@@ -1657,6 +1640,41 @@
mPendingConfigEvents.clear();
}
+template <typename T>
+ssize_t AudioFlinger::ThreadBase::ActiveTracks<T>::add(const sp<T> &track) {
+ ssize_t index = mActiveTracks.indexOf(track);
+ if (index >= 0) {
+ ALOGW("ActiveTracks<T>::add track %p already there", track.get());
+ return index;
+ }
+ mActiveTracksGeneration++;
+ mLatestActiveTrack = track;
+ BatteryNotifier::getInstance().noteStartAudio(track->uid());
+ return mActiveTracks.add(track);
+}
+
+template <typename T>
+ssize_t AudioFlinger::ThreadBase::ActiveTracks<T>::remove(const sp<T> &track) {
+ ssize_t index = mActiveTracks.remove(track);
+ if (index < 0) {
+ ALOGW("ActiveTracks<T>::remove nonexistent track %p", track.get());
+ return index;
+ }
+ mActiveTracksGeneration++;
+ BatteryNotifier::getInstance().noteStopAudio(track->uid());
+ // mLatestActiveTrack is not cleared even if is the same as track.
+ return index;
+}
+
+template <typename T>
+void AudioFlinger::ThreadBase::ActiveTracks<T>::clear() {
+ for (const sp<T> &track : mActiveTracks) {
+ BatteryNotifier::getInstance().noteStopAudio(track->uid());
+ }
+ mLastActiveTracksGeneration = mActiveTracksGeneration;
+ mActiveTracks.clear();
+ mLatestActiveTrack.clear();
+}
// ----------------------------------------------------------------------------
// Playback
@@ -1683,7 +1701,6 @@
mSuspended(0), mBytesWritten(0),
mFramesWritten(0),
mSuspendedFrames(0),
- mActiveTracksGeneration(0),
// mStreamTypes[] initialized in constructor body
mOutput(output),
mLastWriteTime(-1), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
@@ -1802,8 +1819,8 @@
result.append(buffer);
Track::appendDumpHeader(result);
for (size_t i = 0; i < numactive; ++i) {
- sp<Track> track = mActiveTracks[i].promote();
- if (track != 0 && mTracks.indexOf(track) < 0) {
+ sp<Track> track = mActiveTracks[i];
+ if (mTracks.indexOf(track) < 0) {
track->dump(buffer, SIZE, true);
result.append(buffer);
}
@@ -2206,9 +2223,6 @@
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
- mWakeLockUids.add(track->uid());
- mActiveTracksGeneration++;
- mLatestActiveTrack = track;
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
@@ -2843,11 +2857,7 @@
}
// indicate all active tracks in the chain
- for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
- sp<Track> track = mActiveTracks[i].promote();
- if (track == 0) {
- continue;
- }
+ for (const sp<Track> &track : mActiveTracks) {
if (session == track->sessionId()) {
ALOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
chain->incActiveTrackCnt();
@@ -2894,11 +2904,7 @@
if (chain == mEffectChains[i]) {
mEffectChains.removeAt(i);
// detach all active tracks from the chain
- for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) {
- sp<Track> track = mActiveTracks[i].promote();
- if (track == 0) {
- continue;
- }
+ for (const sp<Track> &track : mActiveTracks) {
if (session == track->sessionId()) {
ALOGV("removeEffectChain_l(): stopping track on chain %p for session Id: %d",
chain.get(), session);
@@ -2975,8 +2981,6 @@
// FIXME could this be made local to while loop?
writeFrames = 0;
- int lastGeneration = 0;
-
cacheParameters_l();
mSleepTimeUs = mIdleSleepTimeUs;
@@ -3074,10 +3078,9 @@
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = mLastWriteTime == -1
? systemTime() : mLastWriteTime;
}
- const size_t size = mActiveTracks.size();
- for (size_t i = 0; i < size; ++i) {
- sp<Track> t = mActiveTracks[i].promote();
- if (t != 0 && !t->isFastTrack()) {
+
+ for (const sp<Track> &t : mActiveTracks) {
+ if (!t->isFastTrack()) {
t->updateTrackFrameInfo(
t->mAudioTrackServerProxy->framesReleased(),
mFramesWritten,
@@ -3098,8 +3101,6 @@
if (!keepWakeLock()) {
releaseWakeLock_l();
released = true;
- mWakeLockUids.clear();
- mActiveTracksGeneration++;
}
ALOGV("wait async completion");
mWaitWorkCV.wait(mLock);
@@ -3133,8 +3134,6 @@
}
releaseWakeLock_l();
- mWakeLockUids.clear();
- mActiveTracksGeneration++;
// wait until we have something to do...
ALOGV("%s going to sleep", myName.string());
mWaitWorkCV.wait(mLock);
@@ -3159,12 +3158,7 @@
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove);
- // compare with previously applied list
- if (lastGeneration != mActiveTracksGeneration) {
- // update wakelock
- updateWakeLockUids_l(mWakeLockUids);
- lastGeneration = mActiveTracksGeneration;
- }
+ mActiveTracks.updateWakeLockUids(this);
// prevent any changes in effect chain list and in each effect chain
// during mixing and effect process as the audio buffers could be deleted
@@ -3382,8 +3376,6 @@
}
releaseWakeLock();
- mWakeLockUids.clear();
- mActiveTracksGeneration++;
ALOGV("Thread %p type %d exiting", this, mType);
return false;
@@ -3397,8 +3389,6 @@
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
mActiveTracks.remove(track);
- mWakeLockUids.remove(track->uid());
- mActiveTracksGeneration++;
ALOGV("removeTracks_l removing track on session %d", track->sessionId());
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
@@ -4017,10 +4007,7 @@
mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found.
for (size_t i=0 ; i<count ; i++) {
- const sp<Track> t = mActiveTracks[i].promote();
- if (t == 0) {
- continue;
- }
+ const sp<Track> t = mActiveTracks[i];
// this const just means the local variable doesn't change
Track* const track = t.get();
@@ -4516,11 +4503,7 @@
size_t i = __builtin_ctz(resetMask);
ALOG_ASSERT(i < count);
resetMask &= ~(1 << i);
- sp<Track> t = mActiveTracks[i].promote();
- if (t == 0) {
- continue;
- }
- Track* track = t.get();
+ sp<Track> track = mActiveTracks[i];
ALOG_ASSERT(track->isFastTrack() && track->isStopped());
track->reset();
}
@@ -4830,7 +4813,7 @@
void AudioFlinger::DirectOutputThread::onAddNewTrack_l()
{
sp<Track> previousTrack = mPreviousTrack.promote();
- sp<Track> latestTrack = mLatestActiveTrack.promote();
+ sp<Track> latestTrack = mActiveTracks.getLatest();
if (previousTrack != 0 && latestTrack != 0) {
if (mType == DIRECT) {
@@ -4856,13 +4839,7 @@
bool doHwResume = false;
// find out which tracks need to be processed
- for (size_t i = 0; i < count; i++) {
- sp<Track> t = mActiveTracks[i].promote();
- // The track died recently
- if (t == 0) {
- continue;
- }
-
+ for (const sp<Track> &t : mActiveTracks) {
if (t->isInvalid()) {
ALOGW("An invalidated track shouldn't be in active list");
tracksToRemove->add(t);
@@ -4877,7 +4854,7 @@
// In theory an older track could underrun and restart after the new one starts
// but as we only care about the transition phase between two tracks on a
// direct output, it is not a problem to ignore the underrun case.
- sp<Track> l = mLatestActiveTrack.promote();
+ sp<Track> l = mActiveTracks.getLatest();
bool last = l.get() == track;
if (track->isPausing()) {
@@ -5411,12 +5388,7 @@
ALOGV("OffloadThread::prepareTracks_l active tracks %zu", count);
// find out which tracks need to be processed
- for (size_t i = 0; i < count; i++) {
- sp<Track> t = mActiveTracks[i].promote();
- // The track died recently
- if (t == 0) {
- continue;
- }
+ for (const sp<Track> &t : mActiveTracks) {
Track* const track = t.get();
#ifdef VERY_VERY_VERBOSE_LOGGING
audio_track_cblk_t* cblk = track->cblk();
@@ -5425,7 +5397,7 @@
// In theory an older track could underrun and restart after the new one starts
// but as we only care about the transition phase between two tracks on a
// direct output, it is not a problem to ignore the underrun case.
- sp<Track> l = mLatestActiveTrack.promote();
+ sp<Track> l = mActiveTracks.getLatest();
bool last = l.get() == track;
if (track->isInvalid()) {
@@ -5884,7 +5856,7 @@
#endif
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
- mInput(input), mActiveTracksGen(0), mRsmpInBuffer(NULL),
+ mInput(input), mRsmpInBuffer(NULL),
// mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l()
mRsmpInRear(0)
#ifdef TEE_SINK
@@ -6042,25 +6014,9 @@
reacquire_wakelock:
sp<RecordTrack> activeTrack;
- int activeTracksGen;
{
Mutex::Autolock _l(mLock);
- size_t size = mActiveTracks.size();
- activeTracksGen = mActiveTracksGen;
- if (size > 0) {
- // FIXME an arbitrary choice
- activeTrack = mActiveTracks[0];
- acquireWakeLock_l(activeTrack->uid());
- if (size > 1) {
- SortedVector<int> tmp;
- for (size_t i = 0; i < size; i++) {
- tmp.add(mActiveTracks[i]->uid());
- }
- updateWakeLockUids_l(tmp);
- }
- } else {
- acquireWakeLock_l(-1);
- }
+ acquireWakeLock_l();
}
// used to request a deferred sleep, to be executed later while mutex is unlocked
@@ -6112,15 +6068,6 @@
goto reacquire_wakelock;
}
- if (mActiveTracksGen != activeTracksGen) {
- activeTracksGen = mActiveTracksGen;
- SortedVector<int> tmp;
- for (size_t i = 0; i < size; i++) {
- tmp.add(mActiveTracks[i]->uid());
- }
- updateWakeLockUids_l(tmp);
- }
-
bool doBroadcast = false;
bool allStopped = true;
for (size_t i = 0; i < size; ) {
@@ -6133,7 +6080,6 @@
}
removeTrack_l(activeTrack);
mActiveTracks.remove(activeTrack);
- mActiveTracksGen++;
size--;
continue;
}
@@ -6143,7 +6089,6 @@
case TrackBase::PAUSING:
mActiveTracks.remove(activeTrack);
- mActiveTracksGen++;
doBroadcast = true;
size--;
continue;
@@ -6183,6 +6128,8 @@
}
}
+ mActiveTracks.updateWakeLockUids(this);
+
if (allStopped) {
standbyIfNotAlreadyInStandby();
}
@@ -6481,7 +6428,6 @@
track->invalidate();
}
mActiveTracks.clear();
- mActiveTracksGen++;
mStartStopCond.broadcast();
}
@@ -6735,7 +6681,6 @@
// or using a separate command thread
recordTrack->mState = TrackBase::STARTING_1;
mActiveTracks.add(recordTrack);
- mActiveTracksGen++;
status_t status = NO_ERROR;
if (recordTrack->isExternalTrack()) {
mLock.unlock();
@@ -6744,7 +6689,6 @@
// FIXME should verify that recordTrack is still in mActiveTracks
if (status != NO_ERROR) {
mActiveTracks.remove(recordTrack);
- mActiveTracksGen++;
recordTrack->clearSyncStartEvent();
ALOGV("RecordThread::start error %d", status);
return status;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index da0c705..1fd2adc 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -400,11 +400,10 @@
effect_uuid_t mType; // effect type UUID
};
- void acquireWakeLock(int uid = -1);
- virtual void acquireWakeLock_l(int uid = -1);
+ void acquireWakeLock();
+ virtual void acquireWakeLock_l();
void releaseWakeLock();
void releaseWakeLock_l();
- void updateWakeLockUids(const SortedVector<int> &uids);
void updateWakeLockUids_l(const SortedVector<int> &uids);
void getPowerManager_l();
void setEffectSuspended_l(const effect_uuid_t *type,
@@ -479,8 +478,88 @@
static const size_t kLogSize = 4 * 1024;
sp<NBLog::Writer> mNBLogWriter;
bool mSystemReady;
- bool mNotifiedBatteryStart;
ExtendedTimestamp mTimestamp;
+
+ // 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.
+ //
+ // During processing and I/O, the threadLoop does not hold the lock;
+ // hence it does not directly use ActiveTracks. Care should be taken
+ // to hold local strong references or defer removal of tracks
+ // if the threadLoop may still be accessing those tracks due to mix, etc.
+ //
+ // This class updates power information appropriately.
+ //
+
+ template <typename T>
+ class ActiveTracks {
+ public:
+ ActiveTracks()
+ : mActiveTracksGeneration(0)
+ , mLastActiveTracksGeneration(0)
+ { }
+
+ ~ActiveTracks() {
+ clear();
+ }
+ // returns the last track added (even though it may have been
+ // subsequently removed from ActiveTracks).
+ //
+ // Used for DirectOutputThread to ensure a flush is called when transitioning
+ // to a new track (even though it may be on the same session).
+ // Used for OffloadThread to ensure that volume and mixer state is
+ // taken from the latest track added.
+ //
+ // The latest track is saved with a weak pointer to prevent keeping an
+ // otherwise useless track alive. Thus the function will return nullptr
+ // if the latest track has subsequently been removed and destroyed.
+ sp<T> getLatest() {
+ return mLatestActiveTrack.promote();
+ }
+
+ // Updates ActiveTracks client uids to the thread wakelock.
+ void updateWakeLockUids(sp<ThreadBase> thread, bool force = false) {
+ if (mActiveTracksGeneration != mLastActiveTracksGeneration || force) {
+ thread->updateWakeLockUids_l(getWakeLockUids());
+ mLastActiveTracksGeneration = mActiveTracksGeneration;
+ }
+ }
+
+ // SortedVector methods
+ ssize_t add(const sp<T> &track);
+ ssize_t remove(const sp<T> &track);
+ size_t size() const {
+ return mActiveTracks.size();
+ }
+ ssize_t indexOf(const sp<T>& item) {
+ return mActiveTracks.indexOf(item);
+ }
+ sp<T> operator[](size_t index) const {
+ return mActiveTracks[index];
+ }
+ typename SortedVector<sp<T>>::iterator begin() {
+ return mActiveTracks.begin();
+ }
+ typename SortedVector<sp<T>>::iterator end() {
+ return mActiveTracks.end();
+ }
+ void clear();
+
+ private:
+ SortedVector<int> getWakeLockUids() {
+ SortedVector<int> wakeLockUids;
+ for (const sp<T> &track : mActiveTracks) {
+ wakeLockUids.add(track->uid());
+ }
+ return wakeLockUids; // moved by underlying SharedBuffer
+ }
+
+ SortedVector<sp<T>> mActiveTracks;
+ int mActiveTracksGeneration;
+ int mLastActiveTracksGeneration;
+ wp<T> mLatestActiveTrack; // latest track added to ActiveTracks
+ };
};
// --- PlaybackThread ---
@@ -559,6 +638,10 @@
virtual void preExit();
virtual bool keepWakeLock() const { return true; }
+ virtual void acquireWakeLock_l() {
+ ThreadBase::acquireWakeLock_l();
+ mActiveTracks.updateWakeLockUids(this, true /* force */);
+ }
public:
@@ -731,10 +814,7 @@
bool mMasterMute;
void setMasterMute_l(bool muted) { mMasterMute = muted; }
protected:
- SortedVector< wp<Track> > mActiveTracks; // FIXME check if this could be sp<>
- SortedVector<int> mWakeLockUids;
- int mActiveTracksGeneration;
- wp<Track> mLatestActiveTrack; // latest track added to mActiveTracks
+ ActiveTracks<Track> mActiveTracks;
// Allocate a track name for a given channel mask.
// Returns name >= 0 if successful, -1 on failure.
@@ -900,8 +980,8 @@
virtual uint32_t suspendSleepTimeUs() const;
virtual void cacheParameters_l();
- virtual void acquireWakeLock_l(int uid = -1) {
- PlaybackThread::acquireWakeLock_l(uid);
+ virtual void acquireWakeLock_l() {
+ PlaybackThread::acquireWakeLock_l();
if (hasFastMixer()) {
mFastMixer->setBoottimeOffset(
mTimestamp.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_BOOTTIME]);
@@ -1340,6 +1420,11 @@
virtual status_t checkEffectCompatibility_l(const effect_descriptor_t *desc,
audio_session_t sessionId);
+ virtual void acquireWakeLock_l() {
+ ThreadBase::acquireWakeLock_l();
+ mActiveTracks.updateWakeLockUids(this, true /* force */);
+ }
+
private:
// Enter standby if not already in standby, and set mStandby flag
void standbyIfNotAlreadyInStandby();
@@ -1351,9 +1436,8 @@
SortedVector < sp<RecordTrack> > mTracks;
// mActiveTracks has dual roles: it indicates the current active track(s), and
// is used together with mStartStopCond to indicate start()/stop() progress
- SortedVector< sp<RecordTrack> > mActiveTracks;
- // generation counter for mActiveTracks
- int mActiveTracksGen;
+ ActiveTracks<RecordTrack> mActiveTracks;
+
Condition mStartStopCond;
// resampler converts input at HAL Hz to output at AudioRecord client Hz