Merge "Camera2: Add flush support" into klp-dev
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index ae4cf69..1e5822f 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -44,6 +44,19 @@
     FLUSH
 };
 
+namespace {
+    // Read empty strings without printing a false error message.
+    String16 readMaybeEmptyString16(const Parcel& parcel) {
+        size_t len;
+        const char16_t* str = parcel.readString16Inplace(&len);
+        if (str != NULL) {
+            return String16(str, len);
+        } else {
+            return String16();
+        }
+    }
+};
+
 class BpCameraDeviceUser : public BpInterface<ICameraDeviceUser>
 {
 public:
@@ -261,7 +274,7 @@
 
             sp<IGraphicBufferProducer> bp;
             if (data.readInt32() != 0) {
-                String16 name = data.readString16();
+                String16 name = readMaybeEmptyString16(data);
                 bp = interface_cast<IGraphicBufferProducer>(
                         data.readStrongBinder());
 
diff --git a/drm/common/DrmInfoEvent.cpp b/drm/common/DrmInfoEvent.cpp
index 27a5a2d..2315aa9 100644
--- a/drm/common/DrmInfoEvent.cpp
+++ b/drm/common/DrmInfoEvent.cpp
@@ -16,16 +16,29 @@
 
 #include <utils/String8.h>
 #include <drm/DrmInfoEvent.h>
+#include <stdlib.h>
 
 using namespace android;
 
 DrmInfoEvent::DrmInfoEvent(int uniqueId, int infoType, const String8 message)
     : mUniqueId(uniqueId),
       mInfoType(infoType),
-      mMessage(message) {
+      mMessage(message),
+      mDrmBuffer() {
 
 }
 
+DrmInfoEvent::DrmInfoEvent(int uniqueId, int infoType, const String8 message,
+        const DrmBuffer& drmBuffer)
+        : mUniqueId(uniqueId), mInfoType(infoType), mMessage(message), mDrmBuffer() {
+    setData(drmBuffer);
+}
+
+DrmInfoEvent::~DrmInfoEvent() {
+    delete [] mDrmBuffer.data;
+}
+
+
 int DrmInfoEvent::getUniqueId() const {
     return mUniqueId;
 }
@@ -38,3 +51,80 @@
     return mMessage;
 }
 
+int DrmInfoEvent::getCount() const {
+    return mAttributes.size();
+}
+
+status_t DrmInfoEvent::put(const String8& key, String8& value) {
+        mAttributes.add(key, value);
+    return DRM_NO_ERROR;
+}
+
+const String8 DrmInfoEvent::get(const String8& key) const {
+    if (mAttributes.indexOfKey(key) != NAME_NOT_FOUND) {
+        return mAttributes.valueFor(key);
+    }
+    return String8("");
+}
+
+const DrmBuffer& DrmInfoEvent::getData() const {
+    return mDrmBuffer;
+}
+
+void DrmInfoEvent::setData(const DrmBuffer& drmBuffer) {
+    delete [] mDrmBuffer.data;
+    mDrmBuffer.data = new char[drmBuffer.length];;
+    mDrmBuffer.length = drmBuffer.length;
+    memcpy(mDrmBuffer.data, drmBuffer.data, drmBuffer.length);
+}
+
+DrmInfoEvent::KeyIterator DrmInfoEvent::keyIterator() const {
+    return KeyIterator(this);
+}
+
+DrmInfoEvent::Iterator DrmInfoEvent::iterator() const {
+    return Iterator(this);
+}
+
+// KeyIterator implementation
+DrmInfoEvent::KeyIterator::KeyIterator(const DrmInfoEvent::KeyIterator& keyIterator)
+        : mDrmInfoEvent(keyIterator.mDrmInfoEvent), mIndex(keyIterator.mIndex) {
+}
+
+bool DrmInfoEvent::KeyIterator::hasNext() {
+    return (mIndex < mDrmInfoEvent->mAttributes.size());
+}
+
+const String8& DrmInfoEvent::KeyIterator::next() {
+    const String8& key = mDrmInfoEvent->mAttributes.keyAt(mIndex);
+    mIndex++;
+    return key;
+}
+
+DrmInfoEvent::KeyIterator& DrmInfoEvent::KeyIterator::operator=(
+        const DrmInfoEvent::KeyIterator& keyIterator) {
+    mDrmInfoEvent = keyIterator.mDrmInfoEvent;
+    mIndex = keyIterator.mIndex;
+    return *this;
+}
+
+// Iterator implementation
+DrmInfoEvent::Iterator::Iterator(const DrmInfoEvent::Iterator& iterator)
+        : mDrmInfoEvent(iterator.mDrmInfoEvent), mIndex(iterator.mIndex) {
+}
+
+DrmInfoEvent::Iterator& DrmInfoEvent::Iterator::operator=(const DrmInfoEvent::Iterator& iterator) {
+    mDrmInfoEvent = iterator.mDrmInfoEvent;
+    mIndex = iterator.mIndex;
+    return *this;
+}
+
+bool DrmInfoEvent::Iterator::hasNext() {
+    return mIndex < mDrmInfoEvent->mAttributes.size();
+}
+
+const String8& DrmInfoEvent::Iterator::next() {
+    const String8& value = mDrmInfoEvent->mAttributes.editValueAt(mIndex);
+    mIndex++;
+    return value;
+}
diff --git a/drm/common/IDrmServiceListener.cpp b/drm/common/IDrmServiceListener.cpp
index 6eeea40..d825afb 100644
--- a/drm/common/IDrmServiceListener.cpp
+++ b/drm/common/IDrmServiceListener.cpp
@@ -32,6 +32,19 @@
     data.writeInt32(event.getType());
     data.writeString8(event.getMessage());
 
+    data.writeInt32(event.getCount());
+    DrmInfoEvent::KeyIterator keyIt = event.keyIterator();
+    while (keyIt.hasNext()) {
+        String8 key = keyIt.next();
+        data.writeString8(key);
+        data.writeString8(event.get(key));
+    }
+    const DrmBuffer& value = event.getData();
+    data.writeInt32(value.length);
+    if (value.length > 0) {
+        data.write(value.data, value.length);
+    }
+
     remote()->transact(NOTIFY, data, &reply);
     return reply.readInt32();
 }
@@ -49,7 +62,24 @@
         int type = data.readInt32();
         const String8& message = data.readString8();
 
-        status_t status = notify(DrmInfoEvent(uniqueId, type, message));
+        DrmInfoEvent event(uniqueId, type, message);
+        int size = data.readInt32();
+        for (int index = 0; index < size; index++) {
+            String8 key(data.readString8());
+            String8 value(data.readString8());
+            event.put(key, value);
+        }
+        int valueSize = data.readInt32();
+        if (valueSize > 0) {
+            char* valueData = new char[valueSize];
+            data.read(valueData, valueSize);
+            DrmBuffer drmBuffer(valueData, valueSize);
+            event.setData(drmBuffer);
+            delete[] valueData;
+        }
+
+        status_t status = notify(event);
+
         reply->writeInt32(status);
 
         return DRM_NO_ERROR;
diff --git a/include/drm/DrmInfoEvent.h b/include/drm/DrmInfoEvent.h
index dfca228..23b2950 100644
--- a/include/drm/DrmInfoEvent.h
+++ b/include/drm/DrmInfoEvent.h
@@ -17,6 +17,8 @@
 #ifndef __DRM_INFO_EVENT_H__
 #define __DRM_INFO_EVENT_H__
 
+#include "drm_framework_common.h"
+
 namespace android {
 
 class String8;
@@ -71,18 +73,70 @@
 
 public:
     /**
-     * Constructor for DrmInfoEvent
+     * Constructor for DrmInfoEvent.
+     * Data in drmBuffer are copied to newly allocated buffer.
      *
      * @param[in] uniqueId Unique session identifier
      * @param[in] infoType Type of information
      * @param[in] message Message description
+     * @param[in] drmBuffer Binary information
      */
     DrmInfoEvent(int uniqueId, int infoType, const String8 message);
+    DrmInfoEvent(int uniqueId, int infoType, const String8 message, const DrmBuffer& drmBuffer);
 
     /**
      * Destructor for DrmInfoEvent
      */
-    virtual ~DrmInfoEvent() {}
+    ~DrmInfoEvent();
+
+public:
+    /**
+     * Iterator for key
+     */
+    class KeyIterator {
+        friend class DrmInfoEvent;
+
+    private:
+        KeyIterator(const DrmInfoEvent* drmInfoEvent)
+                : mDrmInfoEvent(const_cast <DrmInfoEvent*> (drmInfoEvent)), mIndex(0) {}
+
+    public:
+        KeyIterator(const KeyIterator& keyIterator);
+        KeyIterator& operator=(const KeyIterator& keyIterator);
+        virtual ~KeyIterator() {}
+
+    public:
+        bool hasNext();
+        const String8& next();
+
+    private:
+        DrmInfoEvent* mDrmInfoEvent;
+        unsigned int mIndex;
+    };
+
+    /**
+     * Iterator
+     */
+    class Iterator {
+        friend class DrmInfoEvent;
+
+    private:
+        Iterator(const DrmInfoEvent* drmInfoEvent)
+                : mDrmInfoEvent(const_cast <DrmInfoEvent*> (drmInfoEvent)), mIndex(0) {}
+
+    public:
+        Iterator(const Iterator& iterator);
+        Iterator& operator=(const Iterator& iterator);
+        virtual ~Iterator() {}
+
+    public:
+        bool hasNext();
+        const String8& next();
+
+    private:
+        DrmInfoEvent* mDrmInfoEvent;
+        unsigned int mIndex;
+    };
 
 public:
     /**
@@ -106,10 +160,69 @@
      */
     const String8 getMessage() const;
 
+    /**
+     * Returns the number of attributes contained in this instance
+     *
+     * @return Number of attributes
+     */
+    int getCount() const;
+
+    /**
+     * Adds optional information as <key, value> pair to this instance
+     *
+     * @param[in] key Key to add
+     * @param[in] value Value to add
+     * @return Returns the error code
+     */
+    status_t put(const String8& key, String8& value);
+
+    /**
+     * Retrieves the value of given key
+     *
+     * @param key Key whose value to be retrieved
+     * @return The value
+     */
+    const String8 get(const String8& key) const;
+
+    /**
+     * Returns KeyIterator object to walk through the keys associated with this instance
+     *
+     * @return KeyIterator object
+     */
+    KeyIterator keyIterator() const;
+
+    /**
+     * Returns Iterator object to walk through the values associated with this instance
+     *
+     * @return Iterator object
+     */
+    Iterator iterator() const;
+
+    /**
+     * Returns the Binary information associated with this instance
+     *
+     * @return Binary information
+     */
+    const DrmBuffer& getData() const;
+
+    /**
+     * Sets the Binary information associated with this instance.
+     * Data in drmBuffer are copied to newly allocated buffer.
+     *
+     * @param[in] drmBuffer Binary information associated with this instance
+     */
+    void setData(const DrmBuffer& drmBuffer);
+
+private:
+    DrmInfoEvent(const DrmInfoEvent& ref);
+    const DrmInfoEvent& operator=(const DrmInfoEvent& ref);
+
 private:
     int mUniqueId;
     int mInfoType;
     const String8 mMessage;
+    KeyedVector<String8, String8> mAttributes;
+    DrmBuffer mDrmBuffer;
 };
 
 };
