Merge "Fix AudioMixer MONO muted track handling" into lmp-dev
diff --git a/camera/CameraUtils.cpp b/camera/CameraUtils.cpp
index 1ff63ab..04244ac 100644
--- a/camera/CameraUtils.cpp
+++ b/camera/CameraUtils.cpp
@@ -73,23 +73,23 @@
                 return INVALID_OPERATION;
         }
     } else {
-        // Front camera needs to be horizontally flipped for
-        // mirror-like behavior.
-        // Note: Flips are applied before rotates.
+        // Front camera needs to be horizontally flipped for mirror-like behavior.
+        // Note: Flips are applied before rotates; using XOR here as some of these flags are
+        // composed in terms of other flip/rotation flags, and are not bitwise-ORable.
         switch (orientation) {
             case 0:
                 flags = NATIVE_WINDOW_TRANSFORM_FLIP_H;
                 break;
             case 90:
-                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H |
+                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
                         NATIVE_WINDOW_TRANSFORM_ROT_270;
                 break;
             case 180:
-                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H |
+                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
                         NATIVE_WINDOW_TRANSFORM_ROT_180;
                 break;
             case 270:
-                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H |
+                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
                         NATIVE_WINDOW_TRANSFORM_ROT_90;
 
                 break;
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 3fb9e36..8000e84 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -48,7 +48,7 @@
             const sp<IMediaHTTPService> &httpService,
             const char *uri,
             const KeyedVector<String8, String8> *headers = NULL,
-            AString *sniffedMIME = NULL);
+            String8 *contentType = NULL);
 
     DataSource() {}
 
@@ -102,10 +102,6 @@
     virtual ~DataSource() {}
 
 private:
-    enum {
-        kDefaultMetaSize = 200000,
-    };
-
     static Mutex gSnifferMutex;
     static List<SnifferFunc> gSniffers;
     static bool gSniffersRegistered;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 2c48306..b5bd988 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1798,7 +1798,9 @@
     //ALOGV("write(%p, %u)", buffer, size);
     if (mTrack != 0) {
         ssize_t ret = mTrack->write(buffer, size);
-        mBytesWritten += ret;
+        if (ret >= 0) {
+            mBytesWritten += ret;
+        }
         return ret;
     }
     return NO_INIT;
@@ -1945,7 +1947,7 @@
 #define LOG_TAG "AudioCache"
 MediaPlayerService::AudioCache::AudioCache(const sp<IMemoryHeap>& heap) :
     mHeap(heap), mChannelCount(0), mFrameCount(1024), mSampleRate(0), mSize(0),
-    mError(NO_ERROR),  mCommandComplete(false)
+    mFrameSize(1), mError(NO_ERROR),  mCommandComplete(false)
 {
 }
 
@@ -1962,14 +1964,14 @@
 status_t MediaPlayerService::AudioCache::getPosition(uint32_t *position) const
 {
     if (position == 0) return BAD_VALUE;
-    *position = mSize;
+    *position = mSize / mFrameSize;
     return NO_ERROR;
 }
 
 status_t MediaPlayerService::AudioCache::getFramesWritten(uint32_t *written) const
 {
     if (written == 0) return BAD_VALUE;
-    *written = mSize;
+    *written = mSize / mFrameSize;
     return NO_ERROR;
 }
 
@@ -2031,6 +2033,8 @@
 
     if (actualSize > 0) {
         sink->write(mBuffer, actualSize);
+        // Could return false on sink->write() error or short count.
+        // Not necessarily appropriate but would work for AudioCache behavior.
     }
 
     return true;
@@ -2053,6 +2057,9 @@
     mChannelCount = (uint16_t)channelCount;
     mFormat = format;
     mMsecsPerFrame = 1.e3 / (float) sampleRate;
+    mFrameSize =  audio_is_linear_pcm(mFormat)
+            ? mChannelCount * audio_bytes_per_sample(mFormat) : 1;
+    mFrameCount = mHeap->getSize() / mFrameSize;
 
     if (cb != NULL) {
         mCallbackThread = new CallbackThread(this, cb, cookie);
@@ -2082,12 +2089,26 @@
     if (p == NULL) return NO_INIT;
     p += mSize;
     ALOGV("memcpy(%p, %p, %u)", p, buffer, size);
-    if (mSize + size > mHeap->getSize()) {
+
+    bool overflow = mSize + size > mHeap->getSize();
+    if (overflow) {
         ALOGE("Heap size overflow! req size: %d, max size: %d", (mSize + size), mHeap->getSize());
         size = mHeap->getSize() - mSize;
     }
+    size -= size % mFrameSize; // consume only integral amounts of frame size
     memcpy(p, buffer, size);
     mSize += size;
+
+    if (overflow) {
+        // Signal heap filled here (last frame may be truncated).
+        // After this point, no more data should be written as the
+        // heap is filled and the AudioCache should be effectively
+        // immutable with respect to future writes.
+        //
+        // It is thus safe for another thread to read the AudioCache.
+        mCommandComplete = true;
+        mSignal.signal();
+    }
     return size;
 }
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 406e3f6..4fe7075 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -194,7 +194,7 @@
         virtual ssize_t         bufferSize() const { return frameSize() * mFrameCount; }
         virtual ssize_t         frameCount() const { return mFrameCount; }
         virtual ssize_t         channelCount() const { return (ssize_t)mChannelCount; }
-        virtual ssize_t         frameSize() const { return ssize_t(mChannelCount * ((mFormat == AUDIO_FORMAT_PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
+        virtual ssize_t         frameSize() const { return (ssize_t)mFrameSize; }
         virtual uint32_t        latency() const;
         virtual float           msecsPerFrame() const;
         virtual status_t        getPosition(uint32_t *position) const;
@@ -244,6 +244,7 @@
         ssize_t             mFrameCount;
         uint32_t            mSampleRate;
         uint32_t            mSize;
+        size_t              mFrameSize;
         int                 mError;
         bool                mCommandComplete;
 
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 8774117..e2bcb1e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1433,6 +1433,10 @@
             format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
             break;
 
+        case VIDEO_ENCODER_VP8:
+            format->setString("mime", MEDIA_MIMETYPE_VIDEO_VP8);
+            break;
+
         default:
             CHECK(!"Should not be here, unsupported video encoding.");
             break;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 76e1d54..f0f4e45 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
+#include "../../libstagefright/include/NuCachedSource2.h"
 #include "../../libstagefright/include/WVMExtractor.h"
 
 namespace android {
@@ -47,7 +48,10 @@
       mAudioIsVorbis(false),
       mIsWidevine(false),
       mUIDValid(uidValid),
-      mUID(uid) {
+      mUID(uid),
+      mMetaDataSize(-1ll),
+      mBitrate(-1ll),
+      mPollBufferingGeneration(0) {
     resetDataSource();
     DataSource::RegisterDefaultSniffers();
 }
@@ -92,18 +96,18 @@
     return OK;
 }
 
-status_t NuPlayer::GenericSource::initFromDataSource(
-        const sp<DataSource> &dataSource,
-        const char* mime) {
+status_t NuPlayer::GenericSource::initFromDataSource() {
     sp<MediaExtractor> extractor;
 
+    CHECK(mDataSource != NULL);
+
     if (mIsWidevine) {
         String8 mimeType;
         float confidence;
         sp<AMessage> dummy;
         bool success;
 
-        success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
+        success = SniffWVM(mDataSource, &mimeType, &confidence, &dummy);
         if (!success
                 || strcasecmp(
                     mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
@@ -111,14 +115,15 @@
             return UNKNOWN_ERROR;
         }
 
-        sp<WVMExtractor> wvmExtractor = new WVMExtractor(dataSource);
-        wvmExtractor->setAdaptiveStreamingMode(true);
+        mWVMExtractor = new WVMExtractor(mDataSource);
+        mWVMExtractor->setAdaptiveStreamingMode(true);
         if (mUIDValid) {
-            wvmExtractor->setUID(mUID);
+            mWVMExtractor->setUID(mUID);
         }
-        extractor = wvmExtractor;
+        extractor = mWVMExtractor;
     } else {
-        extractor = MediaExtractor::Create(dataSource, mime);
+        extractor = MediaExtractor::Create(mDataSource,
+                mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str());
     }
 
     if (extractor == NULL) {
@@ -133,14 +138,20 @@
         }
     }
 
+    int32_t totalBitrate = 0;
+
     for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        sp<MediaSource> track = extractor->getTrack(i);
+
         sp<MetaData> meta = extractor->getTrackMetaData(i);
 
         const char *mime;
         CHECK(meta->findCString(kKeyMIMEType, &mime));
 
-        sp<MediaSource> track = extractor->getTrack(i);
-
+        // Do the string compare immediately with "mime",
+        // we can't assume "mime" would stay valid after another
+        // extractor operation, some extractors might modify meta
+        // during getTrack() and make it invalid.
         if (!strncasecmp(mime, "audio/", 6)) {
             if (mAudioTrack.mSource == NULL) {
                 mAudioTrack.mIndex = i;
@@ -177,9 +188,18 @@
                     mDurationUs = durationUs;
                 }
             }
+
+            int32_t bitrate;
+            if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) {
+                totalBitrate += bitrate;
+            } else {
+                totalBitrate = -1;
+            }
         }
     }
 
+    mBitrate = totalBitrate;
+
     return OK;
 }
 
@@ -213,34 +233,53 @@
 
 void NuPlayer::GenericSource::onPrepareAsync() {
     // delayed data source creation
-    AString sniffedMIME;
-    sp<DataSource> dataSource;
+    if (mDataSource == NULL) {
+        if (!mUri.empty()) {
+            mIsWidevine = !strncasecmp(mUri.c_str(), "widevine://", 11);
 
-    if (!mUri.empty()) {
-        mIsWidevine = !strncasecmp(mUri.c_str(), "widevine://", 11);
+            mDataSource = DataSource::CreateFromURI(
+                   mHTTPService, mUri.c_str(), &mUriHeaders, &mContentType);
+        } else {
+            // set to false first, if the extractor
+            // comes back as secure, set it to true then.
+            mIsWidevine = false;
 
-        dataSource = DataSource::CreateFromURI(
-               mHTTPService, mUri.c_str(), &mUriHeaders, &sniffedMIME);
-    } else {
-        // set to false first, if the extractor
-        // comes back as secure, set it to true then.
-        mIsWidevine = false;
+            mDataSource = new FileSource(mFd, mOffset, mLength);
+        }
 
-        dataSource = new FileSource(mFd, mOffset, mLength);
+        if (mDataSource == NULL) {
+            ALOGE("Failed to create data source!");
+            notifyPreparedAndCleanup(UNKNOWN_ERROR);
+            return;
+        }
+
+        if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+            mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
+        }
+
+        if (mIsWidevine || mCachedSource != NULL) {
+            schedulePollBuffering();
+        }
     }
 
-    if (dataSource == NULL) {
-        ALOGE("Failed to create data source!");
-        notifyPrepared(UNKNOWN_ERROR);
+    // check initial caching status
+    status_t err = prefillCacheIfNecessary();
+    if (err != OK) {
+        if (err == -EAGAIN) {
+            (new AMessage(kWhatPrepareAsync, id()))->post(200000);
+        } else {
+            ALOGE("Failed to prefill data cache!");
+            notifyPreparedAndCleanup(UNKNOWN_ERROR);
+        }
         return;
     }
 
-    status_t err = initFromDataSource(
-            dataSource, sniffedMIME.empty() ? NULL : sniffedMIME.c_str());
+    // init extrator from data source
+    err = initFromDataSource();
 
     if (err != OK) {
         ALOGE("Failed to init from data source!");
-        notifyPrepared(err);
+        notifyPreparedAndCleanup(err);
         return;
     }
 
@@ -258,6 +297,89 @@
     notifyPrepared();
 }
 
+void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
+    if (err != OK) {
+        mMetaDataSize = -1ll;
+        mContentType = "";
+        mSniffedMIME = "";
+        mDataSource.clear();
+        mCachedSource.clear();
+
+        cancelPollBuffering();
+    }
+    notifyPrepared(err);
+}
+
+status_t NuPlayer::GenericSource::prefillCacheIfNecessary() {
+    CHECK(mDataSource != NULL);
+
+    if (mCachedSource == NULL) {
+        // no prefill if the data source is not cached
+        return OK;
+    }
+
+    // We're not doing this for streams that appear to be audio-only
+    // streams to ensure that even low bandwidth streams start
+    // playing back fairly instantly.
+    if (!strncasecmp(mContentType.string(), "audio/", 6)) {
+        return OK;
+    }
+
+    // We're going to prefill the cache before trying to instantiate
+    // the extractor below, as the latter is an operation that otherwise
+    // could block on the datasource for a significant amount of time.
+    // During that time we'd be unable to abort the preparation phase
+    // without this prefill.
+
+    // Initially make sure we have at least 192 KB for the sniff
+    // to complete without blocking.
+    static const size_t kMinBytesForSniffing = 192 * 1024;
+    static const size_t kDefaultMetaSize = 200000;
+
+    status_t finalStatus;
+
+    size_t cachedDataRemaining =
+            mCachedSource->approxDataRemaining(&finalStatus);
+
+    if (finalStatus != OK || (mMetaDataSize >= 0
+            && (off64_t)cachedDataRemaining >= mMetaDataSize)) {
+        ALOGV("stop caching, status %d, "
+                "metaDataSize %lld, cachedDataRemaining %zu",
+                finalStatus, mMetaDataSize, cachedDataRemaining);
+        return OK;
+    }
+
+    ALOGV("now cached %zu bytes of data", cachedDataRemaining);
+
+    if (mMetaDataSize < 0
+            && cachedDataRemaining >= kMinBytesForSniffing) {
+        String8 tmp;
+        float confidence;
+        sp<AMessage> meta;
+        if (!mCachedSource->sniff(&tmp, &confidence, &meta)) {
+            return UNKNOWN_ERROR;
+        }
+
+        // We successfully identified the file's extractor to
+        // be, remember this mime type so we don't have to
+        // sniff it again when we call MediaExtractor::Create()
+        mSniffedMIME = tmp.string();
+
+        if (meta == NULL
+                || !meta->findInt64("meta-data-size",
+                        reinterpret_cast<int64_t*>(&mMetaDataSize))) {
+            mMetaDataSize = kDefaultMetaSize;
+        }
+
+        if (mMetaDataSize < 0ll) {
+            ALOGE("invalid metaDataSize = %lld bytes", mMetaDataSize);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return -EAGAIN;
+}
+
 void NuPlayer::GenericSource::start() {
     ALOGI("start");
 
@@ -282,6 +404,65 @@
     return OK;
 }
 
+void NuPlayer::GenericSource::schedulePollBuffering() {
+    sp<AMessage> msg = new AMessage(kWhatPollBuffering, id());
+    msg->setInt32("generation", mPollBufferingGeneration);
+    msg->post(1000000ll);
+}
+
+void NuPlayer::GenericSource::cancelPollBuffering() {
+    ++mPollBufferingGeneration;
+}
+
+void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) {
+    sp<AMessage> msg = dupNotify();
+    msg->setInt32("what", kWhatBufferingUpdate);
+    msg->setInt32("percentage", percentage);
+    msg->post();
+}
+
+void NuPlayer::GenericSource::onPollBuffering() {
+    status_t finalStatus = UNKNOWN_ERROR;
+    int64_t cachedDurationUs = 0ll;
+
+    if (mCachedSource != NULL) {
+        size_t cachedDataRemaining =
+                mCachedSource->approxDataRemaining(&finalStatus);
+
+        if (finalStatus == OK) {
+            off64_t size;
+            int64_t bitrate = 0ll;
+            if (mDurationUs > 0 && mCachedSource->getSize(&size) == OK) {
+                bitrate = size * 8000000ll / mDurationUs;
+            } else if (mBitrate > 0) {
+                bitrate = mBitrate;
+            }
+            if (bitrate > 0) {
+                cachedDurationUs = cachedDataRemaining * 8000000ll / bitrate;
+            }
+        }
+    } else if (mWVMExtractor != NULL) {
+        cachedDurationUs
+            = mWVMExtractor->getCachedDurationUs(&finalStatus);
+    }
+
+    if (finalStatus == ERROR_END_OF_STREAM) {
+        notifyBufferingUpdate(100);
+        cancelPollBuffering();
+        return;
+    } else if (cachedDurationUs > 0ll && mDurationUs > 0ll) {
+        int percentage = 100.0 * cachedDurationUs / mDurationUs;
+        if (percentage > 100) {
+            percentage = 100;
+        }
+
+        notifyBufferingUpdate(percentage);
+    }
+
+    schedulePollBuffering();
+}
+
+
 void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
       case kWhatPrepareAsync:
@@ -364,7 +545,15 @@
 
           break;
       }
-
+      case kWhatPollBuffering:
+      {
+          int32_t generation;
+          CHECK(msg->findInt32("generation", &generation));
+          if (generation == mPollBufferingGeneration) {
+              onPollBuffering();
+          }
+          break;
+      }
       default:
           Source::onMessageReceived(msg);
           break;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index d3081de..663bfae 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -33,6 +33,8 @@
 struct IMediaHTTPService;
 struct MediaSource;
 class MediaBuffer;
+struct NuCachedSource2;
+struct WVMExtractor;
 
 struct NuPlayer::GenericSource : public NuPlayer::Source {
     GenericSource(const sp<AMessage> &notify, bool uidValid, uid_t uid);
@@ -76,6 +78,7 @@
         kWhatSendSubtitleData,
         kWhatSendTimedTextData,
         kWhatChangeAVSource,
+        kWhatPollBuffering,
     };
 
     Vector<sp<MediaSource> > mSources;
@@ -105,14 +108,24 @@
     int64_t mOffset;
     int64_t mLength;
 
-    sp<ALooper> mLooper;
+    sp<DataSource> mDataSource;
+    sp<NuCachedSource2> mCachedSource;
+    sp<WVMExtractor> mWVMExtractor;
+    String8 mContentType;
+    AString mSniffedMIME;
+    off64_t mMetaDataSize;
+    int64_t mBitrate;
+    int32_t mPollBufferingGeneration;
 
+    sp<ALooper> mLooper;
 
     void resetDataSource();
 
-    status_t initFromDataSource(
-            const sp<DataSource> &dataSource,
-            const char *mime);
+    status_t initFromDataSource();
+
+    status_t prefillCacheIfNecessary();
+
+    void notifyPreparedAndCleanup(status_t err);
 
     void onPrepareAsync();
 
@@ -133,6 +146,11 @@
             media_track_type trackType,
             int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL, bool formatChange = false);
 
+    void schedulePollBuffering();
+    void cancelPollBuffering();
+    void onPollBuffering();
+    void notifyBufferingUpdate(int percentage);
+
     DISALLOW_EVIL_CONSTRUCTORS(GenericSource);
 };
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 5f2c20a..2b7457b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -280,7 +280,7 @@
         msg->setObject(
                 "native-window",
                 new NativeWindowWrapper(
-                    new Surface(bufferProducer)));
+                    new Surface(bufferProducer, true /* controlledByApp */)));
     }
 
     msg->post();
