Make max fast tracks configurable using a property

ro.audio.max_fast_tracks

Rename the currently configured maximum number of fast tracks
from FastMixerState::kMaxFastTracks to FastMixerState::sMaxFastTracks.

There is no guarantee that the CPU will be able to handle
the configured number of fast tracks.

Bug: 27564141
Change-Id: If9af226d839b226503488c3cb20a4bb8950b429d
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 26cd1f9..546ef25 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -77,7 +77,7 @@
     mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount);
 
     unsigned i;
-    for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
+    for (i = 0; i < FastMixerState::sMaxFastTracks; ++i) {
         mFastTrackNames[i] = -1;
         mGenerations[i] = 0;
     }
@@ -187,7 +187,7 @@
             // FIXME new may block for unbounded time at internal mutex of the heap
             //       implementation; it would be better to have normal mixer allocate for us
             //       to avoid blocking here and to prevent possible priority inversion
-            mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::kMaxFastTracks);
+            mMixer = new AudioMixer(frameCount, mSampleRate, FastMixerState::sMaxFastTracks);
             const size_t mixerFrameSize = mSinkChannelCount
                     * audio_bytes_per_sample(mMixerBufferFormat);
             mMixerBufferSize = mixerFrameSize * frameCount;
@@ -214,7 +214,7 @@
         }
         mMixerBufferState = UNDEFINED;
 #if !LOG_NDEBUG
-        for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
+        for (unsigned i = 0; i < FastMixerState::sMaxFastTracks; ++i) {
             mFastTrackNames[i] = -1;
         }
 #endif
diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp
index b10942b..2326e2a 100644
--- a/services/audioflinger/FastMixerDumpState.cpp
+++ b/services/audioflinger/FastMixerDumpState.cpp
@@ -166,10 +166,10 @@
     // Instead we always display all tracks, with an indication
     // of whether we think the track is active.
     uint32_t trackMask = mTrackMask;
-    dprintf(fd, "  Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
-            FastMixerState::kMaxFastTracks, trackMask);
+    dprintf(fd, "  Fast tracks: sMaxFastTracks=%u activeMask=%#x\n",
+            FastMixerState::sMaxFastTracks, trackMask);
     dprintf(fd, "  Index Active Full Partial Empty  Recent Ready\n");
-    for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) {
+    for (uint32_t i = 0; i < FastMixerState::sMaxFastTracks; ++i, trackMask >>= 1) {
         bool isActive = trackMask & 1;
         const FastTrackDump *ftDump = &mTracks[i];
         const FastTrackUnderruns& underruns = ftDump->mUnderruns;
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index a8c2634..ad471fb 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cutils/properties.h>
 #include "FastMixerState.h"
 
 namespace android {
@@ -33,6 +34,10 @@
     mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0),
     mFrameCount(0), mTeeSink(NULL)
 {
+    int ok = pthread_once(&sMaxFastTracksOnce, sMaxFastTracksInit);
+    if (ok != 0) {
+        ALOGE("%s pthread_once failed: %d", __func__, ok);
+    }
 }
 
 FastMixerState::~FastMixerState()
@@ -40,6 +45,12 @@
 }
 
 // static
+unsigned FastMixerState::sMaxFastTracks = kDefaultFastTracks;
+
+// static
+pthread_once_t FastMixerState::sMaxFastTracksOnce = PTHREAD_ONCE_INIT;
+
+// static
 const char *FastMixerState::commandToString(Command command)
 {
     const char *str = FastThreadState::commandToString(command);
@@ -54,4 +65,18 @@
     LOG_ALWAYS_FATAL("%s", __func__);
 }
 
+// static
+void FastMixerState::sMaxFastTracksInit()
+{
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("ro.audio.max_fast_tracks", value, NULL) > 0) {
+        char *endptr;
+        unsigned long ul = strtoul(value, &endptr, 0);
+        if (*endptr == '\0' && kMinFastTracks <= ul && ul <= kMaxFastTracks) {
+            sMaxFastTracks = (unsigned) ul;
+        }
+    }
+    ALOGI("sMaxFastTracks = %u", sMaxFastTracks);
+}
+
 }   // namespace android
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 916514f..5a55c7a 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -54,7 +54,13 @@
                 FastMixerState();
     /*virtual*/ ~FastMixerState();
 