diff --git a/include/media/IRemoteDisplayClient.h b/include/media/IRemoteDisplayClient.h
index 7b0fa9e..0e6d55d 100644
--- a/include/media/IRemoteDisplayClient.h
+++ b/include/media/IRemoteDisplayClient.h
@@ -49,7 +49,7 @@
     // Provides a surface texture that the client should use to stream buffers to
     // the remote display.
     virtual void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer,
-            uint32_t width, uint32_t height, uint32_t flags) = 0; // one-way
+            uint32_t width, uint32_t height, uint32_t flags, uint32_t session) = 0; // one-way
 
     // Indicates that the remote display has been disconnected normally.
     // This method should only be called once the client has called 'dispose()'
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 1afd7f7..923c8b2 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -48,6 +48,7 @@
     MEDIA_TIMED_TEXT        = 99,
     MEDIA_ERROR             = 100,
     MEDIA_INFO              = 200,
+    MEDIA_SUBTITLE_DATA     = 201,
 };
 
 // Generic error codes for the media player framework.  Errors are fatal, the
@@ -176,6 +177,7 @@
     MEDIA_TRACK_TYPE_VIDEO = 1,
     MEDIA_TRACK_TYPE_AUDIO = 2,
     MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
+    MEDIA_TRACK_TYPE_SUBTITLE = 4,
 };
 
 // ----------------------------------------------------------------------------