@@ -756,7 +756,7 @@
                     ALOGV("initiating %s decoder shutdown",
                          audio ? "audio" : "video");
 
-                    (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
+                    getDecoder(audio)->initiateShutdown();
 
                     if (audio) {
                         mFlushingAudio = SHUTTING_DOWN_DECODER;
@@ -803,6 +803,14 @@
                     err = UNKNOWN_ERROR;
                 }
                 mRenderer->queueEOS(audio, err);
+                if (audio && mFlushingAudio != NONE) {
+                    mAudioDecoder.clear();
+                    mFlushingAudio = SHUT_DOWN;
+                } else if (!audio && mFlushingVideo != NONE){
+                    mVideoDecoder.clear();
+                    mFlushingVideo = SHUT_DOWN;
+                }
+                finishFlushIfPossible();
             } else if (what == Decoder::kWhatDrainThisBuffer) {
                 renderBuffer(audio, msg);
             } else {
@@ -964,11 +972,13 @@
 }
 
 void NuPlayer::finishFlushIfPossible() {
-    if (mFlushingAudio != FLUSHED && mFlushingAudio != SHUT_DOWN) {
+    if (mFlushingAudio != NONE && mFlushingAudio != FLUSHED
+            && mFlushingAudio != SHUT_DOWN) {
         return;
     }
 
-    if (mFlushingVideo != FLUSHED && mFlushingVideo != SHUT_DOWN) {
+    if (mFlushingVideo != NONE && mFlushingVideo != FLUSHED
+            && mFlushingVideo != SHUT_DOWN) {
         return;
     }
 
@@ -979,11 +989,11 @@
         mTimeDiscontinuityPending = false;
     }
 
-    if (mAudioDecoder != NULL) {
+    if (mAudioDecoder != NULL && mFlushingAudio == FLUSHED) {
         mAudioDecoder->signalResume();
     }
 
-    if (mVideoDecoder != NULL) {
+    if (mVideoDecoder != NULL && mFlushingVideo == FLUSHED) {
         mVideoDecoder->signalResume();
     }
 
@@ -1221,8 +1231,8 @@
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
 
-    if ((audio && IsFlushingState(mFlushingAudio))
-            || (!audio && IsFlushingState(mFlushingVideo))) {
+    if ((audio && mFlushingAudio != NONE)
+            || (!audio && mFlushingVideo != NONE)) {
         reply->setInt32("err", INFO_DISCONTINUITY);
         reply->post();
         return OK;
@@ -1282,7 +1292,24 @@
                 mTimeDiscontinuityPending =
                     mTimeDiscontinuityPending || timeChange;
 
-                if (mFlushingAudio == NONE && mFlushingVideo == NONE) {
+                bool seamlessFormatChange = false;
+                sp<AMessage> newFormat = mSource->getFormat(audio);
+                if (formatChange) {
+                    seamlessFormatChange =
+                        getDecoder(audio)->supportsSeamlessFormatChange(newFormat);
+                    // treat seamless format change separately
+                    formatChange = !seamlessFormatChange;
+                }
+                bool shutdownOrFlush = formatChange || timeChange;
+
+                // We want to queue up scan-sources only once per discontinuity.
+                // We control this by doing it only if neither audio nor video are
+                // flushing or shutting down.  (After handling 1st discontinuity, one
+                // of the flushing states will not be NONE.)
+                // No need to scan sources if this discontinuity does not result
+                // in a flush or shutdown, as the flushing state will stay NONE.
+                if (mFlushingAudio == NONE && mFlushingVideo == NONE &&
+                        shutdownOrFlush) {
                     // And we'll resume scanning sources once we're done
                     // flushing.
                     mDeferredActions.push_front(
@@ -1290,27 +1317,19 @@
                                 &NuPlayer::performScanSources));
                 }
 
-                if (formatChange || timeChange) {
-
-                    sp<AMessage> newFormat = mSource->getFormat(audio);
-                    sp<Decoder> &decoder = audio ? mAudioDecoder : mVideoDecoder;
-                    if (formatChange && !decoder->supportsSeamlessFormatChange(newFormat)) {
-                        flushDecoder(audio, /* needShutdown = */ true);
-                    } else {
-                        flushDecoder(audio, /* needShutdown = */ false);
-                        err = OK;
-                    }
+                if (formatChange /* not seamless */) {
+                    // must change decoder
+                    flushDecoder(audio, /* needShutdown = */ true);
+                } else if (timeChange) {
+                    // need to flush
+                    flushDecoder(audio, /* needShutdown = */ false, newFormat);
+                    err = OK;
+                } else if (seamlessFormatChange) {
+                    // reuse existing decoder and don't flush
+                    updateDecoderFormatWithoutFlush(audio, newFormat);
+                    err = OK;
                 } else {
                     // This stream is unaffected by the discontinuity
-
-                    if (audio) {
-                        mFlushingAudio = FLUSHED;
-                    } else {
-                        mFlushingVideo = FLUSHED;
-                    }
-
-                    finishFlushIfPossible();
-
                     return -EWOULDBLOCK;
                 }
             }
@@ -1361,7 +1380,8 @@
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
 
-    if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) {
+    if ((audio && mFlushingAudio != NONE)
+            || (!audio && mFlushingVideo != NONE)) {
         // We're currently attempting to flush the decoder, in order
         // to complete this, the decoder wants all its buffers back,
         // so we don't want any output buffers it sent us (from before
@@ -1486,50 +1506,53 @@
     driver->notifyListener(msg, ext1, ext2, in);
 }
 
-void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
+void NuPlayer::flushDecoder(
+        bool audio, bool needShutdown, const sp<AMessage> &newFormat) {
     ALOGV("[%s] flushDecoder needShutdown=%d",
           audio ? "audio" : "video", needShutdown);
 
-    if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) {
+    const sp<Decoder> &decoder = getDecoder(audio);
+    if (decoder == NULL) {
         ALOGI("flushDecoder %s without decoder present",
              audio ? "audio" : "video");
+        return;
     }
 
     // Make sure we don't continue to scan sources until we finish flushing.
     ++mScanSourcesGeneration;
     mScanSourcesPending = false;
 
-    (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
+    decoder->signalFlush(newFormat);
     mRenderer->flush(audio);
 
     FlushStatus newStatus =
         needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER;
 
     if (audio) {
-        CHECK(mFlushingAudio == NONE
-                || mFlushingAudio == AWAITING_DISCONTINUITY);
-
+        ALOGE_IF(mFlushingAudio != NONE,
+                "audio flushDecoder() is called in state %d", mFlushingAudio);
         mFlushingAudio = newStatus;
-
-        if (mFlushingVideo == NONE) {
-            mFlushingVideo = (mVideoDecoder != NULL)
-                ? AWAITING_DISCONTINUITY
-                : FLUSHED;
-        }
     } else {
-        CHECK(mFlushingVideo == NONE
-                || mFlushingVideo == AWAITING_DISCONTINUITY);
-
+        ALOGE_IF(mFlushingVideo != NONE,
+                "video flushDecoder() is called in state %d", mFlushingVideo);
         mFlushingVideo = newStatus;
-
-        if (mFlushingAudio == NONE) {
-            mFlushingAudio = (mAudioDecoder != NULL)
-                ? AWAITING_DISCONTINUITY
-                : FLUSHED;
-        }
     }
 }
 
+void NuPlayer::updateDecoderFormatWithoutFlush(
+        bool audio, const sp<AMessage> &format) {
+    ALOGV("[%s] updateDecoderFormatWithoutFlush", audio ? "audio" : "video");
+
+    const sp<Decoder> &decoder = getDecoder(audio);
+    if (decoder == NULL) {
+        ALOGI("updateDecoderFormatWithoutFlush %s without decoder present",
+             audio ? "audio" : "video");
+        return;
+    }
+
+    decoder->signalUpdateFormat(format);
+}
+
 void NuPlayer::queueDecoderShutdown(
         bool audio, bool video, const sp<AMessage> &reply) {
     ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video);
@@ -1616,18 +1639,6 @@
         // an intermediate state, i.e. one more more decoders are currently
         // flushing or shutting down.
 
-        if (mRenderer != NULL) {
-            // There's an edge case where the renderer owns all output
-            // buffers and is paused, therefore the decoder will not read
-            // more input data and will never encounter the matching
-            // discontinuity. To avoid this, we resume the renderer.
-
-            if (mFlushingAudio == AWAITING_DISCONTINUITY
-                    || mFlushingVideo == AWAITING_DISCONTINUITY) {
-                mRenderer->resume();
-            }
-        }
-
         if (mFlushingAudio != NONE || mFlushingVideo != NONE) {
             // We're currently flushing, postpone the reset until that's
             // completed.
@@ -1692,14 +1703,6 @@
 
     mTimeDiscontinuityPending = true;
 
-    if (mFlushingAudio == NONE && (!audio || mAudioDecoder == NULL)) {
-        mFlushingAudio = FLUSHED;
-    }
-
-    if (mFlushingVideo == NONE && (!video || mVideoDecoder == NULL)) {
-        mFlushingVideo = FLUSHED;
-    }
-
     if (audio && mAudioDecoder != NULL) {
         flushDecoder(true /* audio */, true /* needShutdown */);
     }
@@ -1764,6 +1767,13 @@
 
     // XXX - ignore error from setVideoScalingMode for now
     setVideoScalingMode(mVideoScalingMode);
+
+    if (mDriver != NULL) {
+        sp<NuPlayerDriver> driver = mDriver.promote();
+        if (driver != NULL) {
+            driver->notifySetSurfaceComplete();
+        }
+    }
 }
 
 void NuPlayer::onSourceNotify(const sp<AMessage> &msg) {
@@ -1829,6 +1839,15 @@
             break;
         }
 
+        case Source::kWhatBufferingUpdate:
+        {
+            int32_t percentage;
+            CHECK(msg->findInt32("percentage", &percentage));
+
+            notifyListener(MEDIA_BUFFERING_UPDATE, percentage, 0);
+            break;
+        }
+
         case Source::kWhatBufferingStart:
         {
             notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index bb76636..511d752 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -25,6 +25,7 @@
 namespace android {
 
 struct ABuffer;
+struct AMessage;
 struct MetaData;
 struct NuPlayerDriver;
 
@@ -145,7 +146,6 @@
 
     enum FlushStatus {
         NONE,
-        AWAITING_DISCONTINUITY,
         FLUSHING_DECODER,
         FLUSHING_DECODER_SHUTDOWN,
         SHUTTING_DOWN_DECODER,
@@ -170,6 +170,10 @@
 
     bool mStarted;
 
+    inline const sp<Decoder> &getDecoder(bool audio) {
+        return audio ? mAudioDecoder : mVideoDecoder;
+    }
+
     void openAudioSink(const sp<AMessage> &format, bool offloadOnly);
     void closeAudioSink();
 
@@ -186,7 +190,9 @@
 
     void finishFlushIfPossible();
 
-    void flushDecoder(bool audio, bool needShutdown);
+    void flushDecoder(
+            bool audio, bool needShutdown, const sp<AMessage> &newFormat = NULL);
+    void updateDecoderFormatWithoutFlush(bool audio, const sp<AMessage> &format);
 
     static bool IsFlushingState(FlushStatus state, bool *needShutdown = NULL);
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 8fce2f4..d1aac50 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -44,11 +44,11 @@
     // Every decoder has its own looper because MediaCodec operations
     // are blocking, but NuPlayer needs asynchronous operations.
     mDecoderLooper = new ALooper;
-    mDecoderLooper->setName("NuPlayerDecoder");
+    mDecoderLooper->setName("NPDecoder");
     mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
 
     mCodecLooper = new ALooper;
-    mCodecLooper->setName("NuPlayerDecoder-MC");
+    mCodecLooper->setName("NPDecoder-CL");
     mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
 }
 
@@ -71,6 +71,19 @@
     return err;
 }
 
+void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) {
+    mCSDsForCurrentFormat.clear();
+    for (int32_t i = 0; ; ++i) {
+        AString tag = "csd-";
+        tag.append(i);
+        sp<ABuffer> buffer;
+        if (!format->findBuffer(tag.c_str(), &buffer)) {
+            break;
+        }
+        mCSDsForCurrentFormat.push(buffer);
+    }
+}
+
 void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
     CHECK(mCodec == NULL);
 
@@ -123,6 +136,8 @@
         handleError(err);
         return;
     }
+    rememberCodecSpecificData(format);
+
     // the following should work in configured state
     CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
     CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
@@ -189,6 +204,12 @@
     msg->post();
 }
 
+void NuPlayer::Decoder::signalUpdateFormat(const sp<AMessage> &format) {
+    sp<AMessage> msg = new AMessage(kWhatUpdateFormat, id());
+    msg->setMessage("format", format);
+    msg->post();
+}
+
 status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
     sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id());
     msg->setPointer("buffers", buffers);
@@ -229,6 +250,15 @@
     reply->setSize("buffer-ix", bufferIx);
     reply->setInt32("generation", mBufferGeneration);
 
+    if (!mCSDsToSubmit.isEmpty()) {
+        sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
+        ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
+        reply->setBuffer("buffer", buffer);
+        mCSDsToSubmit.removeAt(0);
+        reply->post();
+        return true;
+    }
+
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatFillThisBuffer);
     notify->setBuffer("buffer", mInputBuffers[bufferIx]);
@@ -312,10 +342,12 @@
         uint32_t flags = 0;
         CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
 
-        int32_t eos;
-        // we do not expect CODECCONFIG or SYNCFRAME for decoder
+        int32_t eos, csd;
+        // we do not expect SYNCFRAME for decoder
         if (buffer->meta()->findInt32("eos", &eos) && eos) {
             flags |= MediaCodec::BUFFER_FLAG_EOS;
+        } else if (buffer->meta()->findInt32("csd", &csd) && csd) {
+            flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
         }
 
         // copy into codec buffer
@@ -448,6 +480,7 @@
     status_t err = OK;
     if (mCodec != NULL) {
         err = mCodec->flush();
+        mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator
         ++mBufferGeneration;
     }
 
@@ -478,10 +511,13 @@
 
         if (mNativeWindow != NULL) {
             // reconnect to surface as MediaCodec disconnected from it
-            CHECK_EQ((int)NO_ERROR,
+            status_t error =
                     native_window_api_connect(
                             mNativeWindow->getNativeWindow().get(),
-                            NATIVE_WINDOW_API_MEDIA));
+                            NATIVE_WINDOW_API_MEDIA);
+            ALOGW_IF(error != NO_ERROR,
+                    "[%s] failed to connect to native window, error=%d",
+                    mComponentName.c_str(), error);
         }
         mComponentName = "decoder";
     }
@@ -512,6 +548,14 @@
             break;
         }
 
