Merge "Change a Stagefright assert to an error check."
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index 74c9d5d..df5be32 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -71,7 +71,8 @@
     bool addStringTag(const char* name, const char* value);
     void endFile();
 
-    virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;
+    virtual bool scanFile(const char* path, long long lastModified,
+            long long fileSize, bool isDirectory) = 0;
     virtual bool handleStringTag(const char* name, const char* value) = 0;
     virtual bool setMimeType(const char* mimeType) = 0;
     virtual bool addNoMediaFolder(const char* path) = 0;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 940470d..b3815c4 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -14,11 +14,12 @@
 
 struct ACodec : public AHierarchicalStateMachine {
     enum {
-        kWhatFillThisBuffer    = 'fill',
-        kWhatDrainThisBuffer   = 'drai',
-        kWhatEOS               = 'eos ',
-        kWhatShutdownCompleted = 'scom',
-        kWhatFlushCompleted    = 'fcom',
+        kWhatFillThisBuffer      = 'fill',
+        kWhatDrainThisBuffer     = 'drai',
+        kWhatEOS                 = 'eos ',
+        kWhatShutdownCompleted   = 'scom',
+        kWhatFlushCompleted      = 'fcom',
+        kWhatOutputFormatChanged = 'outC',
     };
 
     ACodec();
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 941f6b9..91ec60c 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -64,6 +64,9 @@
 
     void post(int64_t delayUs = 0);
 
+    // Performs a deep-copy of "this", contained messages are in turn "dup'ed".
+    // Warning: RefBase items, i.e. "objects" are _not_ copied but only have
+    // their refcount incremented.
     sp<AMessage> dup() const;
 
     AString debugString(int32_t indent = 0) const;
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index c31b622..5ec573e 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -84,6 +84,7 @@
     // place to copy file or directory name
     char* fileSpot = path + strlen(path);
     struct dirent* entry;
+    struct stat statbuf;
 
     // ignore directories that contain a  ".nomedia" file
     if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
@@ -125,7 +126,6 @@
             // If the type is unknown, stat() the file instead.
             // This is sometimes necessary when accessing NFS mounted filesystems, but
             // could be needed in other cases well.
-            struct stat statbuf;
             if (stat(path, &statbuf) == 0) {
                 if (S_ISREG(statbuf.st_mode)) {
                     type = DT_REG;
@@ -142,8 +142,15 @@
                 // for example, the Mac ".Trashes" directory
                 if (name[0] == '.') continue;
 
+                // report the directory to the client
+                if (stat(path, &statbuf) == 0) {
+                    client.scanFile(path, statbuf.st_mtime, 0, true);
+                }
+
+                // and now process its contents
                 strcat(fileSpot, "/");
-                int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, exceptionCheck, exceptionEnv);
+                int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client,
+                        exceptionCheck, exceptionEnv);
                 if (err) {
                     // pass exceptions up - ignore other errors
                     if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
@@ -151,11 +158,8 @@
                     continue;
                 }
             } else {
-                struct stat statbuf;
                 stat(path, &statbuf);
-                if (statbuf.st_size > 0) {
-                    client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
-                }
+                client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false);
                 if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
             }
         }
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
index 89a5e69..9738e33 100644
--- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -166,6 +166,9 @@
     sp<MediaSource> mDecoder;
     sp<AMessage> mNotify;
     bool mEOS;
+    bool mSentFormat;
+
+    void sendFormatChange();
 
     DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
 };
@@ -174,7 +177,8 @@
         const sp<MediaSource> &decoder, const sp<AMessage> &notify)
     : mDecoder(decoder),
       mNotify(notify),