diff --git a/media/libmedia/IRemoteDisplayClient.cpp b/media/libmedia/IRemoteDisplayClient.cpp
index 5c494b3..7190879 100644
--- a/media/libmedia/IRemoteDisplayClient.cpp
+++ b/media/libmedia/IRemoteDisplayClient.cpp
@@ -38,7 +38,7 @@
     }
 
     void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer,
-            uint32_t width, uint32_t height, uint32_t flags)
+            uint32_t width, uint32_t height, uint32_t flags, uint32_t session)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IRemoteDisplayClient::getInterfaceDescriptor());
@@ -46,6 +46,7 @@
         data.writeInt32(width);
         data.writeInt32(height);
         data.writeInt32(flags);
+        data.writeInt32(session);
         remote()->transact(ON_DISPLAY_CONNECTED, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
@@ -80,7 +81,8 @@
             uint32_t width = data.readInt32();
             uint32_t height = data.readInt32();
             uint32_t flags = data.readInt32();
-            onDisplayConnected(surfaceTexture, width, height, flags);
+            uint32_t session = data.readInt32();
+            onDisplayConnected(surfaceTexture, width, height, flags, session);
             return NO_ERROR;
         }
         case ON_DISPLAY_DISCONNECTED: {
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 056cc0a..4323d0c 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -756,6 +756,9 @@
     case MEDIA_TIMED_TEXT:
         ALOGV("Received timed text message");
         break;
+    case MEDIA_SUBTITLE_DATA:
+        ALOGV("Received subtitle data message");
+        break;
     default:
         ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
         break;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index c8901ce..d8b35d7 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -43,7 +43,8 @@
       mUID(uid),
       mFlags(0),
       mFinalResult(OK),
-      mOffset(0) {
+      mOffset(0),
+      mFetchSubtitleDataGeneration(0) {
     if (headers) {
         mExtraHeaders = *headers;
 
@@ -120,6 +121,28 @@
     return mLiveSession->getDuration(durationUs);
 }
 
+status_t NuPlayer::HTTPLiveSource::getTrackInfo(Parcel *reply) const {
+    return mLiveSession->getTrackInfo(reply);
+}
+
+status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select) {
+    status_t err = mLiveSession->selectTrack(trackIndex, select);
+
+    if (err == OK) {
+        mFetchSubtitleDataGeneration++;
+        if (select) {
+            sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
+            msg->setInt32("generation", mFetchSubtitleDataGeneration);
+            msg->post();
+        }
+    }
+
+    // LiveSession::selectTrack returns BAD_VALUE when selecting the currently
+    // selected track, or unselecting a non-selected track. In this case it's an
+    // no-op so we return OK.
+    return (err == OK || err == BAD_VALUE) ? OK : err;
+}
+
 status_t NuPlayer::HTTPLiveSource::seekTo(int64_t seekTimeUs) {
     return mLiveSession->seekTo(seekTimeUs);
 }
@@ -132,6 +155,39 @@
             break;
         }
 
+        case kWhatFetchSubtitleData:
+        {
+            int32_t generation;
+            CHECK(msg->findInt32("generation", &generation));
+
+            if (generation != mFetchSubtitleDataGeneration) {
+                // stale
+                break;
+            }
+
+            sp<ABuffer> buffer;
+            if (mLiveSession->dequeueAccessUnit(
+                    LiveSession::STREAMTYPE_SUBTITLES, &buffer) == OK) {
+                sp<AMessage> notify = dupNotify();
+                notify->setInt32("what", kWhatSubtitleData);
+                notify->setBuffer("buffer", buffer);
+                notify->post();
+
+                int64_t timeUs, baseUs, durationUs, delayUs;
+                CHECK(buffer->meta()->findInt64("baseUs", &baseUs));
+                CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+                CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
+                delayUs = baseUs + timeUs - ALooper::GetNowUs();
+
+                msg->post(delayUs > 0ll ? delayUs : 0ll);
+            } else {
+                // try again in 1 second
+                msg->post(1000000ll);
+            }
+
+            break;
+        }
+
         default:
             Source::onMessageReceived(msg);
             break;
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
index aa9434b..bcc3f8b 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.h
@@ -41,6 +41,8 @@
 
     virtual status_t feedMoreTSData();
     virtual status_t getDuration(int64_t *durationUs);
+    virtual status_t getTrackInfo(Parcel *reply) const;
+    virtual status_t selectTrack(size_t trackIndex, bool select);
     virtual status_t seekTo(int64_t seekTimeUs);
 
 protected:
@@ -56,6 +58,7 @@
 
     enum {
         kWhatSessionNotify,
+        kWhatFetchSubtitleData,
     };
 
     AString mURL;
@@ -67,6 +70,7 @@
     off64_t mOffset;
     sp<ALooper> mLiveLooper;
     sp<LiveSession> mLiveSession;
+    int32_t mFetchSubtitleDataGeneration;
 
     void onSessionNotify(const sp<AMessage> &msg);
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index b411f34..e1735fa 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -340,6 +340,46 @@
             break;
         }
 