+        case kWhatUpdateFormat:
+        {
+            sp<AMessage> format;
+            CHECK(msg->findMessage("format", &format));
+            rememberCodecSpecificData(format);
+            break;
+        }
+
         case kWhatGetInputBuffers:
         {
             uint32_t replyID;
@@ -563,6 +607,10 @@
 
         case kWhatFlush:
         {
+            sp<AMessage> format;
+            if (msg->findMessage("new-format", &format)) {
+                rememberCodecSpecificData(format);
+            }
             onFlush();
             break;
         }
@@ -585,8 +633,12 @@
     }
 }
 
-void NuPlayer::Decoder::signalFlush() {
-    (new AMessage(kWhatFlush, id()))->post();
+void NuPlayer::Decoder::signalFlush(const sp<AMessage> &format) {
+    sp<AMessage> msg = new AMessage(kWhatFlush, id());
+    if (format != NULL) {
+        msg->setMessage("new-format", format);
+    }
+    msg->post();
 }
 
 void NuPlayer::Decoder::signalResume() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index c6fc237..67bddb8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -36,7 +36,8 @@
     virtual void init();
 
     status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const;
-    virtual void signalFlush();
+    virtual void signalFlush(const sp<AMessage> &format = NULL);
+    virtual void signalUpdateFormat(const sp<AMessage> &format);
     virtual void signalResume();
     virtual void initiateShutdown();
 
@@ -67,6 +68,7 @@
         kWhatRenderBuffer       = 'rndr',
         kWhatFlush              = 'flus',
         kWhatShutdown           = 'shuD',
+        kWhatUpdateFormat       = 'uFmt',
     };
 
     sp<AMessage> mNotify;