-      mEOS(false) {
+      mEOS(false),
+      mSentFormat(false) {
 }
 
 DecoderWrapper::WrapperReader::~WrapperReader() {
@@ -215,12 +219,17 @@
             MediaBuffer *src;
             status_t err = mDecoder->read(&src, &options);
 
-            sp<AMessage> notify = mNotify->dup();
-
-            sp<AMessage> realNotify;
-            CHECK(notify->findMessage("real-notify", &realNotify));
-
             if (err == OK) {
+                if (!mSentFormat) {
+                    sendFormatChange();
+                    mSentFormat = true;
+                }
+
+                sp<AMessage> notify = mNotify->dup();
+
+                sp<AMessage> realNotify;
+                CHECK(notify->findMessage("real-notify", &realNotify));
+
                 realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
 
                 sp<ABuffer> dst = new ABuffer(src->range_length());
@@ -236,12 +245,23 @@
                 dst->meta()->setInt64("timeUs", timeUs);
 
                 realNotify->setObject("buffer", dst);
+
+                notify->post();
+            } else if (err == INFO_FORMAT_CHANGED) {
+                sendFormatChange();
+
+                readMore(false /* flush */);
             } else {
+                sp<AMessage> notify = mNotify->dup();
+
+                sp<AMessage> realNotify;
+                CHECK(notify->findMessage("real-notify", &realNotify));
+
                 realNotify->setInt32("what", ACodec::kWhatEOS);
                 mEOS = true;
-            }
 
-            notify->post();
+                notify->post();
+            }
             break;
         }
 
@@ -251,6 +271,46 @@
     }
 }
 
+void DecoderWrapper::WrapperReader::sendFormatChange() {
+    sp<AMessage> notify = mNotify->dup();
+
+    sp<AMessage> realNotify;
+    CHECK(notify->findMessage("real-notify", &realNotify));
+
+    realNotify->setInt32("what", ACodec::kWhatOutputFormatChanged);
+
+    sp<MetaData> meta = mDecoder->getFormat();
+
+    const char *mime;
+    CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+    realNotify->setString("mime", mime);
+
+    if (!strncasecmp("audio/", mime, 6)) {
+        int32_t numChannels;
+        CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+
+        int32_t sampleRate;
+        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+        realNotify->setInt32("channel-count", numChannels);
+        realNotify->setInt32("sample-rate", sampleRate);
+    } else {
+        CHECK(!strncasecmp("video/", mime, 6));
+
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        realNotify->setInt32("width", width);
+        realNotify->setInt32("height", height);
+    }
+
+    notify->post();
+
+    mSentFormat = true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 DecoderWrapper::DecoderWrapper()
@@ -327,24 +387,33 @@
 
         case kWhatFillBufferDone:
         {
-            CHECK_GT(mNumPendingDecodes, 0);
-            --mNumPendingDecodes;
-
-            if (mFlushing) {
-                completeFlushIfPossible();
-                break;
-            }
-
             sp<AMessage> notify;
             CHECK(msg->findMessage("real-notify", &notify));
 
-            sp<AMessage> reply =
-                new AMessage(kWhatOutputBufferDrained, id());
+            int32_t what;
+            CHECK(notify->findInt32("what", &what));
 
-            notify->setMessage("reply", reply);
+            if (what == ACodec::kWhatDrainThisBuffer) {
+                CHECK_GT(mNumPendingDecodes, 0);
+                --mNumPendingDecodes;
+
+                sp<AMessage> reply =
+                    new AMessage(kWhatOutputBufferDrained, id());
+
+                notify->setMessage("reply", reply);
+
+                ++mNumOutstandingOutputBuffers;
+            } else if (what == ACodec::kWhatEOS) {
+                CHECK_GT(mNumPendingDecodes, 0);
+                --mNumPendingDecodes;
+
+                if (mFlushing) {
+                    completeFlushIfPossible();
+                    break;
+                }
+            }
+
             notify->post();
-
-            ++mNumOutstandingOutputBuffers;
             break;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 403029a..e99c24a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -198,6 +198,21 @@
                     mFlushingAudio = NONE;
                     mFlushingVideo = NONE;
                 }
+            } else if (what == ACodec::kWhatOutputFormatChanged) {
+                CHECK(audio);
+
+                int32_t numChannels;
+                CHECK(codecRequest->findInt32("channel-count", &numChannels));
+
+                int32_t sampleRate;
+                CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
+
+                LOGI("Audio output format changed to %d Hz, %d channels",
+                     sampleRate, numChannels);
+
+                mAudioSink->close();
+                CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
+                mAudioSink->start();
             } else {
                 CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
 
@@ -365,18 +380,6 @@
     const sp<MetaData> &meta = source->getFormat();
     (*decoder)->configure(meta);
 
-    if (audio) {
-        int32_t sampleRate;
-        int32_t channelCount;
-        CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
-        CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
-
-        channelCount = 2;  // XXX
-
-        CHECK_EQ(mAudioSink->open(sampleRate, channelCount), (status_t)OK);
-        mAudioSink->start();
-    }
-
     return OK;
 }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 7613d04..d1525cf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -33,8 +33,6 @@
 #include "UDPPusher.h"
 
 #include <binder/IPCThreadState.h>