+        case kWhatGetTrackInfo:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            status_t err = INVALID_OPERATION;
+            if (mSource != NULL) {
+                Parcel* reply;
+                CHECK(msg->findPointer("reply", (void**)&reply));
+                err = mSource->getTrackInfo(reply);
+            }
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+
+            response->postReply(replyID);
+            break;
+        }
+
+        case kWhatSelectTrack:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            status_t err = INVALID_OPERATION;
+            if (mSource != NULL) {
+                size_t trackIndex;
+                int32_t select;
+                CHECK(msg->findSize("trackIndex", &trackIndex));
+                CHECK(msg->findInt32("select", &select));
+                err = mSource->selectTrack(trackIndex, select);
+            }
+
+            sp<AMessage> response = new AMessage;
+            response->setInt32("err", err);
+
+            response->postReply(replyID);
+            break;
+        }
+
         case kWhatPollDuration:
         {
             int32_t generation;
@@ -1045,7 +1085,7 @@
     mRenderer->queueBuffer(audio, buffer, reply);
 }
 
-void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
+void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) {
     if (mDriver == NULL) {
         return;
     }
@@ -1056,7 +1096,7 @@
         return;
     }
 
-    driver->notifyListener(msg, ext1, ext2);
+    driver->notifyListener(msg, ext1, ext2, in);
 }
 
 void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
@@ -1132,6 +1172,26 @@
     return OK;
 }
 
+status_t NuPlayer::getTrackInfo(Parcel* reply) const {
+    sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, id());
+    msg->setPointer("reply", reply);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    return err;
+}
+
+status_t NuPlayer::selectTrack(size_t trackIndex, bool select) {
+    sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
+    msg->setSize("trackIndex", trackIndex);
+    msg->setInt32("select", select);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+
+    return err;
+}
+
 void NuPlayer::schedulePollDuration() {
     sp<AMessage> msg = new AMessage(kWhatPollDuration, id());
     msg->setInt32("generation", mPollDurationGeneration);
@@ -1371,6 +1431,29 @@
             break;
         }
 