@@ -80,6 +82,8 @@
 
     Vector<sp<ABuffer> > mInputBuffers;
     Vector<sp<ABuffer> > mOutputBuffers;
+    Vector<sp<ABuffer> > mCSDsForCurrentFormat;
+    Vector<sp<ABuffer> > mCSDsToSubmit;
     Vector<bool> mInputBufferIsDequeued;
     Vector<MediaBuffer *> mMediaBuffers;
 
@@ -103,6 +107,7 @@
     AString mComponentName;
 
     bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
+    void rememberCodecSpecificData(const sp<AMessage> &format);
 
     DISALLOW_EVIL_CONSTRUCTORS(Decoder);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 140e1ae..c4bbcdf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -34,8 +34,11 @@
     : mState(STATE_IDLE),
       mIsAsyncPrepare(false),
       mAsyncResult(UNKNOWN_ERROR),
+      mSetSurfaceInProgress(false),
       mDurationUs(-1),
       mPositionUs(-1),
+      mNotifyTimeRealUs(-1),
+      mPauseStartedTimeUs(-1),
       mNumFramesTotal(0),
       mNumFramesDropped(0),
       mLooper(new ALooper),
@@ -134,6 +137,10 @@
         const sp<IGraphicBufferProducer> &bufferProducer) {
     Mutex::Autolock autoLock(mLock);
 
+    if (mSetSurfaceInProgress) {
+        return INVALID_OPERATION;
+    }
+
     switch (mState) {
         case STATE_SET_DATASOURCE_PENDING:
         case STATE_RESET_IN_PROGRESS:
@@ -143,8 +150,14 @@
             break;
     }
 
+    mSetSurfaceInProgress = true;
+
     mPlayer->setVideoSurfaceTextureAsync(bufferProducer);
 
+    while (mSetSurfaceInProgress) {
+        mCondition.wait(mLock);
+    }
+
     return OK;
 }
 
@@ -227,7 +240,7 @@
 
             if (mStartupSeekTimeUs >= 0) {
                 if (mStartupSeekTimeUs == 0) {
-                    notifySeekComplete();
+                    notifySeekComplete_l();
                 } else {
                     mPlayer->seekToAsync(mStartupSeekTimeUs);
                 }
@@ -238,12 +251,20 @@
         }
 
         case STATE_RUNNING:
+        {
+            if (mAtEOS) {
+                mPlayer->seekToAsync(0);
+                mAtEOS = false;
+                mPositionUs = -1;
+            }
             break;
+        }
 
         case STATE_PAUSED:
         case STATE_STOPPED_AND_PREPARED:
         {
             mPlayer->resume();
+            mPositionUs -= ALooper::GetNowUs() - mPauseStartedTimeUs;
             break;
         }
 
@@ -252,6 +273,7 @@
     }
 
     mState = STATE_RUNNING;
+    mPauseStartedTimeUs = -1;
 
     return OK;
 }
@@ -265,8 +287,9 @@
             // fall through
 
         case STATE_PAUSED:
-            notifyListener(MEDIA_STOPPED);
-            // fall through
+            mState = STATE_STOPPED;
+            notifyListener_l(MEDIA_STOPPED);
+            break;
 
         case STATE_PREPARED:
         case STATE_STOPPED:
@@ -278,6 +301,7 @@
         default:
             return INVALID_OPERATION;
     }
+    setPauseStartedTimeIfNeeded();
 
     return OK;
 }
@@ -291,7 +315,9 @@
             return OK;
 
         case STATE_RUNNING:
-            notifyListener(MEDIA_PAUSED);
+            setPauseStartedTimeIfNeeded();
+            mState = STATE_PAUSED;
+            notifyListener_l(MEDIA_PAUSED);
             mPlayer->pause();
             break;
 
@@ -299,8 +325,6 @@
             return INVALID_OPERATION;
     }
 
-    mState = STATE_PAUSED;
-
     return OK;
 }
 
@@ -320,7 +344,7 @@
             // 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();
+            notifySeekComplete_l();
             break;
         }
 
@@ -329,7 +353,7 @@
         {
             mAtEOS = false;
             // seeks can take a while, so we essentially paused
-            notifyListener(MEDIA_PAUSED);
+            notifyListener_l(MEDIA_PAUSED);
             mPlayer->seekToAsync(seekTimeUs);
             break;
         }
@@ -338,6 +362,8 @@
             return INVALID_OPERATION;
     }
 
+    mPositionUs = seekTimeUs;
+    mNotifyTimeRealUs = -1;
     return OK;
 }
 
@@ -346,8 +372,12 @@
 
     if (mPositionUs < 0) {
         *msec = 0;
+    } else if (mNotifyTimeRealUs == -1) {
+        *msec = mPositionUs / 1000;
     } else {
-        *msec = (mPositionUs + 500ll) / 1000;
+        int64_t nowUs =
+                (isPlaying() ?  ALooper::GetNowUs() : mPauseStartedTimeUs);
+        *msec = (mPositionUs + nowUs - mNotifyTimeRealUs + 500ll) / 1000;
     }
 
     return OK;
@@ -380,7 +410,7 @@
         {
             CHECK(mIsAsyncPrepare);
 
-            notifyListener(MEDIA_PREPARED);
+            notifyListener_l(MEDIA_PREPARED);
             break;
         }
 
@@ -389,7 +419,7 @@
     }
 
     if (mState != STATE_STOPPED) {
-        notifyListener(MEDIA_STOPPED);
+        notifyListener_l(MEDIA_STOPPED);
     }
 
     mState = STATE_RESET_IN_PROGRESS;
@@ -514,6 +544,15 @@
     mCondition.broadcast();
 }
 
+void NuPlayerDriver::notifySetSurfaceComplete() {
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(mSetSurfaceInProgress);
+    mSetSurfaceInProgress = false;
+
+    mCondition.broadcast();
+}
+
 void NuPlayerDriver::notifyDuration(int64_t durationUs) {
     Mutex::Autolock autoLock(mLock);
     mDurationUs = durationUs;
@@ -522,26 +561,29 @@
 void NuPlayerDriver::notifyPosition(int64_t positionUs) {
     Mutex::Autolock autoLock(mLock);
     mPositionUs = positionUs;
+    mNotifyTimeRealUs = ALooper::GetNowUs();
 }
 
 void NuPlayerDriver::notifySeekComplete() {
+    Mutex::Autolock autoLock(mLock);
+    notifySeekComplete_l();
+}
+
+void NuPlayerDriver::notifySeekComplete_l() {
     bool wasSeeking = true;
-    {
-        Mutex::Autolock autoLock(mLock);
-        if (mState == STATE_STOPPED_AND_PREPARING) {
-            wasSeeking = false;
-            mState = STATE_STOPPED_AND_PREPARED;
-            mCondition.broadcast();
-            if (!mIsAsyncPrepare) {
-                // if we are preparing synchronously, no need to notify listener
-                return;
-            }
-        } else if (mState == STATE_STOPPED) {
-            // no need to notify listener
+    if (mState == STATE_STOPPED_AND_PREPARING) {
+        wasSeeking = false;
+        mState = STATE_STOPPED_AND_PREPARED;
+        mCondition.broadcast();
+        if (!mIsAsyncPrepare) {
+            // if we are preparing synchronously, no need to notify listener
             return;
         }
+    } else if (mState == STATE_STOPPED) {
+        // no need to notify listener
+        return;
     }
-    notifyListener(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
+    notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
 }
 
 void NuPlayerDriver::notifyFrameStats(
@@ -573,11 +615,19 @@
 
 void NuPlayerDriver::notifyListener(
         int msg, int ext1, int ext2, const Parcel *in) {
+    Mutex::Autolock autoLock(mLock);
+    notifyListener_l(msg, ext1, ext2, in);
+}
+
+void NuPlayerDriver::notifyListener_l(
+        int msg, int ext1, int ext2, const Parcel *in) {
     switch (msg) {
         case MEDIA_PLAYBACK_COMPLETE:
         {
             if (mLooping) {
+                mLock.unlock();
                 mPlayer->seekToAsync(0);
+                mLock.lock();
                 break;
             }
             // fall through
@@ -586,6 +636,7 @@
         case MEDIA_ERROR:
         {
             mAtEOS = true;
+            setPauseStartedTimeIfNeeded();
             break;
         }
 
@@ -593,7 +644,9 @@
             break;
     }
 
+    mLock.unlock();
     sendEvent(msg, ext1, ext2, in);
+    mLock.lock();
 }
 
 void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
@@ -622,15 +675,17 @@
     mAsyncResult = err;
 
     if (err == OK) {
-        if (mIsAsyncPrepare) {
-            notifyListener(MEDIA_PREPARED);
-        }
+        // update state before notifying client, so that if client calls back into NuPlayerDriver
+        // in response, NuPlayerDriver has the right state
         mState = STATE_PREPARED;
-    } else {
         if (mIsAsyncPrepare) {
-            notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+            notifyListener_l(MEDIA_PREPARED);
         }
+    } else {
         mState = STATE_UNPREPARED;
+        if (mIsAsyncPrepare) {
+            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+        }
     }
 
     mCondition.broadcast();
@@ -642,4 +697,10 @@
     mPlayerFlags = flags;
 }
 
+void NuPlayerDriver::setPauseStartedTimeIfNeeded() {
+    if (mPauseStartedTimeUs == -1) {
+        mPauseStartedTimeUs = ALooper::GetNowUs();
+    }
+}
+
 }  // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index f520395..e81d605 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -66,9 +66,11 @@
     void notifySetDataSourceCompleted(status_t err);
     void notifyPrepareCompleted(status_t err);
     void notifyResetComplete();
+    void notifySetSurfaceComplete();
     void notifyDuration(int64_t durationUs);
     void notifyPosition(int64_t positionUs);
     void notifySeekComplete();
+    void notifySeekComplete_l();
     void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped);
     void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
     void notifyFlagsChanged(uint32_t flags);
@@ -101,8 +103,11 @@
 
     // The following are protected through "mLock"
     // >>>
+    bool mSetSurfaceInProgress;
     int64_t mDurationUs;
     int64_t mPositionUs;
+    int64_t mNotifyTimeRealUs;
+    int64_t mPauseStartedTimeUs;
     int64_t mNumFramesTotal;
     int64_t mNumFramesDropped;
     // <<<
@@ -117,6 +122,8 @@
     int64_t mStartupSeekTimeUs;
 
     status_t prepare_l();
+    void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
+    void setPauseStartedTimeIfNeeded();
 
     DISALLOW_EVIL_CONSTRUCTORS(NuPlayerDriver);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 3777f64..a3c976d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -93,10 +93,14 @@
     {
         Mutex::Autolock autoLock(mFlushLock);
         if (audio) {
-            CHECK(!mFlushingAudio);
+            if (mFlushingAudio) {
+                return;
+            }
             mFlushingAudio = true;
         } else {
-            CHECK(!mFlushingVideo);
+            if (mFlushingVideo) {
+                return;
+            }
             mFlushingVideo = true;
         }
     }
@@ -115,6 +119,14 @@
     mSyncQueues = false;
 }
 
+void NuPlayer::Renderer::signalAudioSinkChanged() {
+    (new AMessage(kWhatAudioSinkChanged, id()))->post();
+}
+
+void NuPlayer::Renderer::signalDisableOffloadAudio() {
+    (new AMessage(kWhatDisableOffloadAudio, id()))->post();
+}
+
 void NuPlayer::Renderer::pause() {
     (new AMessage(kWhatPause, id()))->post();
 }
@@ -251,14 +263,6 @@
     msg->post(delayUs);
 }
 
-void NuPlayer::Renderer::signalAudioSinkChanged() {
-    (new AMessage(kWhatAudioSinkChanged, id()))->post();
-}
-
-void NuPlayer::Renderer::signalDisableOffloadAudio() {
-    (new AMessage(kWhatDisableOffloadAudio, id()))->post();
-}
-
 void NuPlayer::Renderer::prepareForMediaRenderingStart() {
     mAudioRenderingStartGeneration = mAudioQueueGeneration;
     mVideoRenderingStartGeneration = mVideoQueueGeneration;
@@ -444,11 +448,13 @@
             copy = numBytesAvailableToWrite;
         }
 
-        CHECK_EQ(mAudioSink->write(
-                    entry->mBuffer->data() + entry->mOffset, copy),
-                 (ssize_t)copy);
+        ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset, copy);
+        if (written < 0) {
+            // An error in AudioSink write is fatal here.
+            LOG_ALWAYS_FATAL("AudioSink write error(%zd) when writing %zu bytes", written, copy);
+        }
 
