am 32f93b7b: (-s ours) DO NOT MERGE: libstagefright: Add Opus support to ACodec::getPortFormat

* commit '32f93b7bedc44b425ba99d69dec998334e759532':
  DO NOT MERGE: libstagefright: Add Opus support to ACodec::getPortFormat
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 59dce91..3f72f34 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -349,18 +349,18 @@
 
     size_t size = mTagToNameMap.size();
     if (size == 0) {
-        fdprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
+        dprintf(fd, "%*sDumping configured vendor tag descriptors: None set\n",
                 indentation, "");
         return;
     }
 
-    fdprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
+    dprintf(fd, "%*sDumping configured vendor tag descriptors: %zu entries\n",
             indentation, "", size);
     for (size_t i = 0; i < size; ++i) {
         uint32_t tag =  mTagToNameMap.keyAt(i);
 
         if (verbosity < 1) {
-            fdprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
+            dprintf(fd, "%*s0x%x\n", indentation + 2, "", tag);
             continue;
         }
         String8 name = mTagToNameMap.valueAt(i);
@@ -369,7 +369,7 @@
         int type = mTagToTypeMap.valueFor(tag);
         const char* typeName = (type >= 0 && type < NUM_TYPES) ?
                 camera_metadata_type_names[type] : "UNKNOWN";
-        fdprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
+        dprintf(fd, "%*s0x%x (%s) with type %d (%s) defined in section %s\n", indentation + 2,
             "", tag, name.string(), type, typeName, sectionName.string());
     }
 
diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h
index 2f000d7..c07f4c9 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -163,17 +163,6 @@
 media_status_t AMediaCodec_releaseOutputBufferAtTime(
         AMediaCodec *mData, size_t idx, int64_t timestampNs);
 
-typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
-
-/**
- * Set a callback to be called when a new buffer is available, or there was a format
- * or buffer change.
- * Note that you cannot perform any operations on the mediacodec from within the callback.
- * If you need to perform mediacodec operations, you must do so on a different thread.
- */
-media_status_t AMediaCodec_setNotificationCallback(
-        AMediaCodec*, OnCodecEvent callback, void *userdata);
-
 
 typedef enum {
     AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 1c808d0..db61e85 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -203,23 +203,6 @@
         mFrameSize = sizeof(uint8_t);
     }
 
-    // validate framecount
-    size_t minFrameCount;
-    status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
-            sampleRate, format, channelMask);
-    if (status != NO_ERROR) {
-        ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; status %d",
-                sampleRate, format, channelMask, status);
-        return status;
-    }
-    ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
-
-    if (frameCount == 0) {
-        frameCount = minFrameCount;
-    } else if (frameCount < minFrameCount) {
-        ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
-        return BAD_VALUE;
-    }
     // mFrameCount is initialized in openRecord_l
     mReqFrameCount = frameCount;
 
@@ -242,7 +225,7 @@
     }
 
     // create the IAudioRecord
-    status = openRecord_l(0 /*epoch*/);
+    status_t status = openRecord_l(0 /*epoch*/);
 
     if (status != NO_ERROR) {
         if (mAudioRecordThread != 0) {
@@ -464,6 +447,29 @@
     size_t frameCount = mReqFrameCount;
 
     if (!(mFlags & AUDIO_INPUT_FLAG_FAST)) {
+        // validate framecount
+        // If fast track was not requested, this preserves
+        // the old behavior of validating on client side.
+        // FIXME Eventually the validation should be done on server side
+        // regardless of whether it's a fast or normal track.  It's debatable
+        // whether to account for the input latency to provision buffers appropriately.
+        size_t minFrameCount;
+        status = AudioRecord::getMinFrameCount(&minFrameCount,
+                mSampleRate, mFormat, mChannelMask);
+        if (status != NO_ERROR) {
+            ALOGE("getMinFrameCount() failed for sampleRate %u, format %#x, channelMask %#x; "
+                    "status %d",
+                    mSampleRate, mFormat, mChannelMask, status);
+            return status;
+        }
+
+        if (frameCount == 0) {
+            frameCount = minFrameCount;
+        } else if (frameCount < minFrameCount) {
+            ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
+            return BAD_VALUE;
+        }
+
         // Make sure that application is notified with sufficient margin before overrun
         if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
             mNotificationFramesAct = frameCount/2;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d8d939a..857e703 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1376,16 +1376,15 @@
 
             sp<NuPlayerDriver> driver = mDriver.promote();
             if (driver != NULL) {
+                // notify duration first, so that it's definitely set when
+                // the app received the "prepare complete" callback.
+                int64_t durationUs;
+                if (mSource->getDuration(&durationUs) == OK) {
+                    driver->notifyDuration(durationUs);
+                }
                 driver->notifyPrepareCompleted(err);
             }
 
-            int64_t durationUs;
-            if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
-                sp<NuPlayerDriver> driver = mDriver.promote();
-                if (driver != NULL) {
-                    driver->notifyDuration(durationUs);
-                }
-            }
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index e4850f0..280b5af 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -284,6 +284,10 @@
         case STATE_PREPARED:
         {
             mStartupSeekTimeUs = seekTimeUs;
+            // pretend that the seek completed. It will actually happen when starting playback.
+            // TODO: actually perform the seek here, so the player is ready to go at the new
+            // location
+            notifySeekComplete();
             break;
         }
 
diff --git a/media/libnbaio/NBLog.cpp b/media/libnbaio/NBLog.cpp
index 4d9a1fa..4d14904 100644
--- a/media/libnbaio/NBLog.cpp
+++ b/media/libnbaio/NBLog.cpp
@@ -438,7 +438,7 @@
 void NBLog::Reader::dumpLine(const String8& timestamp, String8& body)
 {
     if (mFd >= 0) {
-        fdprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
+        dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
     } else {
         ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
     }
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index 11b80bf..8af0880 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -27,7 +27,6 @@
 #include <media/stagefright/MetaData.h>
 
 #include <ui/GraphicBuffer.h>
-#include <sys/atomics.h>
 
 namespace android {
 
@@ -92,7 +91,7 @@
         return;
     }
 
-    int prevCount = __atomic_dec(&mRefCount);
+    int prevCount = __sync_fetch_and_sub(&mRefCount, 1);
     if (prevCount == 1) {
         if (mObserver == NULL) {
             delete this;
@@ -112,7 +111,7 @@
 }
 
 void MediaBuffer::add_ref() {
-    (void) __atomic_inc(&mRefCount);
+    (void) __sync_fetch_and_add(&mRefCount, 1);
 }
 
 void *MediaBuffer::data() const {
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index bd2541f..2ac16c7 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -61,6 +61,8 @@
     virtual void onMessageReceived(const sp<AMessage> &msg);
 };
 
+typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
+
 struct AMediaCodec {
     sp<android::MediaCodec> mCodec;
     sp<ALooper> mLooper;
@@ -347,7 +349,7 @@
     return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
 }
 
-EXPORT
+//EXPORT
 media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
     mData->mCallback = callback;
     mData->mCallbackUserData = userdata;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 457ac3d..5b09d54 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -430,7 +430,7 @@
         if (mLogMemoryDealer != 0) {
             sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
             if (binder != 0) {
-                fdprintf(fd, "\nmedia.log:\n");
+                dprintf(fd, "\nmedia.log:\n");
                 Vector<String16> args;
                 binder->dump(fd, args);
             }
@@ -2606,7 +2606,7 @@
             }
         } else {
             if (fd >= 0) {
-                fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
+                dprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
             }
         }
         char teeTime[16];
@@ -2660,11 +2660,11 @@
             write(teeFd, &temp, sizeof(temp));
             close(teeFd);
             if (fd >= 0) {
-                fdprintf(fd, "tee copied to %s\n", teePath);
+                dprintf(fd, "tee copied to %s\n", teePath);
             }
         } else {
             if (fd >= 0) {
-                fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
+                dprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
             }
         }
     }
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 805eaa4..8d57451 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -34,6 +34,7 @@
 #include <system/audio.h>
 
 #include <audio_utils/primitives.h>
+#include <audio_utils/format.h>
 #include <common_time/local_clock.h>
 #include <common_time/cc_helper.h>
 
@@ -88,6 +89,103 @@
     }
 }
 