-    static const unsigned kMaxFastTracks = 8;   // must be between 2 and 32 inclusive
+    // These are the minimum, maximum, and default values for maximum number of fast tracks
+    static const unsigned kMinFastTracks = 2;
+    static const unsigned kMaxFastTracks = 32;
+    static const unsigned kDefaultFastTracks = 8;
+
+    static unsigned sMaxFastTracks;             // Configured maximum number of fast tracks
+    static pthread_once_t sMaxFastTracksOnce;   // Protects initializer for sMaxFastTracks
 
     // all pointer fields use raw pointers; objects are owned and ref-counted by the normal mixer
     FastTrack   mFastTracks[kMaxFastTracks];
@@ -76,6 +82,10 @@
 
     // never returns NULL; asserts if command is invalid
     static const char *commandToString(Command command);
+
+    // initialize sMaxFastTracks
+    static void sMaxFastTracksInit();
+
 };  // struct FastMixerState
 
 }   // namespace android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ff67fb2..60aea87 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1602,7 +1602,7 @@
         mSignalPending(false),
         mScreenState(AudioFlinger::mScreenState),
         // index 0 is reserved for normal mixer's submix
-        mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+        mFastTrackAvailMask(((1 << FastMixerState::sMaxFastTracks) - 1) & ~1),
         mHwSupportsPause(false), mHwPaused(false), mFlushPending(false)
 {
     snprintf(mThreadName, kThreadNameLength, "AudioOut_%X", id);
@@ -2103,7 +2103,7 @@
     track->mName = -1;
     if (track->isFastTrack()) {
         int index = track->mFastIndex;
-        ALOG_ASSERT(0 < index && index < (int)FastMixerState::kMaxFastTracks);
+        ALOG_ASSERT(0 < index && index < (int)FastMixerState::sMaxFastTracks);
         ALOG_ASSERT(!(mFastTrackAvailMask & (1 << index)));
         mFastTrackAvailMask |= 1 << index;
         // redundant as track is about to be destroyed, for dumpsys only
@@ -3859,7 +3859,7 @@
             // at the identical fast mixer slot within the same normal mix cycle,
             // is impossible because the slot isn't marked available until the end of each cycle.
             int j = track->mFastIndex;
-            ALOG_ASSERT(0 < j && j < (int)FastMixerState::kMaxFastTracks);
+            ALOG_ASSERT(0 < j && j < (int)FastMixerState::sMaxFastTracks);
             ALOG_ASSERT(!(mFastTrackAvailMask & (1 << j)));
             FastTrack *fastTrack = &state->mFastTracks[j];
 
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index e71840d..16238f8 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -913,7 +913,7 @@
 public:
     virtual     bool        hasFastMixer() const { return mFastMixer != 0; }
     virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const {
-                              ALOG_ASSERT(fastIndex < FastMixerState::kMaxFastTracks);
+                              ALOG_ASSERT(fastIndex < FastMixerState::sMaxFastTracks);
                               return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
                             }
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 61b30c1..9d430aa 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -402,7 +402,7 @@
         //mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
         ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
         int i = __builtin_ctz(thread->mFastTrackAvailMask);
-        ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);
+        ALOG_ASSERT(0 < i && i < (int)FastMixerState::sMaxFastTracks);
         // FIXME This is too eager.  We allocate a fast track index before the
         //       fast track becomes active.  Since fast tracks are a scarce resource,
         //       this means we are potentially denying other more important fast tracks from