-        entry->mOffset += copy;
+        entry->mOffset += written;
         if (entry->mOffset == entry->mBuffer->size()) {
             entry->mNotifyConsumed->post();
             mAudioQueue.erase(mAudioQueue.begin());
@@ -456,13 +462,33 @@
             entry = NULL;
         }
 
-        numBytesAvailableToWrite -= copy;
-        size_t copiedFrames = copy / mAudioSink->frameSize();
+        numBytesAvailableToWrite -= written;
+        size_t copiedFrames = written / mAudioSink->frameSize();
         mNumFramesWritten += copiedFrames;
 
         notifyIfMediaRenderingStarted();
-    }
 
+        if (written != (ssize_t)copy) {
+            // A short count was received from AudioSink::write()
+            //
+            // AudioSink write should block until exactly the number of bytes are delivered.
+            // But it may return with a short count (without an error) when:
+            //
+            // 1) Size to be copied is not a multiple of the frame size. We consider this fatal.
+            // 2) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded.
+
+            // (Case 1)
+            // Must be a multiple of the frame size.  If it is not a multiple of a frame size, it
+            // needs to fail, as we should not carry over fractional frames between calls.
+            CHECK_EQ(copy % mAudioSink->frameSize(), 0);
+
+            // (Case 2)
+            // Return early to the caller.
+            // Beware of calling immediately again as this may busy-loop if you are not careful.
+            ALOGW("AudioSink write short frame count %zd < %zu", written, copy);
+            break;
+        }
+    }
     notifyPosition();
 
     return !mAudioQueue.empty();
@@ -717,6 +743,15 @@
     int32_t audio;
     CHECK(msg->findInt32("audio", &audio));
 
+    {
+        Mutex::Autolock autoLock(mFlushLock);
+        if (audio) {
+            mFlushingAudio = false;
+        } else {
+            mFlushingVideo = false;
+        }
+    }
+
     // If we're currently syncing the queues, i.e. dropping audio while
     // aligning the first audio/video buffer times and only one of the
     // two queues has data, we may starve that queue by not requesting
@@ -735,17 +770,18 @@
         {
             Mutex::Autolock autoLock(mLock);
             flushQueue(&mAudioQueue);
+
+            ++mAudioQueueGeneration;
+            prepareForMediaRenderingStart();
+
+            if (offloadingAudio()) {
+                mFirstAudioTimeUs = -1;
+            }
         }
 
-        Mutex::Autolock autoLock(mFlushLock);
-        mFlushingAudio = false;
-
         mDrainAudioQueuePending = false;
-        ++mAudioQueueGeneration;
 
