Provisional support for secure decryption of media streams.

Change-Id: Ib3982a9c960bfdb0cb7e1b174440b141b194cfbe
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index fea62cc..cf2909e 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -28,6 +28,7 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/NuMediaExtractor.h>
 #include <gui/SurfaceComposerClient.h>
@@ -36,7 +37,9 @@
     fprintf(stderr, "usage: %s [-a] use audio\n"
                     "\t\t[-v] use video\n"
                     "\t\t[-p] playback\n"
-                    "\t\t[-S] allocate buffers from a surface\n", me);
+                    "\t\t[-S] allocate buffers from a surface\n"
+                    "\t\t[-D] decrypt input buffers\n",
+                    me);
 
     exit(1);
 }
@@ -63,7 +66,8 @@
         const char *path,
         bool useAudio,
         bool useVideo,
-        const android::sp<android::Surface> &surface) {
+        const android::sp<android::Surface> &surface,
+        bool decryptInputBuffers) {
     using namespace android;
 
     static int64_t kTimeout = 500ll;
@@ -109,13 +113,31 @@
         state->mNumBuffersDecoded = 0;
         state->mIsAudio = isAudio;
 
-        state->mCodec = MediaCodec::CreateByType(
-                looper, mime.c_str(), false /* encoder */);
+        if (decryptInputBuffers && !isAudio) {
+            static const MediaCodecList *list = MediaCodecList::getInstance();
+
+            ssize_t index =
+                list->findCodecByType(mime.c_str(), false /* encoder */);
+
+            CHECK_GE(index, 0);
+
+            const char *componentName = list->getCodecName(index);
+
+            AString fullName = componentName;
+            fullName.append(".secure");
+
+            state->mCodec = MediaCodec::CreateByComponentName(
+                    looper, fullName.c_str());
+        } else {
+            state->mCodec = MediaCodec::CreateByType(
+                    looper, mime.c_str(), false /* encoder */);
+        }
 
         CHECK(state->mCodec != NULL);
 
         err = state->mCodec->configure(
-                format, isVideo ? surface : NULL, 0 /* flags */);
+                format, isVideo ? surface : NULL,
+                decryptInputBuffers ? MediaCodec::CONFIGURE_FLAG_SECURE : 0);
 
         CHECK_EQ(err, (status_t)OK);
 
@@ -202,12 +224,24 @@
                     err = extractor->getSampleTime(&timeUs);
                     CHECK_EQ(err, (status_t)OK);
 
+                    uint32_t bufferFlags = 0;
+
+                    uint32_t sampleFlags;
+                    err = extractor->getSampleFlags(&sampleFlags);
+                    CHECK_EQ(err, (status_t)OK);
+
+                    if (sampleFlags & NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED) {
+                        CHECK(decryptInputBuffers);
+
+                        bufferFlags |= MediaCodec::BUFFER_FLAG_ENCRYPTED;
+                    }
+
                     err = state->mCodec->queueInputBuffer(
                             index,
                             0 /* offset */,
                             buffer->size(),
                             timeUs,
-                            0 /* flags */);
+                            bufferFlags);
 
                     CHECK_EQ(err, (status_t)OK);
 
@@ -341,9 +375,10 @@
     bool useVideo = false;
     bool playback = false;
     bool useSurface = false;
+    bool decryptInputBuffers = false;
 
     int res;
-    while ((res = getopt(argc, argv, "havpS")) >= 0) {
+    while ((res = getopt(argc, argv, "havpSD")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -369,6 +404,12 @@
                 break;
             }
 
+            case 'D':
+            {
+                decryptInputBuffers = true;
+                break;
+            }
+
             case '?':
             case 'h':
             default:
@@ -440,7 +481,8 @@
         player->stop();
         player->reset();
     } else {
-        decode(looper, argv[0], useAudio, useVideo, surface);
+        decode(looper, argv[0],
+               useAudio, useVideo, surface, decryptInputBuffers);
     }
 
     if (playback || (useSurface && useVideo)) {
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 64df5d1..3bbfbdc 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -287,6 +287,11 @@
 
             msg->setInt32("channel-count", numChannels);
             msg->setInt32("sample-rate", sampleRate);
+
+            int32_t isADTS;
+            if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
+                msg->setInt32("is-adts", true);
+            }
         }
 
         uint32_t type;
