Merge "handle supplicant stop after child states exit"
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisRequest.java
index 6a9b2dc..7efc264 100644
--- a/core/java/android/speech/tts/FileSynthesisRequest.java
+++ b/core/java/android/speech/tts/FileSynthesisRequest.java
@@ -45,6 +45,7 @@
     private int mChannelCount;
     private RandomAccessFile mFile;
     private boolean mStopped = false;
+    private boolean mDone = false;
 
     FileSynthesisRequest(String text, File fileName) {
         super(text);
@@ -89,6 +90,11 @@
     }
 
     @Override
+    boolean isDone() {
+        return mDone;
+    }
+
+    @Override
     public int start(int sampleRateInHz, int audioFormat, int channelCount) {
         if (DBG) {
             Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
@@ -164,6 +170,7 @@
                 mFile.write(
                         makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength));
                 closeFile();
+                mDone = true;
                 return TextToSpeech.SUCCESS;
             } catch (IOException ex) {
                 Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
@@ -174,6 +181,14 @@
     }
 
     @Override
+    public void error() {
+        if (DBG) Log.d(TAG, "FileSynthesisRequest.error()");
+        synchronized (mStateLock) {
+            cleanUp();
+        }
+    }
+
+    @Override
     public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
             byte[] buffer, int offset, int length) {
         synchronized (mStateLock) {
@@ -187,9 +202,11 @@
             out = new FileOutputStream(mFileName);
             out.write(makeWavHeader(sampleRateInHz, audioFormat, channelCount, length));
             out.write(buffer, offset, length);
+            mDone = true;
             return TextToSpeech.SUCCESS;
         } catch (IOException ex) {
             Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+            mFileName.delete();
             return TextToSpeech.ERROR;
         } finally {
             try {
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index 2267015..227b042 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -50,6 +50,7 @@
     private final Object mStateLock = new Object();
     private AudioTrack mAudioTrack = null;
     private boolean mStopped = false;
+    private boolean mDone = false;
 
     PlaybackSynthesisRequest(String text, int streamType, float volume, float pan) {
         super(text);
@@ -85,6 +86,11 @@
         return MIN_AUDIO_BUFFER_SIZE;
     }
 
+    @Override
+    boolean isDone() {
+        return mDone;
+    }
+
     // TODO: add a thread that writes to the AudioTrack?
     @Override
     public int start(int sampleRateInHz, int audioFormat, int channelCount) {
@@ -183,12 +189,21 @@
                 Log.e(TAG, "done(): Not started");
                 return TextToSpeech.ERROR;
             }
+            mDone = true;
             cleanUp();
         }
         return TextToSpeech.SUCCESS;
     }
 
     @Override
+    public void error() {
+        if (DBG) Log.d(TAG, "error()");
+        synchronized (mStateLock) {
+            cleanUp();
+        }
+    }
+
+    @Override
     public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
             byte[] buffer, int offset, int length) {
         if (DBG) {
@@ -217,6 +232,7 @@
             try {
                 mAudioTrack.write(buffer, offset, length);
                 mAudioTrack.play();
+                mDone = true;
             } catch (IllegalStateException ex) {
                 Log.e(TAG, "Playback error", ex);
                 return TextToSpeech.ERROR;
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
index f4bb852..515218b 100644
--- a/core/java/android/speech/tts/SynthesisRequest.java
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -114,6 +114,11 @@
     public abstract int getMaxBufferSize();
 
     /**
+     * Checks whether the synthesis request completed successfully.
+     */
+    abstract boolean isDone();
+
+    /**
      * Aborts the speech request.
      *
      * Can be called from multiple threads.
@@ -162,6 +167,14 @@
     public abstract int done();
 
     /**
+     * The service should call this method if the speech synthesis fails.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     */
+    public abstract void error();
+
+    /**
      * The service can call this method instead of using {@link #start}, {@link #audioAvailable}
      * and {@link #done} if all the audio data is available in a single buffer.
      *
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a408ea2..da97fb4 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -150,12 +150,10 @@
      *
      * Called on the synthesis thread.
      *
-     * @param request The synthesis request. The method should
-     *         call {@link SynthesisRequest#start}, {@link SynthesisRequest#audioAvailable},
-     *         and {@link SynthesisRequest#done} on this request.
-     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     * @param request The synthesis request. The method should use the methods in the request
+     *         object to communicate the results of the synthesis.
      */
-    protected abstract int onSynthesizeText(SynthesisRequest request);
+    protected abstract void onSynthesizeText(SynthesisRequest request);
 
     private boolean areDefaultsEnforced() {
         return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
@@ -442,7 +440,8 @@
                 synthesisRequest = mSynthesisRequest;
             }
             setRequestParams(synthesisRequest);
-            return TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+            TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+            return synthesisRequest.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
         }
 
         protected SynthesisRequest createSynthesisRequest() {
diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h
index 4fac89a..a41c91b 100644
--- a/core/jni/android/graphics/RtlProperties.h
+++ b/core/jni/android/graphics/RtlProperties.h
@@ -52,7 +52,7 @@
 #define DEBUG_ADVANCES 0
 
 // Define if we want (1) to have Glyphs debug values or not (0)
-#define DEBUG_GLYPHS 1
+#define DEBUG_GLYPHS 0
 
 } // namespace android
 #endif // ANDROID_RTL_PROPERTIES_H
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 088202e..77a731a 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -420,7 +420,9 @@
             UBiDi* bidi = ubidi_open();
             if (bidi) {
                 UErrorCode status = U_ZERO_ERROR;
+#if DEBUG_GLYPHS
                 LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq);
+#endif
                 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
                 if (U_SUCCESS(status)) {
                     int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
@@ -430,7 +432,6 @@
 #endif
 
                     if (rc == 1 || !U_SUCCESS(status)) {
-                        LOGD("HERE !!!");
                         computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount,
                                 dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
                         ubidi_close(bidi);
@@ -517,16 +518,25 @@
 #endif
 
     // Get Advances and their total
-    jfloat totalAdvance = 0;
-    for (size_t i = 0; i < count; i++) {
-        totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]);
-#if DEBUG_ADVANCES
-        LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i],
-                totalAdvance);
-#endif
+    jfloat totalAdvance = outAdvances[0] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]);
+    for (size_t i = 1; i < count; i++) {
+        size_t clusterPrevious = shaperItem.log_clusters[i - 1];
+        size_t cluster = shaperItem.log_clusters[i];
+        if (cluster == clusterPrevious) {
+            outAdvances[i] = 0;
+        } else {
+            totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]);
+        }
     }
     *outTotalAdvance = totalAdvance;
 
