Merge "Camera2: State must be STOPPED before JPEG callback is fired." into jb-mr1-dev
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index c4c37b6..6090176 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -32,6 +32,7 @@
 namespace android {
 
 struct ICrypto;
+struct IHDCP;
 class IMediaRecorder;
 class IOMX;
 class IRemoteDisplay;
@@ -51,6 +52,7 @@
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
     virtual sp<IOMX>            getOMX() = 0;
     virtual sp<ICrypto>         makeCrypto() = 0;
+    virtual sp<IHDCP>           makeHDCP() = 0;
 
     // Connects to a remote display.
     // 'iface' specifies the address of the local interface on which to listen for
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index c2ec439..c702d76 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -21,6 +21,7 @@
 #include <binder/Parcel.h>
 #include <binder/IMemory.h>
 #include <media/ICrypto.h>
+#include <media/IHDCP.h>
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
 #include <media/IOMX.h>
@@ -41,6 +42,7 @@
     CREATE_METADATA_RETRIEVER,
     GET_OMX,
     MAKE_CRYPTO,
+    MAKE_HDCP,
     ENABLE_REMOTE_DISPLAY,
     ADD_BATTERY_DATA,
     PULL_BATTERY_DATA,
@@ -125,6 +127,13 @@
         return interface_cast<ICrypto>(reply.readStrongBinder());
     }
 
+    virtual sp<IHDCP> makeHDCP() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+        remote()->transact(MAKE_HDCP, data, &reply);
+        return interface_cast<IHDCP>(reply.readStrongBinder());
+    }
+
     virtual status_t enableRemoteDisplay(const char *iface) {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
@@ -237,6 +246,12 @@
             reply->writeStrongBinder(crypto->asBinder());
             return NO_ERROR;
         } break;
+        case MAKE_HDCP: {
+            CHECK_INTERFACE(IMediaPlayerService, data, reply);
+            sp<IHDCP> hdcp = makeHDCP();
+            reply->writeStrongBinder(hdcp->asBinder());
+            return NO_ERROR;
+        } break;
         case ENABLE_REMOTE_DISPLAY: {
             CHECK_INTERFACE(IMediaPlayerService, data, reply);
             const char *iface = NULL;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index c7227b0..5b5ed71 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -9,6 +9,7 @@
 LOCAL_SRC_FILES:=               \
     ActivityManager.cpp         \
     Crypto.cpp                  \
+    HDCP.cpp                    \
     MediaPlayerFactory.cpp      \
     MediaPlayerService.cpp      \
     MediaRecorderClient.cpp     \
diff --git a/media/libmediaplayerservice/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp
index 6f8a465..e7dea6e 100644
--- a/media/libmediaplayerservice/HDCP.cpp
+++ b/media/libmediaplayerservice/HDCP.cpp
@@ -36,13 +36,16 @@
         return;
     }
 
-    typedef HDCPModule *(*CreateHDCPModuleFunc)();
+    typedef HDCPModule *(*CreateHDCPModuleFunc)(
+            void *, HDCPModule::ObserverFunc);
+
     CreateHDCPModuleFunc createHDCPModule =
         (CreateHDCPModuleFunc)dlsym(mLibHandle, "createHDCPModule");
 
     if (createHDCPModule == NULL) {
         ALOGE("Unable to find symbol 'createHDCPModule'.");
-    } else if ((mHDCPModule = createHDCPModule()) == NULL) {
+    } else if ((mHDCPModule = createHDCPModule(
+                    this, &HDCP::ObserveWrapper)) == NULL) {
         ALOGE("createHDCPModule failed.");
     }
 }
@@ -97,5 +100,16 @@
     return mHDCPModule->encrypt(inData, size, streamCTR, outInputCTR, outData);
 }
 
+// static
+void HDCP::ObserveWrapper(void *me, int msg, int ext1, int ext2) {
+    static_cast<HDCP *>(me)->observe(msg, ext1, ext2);
+}
+
+void HDCP::observe(int msg, int ext1, int ext2) {
+    if (mObserver != NULL) {
+        mObserver->notify(msg, ext1, ext2, NULL /* obj */);
+    }
+}
+
 }  // namespace android
 
diff --git a/media/libmediaplayerservice/HDCP.h b/media/libmediaplayerservice/HDCP.h
index 2e27689..4ee664d 100644
--- a/media/libmediaplayerservice/HDCP.h
+++ b/media/libmediaplayerservice/HDCP.h
@@ -39,6 +39,9 @@
     HDCPModule *mHDCPModule;
     sp<IHDCPObserver> mObserver;
 
+    static void ObserveWrapper(void *me, int msg, int ext1, int ext2);
+    void observe(int msg, int ext1, int ext2);
+
     DISALLOW_EVIL_CONSTRUCTORS(HDCP);
 };
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 1be4edf..eaa44e2 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -72,6 +72,7 @@
 #include <OMX.h>
 
 #include "Crypto.h"
+#include "HDCP.h"
 #include "RemoteDisplay.h"
 
 namespace {
@@ -281,6 +282,10 @@
     return new Crypto;
 }
 