diff --git a/include/media/ICrypto.h b/include/media/ICrypto.h
new file mode 100644
index 0000000..916abe0
--- /dev/null
+++ b/include/media/ICrypto.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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 <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#ifndef ANDROID_ICRYPTO_H_
+
+#define ANDROID_ICRYPTO_H_
+
+namespace android {
+
+struct ICrypto : public IInterface {
+    DECLARE_META_INTERFACE(Crypto);
+
+    virtual status_t initialize() = 0;
+    virtual status_t terminate() = 0;
+
+    virtual status_t setEntitlementKey(
+            const void *key, size_t keyLength) = 0;
+
+    virtual status_t setEntitlementControlMessage(
+            const void *msg, size_t msgLength) = 0;
+
+    // "dstData" is in media_server's address space (but inaccessible).
+    virtual ssize_t decryptVideo(
+            const void *iv, size_t ivLength,
+            const void *srcData, size_t srcDataSize,
+            void *dstData, size_t dstDataOffset) = 0;
+
+    // "dstData" is in the calling process' address space.
+    virtual ssize_t decryptAudio(
+            const void *iv, size_t ivLength,
+            const void *srcData, size_t srcDataSize,
+            void *dstData, size_t dstDataSize) = 0;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(ICrypto);
+};
+
+struct BnCrypto : public BnInterface<ICrypto> {
+    virtual status_t onTransact(
+            uint32_t code, const Parcel &data, Parcel *reply,
+            uint32_t flags = 0);
+};
+
+}  // namespace android
+
+#endif // ANDROID_ICRYPTO_H_
+
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 4f46fcd..76c45a0 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -31,6 +31,7 @@
 
 namespace android {
 
+struct ICrypto;
 class IMediaRecorder;
 class IOMX;
 struct IStreamSource;
@@ -47,6 +48,7 @@
     virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
     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;
 
     // codecs and audio devices usage tracking for the battery app
     enum BatteryDataBits {
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index fa1a416..7d7af63 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -89,6 +89,10 @@
         kPortIndexOutput = 1
     };
 
+    enum {
+        kFlagIsSecure   = 1,
+    };
+
     struct BufferInfo {
         enum Status {
             OWNED_BY_US,
@@ -118,6 +122,7 @@
     sp<FlushingState> mFlushingState;
 
     AString mComponentName;
+    uint32_t mFlags;
     uint32_t mQuirks;
     sp<IOMX> mOMX;
     IOMX::node_id mNode;
@@ -176,7 +181,8 @@
 
     status_t setupAACCodec(
             bool encoder,
-            int32_t numChannels, int32_t sampleRate, int32_t bitRate);
+            int32_t numChannels, int32_t sampleRate, int32_t bitRate,
+            bool isADTS);
 
     status_t selectAudioPortFormat(
             OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 72ac56a..0fc88e1 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -27,18 +27,21 @@
 struct ABuffer;
 struct ACodec;
 struct AMessage;
+struct ICrypto;
 struct SoftwareRenderer;
 struct SurfaceTextureClient;
 
 struct MediaCodec : public AHandler {
     enum ConfigureFlags {
         CONFIGURE_FLAG_ENCODE   = 1,
+        CONFIGURE_FLAG_SECURE   = 2,
     };
 
     enum BufferFlags {
         BUFFER_FLAG_SYNCFRAME   = 1,
         BUFFER_FLAG_CODECCONFIG = 2,
         BUFFER_FLAG_EOS         = 4,
+        BUFFER_FLAG_ENCRYPTED   = 8,
     };
 
     static sp<MediaCodec> CreateByType(
@@ -137,11 +140,13 @@
         kFlagStickyError                = 8,
         kFlagDequeueInputPending        = 16,
         kFlagDequeueOutputPending       = 32,
+        kFlagIsSecure                   = 64,
     };
 
     struct BufferInfo {
         void *mBufferID;
         sp<ABuffer> mData;
+        sp<ABuffer> mEncryptedData;
         sp<AMessage> mNotify;
         bool mOwnedByClient;
     };
@@ -165,6 +170,8 @@
     int32_t mDequeueOutputTimeoutGeneration;
     uint32_t mDequeueOutputReplyID;
 
+    sp<ICrypto> mCrypto;
+
     MediaCodec(const sp<ALooper> &looper);
 
     static status_t PostAndAwaitResponse(
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index c3ccb56..639446e 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -128,6 +128,12 @@
     kKeyTextFormatData    = 'text',  // raw data
 
     kKeyRequiresSecureBuffers = 'secu',  // bool (int32_t)
+
+    kKeyScrambling        = 'scrm',  // int32_t
+    kKeyEMM               = 'emm ',  // raw data
+    kKeyECM               = 'ecm ',  // raw data
+
+    kKeyIsADTS            = 'adts',  // bool (int32_t)
 };
 
 enum {
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 96efdff..07c7be5 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -31,6 +31,11 @@
 struct MediaSource;
 
 struct NuMediaExtractor : public RefBase {
+    enum SampleFlags {
+        SAMPLE_FLAG_SYNC        = 1,
+        SAMPLE_FLAG_ENCRYPTED   = 2,
+    };
+
     NuMediaExtractor();
 
     status_t setDataSource(const char *path);
@@ -46,6 +51,7 @@
     status_t readSampleData(const sp<ABuffer> &buffer);
     status_t getSampleTrackIndex(size_t *trackIndex);
     status_t getSampleTime(int64_t *sampleTimeUs);
+    status_t getSampleFlags(uint32_t *sampleFlags);
 
 protected:
     virtual ~NuMediaExtractor();
@@ -61,7 +67,9 @@
         status_t mFinalResult;
         MediaBuffer *mSample;
         int64_t mSampleTimeUs;
-        uint32_t mFlags;  // bitmask of "TrackFlags"
+        uint32_t mSampleFlags;
+
+        uint32_t mTrackFlags;  // bitmask of "TrackFlags"
     };
 
     sp<MediaExtractor> mImpl;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 7c612ba..7d51dee 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -238,7 +238,11 @@
     void setComponentRole();
 
     void setAMRFormat(bool isWAMR, int32_t bitRate);
-    status_t setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate);
+
+    status_t setAACFormat(
+            int32_t numChannels, int32_t sampleRate, int32_t bitRate,
+            bool isADTS);
+
     void setG711Format(int32_t numChannels);
 
     status_t setVideoPortFormatType(
diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
index 55ade64..0f8f1e1 100644
--- a/include/media/stagefright/foundation/AString.h
+++ b/include/media/stagefright/foundation/AString.h
@@ -73,6 +73,7 @@
     int compare(const AString &other) const;
 
     bool startsWith(const char *prefix) const;
+    bool endsWith(const char *suffix) const;
 
     void tolower();
 
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index bccf1f9..d06e302 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -48,6 +48,7 @@
     public static int FLAG_SYNCFRAME   = 1;
     public static int FLAG_CODECCONFIG = 2;
     public static int FLAG_EOS         = 4;
+    public static int FLAG_ENCRYPTED   = 8;
 
     /** Instantiate a codec component by mime type. For decoder components
         this is the mime type of media that this decoder should be able to
@@ -82,6 +83,7 @@
     public native final void release();
 
     public static int CONFIGURE_FLAG_ENCODE = 1;
+    public static int CONFIGURE_FLAG_SECURE = 2;
 
     /** Configures a component.
      *  @param format A map of string/value pairs describing the input format
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 5732c72..9ea3d0e 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -65,6 +65,13 @@
     // Returns the current sample's presentation time in microseconds.
     public native long getSampleTime();
 
+    // Keep these in sync with their equivalents in NuMediaExtractor.h
+    public static final int SAMPLE_FLAG_SYNC      = 1;
+    public static final int SAMPLE_FLAG_ENCRYPTED = 2;
+
+    // Returns the current sample's flags.
+    public native int getSampleFlags();
+
     private static native final void native_init();
     private native final void native_setup(String path);
     private native final void native_finalize();
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 0c86fc2..8c661b7 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -160,6 +160,10 @@
     return mImpl->getSampleTime(sampleTimeUs);
 }
 
+status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
+    return mImpl->getSampleFlags(sampleFlags);
+}
+
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -343,6 +347,28 @@
     return sampleTimeUs;
 }
 
+static jint android_media_MediaExtractor_getSampleFlags(
+        JNIEnv *env, jobject thiz) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return -1ll;
+    }
+
+    uint32_t sampleFlags;
+    status_t err = extractor->getSampleFlags(&sampleFlags);
+
+    if (err == ERROR_END_OF_STREAM) {
+        return -1ll;
+    } else if (err != OK) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    return sampleFlags;
+}
+
 static void android_media_MediaExtractor_native_init(JNIEnv *env) {
     jclass clazz = env->FindClass("android/media/MediaExtractor");
     CHECK(clazz != NULL);
@@ -412,6 +438,9 @@
     { "getSampleTime", "()J",
         (void *)android_media_MediaExtractor_getSampleTime },
 
+    { "getSampleFlags", "()I",
+        (void *)android_media_MediaExtractor_getSampleFlags },
+
     { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
 
     { "native_setup", "(Ljava/lang/String;)V",
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 70e58c6..49a64d6 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -43,6 +43,7 @@
     status_t readSampleData(jobject byteBuf, size_t offset, size_t *sampleSize);
     status_t getSampleTrackIndex(size_t *trackIndex);
     status_t getSampleTime(int64_t *sampleTimeUs);
+    status_t getSampleFlags(uint32_t *sampleFlags);
 
 protected:
     virtual ~JMediaExtractor();
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 8b2321c..1190448 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -324,7 +324,7 @@
                 env->DeleteLocalRef(byteArray); byteArray = NULL;
             }
 
-            msg->setObject(key.c_str(), buffer);
+            msg->setBuffer(key.c_str(), buffer);
         }
     }
 
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index c34e23b..a758850 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -17,6 +17,7 @@
     IAudioFlingerClient.cpp \
     IAudioTrack.cpp \
     IAudioRecord.cpp \
+    ICrypto.cpp \
     AudioRecord.cpp \
     AudioSystem.cpp \
     mediaplayer.cpp \
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
new file mode 100644
index 0000000..827d7af
--- /dev/null
+++ b/media/libmedia/ICrypto.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ICrypto"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <media/ICrypto.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+enum {
+    INITIALIZE = IBinder::FIRST_CALL_TRANSACTION,
+    TERMINATE,
+    SET_ENTITLEMENT_KEY,
+    SET_ECM,
+    DECRYPT_VIDEO,
+    DECRYPT_AUDIO,
+};
+
+struct BpCrypto : public BpInterface<ICrypto> {
+    BpCrypto(const sp<IBinder> &impl)
+        : BpInterface<ICrypto>(impl) {
+    }
+
+    virtual status_t initialize() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+        remote()->transact(INITIALIZE, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t terminate() {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+        remote()->transact(TERMINATE, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t setEntitlementKey(
+            const void *key, size_t keyLength) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+        data.writeInt32(keyLength);
+        data.write(key, keyLength);
+        remote()->transact(SET_ENTITLEMENT_KEY, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t setEntitlementControlMessage(
+            const void *msg, size_t msgLength) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+        data.writeInt32(msgLength);
+        data.write(msg, msgLength);
+        remote()->transact(SET_ECM, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual ssize_t decryptVideo(
+            const void *iv, size_t ivLength,
+            const void *srcData, size_t srcDataSize,
+            void *dstData, size_t dstDataOffset) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+        if (iv == NULL) {
+            if (ivLength > 0) {
+                return -EINVAL;
+            }
+
+            data.writeInt32(-1);
+        } else {
+            data.writeInt32(ivLength);
+            data.write(iv, ivLength);
+        }
+
+        data.writeInt32(srcDataSize);
+        data.write(srcData, srcDataSize);
+
+        data.writeIntPtr((intptr_t)dstData);
+        data.writeInt32(dstDataOffset);
+
+        remote()->transact(DECRYPT_VIDEO, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual ssize_t decryptAudio(
+            const void *iv, size_t ivLength,
+            const void *srcData, size_t srcDataSize,
+            void *dstData, size_t dstDataSize) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+        if (iv == NULL) {
+            if (ivLength > 0) {
+                return -EINVAL;
+            }
+
+            data.writeInt32(-1);
+        } else {
+            data.writeInt32(ivLength);
+            data.write(iv, ivLength);
+        }
+
+        data.writeInt32(srcDataSize);
+        data.write(srcData, srcDataSize);
+        data.writeInt32(dstDataSize);
+
+        remote()->transact(DECRYPT_AUDIO, data, &reply);
+
+        ssize_t res = reply.readInt32();
+
+        if (res <= 0) {
+            return res;
+        }
+
+        reply.read(dstData, res);
+
+        return res;
+    }
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
+};
+
+IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto");
+
+////////////////////////////////////////////////////////////////////////////////
+
+status_t BnCrypto::onTransact(
+    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+    switch (code) {
+        case INITIALIZE:
+        {
+            CHECK_INTERFACE(ICrypto, data, reply);
+            reply->writeInt32(initialize());
+
+            return OK;
+        }
+
+        case TERMINATE:
+        {
+            CHECK_INTERFACE(ICrypto, data, reply);
+            reply->writeInt32(terminate());
+
+            return OK;
+        }
+
+        case SET_ENTITLEMENT_KEY:
+        {
+            CHECK_INTERFACE(ICrypto, data, reply);
+
+            size_t keyLength = data.readInt32();
+            void *key = malloc(keyLength);
+            data.read(key, keyLength);
+
+            reply->writeInt32(setEntitlementKey(key, keyLength));
+
+            free(key);
+            key = NULL;
+
+            return OK;
+        }
+
+        case SET_ECM:
+        {
+            CHECK_INTERFACE(ICrypto, data, reply);
+
+            size_t msgLength = data.readInt32();
+            void *msg = malloc(msgLength);
+            data.read(msg, msgLength);
+
+            reply->writeInt32(setEntitlementControlMessage(msg, msgLength));
+
+            free(msg);
+            msg = NULL;
+
+            return OK;
+        }
+
+        case DECRYPT_VIDEO:
+        {
+            CHECK_INTERFACE(ICrypto, data, reply);
+
+            void *iv = NULL;
+
+            int32_t ivLength = data.readInt32();
+            if (ivLength >= 0) {
+                iv = malloc(ivLength);
+                data.read(iv, ivLength);
+            }
+
+            size_t srcDataSize = data.readInt32();
+            void *srcData = malloc(srcDataSize);
+            data.read(srcData, srcDataSize);
+
+            void *dstData = (void *)data.readIntPtr();
+            size_t dstDataOffset = data.readInt32();
+
+            reply->writeInt32(
+                    decryptVideo(
+                        iv,
+                        ivLength < 0 ? 0 : ivLength,
+                        srcData,
+                        srcDataSize,
+                        dstData,
+                        dstDataOffset));
+
+            free(srcData);
+            srcData = NULL;
+
+            if (iv != NULL) {
+                free(iv);
+                iv = NULL;
+            }
+
+            return OK;
+        }
+
+        case DECRYPT_AUDIO:
+        {
+            CHECK_INTERFACE(ICrypto, data, reply);
+
+            void *iv = NULL;
+
+            int32_t ivLength = data.readInt32();
+            if (ivLength >= 0) {
+                iv = malloc(ivLength);
+                data.read(iv, ivLength);
+            }
+
+            size_t srcDataSize = data.readInt32();
+            void *srcData = malloc(srcDataSize);
+            data.read(srcData, srcDataSize);
+
+            size_t dstDataSize = data.readInt32();
+            void *dstData = malloc(dstDataSize);
+
+            ssize_t res =
+                decryptAudio(
+                        iv,
+                        ivLength < 0 ? 0 : ivLength,
+                        srcData,
+                        srcDataSize,
+                        dstData,
+                        dstDataSize);
+
+            reply->writeInt32(res);
+
+            if (res > 0) {
+                reply->write(dstData, res);
+            }
+
+            free(dstData);
+            dstData = NULL;
+
+            free(srcData);
+            srcData = NULL;
+
+            if (iv != NULL) {
+                free(iv);
+                iv = NULL;
+            }
+
+            return OK;
+        }
+
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index f5fccef..9120617 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -20,6 +20,7 @@
 
 #include <binder/Parcel.h>
 #include <binder/IMemory.h>
+#include <media/ICrypto.h>
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
 #include <media/IOMX.h>
@@ -36,6 +37,7 @@
     CREATE_MEDIA_RECORDER,
     CREATE_METADATA_RETRIEVER,
     GET_OMX,
+    MAKE_CRYPTO,
     ADD_BATTERY_DATA,
     PULL_BATTERY_DATA
 };
@@ -111,6 +113,13 @@
         return interface_cast<IOMX>(reply.readStrongBinder());
     }
 
+    virtual sp<ICrypto> makeCrypto() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+        remote()->transact(MAKE_CRYPTO, data, &reply);
+        return interface_cast<ICrypto>(reply.readStrongBinder());
+    }
+
     virtual void addBatteryData(uint32_t params) {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
@@ -191,6 +200,12 @@
             reply->writeStrongBinder(omx->asBinder());
             return NO_ERROR;
         } break;
+        case MAKE_CRYPTO: {
+            CHECK_INTERFACE(IMediaPlayerService, data, reply);
+            sp<ICrypto> crypto = makeCrypto();
+            reply->writeStrongBinder(crypto->asBinder());
+            return NO_ERROR;
+        } break;
         case ADD_BATTERY_DATA: {
             CHECK_INTERFACE(IMediaPlayerService, data, reply);
             uint32_t params = data.readInt32();
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 4c6e0bd..675c563 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -7,6 +7,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:=               \
+    Crypto.cpp                  \
     MediaRecorderClient.cpp     \
     MediaPlayerService.cpp      \
     MetadataRetrieverClient.cpp \
@@ -42,7 +43,7 @@
 	$(TOP)/frameworks/base/media/libstagefright/include             \
 	$(TOP)/frameworks/base/media/libstagefright/rtsp                \
 	$(TOP)/frameworks/native/include/media/openmax                  \
-	$(TOP)/external/tremolo/Tremolo
+	$(TOP)/external/tremolo/Tremolo                                 \
 
 LOCAL_MODULE:= libmediaplayerservice
 
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
new file mode 100644
index 0000000..e02035f
--- /dev/null
+++ b/media/libmediaplayerservice/Crypto.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Crypto"
+#include <utils/Log.h>
+
+#include "Crypto.h"
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+Crypto::Crypto() {
+}
+
+Crypto::~Crypto() {
+}
+
+status_t Crypto::initialize() {
+    return ERROR_UNSUPPORTED;
+}
+
+status_t Crypto::terminate() {
+    return ERROR_UNSUPPORTED;
+}
+
+status_t Crypto::setEntitlementKey(
+        const void *key, size_t keyLength) {
+    return ERROR_UNSUPPORTED;
+}
+
+status_t Crypto::setEntitlementControlMessage(
+        const void *msg, size_t msgLength) {
+    return ERROR_UNSUPPORTED;
+}
+
+ssize_t Crypto::decryptVideo(
+        const void *iv, size_t ivLength,
+        const void *srcData, size_t srcDataSize,
+        void *dstData, size_t dstDataOffset) {
+    return ERROR_UNSUPPORTED;
+}
+
+ssize_t Crypto::decryptAudio(
+        const void *iv, size_t ivLength,
+        const void *srcData, size_t srcDataSize,
+        void *dstData, size_t dstDataSize) {
+    return ERROR_UNSUPPORTED;
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h
new file mode 100644
index 0000000..9855496
--- /dev/null
+++ b/media/libmediaplayerservice/Crypto.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef CRYPTO_H_
+
+#define CRYPTO_H_
+
+#include <media/ICrypto.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct Crypto : public BnCrypto {
+    Crypto();
+
+    virtual status_t initialize();
+    virtual status_t terminate();
+
+    virtual status_t setEntitlementKey(
+            const void *key, size_t keyLength);
+
+    virtual status_t setEntitlementControlMessage(
+            const void *msg, size_t msgLength);
+
+    virtual ssize_t decryptVideo(
+            const void *iv, size_t ivLength,
+            const void *srcData, size_t srcDataSize,
+            void *dstData, size_t dstDataOffset);
+
+    virtual ssize_t decryptAudio(
+            const void *iv, size_t ivLength,
+            const void *srcData, size_t srcDataSize,
+            void *dstData, size_t dstDataSize);
+
+protected:
+    virtual ~Crypto();
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(Crypto);
+};
+
+}  // namespace android
+
+#endif  // CRYPTO_H_
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 840e475..123d07ff 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -70,6 +70,8 @@
 
 #include <OMX.h>
 
+#include "Crypto.h"
+
 namespace android {
 sp<MediaPlayerBase> createAAH_TXPlayer();
 sp<MediaPlayerBase> createAAH_RXPlayer();
@@ -292,6 +294,16 @@
     return mOMX;
 }
 
+sp<ICrypto> MediaPlayerService::makeCrypto() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mCrypto == NULL) {
+        mCrypto = new Crypto;
+    }
+
+    return mCrypto;
+}
+
 status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
 {
     const size_t SIZE = 256;
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index d4e0eb1..b08dd6c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -240,6 +240,7 @@
     virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
     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 status_t            dump(int fd, const Vector<String16>& args);
 
@@ -419,6 +420,7 @@
                 SortedVector< wp<MediaRecorderClient> > mMediaRecorderClients;
                 int32_t                     mNextConnId;
                 sp<IOMX>                    mOMX;
+                sp<ICrypto>                 mCrypto;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 1600141..5733229 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -125,6 +125,11 @@
 
         msg->setInt32("channel-count", numChannels);
         msg->setInt32("sample-rate", sampleRate);
+
+        int32_t isADTS;
+        if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
+            msg->setInt32("is-adts", true);
+        }
     }
 
     int32_t maxInputSize;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index e5ad4b7..db2beda 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -427,24 +427,34 @@
                 sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
                 CHECK(mem.get() != NULL);
 
-                IOMX::buffer_id buffer;
+                BufferInfo info;
+                info.mStatus = BufferInfo::OWNED_BY_US;
 
                 uint32_t requiresAllocateBufferBit =
                     (portIndex == kPortIndexInput)
                         ? OMXCodec::kRequiresAllocateBufferOnInputPorts
                         : OMXCodec::kRequiresAllocateBufferOnOutputPorts;
 
-                if (mQuirks & requiresAllocateBufferBit) {
+                if (portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) {
+                    mem.clear();
+
+                    void *ptr;
+                    err = mOMX->allocateBuffer(
+                            mNode, portIndex, def.nBufferSize, &info.mBufferID,
+                            &ptr);
+
+                    info.mData = new ABuffer(ptr, def.nBufferSize);
+                } else if (mQuirks & requiresAllocateBufferBit) {
                     err = mOMX->allocateBufferWithBackup(
-                            mNode, portIndex, mem, &buffer);
+                            mNode, portIndex, mem, &info.mBufferID);
                 } else {
-                    err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+                    err = mOMX->useBuffer(mNode, portIndex, mem, &info.mBufferID);
                 }
 
-                BufferInfo info;
-                info.mBufferID = buffer;
-                info.mStatus = BufferInfo::OWNED_BY_US;
-                info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+                if (mem != NULL) {
+                    info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+                }
+
                 mBuffers[portIndex].push(info);
             }
         }
@@ -840,7 +850,13 @@
                 || !msg->findInt32("sample-rate", &sampleRate)) {
             err = INVALID_OPERATION;
         } else {
-            err = setupAACCodec(encoder, numChannels, sampleRate, bitRate);
+            int32_t isADTS;
+            if (!msg->findInt32("is-adts", &isADTS)) {
+                isADTS = 0;
+            }
+
+            err = setupAACCodec(
+                    encoder, numChannels, sampleRate, bitRate, isADTS != 0);
         }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
         err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
@@ -934,7 +950,11 @@
 
 status_t ACodec::setupAACCodec(
         bool encoder,
-        int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
+        int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
+    if (encoder && isADTS) {
+        return -EINVAL;
+    }
+
     status_t err = setupRawAudioFormat(
             encoder ? kPortIndexInput : kPortIndexOutput,
             sampleRate,
@@ -1021,7 +1041,11 @@
 
     profile.nChannels = numChannels;
     profile.nSampleRate = sampleRate;
-    profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+    profile.eAACStreamFormat =
+        isADTS
+            ? OMX_AUDIO_AACStreamFormatMP4ADTS
+            : OMX_AUDIO_AACStreamFormatMP4FF;
 
     return mOMX->setParameter(
             mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
@@ -2653,6 +2677,12 @@
     observer->setNotificationMessage(notify);
 
     mCodec->mComponentName = componentName;
+    mCodec->mFlags = 0;
+
+    if (componentName.endsWith(".secure")) {
+        mCodec->mFlags |= kFlagIsSecure;
+    }
+
     mCodec->mQuirks = quirks;
     mCodec->mOMX = omx;
     mCodec->mNode = node;
@@ -2701,6 +2731,7 @@
         mCodec->mNode = NULL;
         mCodec->mOMX.clear();
         mCodec->mQuirks = 0;
+        mCodec->mFlags = 0;
         mCodec->mComponentName.clear();
 
         mCodec->changeState(mCodec->mUninitializedState);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index a9e7f360..42b5c7e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -22,10 +22,14 @@
 
 #include "include/SoftwareRenderer.h"
 
+#include <binder/IServiceManager.h>
 #include <gui/SurfaceTextureClient.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/ACodec.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -528,6 +532,12 @@
                         info.mOwnedByClient = false;
                         CHECK(msg->findBuffer(name.c_str(), &info.mData));
 
+                        if (portIndex == kPortIndexInput
+                                && (mFlags & kFlagIsSecure)) {
+                            info.mEncryptedData =
+                                new ABuffer(info.mData->capacity());
+                        }
+
                         buffers->push_back(info);
                     }
 
@@ -742,6 +752,59 @@
                 format->setInt32("encoder", true);
             }
 
+            if (flags & CONFIGURE_FLAG_SECURE) {
+                mFlags |= kFlagIsSecure;
+
+                sp<IServiceManager> sm = defaultServiceManager();
+
+                sp<IBinder> binder =
+                    sm->getService(String16("media.player"));
+
+                sp<IMediaPlayerService> service =
+                    interface_cast<IMediaPlayerService>(binder);
+
+                CHECK(service != NULL);
+
+                mCrypto = service->makeCrypto();
+
+                status_t err = mCrypto->initialize();
+
+                if (err == OK) {
+                    sp<ABuffer> emm;
+                    if (format->findBuffer("emm", &emm)) {
+                        err = mCrypto->setEntitlementKey(
+                                emm->data(), emm->size());
+                    }
+                }
+
+                if (err == OK) {
+                    sp<ABuffer> ecm;
+                    if (format->findBuffer("ecm", &ecm)) {
+                        CHECK_EQ(ecm->size(), 80u);
+
+                        // bytes 16..47 of the original ecm stream data.
+                        err = mCrypto->setEntitlementControlMessage(
+                                ecm->data() + 16, 32);
+                    }
+                }
+
+                if (err != OK) {
+                    ALOGE("failed to instantiate crypto service.");
+
+                    mCrypto.clear();
+
+                    setState(INITIALIZED);
+
+                    sp<AMessage> response = new AMessage;
+                    response->setInt32("err", UNKNOWN_ERROR);
+
+                    response->postReply(mReplyID);
+                    break;
+                }
+            } else {
+                mFlags &= ~kFlagIsSecure;
+            }
+
             mCodec->initiateConfigureComponent(format);
             break;
         }
@@ -983,7 +1046,10 @@
             for (size_t i = 0; i < srcBuffers.size(); ++i) {
                 const BufferInfo &info = srcBuffers.itemAt(i);
 
-                dstBuffers->push_back(info.mData);
+                dstBuffers->push_back(
+                        (portIndex == kPortIndexInput
+                            && (mFlags & kFlagIsSecure))
+                                ? info.mEncryptedData : info.mData);
             }
 
             (new AMessage)->postReply(replyID);
@@ -1037,10 +1103,15 @@
 }
 
 void MediaCodec::setState(State newState) {
-    if (newState == UNINITIALIZED) {
+    if (newState == INITIALIZED) {
         delete mSoftRenderer;
         mSoftRenderer = NULL;
 
+        if (mCrypto != NULL) {
+            mCrypto->terminate();
+            mCrypto.clear();
+        }
+
         mNativeWindow.clear();
 
         mOutputFormat.clear();
@@ -1150,6 +1221,43 @@
         info->mData->meta()->setInt32("csd", true);
     }
 
+    if (mFlags & kFlagIsSecure) {
+        uint8_t iv[16];
+        memset(iv, 0, sizeof(iv));
+
+        ssize_t outLength;
+
+        if (mFlags & kFlagIsSoftwareCodec) {
+            outLength = mCrypto->decryptAudio(
+                    (flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL,
+                    (flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0,
+                        info->mEncryptedData->base() + offset,
+                        size,
+                        info->mData->base(),
+                        info->mData->capacity());
+        } else {
+            outLength = mCrypto->decryptVideo(
+                    (flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL,
+                    (flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0,
+                        info->mEncryptedData->base() + offset,
+                        size,
+                        info->mData->base(),
+                        0  /* offset */);
+        }
+
+        if (outLength < 0) {
+            return outLength;
+        }
+
+        if ((size_t)outLength > info->mEncryptedData->capacity()) {
+            return -ERANGE;
+        }
+
+        info->mData->setRange(0, outLength);
+    } else if (flags & BUFFER_FLAG_ENCRYPTED) {
+        return -EINVAL;
+    }
+
     reply->setBuffer("buffer", info->mData);
     reply->post();
 
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index afd4763..224ec33 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -107,6 +107,11 @@
 
         msg->setInt32("channel-count", numChannels);
         msg->setInt32("sample-rate", sampleRate);
+
+        int32_t isADTS;
+        if (meta->findInt32(kKeyIsADTS, &isADTS)) {
+            msg->setInt32("is-adts", true);
+        }
     }
 
     int32_t maxInputSize;
@@ -232,6 +237,20 @@
         msg->setBuffer("csd-1", buffer);
     }
 