+        case Source::kWhatSubtitleData:
+        {
+            sp<ABuffer> buffer;
+            CHECK(msg->findBuffer("buffer", &buffer));
+
+            int32_t trackIndex;
+            int64_t timeUs, durationUs;
+            CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
+            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+            CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
+
+            Parcel in;
+            in.writeInt32(trackIndex);
+            in.writeInt64(timeUs);
+            in.writeInt64(durationUs);
+            in.writeInt32(buffer->size());
+            in.writeInt32(buffer->size());
+            in.write(buffer->data(), buffer->size());
+
+            notifyListener(MEDIA_SUBTITLE_DATA, 0, 0, &in);
+            break;
+        }
+
         case Source::kWhatQueueDecoderShutdown:
         {
             int32_t audio, video;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 8b6c8c1..13350f3 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -60,6 +60,8 @@
     void seekToAsync(int64_t seekTimeUs);
 
     status_t setVideoScalingMode(int32_t mode);
+    status_t getTrackInfo(Parcel* reply) const;
+    status_t selectTrack(size_t trackIndex, bool select);
 
 protected:
     virtual ~NuPlayer();
@@ -101,6 +103,8 @@
         kWhatResume                     = 'rsme',
         kWhatPollDuration               = 'polD',
         kWhatSourceNotify               = 'srcN',
+        kWhatGetTrackInfo               = 'gTrI',
+        kWhatSelectTrack                = 'selT',
     };
 
     wp<NuPlayerDriver> mDriver;
@@ -157,7 +161,7 @@
     status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
     void renderBuffer(bool audio, const sp<AMessage> &msg);
 
-    void notifyListener(int msg, int ext1, int ext2);
+    void notifyListener(int msg, int ext1, int ext2, const Parcel *in = NULL);
 
     void finishFlushIfPossible();
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index cf0373c..47834fd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -392,6 +392,23 @@
             return mPlayer->setVideoScalingMode(mode);
         }
 
+        case INVOKE_ID_GET_TRACK_INFO:
+        {
+            return mPlayer->getTrackInfo(reply);
+        }
+
+        case INVOKE_ID_SELECT_TRACK:
+        {
+            int trackIndex = request.readInt32();
+            return mPlayer->selectTrack(trackIndex, true /* select */);
+        }
+
+        case INVOKE_ID_UNSELECT_TRACK:
+        {
+            int trackIndex = request.readInt32();
+            return mPlayer->selectTrack(trackIndex, false /* select */);
+        }
+
         default:
         {
             return INVALID_OPERATION;
@@ -495,12 +512,13 @@
     return OK;
 }
 
-void NuPlayerDriver::notifyListener(int msg, int ext1, int ext2) {
+void NuPlayerDriver::notifyListener(
+        int msg, int ext1, int ext2, const Parcel *in) {
     if (msg == MEDIA_PLAYBACK_COMPLETE || msg == MEDIA_ERROR) {
         mAtEOS = true;
     }
 
-    sendEvent(msg, ext1, ext2);
+    sendEvent(msg, ext1, ext2, in);
 }
 
 void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 5df0cfb..99f72a6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -69,7 +69,7 @@
     void notifyPosition(int64_t positionUs);
     void notifySeekComplete();
     void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped);
-    void notifyListener(int msg, int ext1 = 0, int ext2 = 0);
+    void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
     void notifyFlagsChanged(uint32_t flags);
 
 protected:
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 81ffd21..e50533a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -42,6 +42,7 @@
         kWhatVideoSizeChanged,
         kWhatBufferingStart,
         kWhatBufferingEnd,
+        kWhatSubtitleData,
         kWhatQueueDecoderShutdown,
     };
 
@@ -71,6 +72,14 @@
         return INVALID_OPERATION;
     }
 
+    virtual status_t getTrackInfo(Parcel* reply) const {
+        return INVALID_OPERATION;
+    }
+
+    virtual status_t selectTrack(size_t trackIndex, bool select) {
+        return INVALID_OPERATION;
+    }
+
     virtual status_t seekTo(int64_t seekTimeUs) {
         return INVALID_OPERATION;
     }
diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk
index 85bd492..f3529f9 100644
--- a/media/libstagefright/httplive/Android.mk
+++ b/media/libstagefright/httplive/Android.mk
@@ -14,6 +14,7 @@
 	$(TOP)/external/openssl/include
 
 LOCAL_SHARED_LIBRARIES := \
+        libbinder \
         libcrypto \
         libcutils \
         libmedia \
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index e91c60b..bd12ddc 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -59,6 +59,7 @@
       mStreamMask(0),
       mCheckBandwidthGeneration(0),
       mLastDequeuedTimeUs(0ll),