-        prepareForMediaRenderingStart();
         if (offloadingAudio()) {
-            mFirstAudioTimeUs = -1;
             mAudioSink->pause();
             mAudioSink->flush();
             mAudioSink->start();
@@ -753,9 +789,6 @@
     } else {
         flushQueue(&mVideoQueue);
 
-        Mutex::Autolock autoLock(mFlushLock);
-        mFlushingVideo = false;
-
         mDrainVideoQueuePending = false;
         ++mVideoQueueGeneration;
 
@@ -853,13 +886,15 @@
 void NuPlayer::Renderer::onPause() {
     CHECK(!mPaused);
 
+    {
+        Mutex::Autolock autoLock(mLock);
+        ++mAudioQueueGeneration;
+        ++mVideoQueueGeneration;
+        prepareForMediaRenderingStart();
+    }
+
     mDrainAudioQueuePending = false;
-    ++mAudioQueueGeneration;
-
     mDrainVideoQueuePending = false;
-    ++mVideoQueueGeneration;
-
-    prepareForMediaRenderingStart();
 
     if (mHasAudio) {
         mAudioSink->pause();
@@ -896,7 +931,12 @@
     uint32_t numFramesPlayed;
     CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
 
-    int64_t currentPositionUs = mFirstAudioTimeUs
+    int64_t firstAudioTimeUs;
+    {
+        Mutex::Autolock autoLock(mLock);
+        firstAudioTimeUs = mFirstAudioTimeUs;
+    }
+    int64_t currentPositionUs = firstAudioTimeUs
             + (numFramesPlayed * mAudioSink->msecsPerFrame()) * 1000ll;
 
     mAudioSink->stop();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 74892b6..45657c2 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -45,6 +45,7 @@
         kWhatPrepared,
         kWhatFlagsChanged,
         kWhatVideoSizeChanged,
+        kWhatBufferingUpdate,
         kWhatBufferingStart,
         kWhatBufferingEnd,
         kWhatSubtitleData,
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index b44d5cc..e4e463a 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -813,7 +813,10 @@
 
     for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
         BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
-        cancelBufferToNativeWindow(info);
+        status_t error = cancelBufferToNativeWindow(info);
+        if (err == 0) {
+            err = error;
+        }
     }
 
     return err;
@@ -888,11 +891,12 @@
     int err = mNativeWindow->cancelBuffer(
         mNativeWindow.get(), info->mGraphicBuffer.get(), -1);
 
-    CHECK_EQ(err, 0);
+    ALOGW_IF(err != 0, "[%s] can not return buffer %u to native window",
+            mComponentName.c_str(), info->mBufferID);
 
     info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
 
-    return OK;
+    return err;
 }
 
 ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
@@ -992,7 +996,7 @@
 
     if (portIndex == kPortIndexOutput && mNativeWindow != NULL
             && info->mStatus == BufferInfo::OWNED_BY_US) {
-        CHECK_EQ((status_t)OK, cancelBufferToNativeWindow(info));
+        cancelBufferToNativeWindow(info);
     }
 
     CHECK_EQ(mOMX->freeBuffer(
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 008da5a..9d6fd78 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -186,9 +186,9 @@
         const sp<IMediaHTTPService> &httpService,
         const char *uri,
         const KeyedVector<String8, String8> *headers,
-        AString *sniffedMIME) {
-    if (sniffedMIME != NULL) {
-        *sniffedMIME = "";
+        String8 *contentType) {
+    if (contentType != NULL) {
+        *contentType = "";
     }
 
     bool isWidevine = !strncasecmp("widevine://", uri, 11);
@@ -226,77 +226,14 @@
         }
 
         if (!isWidevine) {
-            String8 contentType = httpSource->getMIMEType();
+            if (contentType != NULL) {
+                *contentType = httpSource->getMIMEType();
+            }
 
-            sp<NuCachedSource2> cachedSource = new NuCachedSource2(
+            source = new NuCachedSource2(
                     httpSource,
                     cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
                     disconnectAtHighwatermark);
-
-            if (strncasecmp(contentType.string(), "audio/", 6)) {
-                // We're not doing this for streams that appear to be audio-only
-                // streams to ensure that even low bandwidth streams start
-                // playing back fairly instantly.
-
-                // We're going to prefill the cache before trying to instantiate
-                // the extractor below, as the latter is an operation that otherwise
-                // could block on the datasource for a significant amount of time.
-                // During that time we'd be unable to abort the preparation phase
-                // without this prefill.
-
-                // Initially make sure we have at least 192 KB for the sniff
-                // to complete without blocking.
-                static const size_t kMinBytesForSniffing = 192 * 1024;
-
-                off64_t metaDataSize = -1ll;
-                for (;;) {
-                    status_t finalStatus;
-                    size_t cachedDataRemaining =
-                            cachedSource->approxDataRemaining(&finalStatus);
-
-                    if (finalStatus != OK || (metaDataSize >= 0
-                            && (off64_t)cachedDataRemaining >= metaDataSize)) {
-                        ALOGV("stop caching, status %d, "
-                                "metaDataSize %lld, cachedDataRemaining %zu",
-                                finalStatus, metaDataSize, cachedDataRemaining);
-                        break;
-                    }
-
-                    ALOGV("now cached %zu bytes of data", cachedDataRemaining);
-
-                    if (metaDataSize < 0
-                            && cachedDataRemaining >= kMinBytesForSniffing) {
-                        String8 tmp;
-                        float confidence;
-                        sp<AMessage> meta;
-                        if (!cachedSource->sniff(&tmp, &confidence, &meta)) {
-                            return NULL;
-                        }
-
-                        // We successfully identified the file's extractor to
-                        // be, remember this mime type so we don't have to
-                        // sniff it again when we call MediaExtractor::Create()
-                        if (sniffedMIME != NULL) {
-                            *sniffedMIME = tmp.string();
-                        }
-
-                        if (meta == NULL
-                                || !meta->findInt64("meta-data-size",
-                                     reinterpret_cast<int64_t*>(&metaDataSize))) {
-                            metaDataSize = kDefaultMetaSize;
-                        }
-
-                        if (metaDataSize < 0ll) {
-                            ALOGE("invalid metaDataSize = %lld bytes", metaDataSize);
-                            return NULL;
-                        }
-                    }
-
-                    usleep(200000);
-                }
-            }
-
-            source = cachedSource;
         } else {
             // We do not want that prefetching, caching, datasource wrapper
             // in the widevine:// case.
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7bb7ed9..76f730f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -716,12 +716,13 @@
                     CHECK(msg->findInt32("err", &err));
                     CHECK(msg->findInt32("actionCode", &actionCode));
 
-                    ALOGE("Codec reported err %#x, actionCode %d", err, actionCode);
+                    ALOGE("Codec reported err %#x, actionCode %d, while in state %d",
+                            err, actionCode, mState);
                     if (err == DEAD_OBJECT) {
                         mFlags |= kFlagSawMediaServerDie;
                     }
 
-                    bool sendErrorReponse = true;
+                    bool sendErrorResponse = true;
 
                     switch (mState) {
                         case INITIALIZING:
@@ -748,7 +749,7 @@
                             // Ignore the error, assuming we'll still get
                             // the shutdown complete notification.
 
-                            sendErrorReponse = false;
+                            sendErrorResponse = false;
 
                             if (mFlags & kFlagSawMediaServerDie) {
                                 // MediaServer died, there definitely won't
@@ -767,15 +768,19 @@
 
                         case FLUSHING:
                         {
-                            setState(
-                                    (mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
+                            if (actionCode == ACTION_CODE_FATAL) {
+                                setState(UNINITIALIZED);
+                            } else {
+                                setState(
+                                        (mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
+                            }
                             break;
                         }
 
                         case FLUSHED:
                         case STARTED:
                         {
-                            sendErrorReponse = false;
+                            sendErrorResponse = false;
 
                             setStickyError(err);
                             postActivityNotificationIfPossible();
@@ -800,7 +805,7 @@
 
                         default:
                         {
-                            sendErrorReponse = false;
+                            sendErrorResponse = false;
 
                             setStickyError(err);
                             postActivityNotificationIfPossible();
@@ -826,7 +831,7 @@
                         }
                     }
 
-                    if (sendErrorReponse) {
+                    if (sendErrorResponse) {
                         PostReplyWithError(mReplyID, err);
                     }
                     break;
@@ -1113,7 +1118,11 @@
 
                 case CodecBase::kWhatFlushCompleted:
                 {
-                    CHECK_EQ(mState, FLUSHING);
+                    if (mState != FLUSHING) {
+                        ALOGW("received FlushCompleted message in state %d",
+                                mState);
+                        break;
+                    }
 
                     if (mFlags & kFlagIsAsync) {
                         setState(FLUSHED);
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 09c6e69..8b4dd6f 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -493,7 +493,8 @@
     return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
 }
 
-void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
+
+void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) {
     if (mSignalledError || mOutputPortSettingsChange != NONE) {
         return;
     }
@@ -505,59 +506,54 @@
     List<BufferInfo *> &inQueue = getPortQueue(0);
     List<BufferInfo *> &outQueue = getPortQueue(1);
 
-    if (portIndex == 0 && mInputBufferCount == 0) {
-        BufferInfo *inInfo = *inQueue.begin();
-        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
-
-        inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
-        inBufferLength[0] = inHeader->nFilledLen;
-
-        AAC_DECODER_ERROR decoderErr =
-            aacDecoder_ConfigRaw(mAACDecoder,
-                                 inBuffer,
-                                 inBufferLength);
-
-        if (decoderErr != AAC_DEC_OK) {
-            ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
-            mSignalledError = true;
-            notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
-            return;
-        }
-
-        mInputBufferCount++;
-        mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
-
-        inInfo->mOwnedByUs = false;
-        inQueue.erase(inQueue.begin());
-        inInfo = NULL;
-        notifyEmptyBufferDone(inHeader);
-        inHeader = NULL;
-
-        configureDownmix();
-        // Only send out port settings changed event if both sample rate
-        // and numChannels are valid.
-        if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
-            ALOGI("Initially configuring decoder: %d Hz, %d channels",
-                mStreamInfo->sampleRate,
-                mStreamInfo->numChannels);
-
-            notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
-            mOutputPortSettingsChange = AWAITING_DISABLED;
-        }
-
-        return;
-    }
-
     while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) {
         if (!inQueue.empty()) {
             INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
             BufferInfo *inInfo = *inQueue.begin();
             OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
 
-            if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
-                mEndOfInput = true;
-            } else {
-                mEndOfInput = false;
+            mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
+            if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
+                BufferInfo *inInfo = *inQueue.begin();
+                OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+                inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+                inBufferLength[0] = inHeader->nFilledLen;
+
+                AAC_DECODER_ERROR decoderErr =
+                    aacDecoder_ConfigRaw(mAACDecoder,
+                                         inBuffer,
+                                         inBufferLength);
+
+                if (decoderErr != AAC_DEC_OK) {
+                    ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                    return;
+                }
+
+                mInputBufferCount++;
+                mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
+
+                inInfo->mOwnedByUs = false;
+                inQueue.erase(inQueue.begin());
+                mLastInHeader = NULL;
+                inInfo = NULL;
+                notifyEmptyBufferDone(inHeader);
+                inHeader = NULL;
+
+                configureDownmix();
+                // Only send out port settings changed event if both sample rate
+                // and numChannels are valid.
+                if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
+                    ALOGI("Initially configuring decoder: %d Hz, %d channels",
+                        mStreamInfo->sampleRate,
+                        mStreamInfo->numChannels);
+
+                    notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                    mOutputPortSettingsChange = AWAITING_DISABLED;
+                }
+                return;
             }
 
             if (inHeader->nFilledLen == 0) {
@@ -567,206 +563,193 @@
                 inInfo = NULL;
                 notifyEmptyBufferDone(inHeader);
                 inHeader = NULL;
-            } else {
-                if (mIsADTS) {
-                    size_t adtsHeaderSize = 0;
-                    // skip 30 bits, aac_frame_length follows.
-                    // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+                continue;
+            }
 
-                    const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+            if (mIsADTS) {
+                size_t adtsHeaderSize = 0;
+                // skip 30 bits, aac_frame_length follows.
+                // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
 
-                    bool signalError = false;
-                    if (inHeader->nFilledLen < 7) {
-                        ALOGE("Audio data too short to contain even the ADTS header. "
-                                "Got %d bytes.", inHeader->nFilledLen);
+                const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+
+                bool signalError = false;
+                if (inHeader->nFilledLen < 7) {
+                    ALOGE("Audio data too short to contain even the ADTS header. "
+                            "Got %d bytes.", inHeader->nFilledLen);
+                    hexdump(adtsHeader, inHeader->nFilledLen);
+                    signalError = true;
+                } else {
+                    bool protectionAbsent = (adtsHeader[1] & 1);
+
+                    unsigned aac_frame_length =
+                        ((adtsHeader[3] & 3) << 11)
+                        | (adtsHeader[4] << 3)
+                        | (adtsHeader[5] >> 5);
+
+                    if (inHeader->nFilledLen < aac_frame_length) {
+                        ALOGE("Not enough audio data for the complete frame. "
+                                "Got %d bytes, frame size according to the ADTS "
+                                "header is %u bytes.",
+                                inHeader->nFilledLen, aac_frame_length);
                         hexdump(adtsHeader, inHeader->nFilledLen);
                         signalError = true;
                     } else {
-                        bool protectionAbsent = (adtsHeader[1] & 1);
+                        adtsHeaderSize = (protectionAbsent ? 7 : 9);
 
-                        unsigned aac_frame_length =
-                            ((adtsHeader[3] & 3) << 11)
-                            | (adtsHeader[4] << 3)
-                            | (adtsHeader[5] >> 5);
+                        inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
+                        inBufferLength[0] = aac_frame_length - adtsHeaderSize;
 
-                        if (inHeader->nFilledLen < aac_frame_length) {
-                            ALOGE("Not enough audio data for the complete frame. "
-                                    "Got %d bytes, frame size according to the ADTS "
-                                    "header is %u bytes.",
-                                    inHeader->nFilledLen, aac_frame_length);
-                            hexdump(adtsHeader, inHeader->nFilledLen);
-                            signalError = true;
-                        } else {
-                            adtsHeaderSize = (protectionAbsent ? 7 : 9);
-
-                            inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
-                            inBufferLength[0] = aac_frame_length - adtsHeaderSize;
-
-                            inHeader->nOffset += adtsHeaderSize;
-                            inHeader->nFilledLen -= adtsHeaderSize;
-                        }
-                    }
-
-                    if (signalError) {
-                        mSignalledError = true;
-
-                        notify(OMX_EventError,
-                               OMX_ErrorStreamCorrupt,
-                               ERROR_MALFORMED,
-                               NULL);
-
-                        return;
-                    }
-                } else {
-                    inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
-                    inBufferLength[0] = inHeader->nFilledLen;
-                }
-
-                // Fill and decode
-                bytesValid[0] = inBufferLength[0];
-
-                INT prevSampleRate = mStreamInfo->sampleRate;
-                INT prevNumChannels = mStreamInfo->numChannels;
-
-                if (inHeader != mLastInHeader) {
-                    mLastInHeader = inHeader;
-                    mCurrentInputTime = inHeader->nTimeStamp;
-                } else {
-                    if (mStreamInfo->sampleRate) {
-                        mCurrentInputTime += mStreamInfo->aacSamplesPerFrame *
-                                1000000ll / mStreamInfo->sampleRate;
-                    } else {
-                        ALOGW("no sample rate yet");
+                        inHeader->nOffset += adtsHeaderSize;
+                        inHeader->nFilledLen -= adtsHeaderSize;
                     }
                 }
-                mAnchorTimes.add(mCurrentInputTime);
-                aacDecoder_Fill(mAACDecoder,
-                                inBuffer,
-                                inBufferLength,
-                                bytesValid);
 
-                 // run DRC check
-                 mDrcWrap.submitStreamData(mStreamInfo);
-                 mDrcWrap.update();
-
-                AAC_DECODER_ERROR decoderErr =
-                    aacDecoder_DecodeFrame(mAACDecoder,
-                                           tmpOutBuffer,
-                                           2048 * MAX_CHANNEL_COUNT,
-                                           0 /* flags */);
-
-                if (decoderErr != AAC_DEC_OK) {
-                    ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
-                }
-
-                if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
-                    ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+                if (signalError) {
                     mSignalledError = true;
-                    notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                    notify(OMX_EventError, OMX_ErrorStreamCorrupt, ERROR_MALFORMED, NULL);
                     return;
                 }
+            } else {
+                inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+                inBufferLength[0] = inHeader->nFilledLen;
+            }
 
-                if (bytesValid[0] != 0) {
-                    ALOGE("bytesValid[0] != 0 should never happen");
-                    mSignalledError = true;
-                    notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
-                    return;
-                }
+            // Fill and decode
+            bytesValid[0] = inBufferLength[0];
 
-                size_t numOutBytes =
-                    mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+            INT prevSampleRate = mStreamInfo->sampleRate;
+            INT prevNumChannels = mStreamInfo->numChannels;
 
-                if (decoderErr == AAC_DEC_OK) {
-                    if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
-                            mStreamInfo->frameSize * mStreamInfo->numChannels)) {
-                        mSignalledError = true;
-                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
-                        return;
-                    }
-                    UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
-                    inHeader->nFilledLen -= inBufferUsedLength;
-                    inHeader->nOffset += inBufferUsedLength;
+            if (inHeader != mLastInHeader) {
+                mLastInHeader = inHeader;
+                mCurrentInputTime = inHeader->nTimeStamp;
+            } else {
+                if (mStreamInfo->sampleRate) {
+                    mCurrentInputTime += mStreamInfo->aacSamplesPerFrame *
+                            1000000ll / mStreamInfo->sampleRate;
                 } else {
-                    ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
-
-                    memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
-
-                    if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
-                            mStreamInfo->frameSize * mStreamInfo->numChannels)) {
-                        mSignalledError = true;
-                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
-                        return;
-                    }
-
-                    // Discard input buffer.
-                    inHeader->nFilledLen = 0;
-
-                    aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-
-                    // fall through
+                    ALOGW("no sample rate yet");
                 }
+            }
+            mAnchorTimes.add(mCurrentInputTime);
+            aacDecoder_Fill(mAACDecoder,
+                            inBuffer,
+                            inBufferLength,
+                            bytesValid);
 
-                /*
-                 * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
-                 * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
-                 * rate system and the sampling rate in the final output is actually
-                 * doubled compared with the core AAC decoder sampling rate.
-                 *
-                 * Explicit signalling is done by explicitly defining SBR audio object
-                 * type in the bitstream. Implicit signalling is done by embedding
-                 * SBR content in AAC extension payload specific to SBR, and hence
-                 * requires an AAC decoder to perform pre-checks on actual audio frames.
-                 *
-                 * Thus, we could not say for sure whether a stream is
-                 * AAC+/eAAC+ until the first data frame is decoded.
-                 */
-                if (mOutputBufferCount > 1) {
-                    if (mStreamInfo->sampleRate != prevSampleRate ||
-                        mStreamInfo->numChannels != prevNumChannels) {
-                        ALOGE("can not reconfigure AAC output");
-                        mSignalledError = true;
-                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
-                        return;
-                    }
-                }
-                if (mInputBufferCount <= 2) { // TODO: <= 1
-                    if (mStreamInfo->sampleRate != prevSampleRate ||
-                        mStreamInfo->numChannels != prevNumChannels) {
-                        ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
-                              prevSampleRate, mStreamInfo->sampleRate,
-                              prevNumChannels, mStreamInfo->numChannels);
+             // run DRC check
+             mDrcWrap.submitStreamData(mStreamInfo);
+             mDrcWrap.update();
 
-                        notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
-                        mOutputPortSettingsChange = AWAITING_DISABLED;
+            AAC_DECODER_ERROR decoderErr =
+                aacDecoder_DecodeFrame(mAACDecoder,
+                                       tmpOutBuffer,
+                                       2048 * MAX_CHANNEL_COUNT,
+                                       0 /* flags */);
 
-                        if (inHeader->nFilledLen == 0) {
-                            inInfo->mOwnedByUs = false;
-                            mInputBufferCount++;
-                            inQueue.erase(inQueue.begin());
-                            mLastInHeader = NULL;
-                            inInfo = NULL;
-                            notifyEmptyBufferDone(inHeader);
-                            inHeader = NULL;
-                        }
-                        return;
-                    }
-                } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
-                    ALOGW("Invalid AAC stream");
+            if (decoderErr != AAC_DEC_OK) {
+                ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+            }
+
+            if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+                ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
+            }
+
+            if (bytesValid[0] != 0) {
+                ALOGE("bytesValid[0] != 0 should never happen");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
+            }
+
+            size_t numOutBytes =
+                mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+
+            if (decoderErr == AAC_DEC_OK) {
+                if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                        mStreamInfo->frameSize * mStreamInfo->numChannels)) {
                     mSignalledError = true;
                     notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
                     return;
                 }
-                if (inHeader->nFilledLen == 0) {
-                    inInfo->mOwnedByUs = false;
-                    mInputBufferCount++;
-                    inQueue.erase(inQueue.begin());
-                    mLastInHeader = NULL;
-                    inInfo = NULL;
-                    notifyEmptyBufferDone(inHeader);
-                    inHeader = NULL;
-                } else {
-                    ALOGV("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+                UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+                inHeader->nFilledLen -= inBufferUsedLength;
+                inHeader->nOffset += inBufferUsedLength;
+            } else {
+                ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
+
+                memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
+
+                if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                        mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                    return;
                 }
+
+                // Discard input buffer.
+                inHeader->nFilledLen = 0;
+
+                aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+
+                // fall through
+            }
+
+            /*
+             * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+             * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+             * rate system and the sampling rate in the final output is actually
+             * doubled compared with the core AAC decoder sampling rate.
+             *
+             * Explicit signalling is done by explicitly defining SBR audio object
+             * type in the bitstream. Implicit signalling is done by embedding
+             * SBR content in AAC extension payload specific to SBR, and hence
+             * requires an AAC decoder to perform pre-checks on actual audio frames.
+             *
+             * Thus, we could not say for sure whether a stream is
+             * AAC+/eAAC+ until the first data frame is decoded.
+             */
+            if (mInputBufferCount <= 2 || mOutputBufferCount > 1) { // TODO: <= 1
+                if (mStreamInfo->sampleRate != prevSampleRate ||
+                    mStreamInfo->numChannels != prevNumChannels) {
+                    ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+                          prevSampleRate, mStreamInfo->sampleRate,
+                          prevNumChannels, mStreamInfo->numChannels);
+
+                    notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                    mOutputPortSettingsChange = AWAITING_DISABLED;
+
+                    if (inHeader->nFilledLen == 0) {
+                        inInfo->mOwnedByUs = false;
+                        mInputBufferCount++;
+                        inQueue.erase(inQueue.begin());
+                        mLastInHeader = NULL;
+                        inInfo = NULL;
+                        notifyEmptyBufferDone(inHeader);
+                        inHeader = NULL;
+                    }
+                    return;
+                }
+            } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+                ALOGW("Invalid AAC stream");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                return;
+            }
+            if (inHeader->nFilledLen == 0) {
+                inInfo->mOwnedByUs = false;
+                mInputBufferCount++;
+                inQueue.erase(inQueue.begin());
+                mLastInHeader = NULL;
+                inInfo = NULL;
+                notifyEmptyBufferDone(inHeader);
+                inHeader = NULL;
+            } else {
+                ALOGV("inHeader->nFilledLen = %d", inHeader->nFilledLen);
             }
         }
 
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 8667a6b..7b18348 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -1019,11 +1019,19 @@
 }
 
 size_t LiveSession::getTrackCount() const {
-    return mPlaylist->getTrackCount();
+    if (mPlaylist == NULL) {
+        return 0;
+    } else {
+        return mPlaylist->getTrackCount();
+    }
 }
 
 sp<AMessage> LiveSession::getTrackInfo(size_t trackIndex) const {
-    return mPlaylist->getTrackInfo(trackIndex);
+    if (mPlaylist == NULL) {
+        return NULL;
+    } else {
+        return mPlaylist->getTrackInfo(trackIndex);
+    }
 }
 
 status_t LiveSession::selectTrack(size_t index, bool select) {
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 98b50dd..7eb6542 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -249,11 +249,15 @@
         mPackets.push_back(buffer);
     } else {
         // hexdump(buffer->data(), buffer->size());
+        if (buffer->size() < 2) {
+            return MALFORMED_PACKET;
+        }
 
-        CHECK_GE(buffer->size(), 2u);
         unsigned AU_headers_length = U16_AT(buffer->data());  // in bits
 
-        CHECK_GE(buffer->size(), 2 + (AU_headers_length + 7) / 8);
+        if (buffer->size() < 2 + (AU_headers_length + 7) / 8) {
+            return MALFORMED_PACKET;
+        }
 
         List<AUHeader> headers;
 
@@ -342,7 +346,9 @@
              it != headers.end(); ++it) {
             const AUHeader &header = *it;
 
-            CHECK_LE(offset + header.mSize, buffer->size());
+            if (buffer->size() < offset + header.mSize) {
+                return MALFORMED_PACKET;
+            }
 
             sp<ABuffer> accessUnit = new ABuffer(header.mSize);
             memcpy(accessUnit->data(), buffer->data() + offset, header.mSize);
@@ -353,7 +359,10 @@
             mPackets.push_back(accessUnit);
         }
 
-        CHECK_EQ(offset, buffer->size());
+        if (offset != buffer->size()) {
+            ALOGW("potentially malformed packet (offset %d, size %d)",
+                    offset, buffer->size());
+        }
     }
 
     queue->erase(queue->begin());
@@ -400,6 +409,7 @@
         const sp<ARTPSource> &source) {
     AssemblyStatus status = addPacket(source);
     if (status == MALFORMED_PACKET) {
+        ALOGI("access unit is damaged");
         mAccessUnitDamaged = true;
     }
     return status;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 7766b90..fd5a426 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -487,12 +487,12 @@
         }
         if (client == NULL) {
             needsNewClient = true;
-            ret = connectHelperLocked(/*cameraClient*/NULL, // Empty binder callbacks
+            ret = connectHelperLocked(/*out*/client,
+                                      /*cameraClient*/NULL, // Empty binder callbacks
                                       cameraId,
                                       internalPackageName,
                                       uid,
-                                      pid,
-                                      client);
+                                      pid);
 
             if (ret != OK) {
                 // Error already logged by callee
@@ -659,14 +659,17 @@
     return true;
 }
 
-status_t CameraService::connectHelperLocked(const sp<ICameraClient>& cameraClient,
-                                      int cameraId,
-                                      const String16& clientPackageName,
-                                      int clientUid,
-                                      int callingPid,
-                                      /*out*/
-                                      sp<Client>& client,
-                                      int halVersion) {
+status_t CameraService::connectHelperLocked(
+        /*out*/
+        sp<Client>& client,
+        /*in*/
+        const sp<ICameraClient>& cameraClient,
+        int cameraId,
+        const String16& clientPackageName,
+        int clientUid,
+        int callingPid,
+        int halVersion,
+        bool legacyMode) {
 
     int facing = -1;
     int deviceVersion = getDeviceVersion(cameraId, &facing);
@@ -678,7 +681,7 @@
           case CAMERA_DEVICE_API_VERSION_1_0:
             client = new CameraClient(this, cameraClient,
                     clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid());
+                    facing, callingPid, clientUid, getpid(), legacyMode);
             break;
           case CAMERA_DEVICE_API_VERSION_2_0:
           case CAMERA_DEVICE_API_VERSION_2_1:
@@ -687,7 +690,7 @@
           case CAMERA_DEVICE_API_VERSION_3_2:
             client = new Camera2Client(this, cameraClient,
                     clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid());
+                    facing, callingPid, clientUid, getpid(), legacyMode);
             break;
           case -1:
             ALOGE("Invalid camera id %d", cameraId);
@@ -704,7 +707,7 @@
             // Only support higher HAL version device opened as HAL1.0 device.
             client = new CameraClient(this, cameraClient,
                     clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid());
+                    facing, callingPid, clientUid, getpid(), legacyMode);
         } else {
             // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
             ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
@@ -760,12 +763,12 @@
             return OK;
         }
 