+template <typename T>
+T min(const T& a, const T& b)
+{
+    return a < b ? a : b;
+}
+
+AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
+        audio_format_t inputFormat, audio_format_t outputFormat) :
+        mTrackBufferProvider(NULL),
+        mChannels(channels),
+        mInputFormat(inputFormat),
+        mOutputFormat(outputFormat),
+        mInputFrameSize(channels * audio_bytes_per_sample(inputFormat)),
+        mOutputFrameSize(channels * audio_bytes_per_sample(outputFormat)),
+        mOutputData(NULL),
+        mOutputCount(0),
+        mConsumed(0)
+{
+    ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
+    if (requiresInternalBuffers()) {
+        mOutputCount = 256;
+        (void)posix_memalign(&mOutputData, 32, mOutputCount * mOutputFrameSize);
+    }
+    mBuffer.frameCount = 0;
+}
+
+AudioMixer::ReformatBufferProvider::~ReformatBufferProvider()
+{
+    ALOGV("~ReformatBufferProvider(%p)", this);
+    if (mBuffer.frameCount != 0) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+    }
+    free(mOutputData);
+}
+
+status_t AudioMixer::ReformatBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+        int64_t pts) {
+    //ALOGV("ReformatBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+    //        this, pBuffer, pBuffer->frameCount, pts);
+    if (!requiresInternalBuffers()) {
+        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+        if (res == OK) {
+            memcpy_by_audio_format(pBuffer->raw, mOutputFormat, pBuffer->raw, mInputFormat,
+                    pBuffer->frameCount * mChannels);
+        }
+        return res;
+    }
+    if (mBuffer.frameCount == 0) {
+        mBuffer.frameCount = pBuffer->frameCount;
+        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+        // TODO: Track down a bug in the upstream provider
+        // LOG_ALWAYS_FATAL_IF(res == OK && mBuffer.frameCount == 0,
+        //        "ReformatBufferProvider::getNextBuffer():"
+        //        " Invalid zero framecount returned from getNextBuffer()");
+        if (res != OK || mBuffer.frameCount == 0) {
+            pBuffer->raw = NULL;
+            pBuffer->frameCount = 0;
+            return res;
+        }
+    }
+    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
+    size_t count = min(mOutputCount, mBuffer.frameCount - mConsumed);
+    count = min(count, pBuffer->frameCount);
+    pBuffer->raw = mOutputData;
+    pBuffer->frameCount = count;
+    //ALOGV("reformatting %d frames from %#x to %#x, %d chan",
+    //        pBuffer->frameCount, mInputFormat, mOutputFormat, mChannels);
+    memcpy_by_audio_format(pBuffer->raw, mOutputFormat,
+            (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, mInputFormat,
+            pBuffer->frameCount * mChannels);
+    return OK;
+}
+
+void AudioMixer::ReformatBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
+    //ALOGV("ReformatBufferProvider(%p)::releaseBuffer(%p(%zu))",
+    //        this, pBuffer, pBuffer->frameCount);
+    if (!requiresInternalBuffers()) {
+        mTrackBufferProvider->releaseBuffer(pBuffer);
+        return;
+    }
+    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
+    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
+        mConsumed = 0;
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+        // ALOG_ASSERT(mBuffer.frameCount == 0);
+    }
+    pBuffer->raw = NULL;
+    pBuffer->frameCount = 0;
+}
+
+void AudioMixer::ReformatBufferProvider::reset() {
+    if (mBuffer.frameCount != 0) {
+        mTrackBufferProvider->releaseBuffer(&mBuffer);
+    }
+    mConsumed = 0;
+}
 
 // ----------------------------------------------------------------------------
 bool AudioMixer::sIsMultichannelCapable = false;
@@ -153,8 +251,13 @@
     mState.mLog = log;
 }
 