+      mRealTimeBaseUs(0ll),
       mReconfigurationInProgress(false),
       mDisconnectReplyID(0) {
     if (mUIDValid) {
@@ -122,11 +123,18 @@
               type,
               extra == NULL ? "NULL" : extra->debugString().c_str());
     } else if (err == OK) {
-        int64_t timeUs;
-        CHECK((*accessUnit)->meta()->findInt64("timeUs",  &timeUs));
-        ALOGV("[%s] read buffer at time %lld us", streamStr, timeUs);
+        if (stream == STREAMTYPE_AUDIO || stream == STREAMTYPE_VIDEO) {
+            int64_t timeUs;
+            CHECK((*accessUnit)->meta()->findInt64("timeUs",  &timeUs));
+            ALOGV("[%s] read buffer at time %lld us", streamStr, timeUs);
 
-        mLastDequeuedTimeUs = timeUs;
+            mLastDequeuedTimeUs = timeUs;
+            mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
+        } else if (stream == STREAMTYPE_SUBTITLES) {
+            (*accessUnit)->meta()->setInt32(
+                    "trackIndex", mPlaylist->getSelectedIndex());
+            (*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs);
+        }
     } else {
         ALOGI("[%s] encountered error %d", streamStr, err);
     }
@@ -325,6 +333,12 @@
             break;
         }
 
+        case kWhatChangeConfiguration:
+        {
+            onChangeConfiguration(msg);
+            break;
+        }
+
         case kWhatChangeConfiguration2:
         {
             onChangeConfiguration2(msg);
@@ -438,7 +452,8 @@
         mBandwidthItems.push(item);
     }
 
-    changeConfiguration(0ll /* timeUs */, initialBandwidthIndex);
+    changeConfiguration(
+            0ll /* timeUs */, initialBandwidthIndex, true /* pickTrack */);
 }
 
 void LiveSession::finishDisconnect() {
@@ -783,16 +798,31 @@
     return false;
 }
 
-void LiveSession::changeConfiguration(int64_t timeUs, size_t bandwidthIndex) {
+status_t LiveSession::getTrackInfo(Parcel *reply) const {
+    return mPlaylist->getTrackInfo(reply);
+}
+
+status_t LiveSession::selectTrack(size_t index, bool select) {
+    status_t err = mPlaylist->selectTrack(index, select);
+    if (err == OK) {
+        (new AMessage(kWhatChangeConfiguration, id()))->post();
+    }
+    return err;
+}
+
+void LiveSession::changeConfiguration(
+        int64_t timeUs, size_t bandwidthIndex, bool pickTrack) {
     CHECK(!mReconfigurationInProgress);
     mReconfigurationInProgress = true;
 
     mPrevBandwidthIndex = bandwidthIndex;
 
-    ALOGV("changeConfiguration => timeUs:%lld us, bwIndex:%d",
-          timeUs, bandwidthIndex);
+    ALOGV("changeConfiguration => timeUs:%lld us, bwIndex:%d, pickTrack:%d",
+          timeUs, bandwidthIndex, pickTrack);
 
-    mPlaylist->pickRandomMediaItems();
+    if (pickTrack) {
+        mPlaylist->pickRandomMediaItems();
+    }
 
     CHECK_LT(bandwidthIndex, mBandwidthItems.size());
     const BandwidthItem &item = mBandwidthItems.itemAt(bandwidthIndex);
@@ -862,6 +892,14 @@
     }
 }
 