-#include <binder/MemoryDealer.h>
-#include <media/IStreamSource.h>
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/AudioPlayer.h>
@@ -161,245 +159,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-struct QueueDataSource;
-
-struct QueueListener : public BnStreamListener {
-    QueueListener(QueueDataSource *owner)
-        : mOwner(owner) {
-    }
-
-    void clearOwner();
-
-    virtual void queueBuffer(size_t index, size_t size);
-
-    virtual void issueCommand(
-            Command cmd, bool synchronous, const sp<AMessage> &msg);
-
-private:
-    Mutex mLock;
-
-    QueueDataSource *mOwner;
-
-    DISALLOW_EVIL_CONSTRUCTORS(QueueListener);
-};
-
-struct QueueDataSource : public DataSource {
-    QueueDataSource(const sp<IStreamSource> &source);
-
-    virtual status_t initCheck() const;
-
-    virtual ssize_t readAt(off64_t offset, void *data, size_t size);
-
-    virtual void queueBuffer(size_t index, size_t size);
-
-    virtual void issueCommand(
-            IStreamListener::Command cmd,
-            bool synchronous,
-            const sp<AMessage> &msg);
-
-protected:
-    virtual ~QueueDataSource();
-
-private:
-    enum {
-        kNumBuffers = 16
-    };
-
-    struct QueueEntry {
-        bool mIsCommand;
-
-        IStreamListener::Command mCommand;
-        sp<AMessage> mCommandMessage;
-
-        size_t mIndex;
-        size_t mOffset;
-        size_t mSize;
-    };
-
-    Mutex mLock;
-    Condition mCondition;
-
-    sp<IStreamSource> mSource;
-    sp<QueueListener> mListener;
-    sp<MemoryDealer> mDealer;
-    Vector<sp<IMemory> > mBuffers;
-
-    List<QueueEntry> mQueue;
-
-    off64_t mPosition;
-    bool mEOS;
-
-    DISALLOW_EVIL_CONSTRUCTORS(QueueDataSource);
-};
-
-QueueDataSource::QueueDataSource(const sp<IStreamSource> &source)
-    : mSource(source),
-      mPosition(0),
-      mEOS(false) {
-    mListener = new QueueListener(this);
-    mSource->setListener(mListener);
-
-    static const size_t kBufferSize = (8192 / 188) * 188;
-
-    mDealer = new MemoryDealer(kNumBuffers * kBufferSize);
-    for (size_t i = 0; i < kNumBuffers; ++i) {
-        sp<IMemory> mem = mDealer->allocate(kBufferSize);
-        CHECK(mem != NULL);
-
-        mBuffers.push(mem);
-    }
-    mSource->setBuffers(mBuffers);
-
-    for (size_t i = 0; i < kNumBuffers; ++i) {
-        mSource->onBufferAvailable(i);
-    }
-}
-
-QueueDataSource::~QueueDataSource() {
-    Mutex::Autolock autoLock(mLock);
-
-    mListener->clearOwner();
-}
-
-status_t QueueDataSource::initCheck() const {
-    return OK;
-}
-
-ssize_t QueueDataSource::readAt(off64_t offset, void *data, size_t size) {
-    if (offset != mPosition) {
-        return -EPIPE;
-    }
-
-    Mutex::Autolock autoLock(mLock);
-
-    if (mEOS) {
-        return ERROR_END_OF_STREAM;
-    }
-
-    size_t sizeDone = 0;
-
-    while (sizeDone < size) {
-        while (mQueue.empty()) {
-            mCondition.wait(mLock);
-        }
-
-        QueueEntry &entry = *mQueue.begin();
-
-        if (entry.mIsCommand) {
-            switch (entry.mCommand) {
-                case IStreamListener::EOS:
-                {
-                    mEOS = true;
-
-                    if (sizeDone > 0) {
-                        offset += sizeDone;
-                        return sizeDone;
-                    } else {
-                        return ERROR_END_OF_STREAM;
-                    }
-                    break;
-                }
-
-                case IStreamListener::DISCONTINUITY:
-                {
-                    CHECK_EQ(size, 188u);
-                    CHECK_EQ(sizeDone, 0u);
-
-                    memset(data, 0, size);
-                    sizeDone = size;
-                    break;
-                }
-
-                default:
-                    break;
-            }
-
-            mQueue.erase(mQueue.begin());
-            continue;
-        }
-
-        size_t copy = size - sizeDone;
-        if (copy > entry.mSize) {
-            copy = entry.mSize;
-        }
-
-        memcpy((uint8_t *)data + sizeDone,
-               (const uint8_t *)mBuffers.itemAt(entry.mIndex)->pointer()
-                    + entry.mOffset,
-               copy);
-
-        entry.mSize -= copy;
-        entry.mOffset += copy;
-        sizeDone += copy;
-
-        if (entry.mSize == 0) {
-            mSource->onBufferAvailable(entry.mIndex);
-            mQueue.erase(mQueue.begin());
-        }
-    }
-
-    mPosition += sizeDone;
-
-    return sizeDone;
-}
-
-void QueueDataSource::queueBuffer(size_t index, size_t size) {
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK_LT(index, mBuffers.size());
-    CHECK_LE(size, mBuffers.itemAt(index)->size());
-
-    QueueEntry entry;
-    entry.mIsCommand = false;
-    entry.mIndex = index;
-    entry.mSize = size;
-    entry.mOffset = 0;
-
-    mQueue.push_back(entry);
-    mCondition.signal();
-}
-
-void QueueDataSource::issueCommand(
-        IStreamListener::Command cmd,
-        bool synchronous,
-        const sp<AMessage> &msg) {
-    Mutex::Autolock autoLock(mLock);
-
-    CHECK(!synchronous);
-
-    QueueEntry entry;
-    entry.mIsCommand = true;
-    entry.mCommand = cmd;
-    entry.mCommandMessage = msg;
-    mQueue.push_back(entry);
-
-    mCondition.signal();
-}
-
-void QueueListener::clearOwner() {
-    Mutex::Autolock autoLock(mLock);
-    mOwner = NULL;
-}
-
-void QueueListener::queueBuffer(size_t index, size_t size) {
-    Mutex::Autolock autoLock(mLock);
-    if (mOwner == NULL) {
-        return;
-    }
-    mOwner->queueBuffer(index, size);
-}
-
-void QueueListener::issueCommand(
-        Command cmd, bool synchronous, const sp<AMessage> &msg) {
-    Mutex::Autolock autoLock(mLock);
-    if (mOwner == NULL) {
-        return;
-    }
-    mOwner->issueCommand(cmd, synchronous, msg);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 AwesomePlayer::AwesomePlayer()
     : mQueueStarted(false),
       mTimeSource(NULL),
@@ -511,14 +270,7 @@
 }
 
 status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source) {
-    Mutex::Autolock autoLock(mLock);
-
-    reset_l();
-
-    sp<DataSource> dataSource = new QueueDataSource(source);
-    sp<MediaExtractor> extractor = new MPEG2TSExtractor(dataSource);
-
-    return setDataSource_l(extractor);
+    return INVALID_OPERATION;
 }
 
 status_t AwesomePlayer::setDataSource_l(
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index d3c7445..dfc9b5a 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -959,6 +959,13 @@
             uint16_t width = U16_AT(&buffer[6 + 18]);
             uint16_t height = U16_AT(&buffer[6 + 20]);
 
+            // The video sample is not stand-compliant if it has invalid dimension.
+            // Use some default width and height value, and
+            // let the decoder figure out the actual width and height (and thus
+            // be prepared for INFO_FOMRAT_CHANGED event).
+            if (width == 0)  width  = 352;
+            if (height == 0) height = 288;
+
             // printf("*** coding='%s' width=%d height=%d\n",
             //        chunk, width, height);
 
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 4ce7265..653c85e 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -158,6 +158,9 @@
         request.append(" HTTP/1.1\r\n");
         request.append("Host: ");
         request.append(mHost);
+        if (mPort != 80) {
+            request.append(StringPrintf(":%u", mPort).c_str());
+        }
         request.append("\r\n");
 
         if (offset != 0) {
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 7da9cb8..0e40acc 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -224,13 +224,22 @@
             }
 
             case kTypeObject:
-            case kTypeMessage:
             {
                 to->u.refValue = from->u.refValue;
                 to->u.refValue->incStrong(msg.get());
                 break;
             }
 
+            case kTypeMessage:
+            {
+                sp<AMessage> copy =
+                    static_cast<AMessage *>(from->u.refValue)->dup();
+
+                to->u.refValue = copy.get();
+                to->u.refValue->incStrong(msg.get());
+                break;
+            }
+
             default:
             {
                 to->u = from->u;
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 62567be..30ac404 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -349,7 +349,11 @@
         bool firstTime = (mPlaylist == NULL);
 
         mPlaylist = fetchPlaylist(url.c_str());
-        CHECK(mPlaylist != NULL);
+        if (mPlaylist == NULL) {
+            LOGE("failed to load playlist at url '%s'", url.c_str());
+            mDataSource->queueEOS(ERROR_IO);
+            return;
+        }
 
         if (firstTime) {
             Mutex::Autolock autoLock(mLock);
@@ -446,7 +450,11 @@
 
     sp<ABuffer> buffer;
     status_t err = fetchFile(uri.c_str(), &buffer);
-    CHECK_EQ(err, (status_t)OK);
+    if (err != OK) {
+        LOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
+        mDataSource->queueEOS(err);
+        return;
+    }
 
     CHECK_EQ((status_t)OK,
              decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer));
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index d4a29c0..38a7cc5 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "M3UParser"
+#include <utils/Log.h>
+
 #include "include/M3UParser.h"
 
 #include <media/stagefright/foundation/AMessage.h>
@@ -88,6 +92,9 @@
     if (!strncasecmp("http://", url, 7)) {
         // "url" is already an absolute URL, ignore base URL.
         out->setTo(url);
+
+        LOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str());
+
         return true;
     }
 
@@ -108,6 +115,8 @@
         out->append(url);
     }
 
