Disconnect HTTP-based data source upon reset

Bug: 17425250

Change-Id: Ieb4c93fd9848489f9dbf35a36474376924fd8eb9
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index d8ed836..0b85b23 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -464,6 +464,15 @@
     mStarted = true;
 }
 
+void NuPlayer::GenericSource::disconnect() {
+    if (mDataSource != NULL) {
+        // disconnect data source
+        if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+            static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect();
+        }
+    }
+}
+
 void NuPlayer::GenericSource::setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position) {
     if (mDecryptHandle != NULL) {
         mDrmManagerClient->setPlaybackStatus(mDecryptHandle, playbackStatus, position);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index c70c48e..454edeb 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -55,6 +55,8 @@
     virtual void pause();
     virtual void resume();
 
+    virtual void disconnect();
+
     virtual status_t feedMoreTSData();
 
     virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index df3e992..078e78b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -306,6 +306,16 @@
 }
 
 void NuPlayer::resetAsync() {
+    if (mSource != NULL) {
+        // During a reset, the data source might be unresponsive already, we need to
+        // disconnect explicitly so that reads exit promptly.
+        // We can't queue the disconnect request to the looper, as it might be
+        // queued behind a stuck read and never gets processed.
+        // Doing a disconnect outside the looper to allows the pending reads to exit
+        // (either successfully or with error).
+        mSource->disconnect();
+    }
+
     (new AMessage(kWhatReset, id()))->post();
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 7ccf3b1..7d994fa 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -67,6 +67,9 @@
     virtual void pause() {}
     virtual void resume() {}
 
+    // Explicitly disconnect the underling data source
+    virtual void disconnect() {}
+
     // Returns OK iff more data was available,
     // an error or ERROR_END_OF_STREAM if not.
     virtual status_t feedMoreTSData() = 0;
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index c1feff8..be2a873 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -191,6 +191,7 @@
       mFinalStatus(OK),
       mLastAccessPos(0),
       mFetching(true),
+      mDisconnecting(false),
       mLastFetchTimeUs(-1),
       mNumRetriesLeft(kMaxNumRetries),
       mHighwaterThresholdBytes(kDefaultHighWaterThreshold),
@@ -244,6 +245,23 @@
     return ERROR_UNSUPPORTED;
 }
 
+void NuCachedSource2::disconnect() {
+    if (mSource->flags() & kIsHTTPBasedSource) {
+        ALOGV("disconnecting HTTPBasedSource");
+
+        {
+            Mutex::Autolock autoLock(mLock);
+            // set mDisconnecting to true, if a fetch returns after
+            // this, the source will be marked as EOS.
+            mDisconnecting = true;
+        }
+
+        // explicitly disconnect from the source, to allow any
+        // pending reads to return more promptly
+        static_cast<HTTPBase *>(mSource.get())->disconnect();
+    }
+}
+
 status_t NuCachedSource2::setCacheStatCollectFreq(int32_t freqMs) {
     if (mSource->flags() & kIsHTTPBasedSource) {
         HTTPBase *source = static_cast<HTTPBase *>(mSource.get());
@@ -327,7 +345,14 @@
 
     Mutex::Autolock autoLock(mLock);
 
-    if (n < 0) {
+    if (n == 0 || mDisconnecting) {
+        ALOGI("ERROR_END_OF_STREAM");
+
+        mNumRetriesLeft = 0;
+        mFinalStatus = ERROR_END_OF_STREAM;
+
+        mCache->releasePage(page);
+    } else if (n < 0) {
         mFinalStatus = n;
         if (n == ERROR_UNSUPPORTED || n == -EPIPE) {
             // These are errors that are not likely to go away even if we
@@ -337,13 +362,6 @@
 
         ALOGE("source returned error %zd, %d retries left", n, mNumRetriesLeft);
         mCache->releasePage(page);
-    } else if (n == 0) {
-        ALOGI("ERROR_END_OF_STREAM");
-
-        mNumRetriesLeft = 0;
-        mFinalStatus = ERROR_END_OF_STREAM;
-
-        mCache->releasePage(page);
     } else {
         if (mFinalStatus != OK) {
             ALOGI("retrying a previously failed read succeeded.");
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 5db4b4b..4252706 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -37,6 +37,8 @@
 
     virtual ssize_t readAt(off64_t offset, void *data, size_t size);
 
+    virtual void disconnect();
+
     virtual status_t getSize(off64_t *size);
     virtual uint32_t flags();
 
@@ -103,6 +105,7 @@
     off64_t mLastAccessPos;
     sp<AMessage> mAsyncResult;
     bool mFetching;
+    bool mDisconnecting;
     int64_t mLastFetchTimeUs;
 
     int32_t mNumRetriesLeft;