+#if DEBUG_ADVANCES
+    for (size_t i = 0; i < count; i++) {
+        LOGD("hb-adv[%d] = %f - log_clusters = %d - total = %f", i,
+                outAdvances[i], shaperItem.log_clusters[i], totalAdvance);
+    }
+#endif
+
     // Get Glyphs
     if (outGlyphs) {
         *outGlyphsCount = shaperItem.num_glyphs;
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 340daaf..96828c6 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -127,11 +127,28 @@
     // be called from the client.
     status_t setDefaultBufferSize(uint32_t w, uint32_t h);
 
-private:
+    // getCurrentBuffer returns the buffer associated with the current image.
+    sp<GraphicBuffer> getCurrentBuffer() const;
+
+    // getCurrentTextureTarget returns the texture target of the current
+    // texture as returned by updateTexImage().
+    GLenum getCurrentTextureTarget() const;
+
+    // getCurrentCrop returns the cropping rectangle of the current buffer
+    Rect getCurrentCrop() const;
+
+    // getCurrentTransform returns the transform of the current buffer
+    uint32_t getCurrentTransform() const;
+
+protected:
 
     // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
     // all slots.
     void freeAllBuffers();
+    static bool isExternalFormat(uint32_t format);
+    static GLenum getTextureTarget(uint32_t format);
+
+private:
 
     // createImage creates a new EGLImage from a GraphicBuffer.
     EGLImageKHR createImage(EGLDisplay dpy,
@@ -194,6 +211,10 @@
     // reset mCurrentTexture to INVALID_BUFFER_SLOT.
     int mCurrentTexture;
 
+    // mCurrentTextureTarget is the GLES texture target to be used with the
+    // current texture.
+    GLenum mCurrentTextureTarget;
+
     // mCurrentTextureBuf is the graphic buffer of the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to properly use
@@ -256,7 +277,7 @@
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
-    Mutex mMutex;
+    mutable Mutex mMutex;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index df82bf2..fe9b049 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -27,6 +27,8 @@
 
 namespace android {
 
+class Surface;
+
 class SurfaceTextureClient
     : public EGLNativeBase<ANativeWindow, SurfaceTextureClient, RefBase>
 {
@@ -36,6 +38,7 @@
     sp<ISurfaceTexture> getISurfaceTexture() const;
 
 private:
+    friend class Surface;
 
     // can't be copied
     SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs);
@@ -78,6 +81,8 @@
 
     void freeAllBuffers();
 
+    int getConnectedApi() const;
+
     enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
     enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
     enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
@@ -121,10 +126,25 @@
     // a timestamp is auto-generated when queueBuffer is called.
     int64_t mTimestamp;
 
+    // mConnectedApi holds the currently connected API to this surface
+    int mConnectedApi;
+
+    // mQueryWidth is the width returned by query(). It is set to width
+    // of the last dequeued buffer or to mReqWidth if no buffer was dequeued.
+    uint32_t mQueryWidth;
+
+    // mQueryHeight is the height returned by query(). It is set to height
+    // of the last dequeued buffer or to mReqHeight if no buffer was dequeued.
+    uint32_t mQueryHeight;
+
+    // mQueryFormat is the format returned by query(). It is set to the last
+    // dequeued format or to mReqFormat if no buffer was dequeued.
+    uint32_t mQueryFormat;
+
     // mMutex is the mutex used to prevent concurrent access to the member
     // variables of SurfaceTexture objects. It must be locked whenever the
     // member variables are accessed.
-    Mutex mMutex;
+    mutable Mutex mMutex;
 };
 
 }; // namespace android
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index e2346f0..39418f0 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -27,6 +27,8 @@
 
 #include <gui/SurfaceTexture.h>
 
+#include <hardware/hardware.h>
+
 #include <surfaceflinger/ISurfaceComposer.h>
 #include <surfaceflinger/SurfaceComposerClient.h>
 #include <surfaceflinger/IGraphicBufferAlloc.h>
@@ -82,6 +84,7 @@
     mUseDefaultSize(true),
     mBufferCount(MIN_BUFFER_SLOTS),
     mCurrentTexture(INVALID_BUFFER_SLOT),
+    mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES),
     mCurrentTransform(0),
     mCurrentTimestamp(0),
     mLastQueued(INVALID_BUFFER_SLOT),