+sp<IHDCP> MediaPlayerService::makeHDCP() {
+    return new HDCP;
+}
+
 sp<IRemoteDisplay> MediaPlayerService::listenForRemoteDisplay(
         const sp<IRemoteDisplayClient>& client, const String8& iface) {
     if (!checkPermission("android.permission.CONTROL_WIFI_DISPLAY")) {
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index ca8a96f..6105ce4 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -250,6 +250,7 @@
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
     virtual sp<IOMX>            getOMX();
     virtual sp<ICrypto>         makeCrypto();
+    virtual sp<IHDCP>           makeHDCP();
 
     virtual sp<IRemoteDisplay> listenForRemoteDisplay(const sp<IRemoteDisplayClient>& client,
             const String8& iface);
diff --git a/media/libstagefright/wifi-display/Android.mk b/media/libstagefright/wifi-display/Android.mk
index 681ba4f..a392532 100644
--- a/media/libstagefright/wifi-display/Android.mk
+++ b/media/libstagefright/wifi-display/Android.mk
@@ -4,6 +4,7 @@
 
 LOCAL_SRC_FILES:= \
         ANetworkSession.cpp             \
+        Parameters.cpp                  \
         ParsedMessage.cpp               \
         sink/LinearRegression.cpp       \
         sink/RTPSink.cpp                \
diff --git a/media/libstagefright/wifi-display/Parameters.cpp b/media/libstagefright/wifi-display/Parameters.cpp
new file mode 100644
index 0000000..f7118b3
--- /dev/null
+++ b/media/libstagefright/wifi-display/Parameters.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Parameters.h"
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+// static
+sp<Parameters> Parameters::Parse(const char *data, size_t size) {
+    sp<Parameters> params = new Parameters;
+    status_t err = params->parse(data, size);
+
+    if (err != OK) {
+        return NULL;
+    }
+
+    return params;
+}
+
+Parameters::Parameters() {}
+
+Parameters::~Parameters() {}
+
+status_t Parameters::parse(const char *data, size_t size) {
+    size_t i = 0;
+    while (i < size) {
+        size_t nameStart = i;
+        while (i < size && data[i] != ':') {
+            ++i;
+        }
+
+        if (i == size || i == nameStart) {
+            return ERROR_MALFORMED;
+        }
+
+        AString name(&data[nameStart], i - nameStart);
+        name.trim();
+        name.tolower();
+
+        ++i;
+
+        size_t valueStart = i;
+
+        while (i + 1 < size && (data[i] != '\r' || data[i + 1] != '\n')) {
+            ++i;
+        }
+
+        AString value(&data[valueStart], i - valueStart);
+        value.trim();
+
+        mDict.add(name, value);
+
+        i += 2;
+    }
+
+    return OK;
+}
+
+bool Parameters::findParameter(const char *name, AString *value) const {
+    AString key = name;
+    key.tolower();
+
+    ssize_t index = mDict.indexOfKey(key);
+
+    if (index < 0) {
+        value->clear();
+
+        return false;
+    }
+
+    *value = mDict.valueAt(index);
+    return true;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/wifi-display/Parameters.h b/media/libstagefright/wifi-display/Parameters.h
new file mode 100644
index 0000000..a5e787e
--- /dev/null
+++ b/media/libstagefright/wifi-display/Parameters.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct Parameters : public RefBase {
+    static sp<Parameters> Parse(const char *data, size_t size);
+
+    bool findParameter(const char *name, AString *value) const;
+
+protected:
+    virtual ~Parameters();
+
+private:
+    KeyedVector<AString, AString> mDict;
+
+    Parameters();
+    status_t parse(const char *data, size_t size);
+
+    DISALLOW_EVIL_CONSTRUCTORS(Parameters);
+};
+
+}  // namespace android
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index e961518..ceea08f 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -29,6 +29,7 @@
 #include <binder/IServiceManager.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
+#include <media/IHDCP.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -63,6 +64,7 @@
     Track(const sp<AMessage> &format);
 
     sp<AMessage> getFormat();
+    bool isAudio() const;
 
     const sp<Converter> &converter() const;
     ssize_t packetizerTrackIndex() const;
@@ -83,6 +85,9 @@
     sp<AMessage> mFormat;
     bool mStarted;
     ssize_t mPacketizerTrackIndex;
+    bool mIsAudio;
+
+    static bool IsAudioFormat(const sp<AMessage> &format);
 
     DISALLOW_EVIL_CONSTRUCTORS(Track);
 };
@@ -97,18 +102,29 @@
       mMediaPuller(mediaPuller),
       mConverter(converter),
       mStarted(false),
-      mPacketizerTrackIndex(-1) {
+      mPacketizerTrackIndex(-1),
+      mIsAudio(IsAudioFormat(mConverter->getOutputFormat())) {
 }
 
 WifiDisplaySource::PlaybackSession::Track::Track(const sp<AMessage> &format)
     : mFormat(format),
-      mPacketizerTrackIndex(-1) {
+      mPacketizerTrackIndex(-1),
+      mIsAudio(IsAudioFormat(mFormat)) {
 }
 
 WifiDisplaySource::PlaybackSession::Track::~Track() {
     stop();
 }
 
+// static
+bool WifiDisplaySource::PlaybackSession::Track::IsAudioFormat(
+        const sp<AMessage> &format) {
+    AString mime;
+    CHECK(format->findString("mime", &mime));
+
+    return !strncasecmp(mime.c_str(), "audio/", 6);
+}
+
 sp<AMessage> WifiDisplaySource::PlaybackSession::Track::getFormat() {
     if (mFormat != NULL) {
         return mFormat;
@@ -117,6 +133,10 @@
     return mConverter->getOutputFormat();
 }
 
+bool WifiDisplaySource::PlaybackSession::Track::isAudio() const {
+    return mIsAudio;
+}
+
 const sp<Converter> &WifiDisplaySource::PlaybackSession::Track::converter() const {
     return mConverter;
 }
@@ -172,11 +192,13 @@
         const sp<ANetworkSession> &netSession,
         const sp<AMessage> &notify,
         const in_addr &interfaceAddr,
-        bool legacyMode)
+        bool legacyMode,
+        const sp<IHDCP> &hdcp)
     : mNetSession(netSession),
       mNotify(notify),
       mInterfaceAddr(interfaceAddr),
       mLegacyMode(legacyMode),
+      mHDCP(hdcp),
       mLastLifesignUs(),
       mVideoTrackIndex(-1),
       mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
@@ -644,6 +666,73 @@
                     sp<ABuffer> accessUnit;
                     CHECK(msg->findBuffer("accessUnit", &accessUnit));
 
+                    bool isHDCPEncrypted = false;
+                    uint64_t inputCTR;
+                    uint8_t HDCP_private_data[16];
+                    if (mHDCP != NULL && !track->isAudio()) {
+                        isHDCPEncrypted = true;
+
+                        status_t err = mHDCP->encrypt(
+                                accessUnit->data(), accessUnit->size(),
+                                trackIndex  /* streamCTR */,
+                                &inputCTR,
+                                accessUnit->data());
+
+                        if (err != OK) {
+                            ALOGI("Failed to HDCP-encrypt media data (err %d)",
+                                  err);
+
+                            // Inform WifiDisplaySource of our premature death
+                            // (wish).
+                            sp<AMessage> notify = mNotify->dup();
+                            notify->setInt32("what", kWhatSessionDead);
+                            notify->post();
+                            break;
+                        }
+
+                        HDCP_private_data[0] = 0x00;
+
+                        HDCP_private_data[1] =
+                            (((trackIndex >> 30) & 3) << 1) | 1;
+
+                        HDCP_private_data[2] = (trackIndex >> 22) & 0xff;
+
+                        HDCP_private_data[3] =
+                            (((trackIndex >> 15) & 0x7f) << 1) | 1;
+
+                        HDCP_private_data[4] = (trackIndex >> 7) & 0xff;
+
+                        HDCP_private_data[5] =
+                            ((trackIndex & 0x7f) << 1) | 1;
+
+                        HDCP_private_data[6] = 0x00;
+
+                        HDCP_private_data[7] =
+                            (((inputCTR >> 60) & 0x0f) << 1) | 1;
+
+                        HDCP_private_data[8] = (inputCTR >> 52) & 0xff;
+
+                        HDCP_private_data[9] =
+                            (((inputCTR >> 45) & 0x7f) << 1) | 1;
+
+                        HDCP_private_data[10] = (inputCTR >> 37) & 0xff;
+
+                        HDCP_private_data[11] =
+                            (((inputCTR >> 30) & 0x7f) << 1) | 1;
+
+                        HDCP_private_data[12] = (inputCTR >> 22) & 0xff;
+
+                        HDCP_private_data[13] =
+                            (((inputCTR >> 15) & 0x7f) << 1) | 1;
+
+                        HDCP_private_data[14] = (inputCTR >> 7) & 0xff;
+
+                        HDCP_private_data[15] =
+                            ((inputCTR & 0x7f) << 1) | 1;
+
+                        flags |= TSPacketizer::IS_ENCRYPTED;
+                    }
+
                     int64_t timeUs;
                     CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
 
@@ -654,7 +743,9 @@
 
                     sp<ABuffer> packets;
                     mPacketizer->packetize(
-                            packetizerTrackIndex, accessUnit, &packets, flags);
+                            packetizerTrackIndex, accessUnit, &packets, flags,
+                            isHDCPEncrypted ? NULL : HDCP_private_data,
+                            isHDCPEncrypted ? 0 : sizeof(HDCP_private_data));
 
                     for (size_t offset = 0;
                             offset < packets->size(); offset += 188) {
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 0047842..0823ee6 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -24,6 +24,7 @@
 
 struct ABuffer;
 struct BufferQueue;
+struct IHDCP;
 struct ISurfaceTexture;
 struct MediaPuller;
 struct MediaSource;
@@ -39,7 +40,8 @@
             const sp<ANetworkSession> &netSession,
             const sp<AMessage> &notify,
             const struct in_addr &interfaceAddr,
-            bool legacyMode);
+            bool legacyMode,
+            const sp<IHDCP> &hdcp);
 
     enum TransportMode {
         TRANSPORT_UDP,
@@ -98,6 +100,7 @@
     sp<AMessage> mNotify;
     in_addr mInterfaceAddr;
     bool mLegacyMode;
+    sp<IHDCP> mHDCP;
 
     int64_t mLastLifesignUs;
 
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index b9a3e9b..613cc28 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -66,6 +66,8 @@
     AString mMIME;
     Vector<sp<ABuffer> > mCSD;
 
+    bool mAudioLacksATDSHeaders;
+
     DISALLOW_EVIL_CONSTRUCTORS(Track);
 };
 