-int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
+int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
+        audio_format_t format, int sessionId)
 {
+    if (!isValidPcmTrackFormat(format)) {
+        ALOGE("AudioMixer::getTrackName invalid format (%#x)", format);
+        return -1;
+    }
     uint32_t names = (~mTrackNames) & mConfiguredNames;
     if (names != 0) {
         int n = __builtin_ctz(names);
@@ -176,7 +279,8 @@
         // t->frameCount
         t->channelCount = audio_channel_count_from_out_mask(channelMask);
         t->enabled = false;
-        t->format = 16;
+        ALOGV_IF(channelMask != AUDIO_CHANNEL_OUT_STEREO,
+                "Non-stereo channel mask: %d\n", channelMask);
         t->channelMask = channelMask;
         t->sessionId = sessionId;
         // setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
@@ -191,9 +295,15 @@
         // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
         t->mainBuffer = NULL;
         t->auxBuffer = NULL;
+        t->mInputBufferProvider = NULL;
+        t->mReformatBufferProvider = NULL;
         t->downmixerBufferProvider = NULL;
         t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
-
+        t->mFormat = format;
+        t->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT;
+        if (t->mFormat != t->mMixerInFormat) {
+            prepareTrackForReformat(t, n);
+        }
         status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
         if (status != OK) {
             ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
@@ -237,9 +347,9 @@
     if (pTrack->downmixerBufferProvider != NULL) {
         // this track had previously been configured with a downmixer, delete it
         ALOGV(" deleting old downmixer");
-        pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider;
         delete pTrack->downmixerBufferProvider;
         pTrack->downmixerBufferProvider = NULL;
+        reconfigureBufferProviders(pTrack);
     } else {
         ALOGV(" nothing to do, no downmixer to delete");
     }
@@ -333,21 +443,51 @@
     }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack"
 
     // initialization successful:
-    // - keep track of the real buffer provider in case it was set before
-    pDbp->mTrackBufferProvider = pTrack->bufferProvider;
-    // - we'll use the downmix effect integrated inside this
-    //    track's buffer provider, and we'll use it as the track's buffer provider
     pTrack->downmixerBufferProvider = pDbp;
-    pTrack->bufferProvider = pDbp;
-
+    reconfigureBufferProviders(pTrack);
     return NO_ERROR;
 
 noDownmixForActiveTrack:
     delete pDbp;
     pTrack->downmixerBufferProvider = NULL;
+    reconfigureBufferProviders(pTrack);
     return NO_INIT;
 }
 
+void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) {
+    ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName);
+    if (pTrack->mReformatBufferProvider != NULL) {
+        delete pTrack->mReformatBufferProvider;
+        pTrack->mReformatBufferProvider = NULL;
+        reconfigureBufferProviders(pTrack);
+    }
+}
+
+status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName)
+{
+    ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat);
+    // discard the previous reformatter if there was one
+     unprepareTrackForReformat(pTrack, trackName);
+     pTrack->mReformatBufferProvider = new ReformatBufferProvider(
+             audio_channel_count_from_out_mask(pTrack->channelMask),
+             pTrack->mFormat, pTrack->mMixerInFormat);
+     reconfigureBufferProviders(pTrack);
+     return NO_ERROR;
+}
+
+void AudioMixer::reconfigureBufferProviders(track_t* pTrack)
+{
+    pTrack->bufferProvider = pTrack->mInputBufferProvider;
+    if (pTrack->mReformatBufferProvider) {
+        pTrack->mReformatBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
+        pTrack->bufferProvider = pTrack->mReformatBufferProvider;
+    }
+    if (pTrack->downmixerBufferProvider) {
+        pTrack->downmixerBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
+        pTrack->bufferProvider = pTrack->downmixerBufferProvider;
+    }
+}
+
 void AudioMixer::deleteTrackName(int name)
 {
     ALOGV("AudioMixer::deleteTrackName(%d)", name);
@@ -364,6 +504,8 @@
     track.resampler = NULL;
     // delete the downmixer
     unprepareTrackForDownmix(&mState.tracks[name], name);
+    // delete the reformatter
+    unprepareTrackForReformat(&mState.tracks[name], name);
 
     mTrackNames &= ~(1<<name);
 }
@@ -435,9 +577,20 @@
                 invalidateState(1 << name);
             }
             break;
-        case FORMAT:
-            ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT);
-            break;
+        case FORMAT: {
+            audio_format_t format = static_cast<audio_format_t>(valueInt);
+            if (track.mFormat != format) {
+                ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
+                track.mFormat = format;
+                ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
+                //if (track.mFormat != track.mMixerInFormat)
+                {
+                    ALOGD("Reformatting!");
+                    prepareTrackForReformat(&track, name);
+                }
+                invalidateState(1 << name);
+            }
+            } break;
         // FIXME do we want to support setting the downmix type from AudioFlinger?
         //         for a specific track? or per mixer?
         /* case DOWNMIX_TYPE:
@@ -550,8 +703,9 @@
                 } else {
                     quality = AudioResampler::DEFAULT_QUALITY;
                 }
+                const int bits = mMixerInFormat == AUDIO_FORMAT_PCM_16_BIT ? 16 : /* FLOAT */ 32;
                 resampler = AudioResampler::create(
-                        format,
+                        bits,
                         // the resampler sees the number of channels after the downmixer, if any
                         (int) (downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount),
                         devSampleRate, quality);
@@ -596,21 +750,13 @@
     name -= TRACK0;
     ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
 
-    if (mState.tracks[name].downmixerBufferProvider != NULL) {
-        // update required?
-        if (mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider != bufferProvider) {
-            ALOGV("AudioMixer::setBufferProvider(%p) for downmix", bufferProvider);
-            // setting the buffer provider for a track that gets downmixed consists in:
-            //  1/ setting the buffer provider to the "downmix / buffer provider" wrapper
-            //     so it's the one that gets called when the buffer provider is needed,
-            mState.tracks[name].bufferProvider = mState.tracks[name].downmixerBufferProvider;
-            //  2/ saving the buffer provider for the track so the wrapper can use it
-            //     when it downmixes.
-            mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider = bufferProvider;
-        }
-    } else {
-        mState.tracks[name].bufferProvider = bufferProvider;
+    if (mState.tracks[name].mReformatBufferProvider != NULL) {
+        mState.tracks[name].mReformatBufferProvider->reset();
+    } else if (mState.tracks[name].downmixerBufferProvider != NULL) {
     }
+
+    mState.tracks[name].mInputBufferProvider = bufferProvider;
+    reconfigureBufferProviders(&mState.tracks[name]);
 }
 
 
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 09e63a6..573ba96 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -104,7 +104,10 @@
     // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
 
     // Allocate a track name.  Returns new track name if successful, -1 on failure.
-    int         getTrackName(audio_channel_mask_t channelMask, int sessionId);
+    // The failure could be because of an invalid channelMask or format, or that
+    // the track capacity of the mixer is exceeded.
+    int         getTrackName(audio_channel_mask_t channelMask,
+                             audio_format_t format, int sessionId);
 
     // Free an allocated track by name
     void        deleteTrackName(int name);
@@ -122,6 +125,13 @@
 
     size_t      getUnreleasedFrames(int name) const;
 
+    static inline bool isValidPcmTrackFormat(audio_format_t format) {
+        return format == AUDIO_FORMAT_PCM_16_BIT ||
+                format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
+                format == AUDIO_FORMAT_PCM_32_BIT ||
+                format == AUDIO_FORMAT_PCM_FLOAT;
+    }
+
 private:
 
     enum {
@@ -143,6 +153,7 @@
     struct state_t;
     struct track_t;
     class DownmixerBufferProvider;
+    class ReformatBufferProvider;
 
     typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
                            int32_t* aux);
@@ -170,7 +181,7 @@
         uint16_t    frameCount;
 
         uint8_t     channelCount;   // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
-        uint8_t     format;         // always 16
+        uint8_t     unused_padding; // formerly format, was always 16
         uint16_t    enabled;        // actually bool
         audio_channel_mask_t channelMask;
 
@@ -193,14 +204,19 @@
         int32_t*           auxBuffer;
 
         // 16-byte boundary
-
+        AudioBufferProvider*     mInputBufferProvider;    // 4 bytes
+        ReformatBufferProvider*  mReformatBufferProvider; // 4 bytes
         DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
 
         int32_t     sessionId;
 
-        audio_format_t mMixerFormat; // at this time: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+        // 16-byte boundary
+        audio_format_t mMixerFormat;     // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+        audio_format_t mFormat;          // input track format
+        audio_format_t mMixerInFormat;   // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+                                         // each track must be converted to this format.
 
-        int32_t     padding[1];
+        int32_t        mUnused[1];       // alignment padding
 
         // 16-byte boundary
 
@@ -239,6 +255,35 @@
         effect_config_t    mDownmixConfig;
     };
 
