Merge "Remove private ANativeWindow_fromSurfaceTexture"
diff --git a/src/android/AacBqToPcmCbRenderer.cpp b/src/android/AacBqToPcmCbRenderer.cpp
index 44aaafc..194662b 100644
--- a/src/android/AacBqToPcmCbRenderer.cpp
+++ b/src/android/AacBqToPcmCbRenderer.cpp
@@ -108,12 +108,12 @@
 }
 
 //--------------------------------------------------------------------------------------------------
-AacBqToPcmCbRenderer::AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params) :
+AacBqToPcmCbRenderer::AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params,
+        IAndroidBufferQueue *androidBufferQueue) :
         AudioToCbRenderer(params),
-        mBqSource(0)
+        mBqSource(new BufferQueueSource(androidBufferQueue))
 {
     SL_LOGD("AacBqToPcmCbRenderer::AacBqToPcmCbRenderer()");
-
 }
 
 
@@ -124,20 +124,6 @@
 
 
 //--------------------------------------------------
-void AacBqToPcmCbRenderer::registerSourceQueueCallback(
-        const void* user, void *context,  const void *caller) {
-    SL_LOGD("AacBqToPcmCbRenderer::registerQueueCallback");
-
-    Mutex::Autolock _l(mBqSourceLock);
-
-    mBqSource = new BufferQueueSource(user, context, caller);
-
-    CHECK(mBqSource != 0);
-    SL_LOGD("AacBqToPcmCbRenderer::registerSourceQueueCallback end");
-}
-
-
-//--------------------------------------------------
 // Event handlers
 void AacBqToPcmCbRenderer::onPrepare() {
     SL_LOGD("AacBqToPcmCbRenderer::onPrepare()");
@@ -157,23 +143,7 @@
         mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = UNKNOWN_CHANNELMASK;
     }
 
-    sp<DataSource> dataSource;
-    {
-        Mutex::Autolock _l(mBqSourceLock);
-        dataSource = mBqSource;
-    }
-    if (dataSource == 0) {
-        SL_LOGE("AacBqToPcmCbRenderer::onPrepare(): Error no data source");
-        notifyPrepared(MEDIA_ERROR_BASE);
-        return;
-    }
-
-    sp<MediaExtractor> extractor = new AacAdtsExtractor(dataSource);
-    if (extractor == 0) {
-        SL_LOGE("AacBqToPcmCbRenderer::onPrepare: Could not instantiate AAC extractor.");
-        notifyPrepared(ERROR_UNSUPPORTED);
-        return;
-    }
+    sp<MediaExtractor> extractor = new AacAdtsExtractor(mBqSource);
 
     // only decoding a single track of data
     const size_t kTrackToDecode = 0;
@@ -235,7 +205,7 @@
 
     //---------------------------------
     // The data source, and audio source (a decoder) are ready to be used
-    mDataSource = dataSource;
+    mDataSource = mBqSource;
     mAudioSource = source;
     mAudioSourceStarted = true;
 