+    if (meta->findData(kKeyEMM, &type, &data, &size)) {
+        sp<ABuffer> emm = new ABuffer(size);
+        memcpy(emm->data(), data, size);
+
+        msg->setBuffer("emm", emm);
+    }
+
+    if (meta->findData(kKeyECM, &type, &data, &size)) {
+        sp<ABuffer> ecm = new ABuffer(size);
+        memcpy(ecm->data(), data, size);
+
+        msg->setBuffer("ecm", ecm);
+    }
+
     *format = msg;
 
     return OK;
@@ -267,13 +286,14 @@
     info->mFinalResult = OK;
     info->mSample = NULL;
     info->mSampleTimeUs = -1ll;
-    info->mFlags = 0;
+    info->mSampleFlags = 0;
+    info->mTrackFlags = 0;
 
     const char *mime;
     CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
 
     if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
-        info->mFlags |= kIsVorbis;
+        info->mTrackFlags |= kIsVorbis;
     }
 
     return OK;
@@ -288,6 +308,7 @@
             info->mSample = NULL;
 
             info->mSampleTimeUs = -1ll;
+            info->mSampleFlags = 0;
         }
     }
 }
@@ -306,6 +327,7 @@
                 info->mSample->release();
                 info->mSample = NULL;
                 info->mSampleTimeUs = -1ll;