@@ -197,6 +200,7 @@
     if (buffer == NULL) {
         return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
     }
+
     if ((mUseDefaultSize) &&
         ((uint32_t(buffer->width) != mDefaultWidth) ||
          (uint32_t(buffer->height) != mDefaultHeight))) {
@@ -263,9 +267,6 @@
     LOGV("SurfaceTexture::updateTexImage");
     Mutex::Autolock lock(mMutex);
 
-    // We always bind the texture even if we don't update its contents.
-    glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName);
-
     // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
     // so this check will fail until a buffer gets queued.
     if (mCurrentTexture != mLastQueued) {
@@ -283,7 +284,15 @@
         while ((error = glGetError()) != GL_NO_ERROR) {
             LOGE("GL error cleared before updating SurfaceTexture: %#04x", error);
         }
-        glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
+
+        GLenum target = getTextureTarget(
+                mSlots[mLastQueued].mGraphicBuffer->format);
+        if (target != mCurrentTextureTarget) {
+            glDeleteTextures(1, &mTexName);
+        }
+        glBindTexture(target, mTexName);
+        glEGLImageTargetTexture2DOES(target, (GLeglImageOES)image);
+
         bool failed = false;
         while ((error = glGetError()) != GL_NO_ERROR) {
             LOGE("error binding external texture image %p (slot %d): %#04x",
@@ -296,14 +305,53 @@
 
         // Update the SurfaceTexture state.
         mCurrentTexture = mLastQueued;
+        mCurrentTextureTarget = target;
         mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
         mCurrentCrop = mLastQueuedCrop;
         mCurrentTransform = mLastQueuedTransform;
         mCurrentTimestamp = mLastQueuedTimestamp;
+    } else {
+        // We always bind the texture even if we don't update its contents.
+        glBindTexture(mCurrentTextureTarget, mTexName);
     }
     return OK;
 }
 
+bool SurfaceTexture::isExternalFormat(uint32_t format)
+{
+    switch (format) {
+    // supported YUV formats
+    case HAL_PIXEL_FORMAT_YV12:
+    // Legacy/deprecated YUV formats
+    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+    case HAL_PIXEL_FORMAT_YCbCr_422_I:
+        return true;
+    }
+
+    // Any OEM format needs to be considered
+    if (format>=0x100 && format<=0x1FF)
+        return true;
+
+    return false;
+}
+
+GLenum SurfaceTexture::getTextureTarget(uint32_t format)
+{
+    GLenum target = GL_TEXTURE_2D;
+#if defined(GL_OES_EGL_image_external)
+    if (isExternalFormat(format)) {
+        target = GL_TEXTURE_EXTERNAL_OES;
+    }
+#endif
+    return target;
+}
+
+GLenum SurfaceTexture::getCurrentTextureTarget() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTextureTarget;
+}
+
 void SurfaceTexture::getTransformMatrix(float mtx[16]) {
     LOGV("SurfaceTexture::getTransformMatrix");
     Mutex::Autolock lock(mMutex);
@@ -445,6 +493,22 @@
     return image;
 }
 
+sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTextureBuf;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTransform;
+}
+
+
 static void mtxMul(float out[16], const float a[16], const float b[16]) {
     out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
     out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 29fc4d3..f4b2416 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -26,8 +26,10 @@
 SurfaceTextureClient::SurfaceTextureClient(
         const sp<ISurfaceTexture>& surfaceTexture):
         mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
-        mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
-        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
+        mReqHeight(0), mReqFormat(0), mReqUsage(0),
+        mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mConnectedApi(0),
+        mQueryWidth(0), mQueryHeight(0), mQueryFormat(0),
+        mMutex() {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = setSwapInterval;
     ANativeWindow::dequeueBuffer    = dequeueBuffer;
@@ -101,9 +103,10 @@
     }
     sp<GraphicBuffer>& gbuf(mSlots[buf]);
     if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
-        gbuf == 0 || gbuf->getWidth() != mReqWidth ||
-        gbuf->getHeight() != mReqHeight ||
-        uint32_t(gbuf->getPixelFormat()) != mReqFormat ||
+        gbuf == 0 ||
+        (mReqWidth && gbuf->getWidth() != mReqWidth) ||
+        (mReqHeight && gbuf->getHeight() != mReqHeight) ||
+        (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) ||
         (gbuf->getUsage() & mReqUsage) != mReqUsage) {
         gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight,
                 mReqFormat, mReqUsage);
@@ -111,6 +114,9 @@
             LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
             return NO_MEMORY;
         }
+        mQueryWidth  = gbuf->width;
+        mQueryHeight = gbuf->height;
+        mQueryFormat = gbuf->format;
     }
     *buffer = gbuf.get();
     return OK;