-        status = connectHelperLocked(cameraClient,
+        status = connectHelperLocked(/*out*/client,
+                                     cameraClient,
                                      cameraId,
                                      clientPackageName,
                                      clientUid,
-                                     callingPid,
-                                     client);
+                                     callingPid);
         if (status != OK) {
             return status;
         }
@@ -823,13 +826,14 @@
             return OK;
         }
 
-        status = connectHelperLocked(cameraClient,
+        status = connectHelperLocked(/*out*/client,
+                                     cameraClient,
                                      cameraId,
                                      clientPackageName,
                                      clientUid,
                                      callingPid,
-                                     client,
-                                     halVersion);
+                                     halVersion,
+                                     /*legacyMode*/true);
         if (status != OK) {
             return status;
         }
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index cb98c96..a7328cf 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -452,14 +452,17 @@
      *
      * Returns OK on success, or a negative error code.
      */
-    status_t            connectHelperLocked(const sp<ICameraClient>& cameraClient,
-                                      int cameraId,
-                                      const String16& clientPackageName,
-                                      int clientUid,
-                                      int callingPid,
-                                      /*out*/
-                                      sp<Client>& client,
-                                      int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED);
+    status_t            connectHelperLocked(
+            /*out*/
+            sp<Client>& client,
+            /*in*/
+            const sp<ICameraClient>& cameraClient,
+            int cameraId,
+            const String16& clientPackageName,
+            int clientUid,
+            int callingPid,
+            int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED,
+            bool legacyMode = false);
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 5eb5181..bc40971 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -53,7 +53,8 @@
         int cameraFacing,
         int clientPid,
         uid_t clientUid,
-        int servicePid):
+        int servicePid,
+        bool legacyMode):
         Camera2ClientBase(cameraService, cameraClient, clientPackageName,
                 cameraId, cameraFacing, clientPid, clientUid, servicePid),
         mParameters(cameraId, cameraFacing)
@@ -62,6 +63,8 @@
 
     SharedParameters::Lock l(mParameters);
     l.mParameters.state = Parameters::DISCONNECTED;