+void LiveSession::onChangeConfiguration(const sp<AMessage> &msg) {
+    if (!mReconfigurationInProgress) {
+        changeConfiguration(-1ll /* timeUs */, getBandwidthIndex());
+    } else {
+        msg->post(1000000ll); // retry in 1 sec
+    }
+}
+
 void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) {
     mContinuation.clear();
 
@@ -948,6 +986,7 @@
     if (timeUs < 0ll) {
         timeUs = mLastDequeuedTimeUs;
     }
+    mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
 
     mStreamMask = streamMask;
     mAudioURI = audioURI;
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index b134725..99b480a8 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -31,6 +31,7 @@
 struct LiveDataSource;
 struct M3UParser;
 struct PlaylistFetcher;
+struct Parcel;
 
 struct LiveSession : public AHandler {
     enum Flags {
@@ -60,6 +61,8 @@
     status_t seekTo(int64_t timeUs);
 
     status_t getDuration(int64_t *durationUs) const;
+    status_t getTrackInfo(Parcel *reply) const;
+    status_t selectTrack(size_t index, bool select);
 
     bool isSeekable() const;
     bool hasDynamicDuration() const;
@@ -85,6 +88,7 @@
         kWhatSeek                       = 'seek',
         kWhatFetcherNotify              = 'notf',
         kWhatCheckBandwidth             = 'bndw',
+        kWhatChangeConfiguration        = 'chC0',
         kWhatChangeConfiguration2       = 'chC2',
         kWhatChangeConfiguration3       = 'chC3',
         kWhatFinishDisconnect2          = 'fin2',
@@ -130,6 +134,7 @@
     sp<AMessage> mContinuation;
 
     int64_t mLastDequeuedTimeUs;
+    int64_t mRealTimeBaseUs;
 
     bool mReconfigurationInProgress;
     uint32_t mDisconnectReplyID;
@@ -151,7 +156,9 @@
 
     static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
 
-    void changeConfiguration(int64_t timeUs, size_t bandwidthIndex);
+    void changeConfiguration(
+            int64_t timeUs, size_t bandwidthIndex, bool pickTrack = false);
+    void onChangeConfiguration(const sp<AMessage> &msg);
     void onChangeConfiguration2(const sp<AMessage> &msg);
     void onChangeConfiguration3(const sp<AMessage> &msg);
 
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index be66252..bc6d629 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -19,11 +19,12 @@
 #include <utils/Log.h>
 
 #include "M3UParser.h"
-
+#include <binder/Parcel.h>
 #include <cutils/properties.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaErrors.h>
+#include <media/mediaplayer.h>
 
 namespace android {
 
@@ -55,6 +56,9 @@
     bool getActiveURI(AString *uri) const;
 
     void pickRandomMediaItems();
+    status_t selectTrack(size_t index, bool select);
+    void getTrackInfo(Parcel* reply) const;
+    size_t countTracks() const;
 
 protected:
     virtual ~MediaGroup();
@@ -150,6 +154,59 @@
 #endif
 }
 
+status_t M3UParser::MediaGroup::selectTrack(size_t index, bool select) {
+    if (mType != TYPE_SUBS) {
+        ALOGE("only select subtitile tracks for now!");
+        return INVALID_OPERATION;
+    }
+
+    if (select) {
+        if (index >= mMediaItems.size()) {
+            ALOGE("track %d does not exist", index);
+            return INVALID_OPERATION;
+        }
+        if (mSelectedIndex == index) {
+            ALOGE("track %d already selected", index);
+            return BAD_VALUE;
+        }
+        ALOGV("selected track %d", index);
+        mSelectedIndex = index;
+    } else {
+        if (mSelectedIndex != index) {
+            ALOGE("track %d is not selected", index);
+            return BAD_VALUE;
+        }
+        ALOGV("unselected track %d", index);
+        mSelectedIndex = -1;
+    }
+
+    return OK;
+}
+
+void M3UParser::MediaGroup::getTrackInfo(Parcel* reply) const {
+    for (size_t i = 0; i < mMediaItems.size(); ++i) {
+        reply->writeInt32(2); // 2 fields
+
+        if (mType == TYPE_AUDIO) {
+            reply->writeInt32(MEDIA_TRACK_TYPE_AUDIO);
+        } else if (mType == TYPE_VIDEO) {
+            reply->writeInt32(MEDIA_TRACK_TYPE_VIDEO);
+        } else if (mType == TYPE_SUBS) {
+            reply->writeInt32(MEDIA_TRACK_TYPE_SUBTITLE);
+        } else {
+            reply->writeInt32(MEDIA_TRACK_TYPE_UNKNOWN);
+        }
+
+        const Media &item = mMediaItems.itemAt(i);
+        const char *lang = item.mLanguage.empty() ? "und" : item.mLanguage.c_str();
+        reply->writeString16(String16(lang));
+    }
+}
+
+size_t M3UParser::MediaGroup::countTracks() const {
+    return mMediaItems.size();
+}
+
 bool M3UParser::MediaGroup::getActiveURI(AString *uri) const {
     for (size_t i = 0; i < mMediaItems.size(); ++i) {
         if (mSelectedIndex >= 0 && i == (size_t)mSelectedIndex) {
@@ -172,7 +229,8 @@
       mIsExtM3U(false),
       mIsVariantPlaylist(false),
       mIsComplete(false),
-      mIsEvent(false) {
+      mIsEvent(false),
+      mSelectedIndex(-1) {
     mInitCheck = parse(data, size);
 }
 
@@ -237,6 +295,39 @@
     }
 }
 
+status_t M3UParser::selectTrack(size_t index, bool select) {
+    for (size_t i = 0, ii = index; i < mMediaGroups.size(); ++i) {
+        sp<MediaGroup> group = mMediaGroups.valueAt(i);
+        size_t tracks = group->countTracks();
+        if (ii < tracks) {
+            status_t err = group->selectTrack(ii, select);
+            if (err == OK) {
+                mSelectedIndex = select ? index : -1;
+            }
+            return err;
+        }
+        ii -= tracks;
+    }
+    return INVALID_OPERATION;
+}
+
+status_t M3UParser::getTrackInfo(Parcel* reply) const {
+    size_t trackCount = 0;
+    for (size_t i = 0; i < mMediaGroups.size(); ++i) {
+        trackCount += mMediaGroups.valueAt(i)->countTracks();
+    }
+    reply->writeInt32(trackCount);
+
+    for (size_t i = 0; i < mMediaGroups.size(); ++i) {
+        mMediaGroups.valueAt(i)->getTrackInfo(reply);
+    }
+    return OK;
+}
+
+ssize_t M3UParser::getSelectedIndex() const {
+    return mSelectedIndex;
+}
+
 bool M3UParser::getTypeURI(size_t index, const char *key, AString *uri) const {
     if (!mIsVariantPlaylist) {
         *uri = mBaseURI;
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index abea286..5248004 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -41,6 +41,9 @@
     bool itemAt(size_t index, AString *uri, sp<AMessage> *meta = NULL);
 
     void pickRandomMediaItems();
+    status_t selectTrack(size_t index, bool select);
+    status_t getTrackInfo(Parcel* reply) const;
+    ssize_t getSelectedIndex() const;
 
     bool getAudioURI(size_t index, AString *uri) const;
     bool getVideoURI(size_t index, AString *uri) const;
@@ -67,6 +70,7 @@
 
     sp<AMessage> mMeta;
     Vector<Item> mItems;
+    ssize_t mSelectedIndex;
 
     // Media groups keyed by group ID.
     KeyedVector<AString, sp<MediaGroup> > mMediaGroups;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 8ae70b7..973b779 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -462,7 +462,11 @@
         sp<AnotherPacketSource> packetSource =
             mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES);
 
-        downloadMore = packetSource->hasBufferAvailable(&finalResult);
+        int64_t bufferedDurationUs =
+                packetSource->getBufferedDurationUs(&finalResult);
+
+        downloadMore = (bufferedDurationUs < kMinBufferedDurationUs);
+        finalResult = OK;
     } else {
         bool first = true;
         int64_t minBufferedDurationUs = 0ll;
@@ -659,7 +663,7 @@
         }
     }
 
-    err = extractAndQueueAccessUnits(buffer);
+    err = extractAndQueueAccessUnits(buffer, itemMeta);
 
     if (err != OK) {
         notifyError(err);
@@ -706,7 +710,7 @@
 }
 
 status_t PlaylistFetcher::extractAndQueueAccessUnits(
-        const sp<ABuffer> &buffer) {
+        const sp<ABuffer> &buffer, const sp<AMessage> &itemMeta) {
     if (buffer->size() > 0 && buffer->data()[0] == 0x47) {
         // Let's assume this is an MPEG2 transport stream.
 
@@ -802,7 +806,10 @@
         const sp<AnotherPacketSource> packetSource =
             mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES);
 
-        buffer->meta()->setInt64("timeUs", 0ll);
+        int64_t durationUs;
+        CHECK(itemMeta->findInt64("durationUs", &durationUs));
+        buffer->meta()->setInt64("timeUs", getSegmentStartTimeUs(mSeqNumber));
+        buffer->meta()->setInt64("durationUs", durationUs);
 
         packetSource->queueAccessUnit(buffer);
         return OK;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 5a2b901..1648e02 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -135,7 +135,8 @@
     void onMonitorQueue();
     void onDownloadNext();
 
-    status_t extractAndQueueAccessUnits(const sp<ABuffer> &buffer);
+    status_t extractAndQueueAccessUnits(
+            const sp<ABuffer> &buffer, const sp<AMessage> &itemMeta);
 
     void notifyError(status_t err);
 
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 4b59e62..d72349d 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -416,7 +416,8 @@
                                 0, // height,
                                 mUsingHDCP
                                     ? IRemoteDisplayClient::kDisplayFlagSecure
-                                    : 0);
+                                    : 0,
+                                0);
                     } else {
                         size_t width, height;
 
@@ -435,7 +436,8 @@
                                 height,
                                 mUsingHDCP
                                     ? IRemoteDisplayClient::kDisplayFlagSecure
-                                    : 0);
+                                    : 0,
+                                playbackSessionID);
                     }
                 }
 
diff --git a/media/libstagefright/wifi-display/wfd.cpp b/media/libstagefright/wifi-display/wfd.cpp
index 04cb319..52e4e26 100644
--- a/media/libstagefright/wifi-display/wfd.cpp
+++ b/media/libstagefright/wifi-display/wfd.cpp
@@ -55,7 +55,8 @@
             const sp<IGraphicBufferProducer> &bufferProducer,
             uint32_t width,
             uint32_t height,
-            uint32_t flags);
+            uint32_t flags,
+            uint32_t session);
 
     virtual void onDisplayDisconnected();
     virtual void onDisplayError(int32_t error);
@@ -91,9 +92,10 @@
         const sp<IGraphicBufferProducer> &bufferProducer,
         uint32_t width,
         uint32_t height,
-        uint32_t flags) {
-    ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x",
-          width, height, flags);
+        uint32_t flags,
+        uint32_t session) {
+    ALOGI("onDisplayConnected width=%u, height=%u, flags = 0x%08x, session = %d",
+          width, height, flags, session);
 
     if (bufferProducer != NULL) {
         mSurfaceTexture = bufferProducer;