@@ -159,13 +165,13 @@
     Mutex::Autolock lock(mMutex);
     switch (what) {
     case NATIVE_WINDOW_WIDTH:
+        *value = mQueryWidth ? mQueryWidth : mReqWidth;
+        return NO_ERROR;
     case NATIVE_WINDOW_HEIGHT:
-        // XXX: How should SurfaceTexture behave if setBuffersGeometry didn't
-        // override the size?
-        *value = 0;
+        *value = mQueryHeight ? mQueryHeight : mReqHeight;
         return NO_ERROR;
     case NATIVE_WINDOW_FORMAT:
-        *value = DEFAULT_FORMAT;
+        *value = mQueryFormat ? mQueryFormat : mReqFormat;
         return NO_ERROR;
     case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
         *value = MIN_UNDEQUEUED_BUFFERS;
@@ -260,16 +266,49 @@
 
 int SurfaceTextureClient::connect(int api) {
     LOGV("SurfaceTextureClient::connect");
-    // XXX: Implement this!
-    return INVALID_OPERATION;
+    Mutex::Autolock lock(mMutex);
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+            if (mConnectedApi) {
+                err = -EINVAL;
+            } else {
+                mConnectedApi = api;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
 }
 
 int SurfaceTextureClient::disconnect(int api) {
     LOGV("SurfaceTextureClient::disconnect");
-    // XXX: Implement this!
-    return INVALID_OPERATION;
+    Mutex::Autolock lock(mMutex);
+    int err = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+            if (mConnectedApi == api) {
+                mConnectedApi = 0;
+            } else {
+                err = -EINVAL;
+            }
+            break;
+        default:
+            err = -EINVAL;
+            break;
+    }
+    return err;
 }
 
+int SurfaceTextureClient::getConnectedApi() const
+{
+    Mutex::Autolock lock(mMutex);
+    return mConnectedApi;
+}
+
+
 int SurfaceTextureClient::setUsage(uint32_t reqUsage)
 {
     LOGV("SurfaceTextureClient::setUsage");
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 1ca2d6d..f9db1a1 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -377,7 +377,7 @@
             mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
         }
 
-        mInitCheck = verifyIfStreamable();
+        mInitCheck = OK;
     } else {
         mInitCheck = err;
     }
@@ -1904,7 +1904,7 @@
 
     off64_t offset;
     size_t size;