+                info->mSampleFlags = 0;
             }
         } else if (info->mFinalResult != OK) {
             continue;
@@ -323,11 +345,25 @@
 
                 info->mFinalResult = err;
                 info->mSampleTimeUs = -1ll;
+                info->mSampleFlags = 0;
                 continue;
             } else {
                 CHECK(info->mSample != NULL);
                 CHECK(info->mSample->meta_data()->findInt64(
                             kKeyTime, &info->mSampleTimeUs));
+
+                info->mSampleFlags = 0;
+
+                int32_t val;
+                if (info->mSample->meta_data()->findInt32(
+                            kKeyIsSyncFrame, &val) && val != 0) {
+                    info->mSampleFlags |= SAMPLE_FLAG_SYNC;
+                }
+
+                if (info->mSample->meta_data()->findInt32(
+                            kKeyScrambling, &val) && val != 0) {
+                    info->mSampleFlags |= SAMPLE_FLAG_ENCRYPTED;
+                }
             }
         }
 
@@ -371,7 +407,7 @@
 
     size_t sampleSize = info->mSample->range_length();
 
-    if (info->mFlags & kIsVorbis) {
+    if (info->mTrackFlags & kIsVorbis) {
         // Each sample's data is suffixed by the number of page samples
         // or -1 if not available.
         sampleSize += sizeof(int32_t);
@@ -387,7 +423,7 @@
 
     memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
 
-    if (info->mFlags & kIsVorbis) {
+    if (info->mTrackFlags & kIsVorbis) {
         int32_t numPageSamples;
         if (!info->mSample->meta_data()->findInt32(
                     kKeyValidSamples, &numPageSamples)) {
@@ -430,4 +466,17 @@
     return OK;
 }
 
+status_t NuMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
+    ssize_t minIndex = fetchTrackSamples();
+
+    if (minIndex < 0) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+    *sampleFlags = info->mSampleFlags;
+
+    return OK;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 8b6e9d5..9769f21 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -515,7 +515,12 @@
         CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
         CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
 
-        status_t err = setAACFormat(numChannels, sampleRate, bitRate);
+        int32_t isADTS;
+        if (!meta->findInt32(kKeyIsADTS, &isADTS)) {
+            isADTS = false;
+        }
+
+        status_t err = setAACFormat(numChannels, sampleRate, bitRate, isADTS);
         if (err != OK) {
             CODEC_LOGE("setAACFormat() failed (err = %d)", err);
             return err;
@@ -3386,11 +3391,17 @@
     }
 }
 
-status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
-    if (numChannels > 2)
+status_t OMXCodec::setAACFormat(
+        int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
+    if (numChannels > 2) {
         ALOGW("Number of channels: (%d) \n", numChannels);
+    }
 
     if (mIsEncoder) {
+        if (isADTS) {
+            return -EINVAL;
+        }
+
         //////////////// input port ////////////////////
         setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
 
@@ -3445,7 +3456,9 @@
                 &profile, sizeof(profile));
 
         if (err != OK) {
-            CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err);
+            CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
+                       "(err = %d)",
+                       err);
             return err;
         }
     } else {
@@ -3459,13 +3472,19 @@
 
         profile.nChannels = numChannels;
         profile.nSampleRate = sampleRate;
-        profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+        profile.eAACStreamFormat =
+            isADTS
+                ? OMX_AUDIO_AACStreamFormatMP4ADTS
+                : OMX_AUDIO_AACStreamFormatMP4FF;
 
         err = mOMX->setParameter(
                 mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
 
         if (err != OK) {
-            CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err);
+            CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
+                       "(err = %d)",
+                       err);
             return err;
         }
     }
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
index ea6c360..90f96c6 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -23,6 +23,7 @@
 #include "pvmp4audiodecoder_api.h"
 
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
 
 namespace android {
 
@@ -42,6 +43,7 @@
         OMX_COMPONENTTYPE **component)
     : SimpleSoftOMXComponent(name, callbacks, appData, component),
       mConfig(new tPVMP4AudioDecoderExternal),