+    LOGV("base:'%s', url:'%s' => '%s'", baseURL, url, out->c_str());
+
     return true;
 }
 
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
index aee01ab..d926cb1 100644
--- a/services/audioflinger/A2dpAudioInterface.cpp
+++ b/services/audioflinger/A2dpAudioInterface.cpp
@@ -260,6 +260,7 @@
     if (pRate) *pRate = lRate;
 
     mDevice = device;
+    mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
     return NO_ERROR;
 }
 
@@ -288,6 +289,7 @@
         if (mStandby) {
             acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
             mStandby = false;
+            mLastWriteTime = systemTime();
         }
 
         status = init();
@@ -308,6 +310,15 @@
             buffer = (char *)buffer + status;
         }
 
+        // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
+        // does no spin and starve other threads.
+        // NOTE: It is likely that the A2DP headset is being disconnected
+        nsecs_t now = systemTime();
+        if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
+            LOGV("A2DP sink runs too fast");
+            usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
+        }
+        mLastWriteTime = now;
         return bytes;
 
     }
@@ -316,7 +327,7 @@
     standby();
 
     // Simulate audio output timing in case of error
-    usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);
+    usleep(mBufferDurationUs);
 
     return status;
 }
diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
index cef1926..dbe2c6a 100644
--- a/services/audioflinger/A2dpAudioInterface.h
+++ b/services/audioflinger/A2dpAudioInterface.h
@@ -117,6 +117,8 @@
                 uint32_t    mDevice;
                 bool        mClosing;
                 bool        mSuspended;
+                nsecs_t     mLastWriteTime;
+                uint32_t    mBufferDurationUs;
     };
 
     friend class A2dpAudioStreamOut;
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 5b9273d..855af9f 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -1605,10 +1605,8 @@
             break;
 
         case AudioSystem::FORCE_SPEAKER:
-            if (!isInCall() || strategy != STRATEGY_DTMF) {
-                device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
-                if (device) break;
-            }
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+            if (device) break;
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
             // A2DP speaker when forcing to speaker output