-    uint32_t dts;
+    uint32_t cts;
     bool isSyncSample;
     bool newBuffer = false;
     if (mBuffer == NULL) {
@@ -1912,7 +1912,7 @@
 
         status_t err =
             mSampleTable->getMetaDataForSample(
-                    mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample);
+                    mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);
 
         if (err != OK) {
             return err;
@@ -1942,7 +1942,7 @@
             mBuffer->set_range(0, size);
             mBuffer->meta_data()->clear();
             mBuffer->meta_data()->setInt64(
-                    kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
+                    kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
 
             if (targetSampleTimeUs >= 0) {
                 mBuffer->meta_data()->setInt64(
@@ -2060,7 +2060,7 @@
 
         mBuffer->meta_data()->clear();
         mBuffer->meta_data()->setInt64(
-                kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
+                kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
 
         if (targetSampleTimeUs >= 0) {
             mBuffer->meta_data()->setInt64(
@@ -2094,87 +2094,6 @@
     return NULL;
 }
 
-status_t MPEG4Extractor::verifyIfStreamable() {
-    if (!(mDataSource->flags() & DataSource::kIsCachingDataSource)) {
-        return OK;
-    }
-
-    Track *audio = findTrackByMimePrefix("audio/");
-    Track *video = findTrackByMimePrefix("video/");
-
-    if (audio == NULL || video == NULL) {
-        return OK;
-    }
-
-    sp<SampleTable> audioSamples = audio->sampleTable;
-    sp<SampleTable> videoSamples = video->sampleTable;
-
-    off64_t maxOffsetDiff = 0;
-    int64_t maxOffsetTimeUs = -1;
-
-    for (uint32_t i = 0; i < videoSamples->countSamples(); ++i) {
-        off64_t videoOffset;
-        uint32_t videoTime;
-        bool isSync;
-        CHECK_EQ((status_t)OK, videoSamples->getMetaDataForSample(
-                    i, &videoOffset, NULL, &videoTime, &isSync));
-
-        int64_t videoTimeUs = (int64_t)(videoTime * 1E6 / video->timescale);
-
-        uint32_t reqAudioTime = (videoTimeUs * audio->timescale) / 1000000;
-        uint32_t j;
-        if (audioSamples->findSampleAtTime(
-            reqAudioTime, &j, SampleTable::kFlagClosest) != OK) {
-            continue;
-        }
-
-        off64_t audioOffset;
-        uint32_t audioTime;
-        CHECK_EQ((status_t)OK, audioSamples->getMetaDataForSample(
-                    j, &audioOffset, NULL, &audioTime));
-
-        int64_t audioTimeUs = (int64_t)(audioTime * 1E6 / audio->timescale);
-
-        off64_t offsetDiff = videoOffset - audioOffset;
-        if (offsetDiff < 0) {
-            offsetDiff = -offsetDiff;
-        }
-
-#if 0
-        printf("%s%d/%d videoTime %.2f secs audioTime %.2f secs "
-               "videoOffset %lld audioOffset %lld offsetDiff %lld\n",
-               isSync ? "*" : " ",
-               i,
-               j,
-               videoTimeUs / 1E6,
-               audioTimeUs / 1E6,
-               videoOffset,
-               audioOffset,
-               offsetDiff);
-#endif
-
-        if (offsetDiff > maxOffsetDiff) {
-            maxOffsetDiff = offsetDiff;
-            maxOffsetTimeUs = videoTimeUs;
-        }
-    }
-
-#if 0
-    printf("max offset diff: %lld at video time: %.2f secs\n",
-           maxOffsetDiff, maxOffsetTimeUs / 1E6);
-#endif
-
-    if (maxOffsetDiff < 1024 * 1024) {
-        return OK;
-    }
-
-    LOGE("This content is not streamable, "
-         "max offset diff: %lld at video time: %.2f secs",
-         maxOffsetDiff, maxOffsetTimeUs / 1E6);
-
-    return ERROR_UNSUPPORTED;
-}
-
 static bool LegacySniffMPEG4(
         const sp<DataSource> &source, String8 *mimeType, float *confidence) {
     uint8_t header[8];
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 423df70..08db902 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -53,6 +53,7 @@
       mNumSampleSizes(0),
       mTimeToSampleCount(0),
       mTimeToSample(NULL),
+      mSampleTimeEntries(NULL),
       mCompositionTimeDeltaEntries(NULL),
       mNumCompositionTimeDeltaEntries(0),
       mSyncSampleOffset(-1),
@@ -73,6 +74,9 @@
     delete[] mCompositionTimeDeltaEntries;
     mCompositionTimeDeltaEntries = NULL;
 
+    delete[] mSampleTimeEntries;
+    mSampleTimeEntries = NULL;
+
     delete[] mTimeToSample;
     mTimeToSample = NULL;
 
@@ -381,67 +385,128 @@
     return time1 > time2 ? time1 - time2 : time2 - time1;
 }
 
-status_t SampleTable::findSampleAtTime(
-        uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
-    // XXX this currently uses decoding time, instead of composition time.
+// static
+int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) {
+    const SampleTimeEntry *a = (const SampleTimeEntry *)_a;
+    const SampleTimeEntry *b = (const SampleTimeEntry *)_b;
 
-    *sample_index = 0;
+    if (a->mCompositionTime < b->mCompositionTime) {
+        return -1;
+    } else if (a->mCompositionTime > b->mCompositionTime) {
+        return 1;
+    }
 
+    return 0;
+}
+
+void SampleTable::buildSampleEntriesTable() {
     Mutex::Autolock autoLock(mLock);
 
-    uint32_t cur_sample = 0;
-    uint32_t time = 0;
+    if (mSampleTimeEntries != NULL) {
+        return;
+    }
+
+    mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes];
+
+    uint32_t sampleIndex = 0;
+    uint32_t sampleTime = 0;
+
     for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
         uint32_t n = mTimeToSample[2 * i];
         uint32_t delta = mTimeToSample[2 * i + 1];
 
-        if (req_time < time + n * delta) {
-            int j = (req_time - time) / delta;
+        for (uint32_t j = 0; j < n; ++j) {
+            CHECK(sampleIndex < mNumSampleSizes);
 
-            uint32_t time1 = time + j * delta;
-            uint32_t time2 = time1 + delta;
+            mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
 
-            uint32_t sampleTime;
-            if (i+1 == mTimeToSampleCount
-                    || (abs_difference(req_time, time1)
-                        < abs_difference(req_time, time2))) {
-                *sample_index = cur_sample + j;
-                sampleTime = time1;
-            } else {
-                *sample_index = cur_sample + j + 1;
-                sampleTime = time2;
-            }
+            mSampleTimeEntries[sampleIndex].mCompositionTime =
+                sampleTime + getCompositionTimeOffset(sampleIndex);
 
-            switch (flags) {
-                case kFlagBefore:
-                {
-                    if (sampleTime > req_time && *sample_index > 0) {
-                        --*sample_index;
-                    }
-                    break;
-                }
-
-                case kFlagAfter:
-                {
-                    if (sampleTime < req_time
-                            && *sample_index + 1 < mNumSampleSizes) {
-                        ++*sample_index;
-                    }
-                    break;
-                }
-
-                default:
-                    break;
-            }
-
-            return OK;
+            ++sampleIndex;
+            sampleTime += delta;
         }
-
-        time += delta * n;
-        cur_sample += n;
     }
 
-    return ERROR_OUT_OF_RANGE;
+    qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry),
+          CompareIncreasingTime);
+}
+
+status_t SampleTable::findSampleAtTime(
+        uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+    buildSampleEntriesTable();
+
+    uint32_t left = 0;
+    uint32_t right = mNumSampleSizes;
+    while (left < right) {
+        uint32_t center = (left + right) / 2;
+        uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime;
+
+        if (req_time < centerTime) {
+            right = center;
+        } else if (req_time > centerTime) {
+            left = center + 1;
+        } else {
+            left = center;
+            break;
+        }
+    }
+
+    if (left == mNumSampleSizes) {
+        --left;
+    }
+
+    uint32_t closestIndex = left;
+
+    switch (flags) {
+        case kFlagBefore:
+        {
+            while (closestIndex > 0
+                    && mSampleTimeEntries[closestIndex].mCompositionTime
+                            > req_time) {
+                --closestIndex;
+            }
+            break;
+        }
+
+        case kFlagAfter:
+        {
+            while (closestIndex + 1 < mNumSampleSizes
+                    && mSampleTimeEntries[closestIndex].mCompositionTime
+                            < req_time) {
+                ++closestIndex;
+            }
+            break;
+        }
+
+        default:
+        {
+            CHECK(flags == kFlagClosest);
+
+            if (closestIndex > 0) {
+                // Check left neighbour and pick closest.
+                uint32_t absdiff1 =
+                    abs_difference(
+                            mSampleTimeEntries[closestIndex].mCompositionTime,
+                            req_time);
+
+                uint32_t absdiff2 =
+                    abs_difference(
+                            mSampleTimeEntries[closestIndex - 1].mCompositionTime,
+                            req_time);
+
+                if (absdiff1 > absdiff2) {
+                    closestIndex = closestIndex - 1;
+                }
+            }
+
+            break;
+        }
+    }
+
+    *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex;
+
+    return OK;
 }
 
 status_t SampleTable::findSyncSampleNear(
@@ -613,7 +678,7 @@
         uint32_t sampleIndex,
         off64_t *offset,
         size_t *size,
-        uint32_t *decodingTime,
+        uint32_t *compositionTime,
         bool *isSyncSample) {
     Mutex::Autolock autoLock(mLock);
 
@@ -630,8 +695,8 @@
         *size = mSampleIterator->getSampleSize();
     }
 
-    if (decodingTime) {
-        *decodingTime = mSampleIterator->getSampleTime();
+    if (compositionTime) {
+        *compositionTime = mSampleIterator->getSampleTime();
     }
 
     if (isSyncSample) {
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index d9ef208..3bd4c7e 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -92,8 +92,6 @@
 
     Track *findTrackByMimePrefix(const char *mimePrefix);
 
-    status_t verifyIfStreamable();
-
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
 };
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index 2f95de9..f44e0a2 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -63,7 +63,7 @@
             uint32_t sampleIndex,
             off64_t *offset,
             size_t *size,
-            uint32_t *decodingTime,
+            uint32_t *compositionTime,
             bool *isSyncSample = NULL);
 
     enum {
@@ -107,6 +107,12 @@
     uint32_t mTimeToSampleCount;
     uint32_t *mTimeToSample;
 
+    struct SampleTimeEntry {
+        uint32_t mSampleIndex;
+        uint32_t mCompositionTime;
+    };
+    SampleTimeEntry *mSampleTimeEntries;
+
     uint32_t *mCompositionTimeDeltaEntries;
     size_t mNumCompositionTimeDeltaEntries;
 
@@ -130,6 +136,10 @@
 
     uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const;
 
+    static int CompareIncreasingTime(const void *, const void *);
+
+    void buildSampleEntriesTable();
+
     SampleTable(const SampleTable &);
     SampleTable &operator=(const SampleTable &);
 };