+      mIsADTS(false),
       mDecoderBuf(NULL),
       mInputBufferCount(0),
       mUpsamplingFactor(2),
@@ -140,7 +142,12 @@
             aacParams->nAACtools = 0;
             aacParams->nAACERtools = 0;
             aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
-            aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+
+            aacParams->eAACStreamFormat =
+                mIsADTS
+                    ? OMX_AUDIO_AACStreamFormatMP4ADTS
+                    : OMX_AUDIO_AACStreamFormatMP4FF;
+
             aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
 
             if (!isConfigured()) {
@@ -215,6 +222,15 @@
                 return OMX_ErrorUndefined;
             }
 
+            if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) {
+                mIsADTS = false;
+            } else if (aacParams->eAACStreamFormat
+                        == OMX_AUDIO_AACStreamFormatMP4ADTS) {
+                mIsADTS = true;
+            } else {
+                return OMX_ErrorUndefined;
+            }
+
             return OMX_ErrorNone;
         }
 
@@ -299,8 +315,35 @@
             mNumSamplesOutput = 0;
         }
 
-        mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
-        mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+        if (mIsADTS) {
+            // skip 30 bits, aac_frame_length follows.
+            // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+
+            const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+
+            CHECK_GE(inHeader->nFilledLen, 7);
+
+            bool protectionAbsent = (adtsHeader[1] & 1);
+
+            unsigned aac_frame_length =
+                ((adtsHeader[3] & 3) << 11)
+                | (adtsHeader[4] << 3)
+                | (adtsHeader[5] >> 5);
+
+            CHECK_GE(inHeader->nFilledLen, aac_frame_length);
+
+            size_t headerSize = (protectionAbsent ? 7 : 9);
+
+            mConfig->pInputBuffer = (UChar *)adtsHeader + headerSize;
+            mConfig->inputBufferCurrentLength = aac_frame_length - headerSize;
+
+            inHeader->nOffset += headerSize;
+            inHeader->nFilledLen -= headerSize;
+        } else {
+            mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
+            mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+        }
+
         mConfig->inputBufferMaxLength = 0;
         mConfig->inputBufferUsedLength = 0;
         mConfig->remainderBits = 0;
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h
index 963fd27..da0b8ed 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.h
@@ -49,6 +49,7 @@
     };
 
     tPVMP4AudioDecoderExternal *mConfig;
+    bool mIsADTS;
     void *mDecoderBuf;
 
     size_t mInputBufferCount;
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index 61b76cf..dee786d 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -310,6 +310,16 @@
     return !strncmp(mData, prefix, strlen(prefix));
 }
 
+bool AString::endsWith(const char *suffix) const {
+    size_t suffixLen = strlen(suffix);
+
+    if (mSize < suffixLen) {
+        return false;
+    }
+
+    return !strcmp(mData + mSize - suffixLen, suffix);
+}
+
 AString StringPrintf(const char *format, ...) {
     va_list ap;
     va_start(ap, format);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index d708ba6..e1ac53c 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -117,6 +117,12 @@
 
             mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
 
+            int32_t scrambling;
+            if (buffer->meta()->findInt32("scrambling", &scrambling)
+                    && scrambling != 0) {
+                mediaBuffer->meta_data()->setInt32(kKeyScrambling, scrambling);
+            }
+
             *out = mediaBuffer;
             return OK;
         }