+    // AudioBufferProvider wrapper that reformats track to acceptable mixer input type
+    class ReformatBufferProvider : public AudioBufferProvider {
+    public:
+        ReformatBufferProvider(int32_t channels,
+                audio_format_t inputFormat, audio_format_t outputFormat);
+        virtual ~ReformatBufferProvider();
+
+        // overrides AudioBufferProvider methods
+        virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+        virtual void releaseBuffer(Buffer* buffer);
+
+        void reset();
+        inline bool requiresInternalBuffers() {
+            return true; //mInputFrameSize < mOutputFrameSize;
+        }
+
+        AudioBufferProvider* mTrackBufferProvider;
+        int32_t              mChannels;
+        audio_format_t       mInputFormat;
+        audio_format_t       mOutputFormat;
+        size_t               mInputFrameSize;
+        size_t               mOutputFrameSize;
+        // (only) required for reformatting to a larger size.
+        AudioBufferProvider::Buffer mBuffer;
+        void*                mOutputData;
+        size_t               mOutputCount;
+        size_t               mConsumed;
+    };
+
     // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
     uint32_t        mTrackNames;
 
@@ -266,6 +311,9 @@
     static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask);
     static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum);
     static void unprepareTrackForDownmix(track_t* pTrack, int trackName);
+    static status_t prepareTrackForReformat(track_t* pTrack, int trackNum);
+    static void unprepareTrackForReformat(track_t* pTrack, int trackName);
+    static void reconfigureBufferProviders(track_t* pTrack);
 
     static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
             int32_t* aux);
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index 3abe8fd..a4446a4 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -455,12 +455,13 @@
     const Constants& c(mConstants);
     const TC* const coefs = mConstants.mFirCoefs;
     TI* impulse = mInBuffer.getImpulse();
-    size_t inputIndex = mInputIndex;
+    size_t inputIndex = 0;
     uint32_t phaseFraction = mPhaseFraction;
     const uint32_t phaseIncrement = mPhaseIncrement;
     size_t outputIndex = 0;
     size_t outputSampleCount = outFrameCount * 2;   // stereo output
-    size_t inFrameCount = getInFrameCountRequired(outFrameCount);
+    size_t inFrameCount = getInFrameCountRequired(outFrameCount) + (phaseFraction != 0);
+    ALOG_ASSERT(0 < inFrameCount && inFrameCount < (1U << 31));
     const uint32_t phaseWrapLimit = c.mL << c.mShift;
 
     // NOTE: be very careful when modifying the code here. register
@@ -474,11 +475,13 @@
         // buffer is empty, fetch a new one
         while (mBuffer.frameCount == 0) {
             mBuffer.frameCount = inFrameCount;
+            ALOG_ASSERT(inFrameCount > 0);
             provider->getNextBuffer(&mBuffer,
                     calculateOutputPTS(outputIndex / 2));
             if (mBuffer.raw == NULL) {
                 goto resample_exit;
             }
+            inFrameCount -= mBuffer.frameCount;
             if (phaseFraction >= phaseWrapLimit) { // read in data
                 mInBuffer.template readAdvance<CHANNELS>(
                         impulse, c.mHalfNumCoefs,
@@ -487,7 +490,7 @@
                 while (phaseFraction >= phaseWrapLimit) {
                     inputIndex++;
                     if (inputIndex >= mBuffer.frameCount) {
-                        inputIndex -= mBuffer.frameCount;
+                        inputIndex = 0;
                         provider->releaseBuffer(&mBuffer);
                         break;
                     }
@@ -535,15 +538,22 @@
 done:
         // often arrives here when input buffer runs out
         if (inputIndex >= frameCount) {
-            inputIndex -= frameCount;
+            inputIndex = 0;
             provider->releaseBuffer(&mBuffer);
-            // mBuffer.frameCount MUST be zero here.
+            ALOG_ASSERT(mBuffer.frameCount == 0);
         }
     }
 
 resample_exit:
+    // Release frames to avoid the count being inaccurate for pts timing.
+    // TODO: Avoid this extra check by making fetch count exact. This is tricky
+    // due to the overfetching mechanism which loads unnecessarily when
+    // mBuffer.frameCount == 0.
+    if (inputIndex) {
+        mBuffer.frameCount = inputIndex;
+        provider->releaseBuffer(&mBuffer);
+    }
     mInBuffer.setImpulse(impulse);
-    mInputIndex = inputIndex;
     mPhaseFraction = phaseFraction;
 }
 
diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp
index 93d185e..877e776 100644
--- a/services/audioflinger/AudioWatchdog.cpp
+++ b/services/audioflinger/AudioWatchdog.cpp
@@ -34,7 +34,7 @@
     } else {
         strcpy(buf, "N/A\n");
     }
-    fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
+    dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s",
             mUnderruns, mLogs, buf);
 }
 
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 1caed11..c840418 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -26,7 +26,6 @@
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
 #include "Configuration.h"
-#include <sys/atomics.h>
 #include <time.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
@@ -53,8 +52,8 @@
     outputSink(NULL),
     outputSinkGen(0),
     mixer(NULL),
-    mixBuffer(NULL),
-    mixBufferState(UNDEFINED),
+    mMixerBuffer(NULL),
+    mMixerBufferState(UNDEFINED),
     format(Format_Invalid),
     sampleRate(0),
     fastTracksGen(0),
@@ -109,7 +108,7 @@
 void FastMixer::onExit()
 {
     delete mixer;
-    delete[] mixBuffer;
+    delete[] mMixerBuffer;
 }
 
 bool FastMixer::isSubClassCommand(FastThreadState::Command command)
@@ -155,14 +154,14 @@
         // FIXME to avoid priority inversion, don't delete here
         delete mixer;
         mixer = NULL;
-        delete[] mixBuffer;
-        mixBuffer = NULL;
+        delete[] mMixerBuffer;
+        mMixerBuffer = NULL;
         if (frameCount > 0 && sampleRate > 0) {
             // 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
             mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
-            mixBuffer = new short[frameCount * FCC_2];
+            mMixerBuffer = new short[frameCount * FCC_2];
             periodNs = (frameCount * 1000000000LL) / sampleRate;    // 1.00
             underrunNs = (frameCount * 1750000000LL) / sampleRate;  // 1.75
             overrunNs = (frameCount * 500000000LL) / sampleRate;    // 0.50
@@ -175,7 +174,7 @@
             forceNs = 0;
             warmupNs = 0;
         }
-        mixBufferState = UNDEFINED;
+        mMixerBufferState = UNDEFINED;
 #if !LOG_NDEBUG
         for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
             fastTrackNames[i] = -1;
@@ -193,7 +192,7 @@
     const unsigned currentTrackMask = current->mTrackMask;
     dumpState->mTrackMask = currentTrackMask;
     if (current->mFastTracksGen != fastTracksGen) {
-        ALOG_ASSERT(mixBuffer != NULL);
+        ALOG_ASSERT(mMixerBuffer != NULL);
         int name;
 
         // process removed tracks first to avoid running out of track names
@@ -224,13 +223,16 @@
             AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
             ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1);
             if (mixer != NULL) {
-                name = mixer->getTrackName(fastTrack->mChannelMask, AUDIO_SESSION_OUTPUT_MIX);
+                name = mixer->getTrackName(fastTrack->mChannelMask,
+                        fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
                 ALOG_ASSERT(name >= 0);
                 fastTrackNames[i] = name;
                 mixer->setBufferProvider(name, bufferProvider);
                 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
-                        (void *) mixBuffer);
+                        (void *) mMixerBuffer);
                 // newly allocated track names default to full scale volume