+
+    mLegacyMode = legacyMode;
 }
 
 status_t Camera2Client::initialize(camera_module_t *module)
@@ -1449,6 +1452,13 @@
         return OK;
     }
 
+    // the camera2 api legacy mode can unconditionally disable the shutter sound
+    if (mLegacyMode) {
+        ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__);
+        l.mParameters.playShutterSound = false;
+        return OK;
+    }
+
     // Disabling shutter sound may not be allowed. In that case only
     // allow the mediaserver process to disable the sound.
     char value[PROPERTY_VALUE_MAX];
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 5ce757a..f5c3a30 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -89,7 +89,8 @@
             int cameraFacing,
             int clientPid,
             uid_t clientUid,
-            int servicePid);
+            int servicePid,
+            bool legacyMode);
 
     virtual ~Camera2Client();
 
@@ -203,6 +204,7 @@
     bool mAfInMotion;
 
     /** Utility members */
+    bool mLegacyMode;
 
     // Wait until the camera device has received the latest control settings
     status_t syncWithDevice();
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index fb6b678..abe1235 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -38,7 +38,7 @@
         const String16& clientPackageName,
         int cameraId, int cameraFacing,
         int clientPid, int clientUid,
-        int servicePid):
+        int servicePid, bool legacyMode):
         Client(cameraService, cameraClient, clientPackageName,
                 cameraId, cameraFacing, clientPid, clientUid, servicePid)
 {
@@ -54,6 +54,7 @@
     // Callback is disabled by default
     mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
     mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
+    mLegacyMode = legacyMode;
     mPlayShutterSound = true;
     LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
 }
@@ -576,6 +577,13 @@
         return OK;
     }
 
+    // the camera2 api legacy mode can unconditionally disable the shutter sound
+    if (mLegacyMode) {
+        ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__);
+        mPlayShutterSound = false;
+        return OK;
+    }
+
     // Disabling shutter sound may not be allowed. In that case only
     // allow the mediaserver process to disable the sound.
     char value[PROPERTY_VALUE_MAX];
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 4b89564..6779f5e 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -64,7 +64,8 @@
             int cameraFacing,
             int clientPid,
             int clientUid,
-            int servicePid);
+            int servicePid,
+            bool legacyMode = false);
     ~CameraClient();
 
     status_t initialize(camera_module_t *module);
@@ -129,6 +130,7 @@
     int                             mPreviewCallbackFlag;
     int                             mOrientation;     // Current display orientation
     bool                            mPlayShutterSound;
+    bool                            mLegacyMode; // camera2 api legacy mode?
 
     // Ensures atomicity among the public methods
     mutable Mutex                   mLock;
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 37de610..b388079 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -51,7 +51,8 @@
         mZslStreamId(NO_STREAM),
         mFrameListHead(0),
         mZslQueueHead(0),
-        mZslQueueTail(0) {
+        mZslQueueTail(0),
+        mHasFocuser(false) {
     // Initialize buffer queue and frame list based on pipeline max depth.
     size_t pipelineMaxDepth = kDefaultMaxPipelineDepth;
     if (client != 0) {
@@ -67,6 +68,11 @@
                         " use default pipeline max depth %zu", __FUNCTION__,
                         kDefaultMaxPipelineDepth);
             }
+
+            entry = device->info().find(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+            if (entry.count > 0 && entry.data.f[0] != 0.) {
+                mHasFocuser = true;
+            }
         }
     }
 
@@ -489,20 +495,23 @@
                     continue;
                 }
 
-                // Make sure the candidate frame has good focus.
-                entry = frame.find(ANDROID_CONTROL_AF_STATE);
-                if (entry.count == 0) {
-                    ALOGW("%s: ZSL queue frame has no AF state field!",
-                            __FUNCTION__);
-                    continue;
-                }
-                uint8_t afState = entry.data.u8[0];
-                if (afState != ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED &&
-                        afState != ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED &&
-                        afState != ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
-                    ALOGW("%s: ZSL queue frame AF state is %d is not good for capture, skip it",
-                            __FUNCTION__, afState);
-                    continue;
+                // Check AF state if device has focuser
+                if (mHasFocuser) {
+                    // Make sure the candidate frame has good focus.
+                    entry = frame.find(ANDROID_CONTROL_AF_STATE);
+                    if (entry.count == 0) {
+                        ALOGW("%s: ZSL queue frame has no AF state field!",
+                                __FUNCTION__);
+                        continue;
+                    }
+                    uint8_t afState = entry.data.u8[0];
+                    if (afState != ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED &&
+                            afState != ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED &&
+                            afState != ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
+                        ALOGW("%s: ZSL queue frame AF state is %d is not good for capture, skip it",
+                                __FUNCTION__, afState);
+                        continue;
+                    }
                 }
 
                 minTimestamp = frameTimestamp;
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index dfb1457..daa352b 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -121,6 +121,8 @@
 
     CameraMetadata mLatestCapturedRequest;
 
+    bool mHasFocuser;
+
     virtual bool threadLoop();
 
     status_t clearZslQueueLocked();
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 86f82a3..80c797a 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -254,9 +254,17 @@
 }
 
 status_t CameraDeviceClient::endConfigure() {
-    // TODO: Implement this.
-    ALOGE("%s: Not implemented yet.", __FUNCTION__);
-    return OK;
+    ALOGV("%s: ending configure (%zu streams)",
+            __FUNCTION__, mStreamMap.size());
+
+    status_t res;
+    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+    Mutex::Autolock icl(mBinderSerializationLock);
+
+    if (!mDevice.get()) return DEAD_OBJECT;
+
+    return mDevice->configureStreams();
 }
 
 status_t CameraDeviceClient::deleteStream(int streamId) {
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 9e124b0..d26e20c 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -141,6 +141,18 @@
     virtual status_t deleteReprocessStream(int id) = 0;
 
     /**
+     * Take the currently-defined set of streams and configure the HAL to use
+     * them. This is a long-running operation (may be several hundered ms).
+     *
+     * The device must be idle (see waitUntilDrained) before calling this.
+     *
+     * Returns OK on success; otherwise on error:
+     * - BAD_VALUE if the set of streams was invalid (e.g. fmts or sizes)
+     * - INVALID_OPERATION if the device was in the wrong state
+     */
+    virtual status_t configureStreams() = 0;
+
+    /**
      * Create a metadata buffer with fields that the HAL device believes are
      * best for the given use case
      */
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index d473a76..8caadd6 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -415,6 +415,19 @@
     return OK;
 }
 
+status_t Camera2Device::configureStreams() {
+    ATRACE_CALL();
+    ALOGV("%s: E", __FUNCTION__);
+
+    /**
+     * HAL2 devices do not need to configure streams;
+     * streams are created on the fly.
+     */
+    ALOGW("%s: No-op for HAL2 devices", __FUNCTION__);
+
+    return OK;
+}
+
 
 status_t Camera2Device::createDefaultRequest(int templateId,
         CameraMetadata *request) {
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index d0ca46e..2a3f1d9 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -64,6 +64,8 @@
     virtual status_t setStreamTransform(int id, int transform);
     virtual status_t deleteStream(int id);
     virtual status_t deleteReprocessStream(int id);
+    // No-op on HAL2 devices
+    virtual status_t configureStreams();
     virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
     virtual status_t waitUntilDrained();
     virtual status_t setNotifyCallback(NotificationListener *listener);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ed350c1..0d33406 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1000,6 +1000,15 @@
     return INVALID_OPERATION;
 }
 
+status_t Camera3Device::configureStreams() {
+    ATRACE_CALL();
+    ALOGV("%s: E", __FUNCTION__);
+
+    Mutex::Autolock il(mInterfaceLock);
+    Mutex::Autolock l(mLock);
+
+    return configureStreamsLocked();
+}
 
 status_t Camera3Device::createDefaultRequest(int templateId,
         CameraMetadata *request) {
@@ -2129,6 +2138,8 @@
         mPaused(true),
         mFrameNumber(0),
         mLatestRequestId(NAME_NOT_FOUND),
+        mCurrentAfTriggerId(0),
+        mCurrentPreCaptureTriggerId(0),
         mRepeatingLastFrameNumber(NO_IN_FLIGHT_REPEATING_FRAMES) {
     mStatusId = statusTracker->addComponent();
 }
@@ -2611,6 +2622,8 @@
 
     if (nextRequest != NULL) {
         nextRequest->mResultExtras.frameNumber = mFrameNumber++;
+        nextRequest->mResultExtras.afTriggerId = mCurrentAfTriggerId;
+        nextRequest->mResultExtras.precaptureTriggerId = mCurrentPreCaptureTriggerId;
     }
     return nextRequest;
 }
@@ -2690,8 +2703,13 @@
         if (tag == ANDROID_CONTROL_AF_TRIGGER_ID || tag == ANDROID_CONTROL_AE_PRECAPTURE_ID) {
             bool isAeTrigger = (trigger.metadataTag == ANDROID_CONTROL_AE_PRECAPTURE_ID);
             uint32_t triggerId = static_cast<uint32_t>(trigger.entryValue);
-            isAeTrigger ? request->mResultExtras.precaptureTriggerId = triggerId :
-                          request->mResultExtras.afTriggerId = triggerId;
+            if (isAeTrigger) {
+                request->mResultExtras.precaptureTriggerId = triggerId;
+                mCurrentPreCaptureTriggerId = triggerId;
+            } else {
+                request->mResultExtras.afTriggerId = triggerId;
+                mCurrentAfTriggerId = triggerId;
+            }
             if (parent->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
                 continue; // Trigger ID tag is deprecated since device HAL 3.2
             }
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 7656237..915c024 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -114,6 +114,8 @@
     virtual status_t deleteStream(int id);
     virtual status_t deleteReprocessStream(int id);
 
+    virtual status_t configureStreams();
+
     virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
 
     // Transitions to the idle state on success
@@ -484,6 +486,8 @@
         TriggerMap         mTriggerMap;
         TriggerMap         mTriggerRemovedMap;
         TriggerMap         mTriggerReplacedMap;
+        uint32_t           mCurrentAfTriggerId;
+        uint32_t           mCurrentPreCaptureTriggerId;
 
         int64_t            mRepeatingLastFrameNumber;
     };
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 50a2c10..cc66459 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -184,15 +184,6 @@
         return INVALID_OPERATION;
     }
 
-    // Only limit dequeue amount when fully configured
-    if (mState == STATE_CONFIGURED &&
-            mHandoutTotalBufferCount == camera3_stream::max_buffers) {
-        ALOGE("%s: Stream %d: Already dequeued maximum number of simultaneous"
-                " buffers (%d)", __FUNCTION__, mId,
-                camera3_stream::max_buffers);
-        return INVALID_OPERATION;
-    }
-
     return OK;
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index d7b1871..3f6254f 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -381,18 +381,7 @@
     if (hal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_2) {
         ALOGV("%s: register_stream_buffers unused as of HAL3.2", __FUNCTION__);
 
-        /**
-         * Skip the NULL check if camera.dev.register_stream is 1.
-         *
-         * For development-validation purposes only.
-         *
-         * TODO: Remove the property check before shipping L (b/13914251).
-         */
-        char value[PROPERTY_VALUE_MAX] = { '\0', };
-        property_get("camera.dev.register_stream", value, "0");
-        int propInt = atoi(value);
-
-        if (propInt == 0 && hal3Device->ops->register_stream_buffers != NULL) {
+        if (hal3Device->ops->register_stream_buffers != NULL) {
             ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; "
                     "must be set to NULL in camera3_device::ops", __FUNCTION__);
             return INVALID_OPERATION;