Propagate errors all the way through the MediaSources and send either MEDIA_PLAYBACK_COMPLETE or MEDIA_ERROR depending on the final reason for running out of buffers to play back.

related-to-bug: 2463749
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 8e5f05f..ea15a5c 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -61,7 +61,7 @@
     status_t seekTo(int64_t time_us);
 
     bool isSeeking();
-    bool reachedEOS();
+    bool reachedEOS(status_t *finalStatus);
 
 private:
     sp<MediaSource> mSource;
@@ -81,6 +81,7 @@
 
     bool mSeeking;
     bool mReachedEOS;
+    status_t mFinalStatus;
     int64_t mSeekTimeUs;
 
     bool mStarted;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 974075d..24c2f46 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -132,6 +132,7 @@
     PortStatus mPortStatus[2];
     bool mInitialBufferSubmit;
     bool mSignalledEOS;
+    status_t mFinalStatus;
     bool mNoMoreOutputData;
     bool mOutputPortSettingsHaveChanged;
     int64_t mSeekTimeUs;
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 7997cd6..57f58be 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -38,6 +38,7 @@
       mPositionTimeRealUs(-1),
       mSeeking(false),
       mReachedEOS(false),
+      mFinalStatus(OK),
       mStarted(false),
       mAudioSink(audioSink) {
 }
@@ -168,6 +169,7 @@
     mPositionTimeRealUs = -1;
     mSeeking = false;
     mReachedEOS = false;
+    mFinalStatus = OK;
     mStarted = false;
 }
 
@@ -181,8 +183,11 @@
     return mSeeking;
 }
 
-bool AudioPlayer::reachedEOS() {
+bool AudioPlayer::reachedEOS(status_t *finalStatus) {
+    *finalStatus = OK;
+
     Mutex::Autolock autoLock(mLock);
+    *finalStatus = mFinalStatus;
     return mReachedEOS;
 }
 
@@ -245,6 +250,7 @@
 
             if (err != OK) {
                 mReachedEOS = true;
+                mFinalStatus = err;
                 break;
             }
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index b3a73b0..ab65b44 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -434,14 +434,22 @@
     }
     mStreamDoneEventPending = false;
 
-    if (mFlags & LOOPING) {
+    if (mStreamDoneStatus == ERROR_END_OF_STREAM && (mFlags & LOOPING)) {
         seekTo_l(0);
 
         if (mVideoSource != NULL) {
             postVideoEvent_l();
         }
     } else {
-        notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
+        if (mStreamDoneStatus == ERROR_END_OF_STREAM) {
+            LOGV("MEDIA_PLAYBACK_COMPLETE");
+            notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
+        } else {
+            LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
+
+            notifyListener_l(
+                    MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, mStreamDoneStatus);
+        }
 
         pause_l();
 
@@ -802,7 +810,7 @@
                     continue;
                 }
 
-                postStreamDoneEvent_l();
+                postStreamDoneEvent_l(err);
                 return;
             }
 
@@ -904,11 +912,13 @@
     mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);
 }
 
-void AwesomePlayer::postStreamDoneEvent_l() {
+void AwesomePlayer::postStreamDoneEvent_l(status_t status) {
     if (mStreamDoneEventPending) {
         return;
     }
     mStreamDoneEventPending = true;
+
+    mStreamDoneStatus = status;
     mQueue.postEvent(mStreamDoneEvent);
 }
 
@@ -947,9 +957,10 @@
         notifyListener_l(MEDIA_SEEK_COMPLETE);
     }
 
-    if (mWatchForAudioEOS && mAudioPlayer->reachedEOS()) {
+    status_t finalStatus;
+    if (mWatchForAudioEOS && mAudioPlayer->reachedEOS(&finalStatus)) {
         mWatchForAudioEOS = false;
-        postStreamDoneEvent_l();
+        postStreamDoneEvent_l(finalStatus);
     }
 
     postCheckAudioStatusEvent_l();
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index a6053b3..165ac09 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1450,6 +1450,14 @@
                 &sampleIndex, SampleTable::kSyncSample_Flag);
 
         if (err != OK) {
+            if (err == ERROR_OUT_OF_RANGE) {
+                // An attempt to seek past the end of the stream would
+                // normally cause this ERROR_OUT_OF_RANGE error. Propagating
+                // this all the way to the MediaPlayer would cause abnormal
+                // termination. Legacy behaviour appears to be to behave as if
+                // we had seeked to the end of stream, ending normally.
+                err = ERROR_END_OF_STREAM;
+            }
             return err;
         }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4075ec1..974413d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1933,6 +1933,7 @@
         CODEC_LOGV("signalling end of input stream.");
         flags |= OMX_BUFFERFLAG_EOS;
 
+        mFinalStatus = err;
         mSignalledEOS = true;
     } else {
         mNoMoreOutputData = false;
@@ -2411,7 +2412,7 @@
     }
 
     if (mFilledBuffers.empty()) {
-        return ERROR_END_OF_STREAM;
+        return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM;
     }
 
     if (mOutputPortSettingsHaveChanged) {
diff --git a/media/libstagefright/Prefetcher.cpp b/media/libstagefright/Prefetcher.cpp
index 363e121..9c73f4a 100644
--- a/media/libstagefright/Prefetcher.cpp
+++ b/media/libstagefright/Prefetcher.cpp
@@ -55,6 +55,7 @@
     size_t mIndex;
     bool mStarted;
     bool mReachedEOS;
+    status_t mFinalStatus;
     int64_t mSeekTimeUs;
     int64_t mCacheDurationUs;
     bool mPrefetcherStopped;
@@ -306,7 +307,7 @@
     }
 
     if (mCachedBuffers.empty()) {
-        return ERROR_END_OF_STREAM;
+        return mReachedEOS ? mFinalStatus : ERROR_END_OF_STREAM;
     }
 
     *out = *mCachedBuffers.begin();
@@ -353,6 +354,7 @@
 
     if (err != OK) {
         mReachedEOS = true;
+        mFinalStatus = err;
         mCondition.signal();
 
         return;
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index ce8eeae..3590987 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -145,10 +145,11 @@
     Condition mPreparedCondition;
     bool mIsAsyncPrepare;
     status_t mPrepareResult;
+    status_t mStreamDoneStatus;
 
     void postVideoEvent_l(int64_t delayUs = -1);
     void postBufferingEvent_l();
-    void postStreamDoneEvent_l();
+    void postStreamDoneEvent_l(status_t status);
     void postCheckAudioStatusEvent_l();
     status_t getPosition_l(int64_t *positionUs);
     status_t play_l();