@@ -76,7 +78,8 @@
       mPID(PID),
       mStreamType(streamType),
       mStreamID(streamID),
-      mContinuityCounter(0) {
+      mContinuityCounter(0),
+      mAudioLacksATDSHeaders(false) {
     CHECK(format->findString("mime", &mMIME));
 
     if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)
@@ -89,6 +92,13 @@
 
             mCSD.push(csd);
         }
+
+        if (!strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
+            int32_t isADTS;
+            if (!mFormat->findInt32("is-adts", &isADTS) || isADTS == 0) {
+                mAudioLacksATDSHeaders = true;
+            }
+        }
     }
 }
 
@@ -130,16 +140,7 @@
 }
 
 bool TSPacketizer::Track::lacksADTSHeader() const {
-    if (strcasecmp(mMIME.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
-        return false;
-    }
-
-    int32_t isADTS;
-    if (mFormat->findInt32("is-adts", &isADTS) && isADTS != 0) {
-        return false;
-    }
-
-    return true;
+    return mAudioLacksATDSHeaders;
 }
 
 sp<ABuffer> TSPacketizer::Track::prependCSD(
@@ -278,7 +279,8 @@
         size_t trackIndex,
         const sp<ABuffer> &_accessUnit,
         sp<ABuffer> *packets,
-        uint32_t flags) {
+        uint32_t flags,
+        const uint8_t *PES_private_data, size_t PES_private_data_len) {
     sp<ABuffer> accessUnit = _accessUnit;
 
     packets->clear();
@@ -292,12 +294,13 @@
 
     const sp<Track> &track = mTracks.itemAt(trackIndex);
 
-    if (track->isH264()) {
+    if (track->isH264() && !(flags & IS_ENCRYPTED)) {
         if (IsIDR(accessUnit)) {
             // prepend codec specific data, i.e. SPS and PPS.
             accessUnit = track->prependCSD(accessUnit);
         }
     } else if (track->lacksADTSHeader()) {
+        CHECK(!(flags & IS_ENCRYPTED));
         accessUnit = track->prependADTSHeader(accessUnit);
     }
 
@@ -336,11 +339,16 @@
     // reserved = b1
     // the first fragment of "buffer" follows
 
+    size_t PES_packet_length = accessUnit->size() + 8;
+    if (PES_private_data_len > 0) {
+        PES_packet_length += PES_private_data_len + 1;
+    }
+
     size_t numTSPackets;
-    if (accessUnit->size() <= 170) {
+    if (PES_packet_length <= 178) {
         numTSPackets = 1;
     } else {
-        numTSPackets = 1 + ((accessUnit->size() - 170) + 183) / 184;
+        numTSPackets = 1 + ((PES_packet_length - 178) + 183) / 184;
     }
 
     if (flags & EMIT_PAT_AND_PMT) {
@@ -554,8 +562,7 @@
 
     uint32_t PTS = (timeUs * 9ll) / 100ll;
 
-    size_t PES_packet_length = accessUnit->size() + 8;
-    bool padding = (accessUnit->size() < (188 - 18));
+    bool padding = (PES_packet_length < (188 - 10));
 
     if (PES_packet_length >= 65536) {
         // This really should only happen for video.
@@ -572,7 +579,7 @@
     *ptr++ = (padding ? 0x30 : 0x10) | track->incrementContinuityCounter();
 
     if (padding) {
-        size_t paddingSize = 188 - 18 - accessUnit->size();
+        size_t paddingSize = 188 - 10 - PES_packet_length;
         *ptr++ = paddingSize - 1;
         if (paddingSize >= 2) {
             *ptr++ = 0x00;
@@ -588,14 +595,23 @@
     *ptr++ = PES_packet_length >> 8;
     *ptr++ = PES_packet_length & 0xff;
     *ptr++ = 0x84;
-    *ptr++ = 0x80;
-    *ptr++ = 0x05;
+    *ptr++ = (PES_private_data_len > 0) ? 0x81 : 0x80;
+
+    *ptr++ = (PES_private_data_len > 0)
+        ? (1 + PES_private_data_len + 0x05) : 0x05;
+
     *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1;
     *ptr++ = (PTS >> 22) & 0xff;
     *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1;
     *ptr++ = (PTS >> 7) & 0xff;
     *ptr++ = ((PTS & 0x7f) << 1) | 1;
 
+    if (PES_private_data_len > 0) {
+        *ptr++ = 0x8e;  // PES_private_data_flag, reserved.
+        memcpy(ptr, PES_private_data, PES_private_data_len);
+        ptr += PES_private_data_len;
+    }
+
     // 18 bytes of TS/PES header leave 188 - 18 = 170 bytes for the payload
 
     size_t sizeLeft = packetDataStart + 188 - ptr;
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
index 9dbeb27..7020fff 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.h
@@ -40,11 +40,13 @@
     enum {
         EMIT_PAT_AND_PMT = 1,
         EMIT_PCR         = 2,
+        IS_ENCRYPTED     = 4,
     };
     status_t packetize(
             size_t trackIndex, const sp<ABuffer> &accessUnit,
             sp<ABuffer> *packets,
-            uint32_t flags);
+            uint32_t flags,
+            const uint8_t *PES_private_data, size_t PES_private_data_len);
 
 protected:
     virtual ~TSPacketizer();
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 8fead96..133b094 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -20,10 +20,13 @@
 
 #include "WifiDisplaySource.h"
 #include "PlaybackSession.h"
+#include "Parameters.h"
 #include "ParsedMessage.h"
 
+#include <binder/IServiceManager.h>
 #include <gui/ISurfaceTexture.h>
-
+#include <media/IHDCP.h>
+#include <media/IMediaPlayerService.h>
 #include <media/IRemoteDisplayClient.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -43,7 +46,14 @@
       mSessionID(0),
       mClientSessionID(0),
       mReaperPending(false),
-      mNextCSeq(1) {
+      mNextCSeq(1)
+#if REQUIRE_HDCP
+      ,mIsHDCP2_0(false)
+      ,mHDCPPort(0)
+      ,mHDCPInitializationComplete(false)
+      ,mSetupTriggerDeferred(false)
+#endif
+{
 }
 
 WifiDisplaySource::~WifiDisplaySource() {
@@ -203,7 +213,11 @@
 
                 case ANetworkSession::kWhatData:
                 {
-                    onReceiveClientData(msg);
+                    status_t err = onReceiveClientData(msg);
+
+                    if (err != OK) {
+                        disconnectClient(err);
+                    }
                     break;
                 }
 
@@ -220,6 +234,13 @@
 
             disconnectClient(OK);
 
+#if REQUIRE_HDCP
+            if (mHDCP != NULL) {
+                mHDCP->shutdownAsync();
+                mHDCP.clear();
+            }
+#endif
+
             status_t err = OK;
 
             sp<AMessage> response = new AMessage;
@@ -312,6 +333,40 @@
             break;
         }
 
+#if REQUIRE_HDCP
+        case kWhatHDCPNotify:
+        {
+            int32_t msgCode, ext1, ext2;
+            CHECK(msg->findInt32("msg", &msgCode));
+            CHECK(msg->findInt32("ext1", &ext1));
+            CHECK(msg->findInt32("ext2", &ext2));
+
+            ALOGV("Saw HDCP notification code %d, ext1 %d, ext2 %d",
+                    msgCode, ext1, ext2);
+
+            switch (msgCode) {
+                case HDCPModule::HDCP_INITIALIZATION_COMPLETE:
+                {
+                    mHDCPInitializationComplete = true;
+
+                    if (mSetupTriggerDeferred) {
+                        mSetupTriggerDeferred = false;
+
+                        sendM5(mClientSessionID);
+                    }
+                    break;
+                }
+
+                default:
+                {
+                    disconnectClient(-EACCES);
+                    break;
+                }
+            }
+            break;
+        }
+#endif
+
         default:
             TRESPASS();
     }
@@ -350,6 +405,9 @@
 
 status_t WifiDisplaySource::sendM3(int32_t sessionID) {
     AString body =
+#if REQUIRE_HDCP
+        "wfd_content_protection\r\n"
+#endif
         "wfd_video_formats\r\n"
         "wfd_audio_codecs\r\n"
         "wfd_client_rtp_ports\r\n";
@@ -405,9 +463,13 @@
         transportString = "TCP";
     }
 
+    // For 720p60:
+    //   use "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n"
+    // For 720p30:
+    //   use "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
     AString body = StringPrintf(
         "wfd_video_formats: "
-        "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n"
+        "28 00 02 02 00000020 00000000 00000000 00 0000 0000 00 none none\r\n"
         "wfd_audio_codecs: AAC 00000001 00\r\n"  // 2 ch AAC 48kHz
         "wfd_presentation_URL: rtsp://%s:%d/wfd1.0/streamid=0 none\r\n"
         "wfd_client_rtp_ports: RTP/AVP/%s;unicast 19000 0 mode=play\r\n",
@@ -512,6 +574,48 @@
         return ERROR_UNSUPPORTED;
     }
 
+    sp<Parameters> params =
+        Parameters::Parse(msg->getContent(), strlen(msg->getContent()));
+
+    if (params == NULL) {
+        return ERROR_MALFORMED;
+    }
+
+#if REQUIRE_HDCP
+    AString value;
+    if (!params->findParameter("wfd_content_protection", &value)) {
+        ALOGE("Sink doesn't appear to support content protection.");
+        return -EACCES;
+    }
+
+    if (value == "none") {
+        ALOGE("Sink does not support content protection.");
+        return -EACCES;
+    }
+
+    bool isHDCP2_0 = false;
+    if (value.startsWith("HDCP2.0 ")) {
+        isHDCP2_0 = true;
+    } else if (!value.startsWith("HDCP2.1 ")) {
+        return ERROR_MALFORMED;
+    }
+
+    int32_t hdcpPort;
+    if (!ParsedMessage::GetInt32Attribute(value.c_str() + 8, "port", &hdcpPort)
+            || hdcpPort < 1 || hdcpPort > 65535) {
+        return ERROR_MALFORMED;
+    }
+
+    mIsHDCP2_0 = isHDCP2_0;
+    mHDCPPort = hdcpPort;
+
+    status_t err = makeHDCP();
+    if (err != OK) {
+        ALOGE("Unable to instantiate HDCP component.");
+        return err;
+    }
+#endif
+
     return sendM4(sessionID);
 }
 
@@ -526,6 +630,15 @@
         return ERROR_UNSUPPORTED;
     }
 
+#if REQUIRE_HDCP
+    if (!mHDCPInitializationComplete) {
+        ALOGI("Deferring SETUP trigger until HDCP initialization completes.");
+
+        mSetupTriggerDeferred = true;
+        return OK;
+    }
+#endif
+
     return sendM5(sessionID);
 }
 
@@ -577,7 +690,7 @@
     msg->post(kPlaybackSessionTimeoutUs - 5000000ll);
 }
 
-void WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {
+status_t WifiDisplaySource::onReceiveClientData(const sp<AMessage> &msg) {
     int32_t sessionID;
     CHECK(msg->findInt32("sessionID", &sessionID));
 
@@ -597,7 +710,7 @@
     int32_t cseq;
     if (!data->findInt32("cseq", &cseq)) {
         sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
-        return;
+        return ERROR_MALFORMED;
     }
 
     if (method.startsWith("RTSP/")) {
@@ -611,7 +724,7 @@
 
         if (index < 0) {
             ALOGW("Received unsolicited server response, cseq %d", cseq);
-            return;
+            return ERROR_MALFORMED;
         }
 
         HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);
@@ -623,76 +736,45 @@
             ALOGW("Response handler for session %d, cseq %d returned "
                   "err %d (%s)",
                   sessionID, cseq, err, strerror(-err));
-        }
-    } else {
-        AString version;
-        data->getRequestField(2, &version);
-        if (!(version == AString("RTSP/1.0"))) {
-            sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
-            return;
+
+            return err;
         }
 
-        if (method == "DESCRIBE") {
-            onDescribeRequest(sessionID, cseq, data);
-        } else if (method == "OPTIONS") {
-            onOptionsRequest(sessionID, cseq, data);
-        } else if (method == "SETUP") {
-            onSetupRequest(sessionID, cseq, data);
-        } else if (method == "PLAY") {
-            onPlayRequest(sessionID, cseq, data);
-        } else if (method == "PAUSE") {
-            onPauseRequest(sessionID, cseq, data);
-        } else if (method == "TEARDOWN") {
-            onTeardownRequest(sessionID, cseq, data);
-        } else if (method == "GET_PARAMETER") {
-            onGetParameterRequest(sessionID, cseq, data);
-        } else if (method == "SET_PARAMETER") {
-            onSetParameterRequest(sessionID, cseq, data);
-        } else {
-            sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
-        }
+        return OK;
     }
+
+    AString version;
+    data->getRequestField(2, &version);
+    if (!(version == AString("RTSP/1.0"))) {
+        sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
+        return ERROR_UNSUPPORTED;
+    }
+
+    status_t err;
+    if (method == "OPTIONS") {
+        err = onOptionsRequest(sessionID, cseq, data);
+    } else if (method == "SETUP") {
+        err = onSetupRequest(sessionID, cseq, data);
+    } else if (method == "PLAY") {
+        err = onPlayRequest(sessionID, cseq, data);
+    } else if (method == "PAUSE") {
+        err = onPauseRequest(sessionID, cseq, data);
+    } else if (method == "TEARDOWN") {
+        err = onTeardownRequest(sessionID, cseq, data);
+    } else if (method == "GET_PARAMETER") {
+        err = onGetParameterRequest(sessionID, cseq, data);
+    } else if (method == "SET_PARAMETER") {
+        err = onSetParameterRequest(sessionID, cseq, data);
+    } else {
+        sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
+
+        err = ERROR_UNSUPPORTED;
+    }
+
+    return err;
 }
 
-void WifiDisplaySource::onDescribeRequest(
-        int32_t sessionID,
-        int32_t cseq,
-        const sp<ParsedMessage> &data) {
-    int64_t nowUs = ALooper::GetNowUs();
-
-    AString sdp;
-    sdp.append("v=0\r\n");
-
-    sdp.append(StringPrintf(
-                "o=- %lld %lld IN IP4 0.0.0.0\r\n", nowUs, nowUs));
-
-    sdp.append(
-            "o=- 0 0 IN IP4 127.0.0.0\r\n"
-            "s=Sample\r\n"
-            "c=IN IP4 0.0.0.0\r\n"
-            "b=AS:502\r\n"
-            "t=0 0\r\n"
-            "a=control:*\r\n"
-            "a=range:npt=now-\r\n"
-            "m=video 0 RTP/AVP 33\r\n"
-            "a=rtpmap:33 MP2T/90000\r\n"
-            "a=control:\r\n");
-
-    AString response = "RTSP/1.0 200 OK\r\n";
-    AppendCommonResponse(&response, cseq);
-
-    response.append("Content-Type: application/sdp\r\n");
-
-    // response.append("Content-Base: rtsp://0.0.0.0:7236\r\n");
-    response.append(StringPrintf("Content-Length: %d\r\n", sdp.size()));
-    response.append("\r\n");
-    response.append(sdp);
-
-    status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
-}
-
-void WifiDisplaySource::onOptionsRequest(
+status_t WifiDisplaySource::onOptionsRequest(
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
@@ -708,19 +790,21 @@
     AppendCommonResponse(&response, cseq);
 
     response.append(
-            "Public: org.wfa.wfd1.0, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, "
+            "Public: org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, "
             "GET_PARAMETER, SET_PARAMETER\r\n");
 
     response.append("\r\n");
 
     status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
 
-    err = sendM3(sessionID);
-    CHECK_EQ(err, (status_t)OK);
+    if (err == OK) {
+        err = sendM3(sessionID);
+    }
+
+    return err;
 }
 
-void WifiDisplaySource::onSetupRequest(
+status_t WifiDisplaySource::onSetupRequest(
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
@@ -729,13 +813,13 @@
         // We only support a single playback session per client.
         // This is due to the reversed keep-alive design in the wfd specs...
         sendErrorResponse(sessionID, "400 Bad Request", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     AString transport;
     if (!data->findString("transport", &transport)) {
         sendErrorResponse(sessionID, "400 Bad Request", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     PlaybackSession::TransportMode transportMode =
@@ -767,7 +851,7 @@
 
             if (badRequest) {
                 sendErrorResponse(sessionID, "400 Bad Request", cseq);
-                return;
+                return ERROR_MALFORMED;
             }
 
             transportMode = PlaybackSession::TRANSPORT_TCP;
@@ -791,7 +875,7 @@
 
         if (badRequest) {
             sendErrorResponse(sessionID, "400 Bad Request", cseq);
-            return;
+            return ERROR_MALFORMED;
         }
 #if 1
     // The older LG dongles doesn't specify client_port=xxx apparently.
@@ -801,7 +885,7 @@
 #endif
     } else {
         sendErrorResponse(sessionID, "461 Unsupported Transport", cseq);
-        return;
+        return ERROR_UNSUPPORTED;
     }
 
     int32_t playbackSessionID = makeUniquePlaybackSessionID();
@@ -813,7 +897,13 @@
     sp<PlaybackSession> playbackSession =
         new PlaybackSession(
                 mNetSession, notify, mInterfaceAddr,
-                mClient == NULL /* legacyMode */);
+                mClient == NULL,  /* legacyMode */
+#if REQUIRE_HDCP
+                mHDCP
+#else
+                NULL
+#endif
+                );
 
     looper()->registerHandler(playbackSession);
 
@@ -822,12 +912,12 @@
 
     if (strncasecmp("rtsp://", uri.c_str(), 7)) {
         sendErrorResponse(sessionID, "400 Bad Request", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     if (!(uri.startsWith("rtsp://") && uri.endsWith("/wfd1.0/streamid=0"))) {
         sendErrorResponse(sessionID, "404 Not found", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     status_t err = playbackSession->init(
@@ -846,10 +936,10 @@
             break;
         case -ENOENT:
             sendErrorResponse(sessionID, "404 Not Found", cseq);
-            return;
+            return err;
         default:
             sendErrorResponse(sessionID, "403 Forbidden", cseq);
-            return;
+            return err;
     }
 
     mClientInfo.mPlaybackSessionID = playbackSessionID;
@@ -891,13 +981,18 @@
     response.append("\r\n");
 
     err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
+
+    if (err != OK) {
+        return err;
+    }
 
     scheduleReaper();
     scheduleKeepAlive(sessionID);
+
+    return OK;
 }
 
-void WifiDisplaySource::onPlayRequest(
+status_t WifiDisplaySource::onPlayRequest(
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
@@ -907,7 +1002,7 @@
 
     if (playbackSession == NULL) {
         sendErrorResponse(sessionID, "454 Session Not Found", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     status_t err = playbackSession->play();
@@ -919,12 +1014,17 @@
     response.append("\r\n");
 
     err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
+
+    if (err != OK) {
+        return err;
+    }
 
     playbackSession->finishPlay();
+
+    return OK;
 }
 
-void WifiDisplaySource::onPauseRequest(
+status_t WifiDisplaySource::onPauseRequest(
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
@@ -934,7 +1034,7 @@
 
     if (playbackSession == NULL) {
         sendErrorResponse(sessionID, "454 Session Not Found", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     status_t err = playbackSession->pause();
@@ -945,10 +1045,11 @@
     response.append("\r\n");
 
     err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
+
+    return err;
 }
 
-void WifiDisplaySource::onTeardownRequest(
+status_t WifiDisplaySource::onTeardownRequest(
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
@@ -958,7 +1059,7 @@
 
     if (playbackSession == NULL) {
         sendErrorResponse(sessionID, "454 Session Not Found", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     AString response = "RTSP/1.0 200 OK\r\n";
@@ -967,12 +1068,17 @@
     response.append("\r\n");
 
     status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
+
+    if (err != OK) {
+        return err;
+    }
 
     disconnectClient(UNKNOWN_ERROR);
+
+    return OK;
 }
 
-void WifiDisplaySource::onGetParameterRequest(
+status_t WifiDisplaySource::onGetParameterRequest(
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
@@ -982,7 +1088,7 @@
 
     if (playbackSession == NULL) {
         sendErrorResponse(sessionID, "454 Session Not Found", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
     playbackSession->updateLiveness();
@@ -992,10 +1098,10 @@
     response.append("\r\n");
 
     status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
+    return err;
 }
 
-void WifiDisplaySource::onSetParameterRequest(
+status_t WifiDisplaySource::onSetParameterRequest(
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
@@ -1005,11 +1111,12 @@
 
     if (playbackSession == NULL) {
         sendErrorResponse(sessionID, "454 Session Not Found", cseq);
-        return;
+        return ERROR_MALFORMED;
     }
 
-    // XXX check that the parameter is about that.
-    playbackSession->requestIDRFrame();
+    if (strstr(data->getContent(), "wfd_idr_request\r\n")) {
+        playbackSession->requestIDRFrame();
+    }
 
     playbackSession->updateLiveness();
 
@@ -1018,7 +1125,7 @@
     response.append("\r\n");
 
     status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-    CHECK_EQ(err, (status_t)OK);
+    return err;
 }
 
 // static
@@ -1103,5 +1210,71 @@
     }
 }
 
+#if REQUIRE_HDCP
+struct WifiDisplaySource::HDCPObserver : public BnHDCPObserver {
+    HDCPObserver(const sp<AMessage> &notify);
+
+    virtual void notify(
+            int msg, int ext1, int ext2, const Parcel *obj);
+
+private:
+    sp<AMessage> mNotify;
+
+    DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver);
+};
+
+WifiDisplaySource::HDCPObserver::HDCPObserver(
+        const sp<AMessage> &notify)
+    : mNotify(notify) {
+}
+
+void WifiDisplaySource::HDCPObserver::notify(
+        int msg, int ext1, int ext2, const Parcel *obj) {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("msg", msg);
+    notify->setInt32("ext1", ext1);
+    notify->setInt32("ext2", ext2);
+    notify->post();
+}
+
+status_t WifiDisplaySource::makeHDCP() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("media.player"));
+    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+    CHECK(service != NULL);
+
+    mHDCP = service->makeHDCP();
+
+    if (mHDCP == NULL) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    sp<AMessage> notify = new AMessage(kWhatHDCPNotify, id());
+    mHDCPObserver = new HDCPObserver(notify);
+
+    status_t err = mHDCP->setObserver(mHDCPObserver);
+
+    if (err != OK) {
+        ALOGE("Failed to set HDCP observer.");
+
+        mHDCPObserver.clear();
+        mHDCP.clear();
+
+        return err;
+    }
+
+    ALOGI("initiating HDCP negotiation w/ host %s:%d",
+            mClientInfo.mRemoteIP.c_str(), mHDCPPort);
+
+    err = mHDCP->initAsync(mClientInfo.mRemoteIP.c_str(), mHDCPPort);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return OK;
+}
+#endif
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 3c8d50f..298cb9b 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -26,6 +26,9 @@
 
 namespace android {
 
+#define REQUIRE_HDCP    0
+
+struct IHDCP;
 struct IRemoteDisplayClient;
 struct ParsedMessage;
 
@@ -48,6 +51,10 @@
 private:
     struct PlaybackSession;
 
+#if REQUIRE_HDCP
+    struct HDCPObserver;
+#endif
+
     enum {
         kWhatStart,
         kWhatRTSPNotify,
@@ -55,6 +62,7 @@
         kWhatReapDeadClients,
         kWhatPlaybackSessionNotify,
         kWhatKeepAlive,
+        kWhatHDCPNotify,
     };
 
     struct ResponseID {
@@ -100,6 +108,18 @@
 
     KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
 
+#if REQUIRE_HDCP
+    bool mIsHDCP2_0;
+    int32_t mHDCPPort;
+    sp<IHDCP> mHDCP;
+    sp<HDCPObserver> mHDCPObserver;
+
+    bool mHDCPInitializationComplete;
+    bool mSetupTriggerDeferred;
+
+    status_t makeHDCP();
+#endif
+
     status_t sendM1(int32_t sessionID);
     status_t sendM3(int32_t sessionID);
     status_t sendM4(int32_t sessionID);
@@ -124,44 +144,39 @@
     void registerResponseHandler(
             int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func);
 
-    void onReceiveClientData(const sp<AMessage> &msg);
+    status_t onReceiveClientData(const sp<AMessage> &msg);
 
-    void onDescribeRequest(
+    status_t onOptionsRequest(
             int32_t sessionID,
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
-    void onOptionsRequest(
+    status_t onSetupRequest(
             int32_t sessionID,
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
-    void onSetupRequest(
+    status_t onPlayRequest(
             int32_t sessionID,
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
-    void onPlayRequest(
+    status_t onPauseRequest(
             int32_t sessionID,
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
-    void onPauseRequest(
+    status_t onTeardownRequest(
             int32_t sessionID,
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
-    void onTeardownRequest(
+    status_t onGetParameterRequest(
             int32_t sessionID,
             int32_t cseq,
             const sp<ParsedMessage> &data);
 
-    void onGetParameterRequest(
-            int32_t sessionID,
-            int32_t cseq,
-            const sp<ParsedMessage> &data);
-
-    void onSetParameterRequest(
+    status_t onSetParameterRequest(
             int32_t sessionID,
             int32_t cseq,
             const sp<ParsedMessage> &data);