diff --git a/src/android/AudioPlayer_to_android.cpp b/src/android/AudioPlayer_to_android.cpp
index d94c4da..e79a161 100644
--- a/src/android/AudioPlayer_to_android.cpp
+++ b/src/android/AudioPlayer_to_android.cpp
@@ -713,7 +713,8 @@
         ap->mAndroidObjState = ANDROID_READY;
 
         if (PLAYER_SUCCESS == data1) {
-            // Most of successful prepare completion is handled by a subclass.
+            // Most of successful prepare completion for ap->mAPlayer
+            // is handled by GenericPlayer and its subclasses.
         } else {
             // SfPlayer prepare() failed prefetching, there is no event in SLPrefetchStatus to
             //  indicate a prefetch error, so we signal it by sending simultaneously two events:
@@ -839,7 +840,7 @@
         break;
 
       case android::GenericPlayer::kEventErrorAfterPrepare: {
-        SL_LOGI("kEventErrorAfterPrepare");
+        SL_LOGV("kEventErrorAfterPrepare");
 
         // assume no callback
         slPrefetchCallback callback = NULL;
@@ -847,12 +848,10 @@
 
         object_lock_exclusive(&ap->mObject);
         if (IsInterfaceInitialized(&ap->mObject, MPH_PREFETCHSTATUS)) {
-            SL_LOGI("inited");
             ap->mPrefetchStatus.mLevel = 0;
             ap->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
             if (!(~ap->mPrefetchStatus.mCallbackEventsMask &
                     (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
-                SL_LOGI("enabled");
                 callback = ap->mPrefetchStatus.mCallback;
                 callbackPContext = ap->mPrefetchStatus.mContext;
             }
@@ -863,7 +862,6 @@
         SL_LOGE("Error after prepare: %d", data1);
 
         // callback with no lock held
-        SL_LOGE("callback=%p context=%p", callback, callbackPContext);
         if (NULL != callback) {
             (*callback)(&ap->mPrefetchStatus.mItf, callbackPContext,
                     SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
@@ -1521,7 +1519,8 @@
     //-----------------------------------
     // AacBqToPcmCbRenderer
     case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
-        android::AacBqToPcmCbRenderer* bqtobq = new android::AacBqToPcmCbRenderer(&app);
+        android::AacBqToPcmCbRenderer* bqtobq = new android::AacBqToPcmCbRenderer(&app,
+                &pAudioPlayer->mAndroidBufferQueue);
         // configures the callback for the sink buffer queue
         bqtobq->setDataPushListener(adecoder_writeToBufferQueue, pAudioPlayer);
         pAudioPlayer->mAPlayer = bqtobq;
@@ -2146,30 +2145,6 @@
 
 
 //-----------------------------------------------------------------------------
-SLresult android_audioPlayer_androidBufferQueue_registerCallback_l(CAudioPlayer *ap) {
-    SLresult result = SL_RESULT_SUCCESS;
-    assert(ap->mAPlayer != 0);
-    // FIXME investigate why these two cases are not handled symmetrically any more
-    switch (ap->mAndroidObjType) {
-      case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: {
-        } break;
-      case AUDIOPLAYER_FROM_ADTS_ABQ_TO_PCM_BUFFERQUEUE: {
-          android::AacBqToPcmCbRenderer* dec =
-                  static_cast<android::AacBqToPcmCbRenderer*>(ap->mAPlayer.get());
-          dec->registerSourceQueueCallback((const void*)ap /*user*/,
-                  ap->mAndroidBufferQueue.mContext /*context*/,
-                  (const void*)&(ap->mAndroidBufferQueue.mItf) /*caller*/);
-        } break;
-      default:
-        SL_LOGE("Error registering AndroidBufferQueue callback: unexpected object type %d",
-                ap->mAndroidObjType);
-        result = SL_RESULT_INTERNAL_ERROR;
-        break;
-    }
-    return result;
-}
-
-//-----------------------------------------------------------------------------
 void android_audioPlayer_androidBufferQueue_clear_l(CAudioPlayer *ap) {
     switch (ap->mAndroidObjType) {
     case AUDIOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE:
diff --git a/src/android/AudioPlayer_to_android.h b/src/android/AudioPlayer_to_android.h
index 96855e6..430b622 100644
--- a/src/android/AudioPlayer_to_android.h
+++ b/src/android/AudioPlayer_to_android.h
@@ -138,9 +138,6 @@
  * Android Buffer Queue
  ****************************/
 /* must be called with a lock on pAudioPlayer->mThis */
-extern SLresult android_audioPlayer_androidBufferQueue_registerCallback_l(
-        CAudioPlayer *pAudioPlayer);
-/* must be called with a lock on pAudioPlayer->mThis */
 extern void android_audioPlayer_androidBufferQueue_clear_l(CAudioPlayer *pAudioPlayer);
 /* must be called with a lock on pAudioPlayer->mThis */
 extern void android_audioPlayer_androidBufferQueue_onRefilled_l(CAudioPlayer *pAudioPlayer);
diff --git a/src/android/BufferQueueSource.cpp b/src/android/BufferQueueSource.cpp
index 1ad2cad..9257f53 100644
--- a/src/android/BufferQueueSource.cpp
+++ b/src/android/BufferQueueSource.cpp
@@ -36,17 +36,11 @@
 };
 
 
-BufferQueueSource::BufferQueueSource(const void* user, void *context,  const void *caller) :
-          mAndroidBufferQueueSource(NULL),
+BufferQueueSource::BufferQueueSource(IAndroidBufferQueue *androidBufferQueue) :
+          mAndroidBufferQueueSource(androidBufferQueue),
           mStreamToBqOffset(0),
           mEosReached(false)
 {
-    if (NULL != user) {
-        mAndroidBufferQueueSource = &((CAudioPlayer*)user)->mAndroidBufferQueue;
-    } else {
-        SL_LOGE("Can't create BufferQueueSource with NULL user");
-    }
-
 }
 
 
diff --git a/src/android/BufferQueueSource.h b/src/android/BufferQueueSource.h
index c91b7b1..c362df2 100644
--- a/src/android/BufferQueueSource.h
+++ b/src/android/BufferQueueSource.h
@@ -33,7 +33,7 @@
     // store an item structure to indicate a processed buffer
     static const SLuint32 kItemProcessed[NB_BUFFEREVENT_ITEM_FIELDS];
 
-    BufferQueueSource(const void* user, void *context,  const void *caller);
+    BufferQueueSource(IAndroidBufferQueue *androidBufferQueue);
 
     virtual status_t initCheck() const;
 
@@ -45,7 +45,7 @@
 
 private:
     // the Android Buffer Queue from which data is consumed
-    IAndroidBufferQueue* mAndroidBufferQueueSource;
+    IAndroidBufferQueue* const mAndroidBufferQueueSource;
 
     // a monotonically increasing offset used to translate an offset from the beginning
     // of the stream, to an offset in each buffer from the buffer queue source
diff --git a/src/android/MediaPlayer_to_android.cpp b/src/android/MediaPlayer_to_android.cpp
index 81653d7..692aa5c 100644
--- a/src/android/MediaPlayer_to_android.cpp
+++ b/src/android/MediaPlayer_to_android.cpp
@@ -52,36 +52,45 @@
     switch(event) {
 
       case android::GenericPlayer::kEventPrepared: {
-
-        SL_LOGV("Received AVPlayer::kEventPrepared for CMediaPlayer %p", mp);
+        SL_LOGV("Received GenericPlayer::kEventPrepared for CMediaPlayer %p", mp);
 
         // assume no callback
         slPrefetchCallback callback = NULL;
-        void* callbackPContext = NULL;
+        void* callbackPContext;
+        XAuint32 events;
 
         object_lock_exclusive(&mp->mObject);
-        // mark object as prepared; same state is used for successfully or unsuccessful prepare
+
+        // mark object as prepared; same state is used for successful or unsuccessful prepare
+        assert(mp->mAndroidObjState == ANDROID_PREPARING);
         mp->mAndroidObjState = ANDROID_READY;
 
-        // AVPlayer prepare() failed prefetching, there is no event in XAPrefetchStatus to
-        //  indicate a prefetch error, so we signal it by sending simulataneously two events:
-        //  - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0
-        //  - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW
-        if (PLAYER_SUCCESS != data1 && IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
-            mp->mPrefetchStatus.mLevel = 0;
-            mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
-            if (!(~mp->mPrefetchStatus.mCallbackEventsMask &
-                    (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
-                callback = mp->mPrefetchStatus.mCallback;
-                callbackPContext = mp->mPrefetchStatus.mContext;
+        if (PLAYER_SUCCESS == data1) {
+            // Most of successful prepare completion for mp->mAVPlayer
+            // is handled by GenericPlayer and its subclasses.
+        } else {
+            // AVPlayer prepare() failed prefetching, there is no event in XAPrefetchStatus to
+            //  indicate a prefetch error, so we signal it by sending simultaneously two events:
+            //  - SL_PREFETCHEVENT_FILLLEVELCHANGE with a level of 0
+            //  - SL_PREFETCHEVENT_STATUSCHANGE with a status of SL_PREFETCHSTATUS_UNDERFLOW
+            SL_LOGE(ERROR_PLAYER_PREFETCH_d, data1);
+            if (IsInterfaceInitialized(&mp->mObject, MPH_XAPREFETCHSTATUS)) {
+                mp->mPrefetchStatus.mLevel = 0;
+                mp->mPrefetchStatus.mStatus = SL_PREFETCHSTATUS_UNDERFLOW;
+                if (!(~mp->mPrefetchStatus.mCallbackEventsMask &
+                        (SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE))) {
+                    callback = mp->mPrefetchStatus.mCallback;
+                    callbackPContext = mp->mPrefetchStatus.mContext;
+                    events = SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE;
+                }
             }
         }
+
         object_unlock_exclusive(&mp->mObject);
 
         // callback with no lock held
         if (NULL != callback) {
-            (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext,
-                    SL_PREFETCHEVENT_FILLLEVELCHANGE | SL_PREFETCHEVENT_STATUSCHANGE);
+            (*callback)(&mp->mPrefetchStatus.mItf, callbackPContext, events);
         }
 
         break;
@@ -723,7 +732,8 @@
             SL_LOGV("Displaying on ANativeWindow of type NATIVE_WINDOW_SURFACE");
             android::sp<android::Surface> nativeSurface(
                     static_cast<android::Surface *>(nativeWindow));
-            mp->mAVPlayer->setVideoSurface(nativeSurface);
+            mp->mAVPlayer->setVideoSurfaceTexture(
+                    nativeSurface->getSurfaceTexture());
             result = SL_RESULT_SUCCESS;
             } break;
         case NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT: { // SurfaceTextureClient
diff --git a/src/android/android_AudioSfDecoder.cpp b/src/android/android_AudioSfDecoder.cpp
index a436fac..a29f09f 100644
--- a/src/android/android_AudioSfDecoder.cpp
+++ b/src/android/android_AudioSfDecoder.cpp
@@ -19,6 +19,7 @@
 #include "sles_allinclusive.h"
 #include "android/android_AudioSfDecoder.h"
 
+#include <binder/IServiceManager.h>
 #include <media/stagefright/foundation/ADebug.h>
 
 
@@ -782,17 +783,25 @@
     updateAudioSink();
 }
 
-static const char* const kUnsupportedCodecs[] = { MEDIA_MIMETYPE_AUDIO_AMR_NB,
+static const char* const kPlaybackOnlyCodecs[] = { MEDIA_MIMETYPE_AUDIO_AMR_NB,
         MEDIA_MIMETYPE_AUDIO_AMR_WB };
-#define NB_UNSUPPORTED_CODECS (sizeof(kUnsupportedCodecs)/sizeof(kUnsupportedCodecs[0]))
+#define NB_PLAYBACK_ONLY_CODECS (sizeof(kPlaybackOnlyCodecs)/sizeof(kPlaybackOnlyCodecs[0]))
 
 bool AudioSfDecoder::isSupportedCodec(const char* mime) {
-    for (unsigned int i = 0 ; i < NB_UNSUPPORTED_CODECS ; i++) {
-        if (!strcasecmp(mime, kUnsupportedCodecs[i])) {
-            return false;
+    bool codecRequiresPermission = false;
+    for (unsigned int i = 0 ; i < NB_PLAYBACK_ONLY_CODECS ; i++) {
+        if (!strcasecmp(mime, kPlaybackOnlyCodecs[i])) {
+            codecRequiresPermission = true;
+            break;
         }
     }
-    return true;
+    if (codecRequiresPermission) {
+        // verify only the system can decode, for playback only
+        return checkCallingPermission(
+                String16("android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"));
+    } else {
+        return true;
+    }
 }
 
 } // namespace android
diff --git a/src/android/android_GenericMediaPlayer.cpp b/src/android/android_GenericMediaPlayer.cpp
index 68bc7f6..f3cebf5 100644
--- a/src/android/android_GenericMediaPlayer.cpp
+++ b/src/android/android_GenericMediaPlayer.cpp
@@ -23,6 +23,7 @@
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/mediaplayer.h>  // media_event_type media_error_type media_info_type
 
 // default delay in Us used when reposting an event when the player is not ready to accept
 // the command yet. This is for instance used when seeking on a MediaPlayer that's still preparing
@@ -57,20 +58,53 @@
     SL_LOGV("MediaPlayerNotificationClient::~MediaPlayerNotificationClient()");
 }
 
-// Map a MEDIA_* enum to a string
-static const char *media_to_string(int msg)
+// Map a media_event_type enum (the msg of an IMediaPlayerClient::notify) to a string or NULL
+static const char *media_event_type_to_string(media_event_type msg)
 {
     switch (msg) {
-#define _(x) case MEDIA_##x: return "MEDIA_" #x;
-      _(PREPARED)
-      _(SET_VIDEO_SIZE)
-      _(SEEK_COMPLETE)
-      _(PLAYBACK_COMPLETE)
-      _(BUFFERING_UPDATE)
-      _(ERROR)
-      _(NOP)
-      _(TIMED_TEXT)
-      _(INFO)
+#define _(code) case code: return #code;
+    _(MEDIA_NOP)
+    _(MEDIA_PREPARED)
+    _(MEDIA_PLAYBACK_COMPLETE)
+    _(MEDIA_BUFFERING_UPDATE)
+    _(MEDIA_SEEK_COMPLETE)
+    _(MEDIA_SET_VIDEO_SIZE)
+    _(MEDIA_TIMED_TEXT)
+    _(MEDIA_ERROR)
+    _(MEDIA_INFO)
+#undef _
+    default:
+        return NULL;
+    }
+}
+
+// Map a media_error_type enum (the ext1 of a MEDIA_ERROR event) to a string or NULL
+static const char *media_error_type_to_string(media_error_type err)
+{
+    switch (err) {
+#define _(code, msg) case code: return msg;
+    _(MEDIA_ERROR_UNKNOWN,                              "Unknown media error")
+    _(MEDIA_ERROR_SERVER_DIED,                          "Server died")
+    _(MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK,   "Not valid for progressive playback")
+#undef _
+    default:
+        return NULL;
+    }
+}
+
+// Map a media_info_type enum (the ext1 of a MEDIA_INFO event) to a string or NULL
+static const char *media_info_type_to_string(media_info_type info)
+{
+    switch (info) {
+#define _(code, msg) case code: return msg;
+    _(MEDIA_INFO_UNKNOWN,             "Unknown info")
+    _(MEDIA_INFO_VIDEO_TRACK_LAGGING, "Video track lagging")
+    _(MEDIA_INFO_BUFFERING_START,     "Buffering start")
+    _(MEDIA_INFO_BUFFERING_END,       "Buffering end")
+    _(MEDIA_INFO_NETWORK_BANDWIDTH,   "Network bandwidth")
+    _(MEDIA_INFO_BAD_INTERLEAVING,    "Bad interleaving")
+    _(MEDIA_INFO_NOT_SEEKABLE,        "Not seekable")
+    _(MEDIA_INFO_METADATA_UPDATE,     "Metadata update")
 #undef _
     default:
         return NULL;
@@ -81,7 +115,7 @@
 // IMediaPlayerClient implementation
 void MediaPlayerNotificationClient::notify(int msg, int ext1, int ext2, const Parcel *obj) {
     SL_LOGV("MediaPlayerNotificationClient::notify(msg=%s (%d), ext1=%d, ext2=%d)",
-            media_to_string(msg), msg, ext1, ext2);
+            media_event_type_to_string((enum media_event_type) msg), msg, ext1, ext2);
 
     sp<GenericMediaPlayer> genericMediaPlayer(mGenericMediaPlayer.promote());
     if (genericMediaPlayer == NULL) {
@@ -89,7 +123,7 @@
         return;
     }
 
-    switch (msg) {
+    switch ((media_event_type) msg) {
       case MEDIA_PREPARED:
         {
         Mutex::Autolock _l(mLock);
@@ -109,9 +143,9 @@
         // so it would normally be racy to access fields within genericMediaPlayer.
         // But in this case mHasVideo is const, so it is safe to access.
         // Or alternatively, we could notify unconditionally and let it decide whether to handle.
-        if (genericMediaPlayer->mHasVideo) {
+        if (genericMediaPlayer->mHasVideo && (ext1 != 0 || ext2 != 0)) {
             genericMediaPlayer->notify(PLAYEREVENT_VIDEO_SIZE_UPDATE,
-                    (int32_t)ext1, (int32_t)ext2, true /*async*/);
+                    (int32_t)ext1 /*width*/, (int32_t)ext2 /*height*/, true /*async*/);
         }
         break;
 
@@ -124,6 +158,14 @@
         break;
 
       case MEDIA_BUFFERING_UPDATE:
+        // if we receive any out-of-range data, then clamp it to reduce further harm
+        if (ext1 < 0) {
+            SL_LOGE("MEDIA_BUFFERING_UPDATE %d%% < 0", ext1);
+            ext1 = 0;
+        } else if (ext1 > 100) {
+            SL_LOGE("MEDIA_BUFFERING_UPDATE %d%% > 100", ext1);
+            ext1 = 100;
+        }
         // values received from Android framework for buffer fill level use percent,
         //   while SL/XA use permille, so does GenericPlayer
         genericMediaPlayer->bufferingUpdate(ext1 * 10 /*fillLevelPerMille*/);
@@ -131,6 +173,8 @@
 
       case MEDIA_ERROR:
         {
+        SL_LOGV("MediaPlayerNotificationClient::notify(msg=MEDIA_ERROR, ext1=%s (%d), ext2=%d)",
+                media_error_type_to_string((media_error_type) ext1), ext1, ext2);
         Mutex::Autolock _l(mLock);
         if (PREPARE_IN_PROGRESS == mPlayerPrepared) {
             mPlayerPrepared = PREPARE_COMPLETED_UNSUCCESSFULLY;
@@ -144,10 +188,31 @@
 
       case MEDIA_NOP:
       case MEDIA_TIMED_TEXT:
-      case MEDIA_INFO:
         break;
 
-      default: { }
+      case MEDIA_INFO:
+        SL_LOGV("MediaPlayerNotificationClient::notify(msg=MEDIA_INFO, ext1=%s (%d), ext2=%d)",
+                media_info_type_to_string((media_info_type) ext1), ext1, ext2);
+        switch (ext1) {
+        case MEDIA_INFO_VIDEO_TRACK_LAGGING:
+            SL_LOGV("MEDIA_INFO_VIDEO_TRACK_LAGGING by %d ms", ext1);
+            break;
+        case MEDIA_INFO_NETWORK_BANDWIDTH:
+            SL_LOGV("MEDIA_INFO_NETWORK_BANDWIDTH %d kbps", ext2);
+            break;
+        case MEDIA_INFO_UNKNOWN:
+        case MEDIA_INFO_BUFFERING_START:
+        case MEDIA_INFO_BUFFERING_END:
+        case MEDIA_INFO_BAD_INTERLEAVING:
+        case MEDIA_INFO_NOT_SEEKABLE:
+        case MEDIA_INFO_METADATA_UPDATE:
+        default:
+            break;
+        }
+        break;
+
+      default:
+        break;
     }
 
 }
@@ -177,10 +242,10 @@
     GenericPlayer(params),
     mHasVideo(hasVideo),
     mSeekTimeMsec(0),
-    mVideoSurface(0),
     mVideoSurfaceTexture(0),
     mPlayer(0),
-    mPlayerClient(new MediaPlayerNotificationClient(this))
+    mPlayerClient(new MediaPlayerNotificationClient(this)),
+    mPlayerDeathNotifier(new MediaPlayerDeathNotifier(mPlayerClient))
 {
     SL_LOGD("GenericMediaPlayer::GenericMediaPlayer()");
 
@@ -199,7 +264,7 @@
         // causes CHECK failure in Nuplayer, but commented out in the subclass preDestroy
         // randomly causes a NPE in StagefrightPlayer, heap corruption, or app hang
         //player->setDataSource(NULL);
-        player->setVideoSurface(NULL);
+        player->setVideoSurfaceTexture(NULL);
         player->disconnect();
         // release all references to the IMediaPlayer
         // FIXME illegal if not on looper
@@ -231,19 +296,6 @@
 }
 
 //--------------------------------------------------
-void GenericMediaPlayer::setVideoSurface(const sp<Surface> &surface) {
-    SL_LOGV("GenericMediaPlayer::setVideoSurface()");
-    // FIXME bug - race condition, should do in looper
-    if (mVideoSurface.get() == surface.get()) {
-        return;
-    }
-    if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)) {
-        mPlayer->setVideoSurface(surface);
-    }
-    mVideoSurface = surface;
-    mVideoSurfaceTexture = NULL;
-}
-
 void GenericMediaPlayer::setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
     SL_LOGV("GenericMediaPlayer::setVideoSurfaceTexture()");
     // FIXME bug - race condition, should do in looper
@@ -254,7 +306,6 @@
         mPlayer->setVideoSurfaceTexture(surfaceTexture);
     }
     mVideoSurfaceTexture = surfaceTexture;
-    mVideoSurface = NULL;
 }
 
 
@@ -267,9 +318,7 @@
     // Attempt to prepare at most once, and only if there is a MediaPlayer
     if (!(mStateFlags & (kFlagPrepared | kFlagPreparedUnsuccessfully)) && (mPlayer != 0)) {
         if (mHasVideo) {
-            if (mVideoSurface != 0) {
-                mPlayer->setVideoSurface(mVideoSurface);
-            } else if (mVideoSurfaceTexture != 0) {
+            if (mVideoSurfaceTexture != 0) {
                 mPlayer->setVideoSurfaceTexture(mVideoSurfaceTexture);
             }
         }
diff --git a/src/android/android_GenericMediaPlayer.h b/src/android/android_GenericMediaPlayer.h
index c3a9d00..3e8fe81 100644
--- a/src/android/android_GenericMediaPlayer.h
+++ b/src/android/android_GenericMediaPlayer.h
@@ -32,7 +32,6 @@
 {
 public:
     MediaPlayerNotificationClient(GenericMediaPlayer* gmp);
-    virtual ~MediaPlayerNotificationClient();
 
     // IMediaPlayerClient implementation
     virtual void notify(int msg, int ext1, int ext2, const Parcel *obj);
@@ -44,6 +43,9 @@
     // completed successfully, or false if it completed unsuccessfully
     bool blockUntilPlayerPrepared();
 
+protected:
+    virtual ~MediaPlayerNotificationClient();
+
 private:
     const wp<GenericMediaPlayer> mGenericMediaPlayer;
     Mutex mLock;                        // protects mPlayerPrepared
@@ -57,6 +59,24 @@
 };
 
 
+class MediaPlayerDeathNotifier : public IMediaDeathNotifier {
+public:
+    MediaPlayerDeathNotifier(const sp<MediaPlayerNotificationClient> playerClient) :
+        mPlayerClient(playerClient) {
+    }
+
+    void died() {
+        mPlayerClient->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0, NULL);
+    }
+
+protected:
+    virtual ~MediaPlayerDeathNotifier() { }
+
+private:
+    const sp<MediaPlayerNotificationClient> mPlayerClient;
+};
+
+
 //--------------------------------------------------------------------------------------------------
 class GenericMediaPlayer : public GenericPlayer
 {
@@ -70,7 +90,6 @@
     // overridden from GenericPlayer
     virtual void getPositionMsec(int* msec); // ANDROID_UNKNOWN_TIME if unknown
 
-    virtual void setVideoSurface(const sp<Surface> &surface);
     virtual void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
 
 protected:
@@ -91,8 +110,6 @@
     const bool mHasVideo;   // const allows MediaPlayerNotificationClient::notify to safely access
     int32_t mSeekTimeMsec;
 
-    // at most one of mVideoSurface and mVideoSurfaceTexture is non-NULL
-    sp<Surface> mVideoSurface;
     sp<ISurfaceTexture> mVideoSurfaceTexture;
 
     // only safe to access from within Realize and looper
@@ -100,6 +117,9 @@
     // Receives Android MediaPlayer events from mPlayer
     const sp<MediaPlayerNotificationClient> mPlayerClient;
 
+    // Receives notifications about death of media.player service
+    const sp<MediaPlayerDeathNotifier> mPlayerDeathNotifier;
+
     // Return a reference to the media player service, or LOGE and return NULL after retries fail
     static const sp<IMediaPlayerService> getMediaPlayerService() {
         return IMediaDeathNotifier::getMediaPlayerService();
diff --git a/src/android/android_GenericPlayer.h b/src/android/android_GenericPlayer.h
index b4104ad..d3206a1 100644
--- a/src/android/android_GenericPlayer.h
+++ b/src/android/android_GenericPlayer.h
@@ -78,7 +78,6 @@
     virtual void getDurationMsec(int* msec); //msec != NULL, ANDROID_UNKNOWN_TIME if unknown
     virtual void getPositionMsec(int* msec) = 0; //msec != NULL, ANDROID_UNKNOWN_TIME if unknown
 
-    virtual void setVideoSurface(const sp<Surface> &surface) {}
     virtual void setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {}
 
     void setVolume(float leftVol, float rightVol);
diff --git a/src/android/android_StreamPlayer.cpp b/src/android/android_StreamPlayer.cpp
index a0ab4f8..7c400c8 100644
--- a/src/android/android_StreamPlayer.cpp
+++ b/src/android/android_StreamPlayer.cpp
@@ -45,8 +45,7 @@
 }
 
 StreamSourceAppProxy::~StreamSourceAppProxy() {
-    // FIXME make this an SL_LOGV later; this just proves that the bug is fixed
-    SL_LOGI("StreamSourceAppProxy::~StreamSourceAppProxy()");
+    SL_LOGV("StreamSourceAppProxy::~StreamSourceAppProxy()");
     disconnect();
 }
 
@@ -331,7 +330,7 @@
         mPlayer->stop();
         // causes CHECK failure in Nuplayer
         //mPlayer->setDataSource(NULL);
-        mPlayer->setVideoSurface(NULL);
+        mPlayer->setVideoSurfaceTexture(NULL);
         mPlayer->disconnect();
         mPlayer.clear();
         {
@@ -340,7 +339,10 @@
             mPreparedPlayer.clear();
         }
     }
-    mStopForDestroyCompleted = true;
+    {
+        Mutex::Autolock _l(mStopForDestroyLock);
+        mStopForDestroyCompleted = true;
+    }
     mStopForDestroyCondition.signal();
 }
 
diff --git a/src/android/include/AacBqToPcmCbRenderer.h b/src/android/include/AacBqToPcmCbRenderer.h
index d144f1e..62cbd6e 100644
--- a/src/android/include/AacBqToPcmCbRenderer.h
+++ b/src/android/include/AacBqToPcmCbRenderer.h
@@ -33,11 +33,10 @@
 {
 public:
 
-    AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params);
+    AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params,
+            IAndroidBufferQueue *androidBufferQueue);
     virtual ~AacBqToPcmCbRenderer();
 
-    void registerSourceQueueCallback(const void* user, void *context,  const void *caller);
-
     // verifies the given memory starts and ends on ADTS frame boundaries.
     // This is for instance used whenever ADTS data is being enqueued through an
     // SL / XA AndroidBufferQueue interface so only parseable ADTS data goes in
@@ -51,9 +50,7 @@
 
 
 private:
-    // mutex used to protect mBqSource
-    Mutex                 mBqSourceLock;
-    sp<BufferQueueSource> mBqSource;
+    const sp<BufferQueueSource> mBqSource;
 
 private:
     DISALLOW_EVIL_CONSTRUCTORS(AacBqToPcmCbRenderer);
diff --git a/src/itf/IAndroidBufferQueue.c b/src/itf/IAndroidBufferQueue.c
index 278b7fd..76ab546 100644
--- a/src/itf/IAndroidBufferQueue.c
+++ b/src/itf/IAndroidBufferQueue.c
@@ -214,19 +214,7 @@
     if (SL_PLAYSTATE_STOPPED == getAssociatedState(thiz)) {
         thiz->mCallback = callback;
         thiz->mContext = pContext;
-
-        // FIXME investigate why these two cases are not handled symmetrically any more
-        switch (InterfaceToObjectID(thiz)) {
-          case SL_OBJECTID_AUDIOPLAYER:
-            result = android_audioPlayer_androidBufferQueue_registerCallback_l(
-                    (CAudioPlayer*) thiz->mThis);
-            break;
-          case XA_OBJECTID_MEDIAPLAYER:
-            result = SL_RESULT_SUCCESS;
-            break;
-          default:
-            result = SL_RESULT_PARAMETER_INVALID;
-        }
+        result = SL_RESULT_SUCCESS;
 
     } else {
         result = SL_RESULT_PRECONDITIONS_VIOLATED;
diff --git a/tests/examples/Android.mk b/tests/examples/Android.mk
index ac06ee7..f9e912e 100644
--- a/tests/examples/Android.mk
+++ b/tests/examples/Android.mk
@@ -293,7 +293,7 @@
 
 LOCAL_CFLAGS += -UNDEBUG
 
-LOCAL_MODULE:= slesTestDecodeAac
+LOCAL_MODULE:= slesTest_decodeAac
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/tests/examples/slesTestDecodeAac.cpp b/tests/examples/slesTestDecodeAac.cpp
index 707ab37..1a51bb0 100644
--- a/tests/examples/slesTestDecodeAac.cpp
+++ b/tests/examples/slesTestDecodeAac.cpp
@@ -45,6 +45,7 @@
 
 #define QUERY_METADATA
 
+#include <assert.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -64,7 +65,7 @@
  * on the AudioPlayer object for decoding, and
  * SL_IID_METADATAEXTRACTION for retrieving the format of the decoded audio.
  */
-#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 3
+#define NUM_EXPLICIT_INTERFACES_FOR_PLAYER 4
 
 /* Number of decoded samples produced by one AAC frame; defined by the standard */
 #define SAMPLES_PER_AAC_FRAME 1024
@@ -115,7 +116,7 @@
 size_t totalEncodeCompletions = 0;     // number of Enqueue completions received
 CentralTendencyStatistics frameStats;
 size_t pauseFrame = 0;              // pause after this many decoded frames, zero means don't pause
-SLboolean createRaw = SL_BOOLEAN_FALSE; // whether to create a .raw file containing PCM data
+SLboolean createRaw = SL_BOOLEAN_TRUE; // whether to create a .raw file containing PCM data
 
 /* constant to identify a buffer context which is the end of the stream to decode */
 static const int kEosBufferCntxt = 1980; // a magic value we can compare against
@@ -124,6 +125,23 @@
 pthread_mutex_t eosLock = PTHREAD_MUTEX_INITIALIZER;
 pthread_cond_t eosCondition = PTHREAD_COND_INITIALIZER;
 
+// These are extensions to OpenMAX AL 1.0.1 values
+
+#define PREFETCHSTATUS_UNKNOWN ((SLuint32) 0)
+#define PREFETCHSTATUS_ERROR   ((SLuint32) (-1))
+
+// Mutex and condition shared with main program to protect prefetch_status
+
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+SLuint32 prefetch_status = PREFETCHSTATUS_UNKNOWN;
+
+/* used to detect errors likely to have occured when the OpenSL ES framework fails to open
+ * a resource, for instance because a file URI is invalid, or an HTTP server doesn't respond.
+ */
+#define PREFETCHEVENT_ERROR_CANDIDATE \
+        (SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE)
+
 //-----------------------------------------------------------------
 /* Exits the application if an error is encountered */
 #define ExitOnError(x) ExitOnErrorFunc(x,__LINE__)
@@ -137,6 +155,40 @@
 }
 
 //-----------------------------------------------------------------
+/* Callback for "prefetch" events, here used to detect audio resource opening errors */
+void PrefetchEventCallback(SLPrefetchStatusItf caller, void *pContext, SLuint32 event)
+{
+    // pContext is unused here, so we pass NULL
+    assert(pContext == NULL);
+    SLpermille level = 0;
+    SLresult result;
+    result = (*caller)->GetFillLevel(caller, &level);
+    ExitOnError(result);
+    SLuint32 status;
+    result = (*caller)->GetPrefetchStatus(caller, &status);
+    ExitOnError(result);
+    printf("prefetch level=%d status=0x%x event=%d\n", level, status, event);
+    SLuint32 new_prefetch_status;
+    if ((PREFETCHEVENT_ERROR_CANDIDATE == (event & PREFETCHEVENT_ERROR_CANDIDATE))
+            && (level == 0) && (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
+        printf("PrefetchEventCallback: Error while prefetching data, exiting\n");
+        new_prefetch_status = PREFETCHSTATUS_ERROR;
+    } else if (event == SL_PREFETCHEVENT_STATUSCHANGE) {
+        new_prefetch_status = status;
+    } else {
+        return;
+    }
+    int ok;
+    ok = pthread_mutex_lock(&mutex);
+    assert(ok == 0);
+    prefetch_status = new_prefetch_status;
+    ok = pthread_cond_signal(&cond);
+    assert(ok == 0);
+    ok = pthread_mutex_unlock(&mutex);
+    assert(ok == 0);
+}
+
+//-----------------------------------------------------------------
 /* Structure for passing information to callback function */
 typedef struct CallbackCntxt_ {
 #ifdef QUERY_METADATA
@@ -147,6 +199,11 @@
     SLint8*   pData;        // Current address of local audio data storage
 } CallbackCntxt;
 
+// used to notify when SL_PLAYEVENT_HEADATEND event is received
+static pthread_mutex_t head_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t head_cond = PTHREAD_COND_INITIALIZER;
+static SLboolean head_atend = SL_BOOLEAN_FALSE;
+
 //-----------------------------------------------------------------
 /* Callback for SLPlayItf through which we receive the SL_PLAYEVENT_HEADATEND event */
 void PlayCallback(SLPlayItf caller, void *pContext, SLuint32 event) {
@@ -162,6 +219,10 @@
     if (event & SL_PLAYEVENT_HEADATEND) {
         printf("SL_PLAYEVENT_HEADATEND position=%u ms, all decoded data has been received\n",
                 position);
+        pthread_mutex_lock(&head_mutex);
+        head_atend = SL_BOOLEAN_TRUE;
+        pthread_cond_signal(&head_cond);
+        pthread_mutex_unlock(&head_mutex);
     }
 }
 
@@ -184,8 +245,7 @@
     // for demonstration purposes:
     // verify what type of information was enclosed in the processed buffer
     if (NULL != pBufferContext) {
-        const int processedCommand = *(int *)pBufferContext;
-        if (kEosBufferCntxt == processedCommand) {
+        if (&kEosBufferCntxt == pBufferContext) {
             fprintf(stdout, "EOS was processed\n");
         }
     }
@@ -193,7 +253,7 @@
     ++totalEncodeCompletions;
     if (endOfEncodedStream) {
         // we continue to receive acknowledgement after each buffer was processed
-        if (pBufferContext == (void *) kEosBufferCntxt) {
+        if (pBufferContext == (void *) &kEosBufferCntxt) {
             printf("Received EOS completion after EOS\n");
         } else if (pBufferContext == NULL) {
             printf("Received ADTS completion after EOS\n");
@@ -411,6 +471,8 @@
     SLAndroidSimpleBufferQueueItf decBuffQueueItf;
     /*   to queue the AAC data to decode */
     SLAndroidBufferQueueItf       aacBuffQueueItf;
+    /*   for prefetch status */
+    SLPrefetchStatusItf           prefetchItf;
 
     SLboolean required[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
     SLInterfaceID iidArray[NUM_EXPLICIT_INTERFACES_FOR_PLAYER];
@@ -435,10 +497,13 @@
     /* Request the AndroidBufferQueue interface */
     required[1] = SL_BOOLEAN_TRUE;
     iidArray[1] = SL_IID_ANDROIDBUFFERQUEUESOURCE;
+    /* Request the PrefetchStatus interface */
+    required[2] = SL_BOOLEAN_TRUE;
+    iidArray[2] = SL_IID_PREFETCHSTATUS;
 #ifdef QUERY_METADATA
     /* Request the MetadataExtraction interface */
-    required[2] = SL_BOOLEAN_TRUE;
-    iidArray[2] = SL_IID_METADATAEXTRACTION;
+    required[3] = SL_BOOLEAN_TRUE;
+    iidArray[3] = SL_IID_METADATAEXTRACTION;
 #endif
 
     /* Setup the data source for queueing AAC buffers of ADTS data */
@@ -529,6 +594,10 @@
     res = (*player)->GetInterface(player, SL_IID_ANDROIDBUFFERQUEUESOURCE, (void*)&aacBuffQueueItf);
     ExitOnError(res);
 
+    /* Get the prefetch status interface which was explicitly requested */
+    res = (*player)->GetInterface(player, SL_IID_PREFETCHSTATUS, (void*)&prefetchItf);
+    ExitOnError(res);
+
 #ifdef QUERY_METADATA
     /* Get the metadata extraction interface which was explicitly requested */
     res = (*player)->GetInterface(player, SL_IID_METADATAEXTRACTION, (void*)&mdExtrItf);
@@ -561,6 +630,13 @@
     }
     printf("\n");
 
+    /* ------------------------------------------------------ */
+    /* Initialize the callback for prefetch errors, if we can't open the resource to decode */
+    res = (*prefetchItf)->RegisterCallback(prefetchItf, PrefetchEventCallback, NULL);
+    ExitOnError(res);
+    res = (*prefetchItf)->SetCallbackEventsMask(prefetchItf, PREFETCHEVENT_ERROR_CANDIDATE);
+    ExitOnError(res);
+
     /* Initialize the callback for the Android buffer queue of the encoded data */
     res = (*aacBuffQueueItf)->RegisterCallback(aacBuffQueueItf, AndroidBufferQueueCallback, NULL);
     ExitOnError(res);
@@ -569,10 +645,14 @@
        we don't want to starve the player initially */
     printf("Enqueueing initial full buffers of encoded ADTS data");
     for (i=0 ; i < NB_BUFFERS_IN_ADTS_QUEUE ; i++) {
-        if (filelen < 7 || frame[0] != 0xFF || (frame[1] & 0xF0) != 0xF0)
+        if (filelen < 7 || frame[0] != 0xFF || (frame[1] & 0xF0) != 0xF0) {
+            printf("\ncorrupt ADTS frame encountered; offset %zu bytes\n",
+                    frame - (unsigned char *) ptr);
+            // Note that prefetch will detect this error soon when it gets a premature EOF
             break;
+        }
         unsigned framelen = ((frame[3] & 3) << 11) | (frame[4] << 3) | (frame[5] >> 5);
-        printf(" %d", i);
+        printf(" %d (%u bytes)", i, framelen);
         res = (*aacBuffQueueItf)->Enqueue(aacBuffQueueItf, NULL /*pBufferContext*/,
                 frame, framelen, NULL, 0);
         ExitOnError(res);
@@ -586,10 +666,12 @@
 
 #ifdef QUERY_METADATA
     /* ------------------------------------------------------ */
-    /* Display the metadata obtained from the decoder */
+    /* Get and display the metadata key names for the decoder */
     //   This is for test / demonstration purposes only where we discover the key and value sizes
     //   of a PCM decoder. An application that would want to directly get access to those values
-    //   can make assumptions about the size of the keys and their matching values (all SLuint32)
+    //   can make assumptions about the size of the keys and their matching values (all SLuint32),
+    //   but it should not make assumptions about the key indices as these are subject to change.
+    //   Note that we don't get the metadata values yet; that happens in the first decode callback.
     SLuint32 itemCount;
     res = (*mdExtrItf)->GetItemCount(mdExtrItf, &itemCount);
     ExitOnError(res);
@@ -667,6 +749,24 @@
     }
 #endif
 
+    // set the player's state to paused, to start prefetching
+    printf("Setting play state to PAUSED\n");
+    res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
+    ExitOnError(res);
+
+    // wait for prefetch status callback to indicate either sufficient data or error
+    printf("Awaiting prefetch complete\n");
+    pthread_mutex_lock(&mutex);
+    while (prefetch_status == PREFETCHSTATUS_UNKNOWN) {
+        pthread_cond_wait(&cond, &mutex);
+    }
+    pthread_mutex_unlock(&mutex);
+    if (prefetch_status == PREFETCHSTATUS_ERROR) {
+        fprintf(stderr, "Error during prefetch, exiting\n");
+        goto destroyRes;
+    }
+    printf("Prefetch is complete\n");
+
     /* ------------------------------------------------------ */
     /* Start decoding */
     printf("Starting to decode\n");
@@ -674,6 +774,7 @@
     ExitOnError(res);
 
     /* Decode until the end of the stream is reached */
+    printf("Awaiting notification that all encoded buffers have been enqueued\n");
     pthread_mutex_lock(&eosLock);
     while (!eos) {
         if (pauseFrame > 0) {
@@ -698,10 +799,15 @@
         }
     }
     pthread_mutex_unlock(&eosLock);
+    printf("All encoded buffers have now been enqueued, but there's still more to do\n");
 
     /* This just means done enqueueing; there may still more data in decode queue! */
-    // FIXME here is where we should wait for HEADATEND
-    usleep(100 * 1000);
+    pthread_mutex_lock(&head_mutex);
+    while (!head_atend) {
+        pthread_cond_wait(&head_cond, &head_mutex);
+    }
+    pthread_mutex_unlock(&head_mutex);
+    printf("Decode is now finished\n");
 
     pthread_mutex_lock(&eosLock);
     printf("Frame counters: encoded=%u decoded=%u\n", encodedFrames, decodedFrames);