+                mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+                        (void *)(uintptr_t)fastTrack->mFormat);
                 mixer->enable(name);
             }
             generations[i] = fastTrack->mGeneration;
@@ -259,6 +261,8 @@
                     }
                     mixer->setParameter(name, AudioMixer::RESAMPLE,
                             AudioMixer::REMOVE, NULL);
+                    mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
+                            (void *)(uintptr_t)fastTrack->mFormat);
                     mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
                             (void *)(uintptr_t) fastTrack->mChannelMask);
                     // already enabled
@@ -281,7 +285,7 @@
     const size_t frameCount = current->mFrameCount;
 
     if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) {
-        ALOG_ASSERT(mixBuffer != NULL);
+        ALOG_ASSERT(mMixerBuffer != NULL);
         // for each track, update volume and check for underrun
         unsigned currentTrackMask = current->mTrackMask;
         while (currentTrackMask != 0) {
@@ -358,26 +362,26 @@
 
         // process() is CPU-bound
         mixer->process(pts);
-        mixBufferState = MIXED;
-    } else if (mixBufferState == MIXED) {
-        mixBufferState = UNDEFINED;
+        mMixerBufferState = MIXED;
+    } else if (mMixerBufferState == MIXED) {
+        mMixerBufferState = UNDEFINED;
     }
     //bool didFullWrite = false;    // dumpsys could display a count of partial writes
-    if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) {
-        if (mixBufferState == UNDEFINED) {
-            memset(mixBuffer, 0, frameCount * FCC_2 * sizeof(short));
-            mixBufferState = ZEROED;
+    if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) {
+        if (mMixerBufferState == UNDEFINED) {
+            memset(mMixerBuffer, 0, frameCount * FCC_2 * sizeof(short));
+            mMixerBufferState = ZEROED;
         }
         // if non-NULL, then duplicate write() to this non-blocking sink
         NBAIO_Sink* teeSink;
         if ((teeSink = current->mTeeSink) != NULL) {
-            (void) teeSink->write(mixBuffer, frameCount);
+            (void) teeSink->write(mMixerBuffer, frameCount);
         }
         // 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++;
         ATRACE_BEGIN("write");
-        ssize_t framesWritten = outputSink->write(mixBuffer, frameCount);
+        ssize_t framesWritten = outputSink->write(mMixerBuffer, frameCount);
         ATRACE_END();
         dumpState->mWriteSequence++;
         if (framesWritten >= 0) {
@@ -461,7 +465,7 @@
 void FastMixerDumpState::dump(int fd) const
 {
     if (mCommand == FastMixerState::INITIAL) {
-        fdprintf(fd, "  FastMixer not initialized\n");
+        dprintf(fd, "  FastMixer not initialized\n");
         return;
     }
 #define COMMAND_MAX 32
@@ -495,10 +499,10 @@
     double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
             (mMeasuredWarmupTs.tv_nsec / 1000000.0);
     double mixPeriodSec = (double) mFrameCount / (double) mSampleRate;
-    fdprintf(fd, "  FastMixer command=%s writeSequence=%u framesWritten=%u\n"
-                 "            numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
-                 "            sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
-                 "            mixPeriod=%.2f ms\n",
+    dprintf(fd, "  FastMixer command=%s writeSequence=%u framesWritten=%u\n"
+                "            numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
+                "            sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
+                "            mixPeriod=%.2f ms\n",
                  string, mWriteSequence, mFramesWritten,
                  mNumTracks, mWriteErrors, mUnderruns, mOverruns,
                  mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
@@ -550,26 +554,26 @@
 #endif
     }
     if (n) {
-        fdprintf(fd, "  Simple moving statistics over last %.1f seconds:\n",
-                     wall.n() * mixPeriodSec);
-        fdprintf(fd, "    wall clock time in ms per mix cycle:\n"
-                     "      mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
-                     wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6,
-                     wall.stddev()*1e-6);
-        fdprintf(fd, "    raw CPU load in us per mix cycle:\n"
-                     "      mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
-                     loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
-                     loadNs.stddev()*1e-3);
+        dprintf(fd, "  Simple moving statistics over last %.1f seconds:\n",
+                    wall.n() * mixPeriodSec);
+        dprintf(fd, "    wall clock time in ms per mix cycle:\n"
+                    "      mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+                    wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6,
+                    wall.stddev()*1e-6);
+        dprintf(fd, "    raw CPU load in us per mix cycle:\n"
+                    "      mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
+                    loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3,
+                    loadNs.stddev()*1e-3);
     } else {
-        fdprintf(fd, "  No FastMixer statistics available currently\n");
+        dprintf(fd, "  No FastMixer statistics available currently\n");
     }
 #ifdef CPU_FREQUENCY_STATISTICS
-    fdprintf(fd, "  CPU clock frequency in MHz:\n"
-                 "    mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
-                 kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3);
-    fdprintf(fd, "  adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n"
-                 "    mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
-                 loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
+    dprintf(fd, "  CPU clock frequency in MHz:\n"
+                "    mean=%.0f min=%.0f max=%.0f stddev=%.0f\n",
+                kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3);
+    dprintf(fd, "  adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n"
+                "    mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
+                loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev());
 #endif
     if (tail != NULL) {
         qsort(tail, n, sizeof(uint32_t), compare_uint32_t);
@@ -580,12 +584,12 @@
             left.sample(tail[i]);
             right.sample(tail[n - (i + 1)]);
         }
-        fdprintf(fd, "  Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
-                     "    left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
-                     "    right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
-                     left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
-                     right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
-                     right.stddev()*1e-6);
+        dprintf(fd, "  Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n"
+                    "    left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n"
+                    "    right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n",
+                    left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6,
+                    right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6,
+                    right.stddev()*1e-6);
         delete[] tail;
     }
 #endif
@@ -595,9 +599,9 @@
     // Instead we always display all tracks, with an indication
     // of whether we think the track is active.
     uint32_t trackMask = mTrackMask;
-    fdprintf(fd, "  Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
+    dprintf(fd, "  Fast tracks: kMaxFastTracks=%u activeMask=%#x\n",
             FastMixerState::kMaxFastTracks, trackMask);
-    fdprintf(fd, "  Index Active Full Partial Empty  Recent Ready\n");
+    dprintf(fd, "  Index Active Full Partial Empty  Recent Ready\n");
     for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) {
         bool isActive = trackMask & 1;
         const FastTrackDump *ftDump = &mTracks[i];
@@ -617,7 +621,7 @@
             mostRecent = "?";
             break;
         }
-        fdprintf(fd, "  %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
+        dprintf(fd, "  %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
                 (underruns.mBitFields.mFull) & UNDERRUN_MASK,
                 (underruns.mBitFields.mPartial) & UNDERRUN_MASK,
                 (underruns.mBitFields.mEmpty) & UNDERRUN_MASK,
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index db89ef4..db3e2c9 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -61,8 +61,8 @@
     NBAIO_Sink *outputSink;
     int outputSinkGen;
     AudioMixer* mixer;
-    short *mixBuffer;
-    enum {UNDEFINED, MIXED, ZEROED} mixBufferState;
+    short *mMixerBuffer;
+    enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState;
     NBAIO_Format format;
     unsigned sampleRate;
     int fastTracksGen;
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 8e6d0d4..3aa8dad 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -20,7 +20,7 @@
 
 FastTrack::FastTrack() :
     mBufferProvider(NULL), mVolumeProvider(NULL),
-    mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mGeneration(0)
+    mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mFormat(AUDIO_FORMAT_INVALID), mGeneration(0)
 {
 }
 
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index e388fb3..661c9ca 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -45,6 +45,7 @@
     ExtendedAudioBufferProvider* mBufferProvider; // must be NULL if inactive, or non-NULL if active
     VolumeProvider*         mVolumeProvider; // optional; if NULL then full-scale
     audio_channel_mask_t    mChannelMask;    // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO
+    audio_format_t          mFormat;         // track format
     int                     mGeneration;     // increment when any field is assigned
 };
 
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index 48399c0..7e01c9f 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -28,12 +28,12 @@
 #ifdef STATE_QUEUE_DUMP
 void StateQueueObserverDump::dump(int fd)
 {
-    fdprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
+    dprintf(fd, "State queue observer: stateChanges=%u\n", mStateChanges);
 }
 
 void StateQueueMutatorDump::dump(int fd)
 {
-    fdprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
+    dprintf(fd, "State queue mutator: pushDirty=%u pushAck=%u blockedSequence=%u\n",
             mPushDirty, mPushAck, mBlockedSequence);
 }
 #endif
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 4972c7a..576350e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -142,8 +142,17 @@
 // FIXME It would be better for client to tell AudioFlinger the value of N,
 // so AudioFlinger could allocate the right amount of memory.
 // See the client's minBufCount and mNotificationFramesAct calculations for details.
+
+// This is the default value, if not specified by property.
 static const int kFastTrackMultiplier = 2;
 
+// The minimum and maximum allowed values
+static const int kFastTrackMultiplierMin = 1;
+static const int kFastTrackMultiplierMax = 2;
+
+// The actual value to use, which can be specified per-device via property af.fast_track_multiplier.
+static int sFastTrackMultiplier = kFastTrackMultiplier;
+
 // See Thread::readOnlyHeap().
 // Initially this heap is used to allocate client buffers for "fast" AudioRecord.
 // Eventually it will be the single buffer that FastCapture writes into via HAL read(),
@@ -152,6 +161,22 @@
 
 // ----------------------------------------------------------------------------
 
+static pthread_once_t sFastTrackMultiplierOnce = PTHREAD_ONCE_INIT;
+
+static void sFastTrackMultiplierInit()
+{
+    char value[PROPERTY_VALUE_MAX];
+    if (property_get("af.fast_track_multiplier", value, NULL) > 0) {
+        char *endptr;
+        unsigned long ul = strtoul(value, &endptr, 0);
+        if (*endptr == '\0' && kFastTrackMultiplierMin <= ul && ul <= kFastTrackMultiplierMax) {
+            sFastTrackMultiplier = (int) ul;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------------
+
 #ifdef ADD_BATTERY_DATA
 // To collect the amplifier usage
 static void addBatteryData(uint32_t params) {
@@ -539,30 +564,30 @@
 
     bool locked = AudioFlinger::dumpTryLock(mLock);
     if (!locked) {
-        fdprintf(fd, "thread %p maybe dead locked\n", this);
+        dprintf(fd, "thread %p maybe dead locked\n", this);
     }
 
-    fdprintf(fd, "  I/O handle: %d\n", mId);
-    fdprintf(fd, "  TID: %d\n", getTid());
-    fdprintf(fd, "  Standby: %s\n", mStandby ? "yes" : "no");
-    fdprintf(fd, "  Sample rate: %u\n", mSampleRate);
-    fdprintf(fd, "  HAL frame count: %zu\n", mFrameCount);
-    fdprintf(fd, "  HAL buffer size: %u bytes\n", mBufferSize);
-    fdprintf(fd, "  Channel Count: %u\n", mChannelCount);
-    fdprintf(fd, "  Channel Mask: 0x%08x (%s)\n", mChannelMask,
+    dprintf(fd, "  I/O handle: %d\n", mId);
+    dprintf(fd, "  TID: %d\n", getTid());
+    dprintf(fd, "  Standby: %s\n", mStandby ? "yes" : "no");
+    dprintf(fd, "  Sample rate: %u\n", mSampleRate);
+    dprintf(fd, "  HAL frame count: %zu\n", mFrameCount);
+    dprintf(fd, "  HAL buffer size: %u bytes\n", mBufferSize);
+    dprintf(fd, "  Channel Count: %u\n", mChannelCount);
+    dprintf(fd, "  Channel Mask: 0x%08x (%s)\n", mChannelMask,
             channelMaskToString(mChannelMask, mType != RECORD).string());
-    fdprintf(fd, "  Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
-    fdprintf(fd, "  Frame size: %zu\n", mFrameSize);
-    fdprintf(fd, "  Pending config events:");
+    dprintf(fd, "  Format: 0x%x (%s)\n", mFormat, formatToString(mFormat));
+    dprintf(fd, "  Frame size: %zu\n", mFrameSize);
+    dprintf(fd, "  Pending config events:");
     size_t numConfig = mConfigEvents.size();
     if (numConfig) {
         for (size_t i = 0; i < numConfig; i++) {
             mConfigEvents[i]->dump(buffer, SIZE);
-            fdprintf(fd, "\n    %s", buffer);
+            dprintf(fd, "\n    %s", buffer);
         }
-        fdprintf(fd, "\n");
+        dprintf(fd, "\n");
     } else {
-        fdprintf(fd, " none\n");
+        dprintf(fd, " none\n");
     }
 
     if (locked) {
@@ -1225,15 +1250,15 @@
 
     // These values are "raw"; they will wrap around.  See prepareTracks_l() for a better way.
     FastTrackUnderruns underruns = getFastTrackUnderruns(0);
-    fdprintf(fd, "  Normal mixer raw underrun counters: partial=%u empty=%u\n",
+    dprintf(fd, "  Normal mixer raw underrun counters: partial=%u empty=%u\n",
             underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
 
     size_t numtracks = mTracks.size();
     size_t numactive = mActiveTracks.size();
-    fdprintf(fd, "  %d Tracks", numtracks);
+    dprintf(fd, "  %d Tracks", numtracks);
     size_t numactiveseen = 0;
     if (numtracks) {
-        fdprintf(fd, " of which %d are active\n", numactive);
+        dprintf(fd, " of which %d are active\n", numactive);
         Track::appendDumpHeader(result);
         for (size_t i = 0; i < numtracks; ++i) {
             sp<Track> track = mTracks[i];
@@ -1265,22 +1290,21 @@
     }
 
     write(fd, result.string(), result.size());
-
 }
 
 void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
 {
-    fdprintf(fd, "\nOutput thread %p:\n", this);
-    fdprintf(fd, "  Normal frame count: %zu\n", mNormalFrameCount);
-    fdprintf(fd, "  Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
-    fdprintf(fd, "  Total writes: %d\n", mNumWrites);
-    fdprintf(fd, "  Delayed writes: %d\n", mNumDelayedWrites);
-    fdprintf(fd, "  Blocked in write: %s\n", mInWrite ? "yes" : "no");
-    fdprintf(fd, "  Suspend count: %d\n", mSuspended);
-    fdprintf(fd, "  Sink buffer : %p\n", mSinkBuffer);
-    fdprintf(fd, "  Mixer buffer: %p\n", mMixerBuffer);
-    fdprintf(fd, "  Effect buffer: %p\n", mEffectBuffer);
-    fdprintf(fd, "  Fast track availMask=%#x\n", mFastTrackAvailMask);
+    dprintf(fd, "\nOutput thread %p:\n", this);
+    dprintf(fd, "  Normal frame count: %zu\n", mNormalFrameCount);
+    dprintf(fd, "  Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
+    dprintf(fd, "  Total writes: %d\n", mNumWrites);
+    dprintf(fd, "  Delayed writes: %d\n", mNumDelayedWrites);
+    dprintf(fd, "  Blocked in write: %s\n", mInWrite ? "yes" : "no");
+    dprintf(fd, "  Suspend count: %d\n", mSuspended);
+    dprintf(fd, "  Sink buffer : %p\n", mSinkBuffer);
+    dprintf(fd, "  Mixer buffer: %p\n", mMixerBuffer);
+    dprintf(fd, "  Effect buffer: %p\n", mEffectBuffer);
+    dprintf(fd, "  Fast track availMask=%#x\n", mFastTrackAvailMask);
 
     dumpBase(fd, args);
 }
@@ -1356,7 +1380,12 @@
         ) {
         // if frameCount not specified, then it defaults to fast mixer (HAL) frame count
         if (frameCount == 0) {
-            frameCount = mFrameCount * kFastTrackMultiplier;
+            // read the fast track multiplier property the first time it is needed
+            int ok = pthread_once(&sFastTrackMultiplierOnce, sFastTrackMultiplierInit);
+            if (ok != 0) {
+                ALOGE("%s pthread_once failed: %d", __func__, ok);
+            }
+            frameCount = mFrameCount * sFastTrackMultiplier;
         }
         ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
                 frameCount, mFrameCount);
@@ -2758,6 +2787,8 @@
         // wrap the source side of the MonoPipe to make it an AudioBufferProvider
         fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe));
         fastTrack->mVolumeProvider = NULL;
+        fastTrack->mChannelMask = mChannelMask; // mPipeSink channel mask for audio to FastMixer
+        fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
         fastTrack->mGeneration++;
         state->mFastTracksGen++;
         state->mTrackMask = 1;
@@ -3210,6 +3241,7 @@
                     fastTrack->mBufferProvider = eabp;
                     fastTrack->mVolumeProvider = vp;
                     fastTrack->mChannelMask = track->mChannelMask;
+                    fastTrack->mFormat = track->mFormat;
                     fastTrack->mGeneration++;
                     state->mTrackMask |= 1 << j;
                     didModify = true;
@@ -3601,9 +3633,10 @@
 }
 
 // getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId)
+int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask,
+        audio_format_t format, int sessionId)
 {
-    return mAudioMixer->getTrackName(channelMask, sessionId);
+    return mAudioMixer->getTrackName(channelMask, format, sessionId);
 }
 
 // deleteTrackName_l() must be called with ThreadBase::mLock held
@@ -3716,7 +3749,8 @@
             delete mAudioMixer;
             mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
             for (size_t i = 0; i < mTracks.size() ; i++) {
-                int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
+                int name = getTrackName_l(mTracks[i]->mChannelMask,
+                        mTracks[i]->mFormat, mTracks[i]->mSessionId);
                 if (name < 0) {
                     break;
                 }
@@ -3748,7 +3782,7 @@
 
     PlaybackThread::dumpInternals(fd, args);
 
-    fdprintf(fd, "  AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
+    dprintf(fd, "  AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames());
 
     // Make a non-atomic copy of fast mixer dump state so it won't change underneath us
     const FastMixerDumpState copy(mFastMixerDumpState);
@@ -4007,7 +4041,7 @@
 
 // getTrackName_l() must be called with ThreadBase::mLock held
 int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused,
-        int sessionId __unused)
+        audio_format_t format __unused, int sessionId __unused)
 {
     return 0;
 }
@@ -5203,6 +5237,7 @@
         // to be at least 2 x the record thread frame count and cover audio hardware latency.
         // This is probably too conservative, but legacy application code may depend on it.
         // If you change this calculation, also review the start threshold which is related.
+        // FIXME It's not clear how input latency actually matters.  Perhaps this should be 0.
         uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream);
         size_t mNormalFrameCount = 2048; // FIXME
         uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
@@ -5435,12 +5470,12 @@
 
 void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
 {
-    fdprintf(fd, "\nInput thread %p:\n", this);
+    dprintf(fd, "\nInput thread %p:\n", this);
 
     if (mActiveTracks.size() > 0) {
-        fdprintf(fd, "  Buffer size: %zu bytes\n", mBufferSize);
+        dprintf(fd, "  Buffer size: %zu bytes\n", mBufferSize);
     } else {
-        fdprintf(fd, "  No active record clients\n");
+        dprintf(fd, "  No active record clients\n");
     }
 
     dumpBase(fd, args);
@@ -5455,9 +5490,9 @@
     size_t numtracks = mTracks.size();
     size_t numactive = mActiveTracks.size();
     size_t numactiveseen = 0;
-    fdprintf(fd, "  %d Tracks", numtracks);
+    dprintf(fd, "  %d Tracks", numtracks);
     if (numtracks) {
-        fdprintf(fd, " of which %d are active\n", numactive);
+        dprintf(fd, " of which %d are active\n", numactive);
         RecordTrack::appendDumpHeader(result);
         for (size_t i = 0; i < numtracks ; ++i) {
             sp<RecordTrack> track = mTracks[i];
@@ -5471,7 +5506,7 @@
             }
         }
     } else {
-        fdprintf(fd, "\n");
+        dprintf(fd, "\n");
     }
 
     if (numactiveseen != numactive) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index f8037c6..8c9943c 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -356,6 +356,8 @@
                 // If a thread does not have such a heap, this method returns 0.
                 virtual sp<MemoryDealer>    readOnlyHeap() const { return 0; }
 
+                virtual sp<IMemory> pipeMemory() const { return 0; }
+
     mutable     Mutex                   mLock;
 
 protected:
@@ -674,7 +676,8 @@
 
     // Allocate a track name for a given channel mask.
     //   Returns name >= 0 if successful, -1 on failure.
-    virtual int             getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0;
+    virtual int             getTrackName_l(audio_channel_mask_t channelMask,
+                                           audio_format_t format, int sessionId) = 0;
     virtual void            deleteTrackName_l(int name) = 0;
 
     // Time to sleep between cycles when:
@@ -831,7 +834,8 @@
 
 protected:
     virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
-    virtual     int         getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+    virtual     int         getTrackName_l(audio_channel_mask_t channelMask,
+                                           audio_format_t format, int sessionId);
     virtual     void        deleteTrackName_l(int name);
     virtual     uint32_t    idleSleepTimeUs() const;
     virtual     uint32_t    suspendSleepTimeUs() const;
@@ -884,7 +888,8 @@
                                                    status_t& status);
 
 protected:
-    virtual     int         getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+    virtual     int         getTrackName_l(audio_channel_mask_t channelMask,
+                                           audio_format_t format, int sessionId);
     virtual     void        deleteTrackName_l(int name);
     virtual     uint32_t    activeSleepTimeUs() const;
     virtual     uint32_t    idleSleepTimeUs() const;
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 5f13be3..4cba3fd 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -39,6 +39,13 @@
         STARTING_2,     // for RecordTrack only
     };
 
+    // where to allocate the data buffer
+    enum alloc_type {
+        ALLOC_CBLK,     // allocate immediately after control block
+        ALLOC_READONLY, // allocate from a separate read-only heap per thread
+        ALLOC_PIPE,     // do not allocate; use the pipe buffer
+    };
+
                         TrackBase(ThreadBase *thread,
                                 const sp<Client>& client,
                                 uint32_t sampleRate,
@@ -50,7 +57,7 @@
                                 int uid,
                                 IAudioFlinger::track_flags_t flags,
                                 bool isOut,
-                                bool useReadOnlyHeap = false);
+                                alloc_type alloc = ALLOC_CBLK);
     virtual             ~TrackBase();
     virtual status_t    initCheck() const { return getCblk() != 0 ? NO_ERROR : NO_MEMORY; }
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index f698fa2..7ddc71c 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -73,7 +73,7 @@
             int clientUid,
             IAudioFlinger::track_flags_t flags,
             bool isOut,
-            bool useReadOnlyHeap)
+            alloc_type alloc)
     :   RefBase(),
         mThread(thread),
         mClient(client),
@@ -117,7 +117,7 @@
     // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
     size_t size = sizeof(audio_track_cblk_t);
     size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
-    if (sharedBuffer == 0 && !useReadOnlyHeap) {
+    if (sharedBuffer == 0 && alloc == ALLOC_CBLK) {
         size += bufferSize;
     }
 
@@ -139,7 +139,8 @@
     // construct the shared structure in-place.
     if (mCblk != NULL) {
         new(mCblk) audio_track_cblk_t();
-        if (useReadOnlyHeap) {
+        switch (alloc) {
+        case ALLOC_READONLY: {
             const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
             if (roHeap == 0 ||
                     (mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
@@ -153,7 +154,17 @@
                 return;
             }
             memset(mBuffer, 0, bufferSize);
-        } else {
+            } break;
+        case ALLOC_PIPE:
+            mBufferMemory = thread->pipeMemory();
+            // mBuffer is the virtual address as seen from current process (mediaserver),
+            // and should normally be coming from mBufferMemory->pointer().
+            // However in this case the TrackBase does not reference the buffer directly.
+            // It should references the buffer via the pipe.
+            // Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL.
+            mBuffer = NULL;
+            break;
+        case ALLOC_CBLK:
             // clear all buffers
             if (sharedBuffer == 0) {
                 mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
@@ -164,6 +175,7 @@
                 mCblk->mFlags = CBLK_FORCEREADY;    // FIXME hack, need to fix the track ready logic
 #endif
             }
+            break;
         }
 
 #ifdef TEE_SINK
@@ -385,7 +397,7 @@
     }
     mServerProxy = mAudioTrackServerProxy;
 
-    mName = thread->getTrackName_l(channelMask, sessionId);
+    mName = thread->getTrackName_l(channelMask, format, sessionId);
     if (mName < 0) {
         ALOGE("no more track names available");
         return;
@@ -1842,7 +1854,7 @@
     :   TrackBase(thread, client, sampleRate, format,
                   channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid,
                   flags, false /*isOut*/,
-                  (flags & IAudioFlinger::TRACK_FAST) != 0 /*useReadOnlyHeap*/),
+                  (flags & IAudioFlinger::TRACK_FAST) != 0 ? ALLOC_READONLY : ALLOC_CBLK),
         mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
         // See real initialization of mRsmpInFront at RecordThread::start()
         mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index b047e1d..db0f57d 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -100,6 +100,7 @@
     STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE),
     STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF),
+    STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
 };
 
 const StringToEnum sFlagNameToEnumTable[] = {
@@ -3164,6 +3165,12 @@
 
     case AUDIO_SOURCE_DEFAULT:
     case AUDIO_SOURCE_MIC:
+    if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
+        device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
+        break;
+    }
+    // FALL THROUGH
+
     case AUDIO_SOURCE_VOICE_RECOGNITION:
     case AUDIO_SOURCE_HOTWORD:
     case AUDIO_SOURCE_VOICE_COMMUNICATION:
diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp
index 346e15f..374dc5e 100644
--- a/services/camera/libcameraservice/utils/CameraTraces.cpp
+++ b/services/camera/libcameraservice/utils/CameraTraces.cpp
@@ -74,10 +74,10 @@
         return BAD_VALUE;
     }
 
-    fdprintf(fd, "Camera traces (%zu):\n", pcsList.size());
+    dprintf(fd, "Camera traces (%zu):\n", pcsList.size());
 
     if (pcsList.empty()) {
-        fdprintf(fd, "  No camera traces collected.\n");
+        dprintf(fd, "  No camera traces collected.\n");
     }
 
     // Print newest items first
diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp
index 0c7fbbd..41dab1f 100644
--- a/services/medialog/MediaLogService.cpp
+++ b/services/medialog/MediaLogService.cpp
@@ -60,7 +60,7 @@
     static const String16 sDump("android.permission.DUMP");
     if (!(IPCThreadState::self()->getCallingUid() == AID_MEDIA ||
             PermissionCache::checkCallingPermission(sDump))) {
-        fdprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
+        dprintf(fd, "Permission Denial: can't dump media.log from pid=%d, uid=%d\n",
                 IPCThreadState::self()->getCallingPid(),
                 IPCThreadState::self()->getCallingUid());
         return NO_ERROR;
@@ -74,7 +74,7 @@
     for (size_t i = 0; i < namedReaders.size(); i++) {
         const NamedReader& namedReader = namedReaders[i];
         if (fd >= 0) {
-            fdprintf(fd, "\n%s:\n", namedReader.name());
+            dprintf(fd, "\n%s:\n", namedReader.name());
         } else {
             ALOGI("%s:", namedReader.name());
         }