Merge "Send 64 bit OMX codec handling to 32 bit MediaServer"
diff --git a/cmds/screenrecord/FrameOutput.cpp b/cmds/screenrecord/FrameOutput.cpp
index 06b1f70..4da16bc 100644
--- a/cmds/screenrecord/FrameOutput.cpp
+++ b/cmds/screenrecord/FrameOutput.cpp
@@ -87,7 +87,7 @@
     return NO_ERROR;
 }
 
-status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec) {
+status_t FrameOutput::copyFrame(FILE* fp, long timeoutUsec, bool rawFrames) {
     Mutex::Autolock _l(mMutex);
     ALOGV("copyFrame %ld\n", timeoutUsec);
 
@@ -152,16 +152,20 @@
                 (endWhenNsec - pixWhenNsec) / 1000000.0);
     }
 
-    // Fill out the header.
-    size_t headerLen = sizeof(uint32_t) * 5;
     size_t rgbDataLen = width * height * kOutBytesPerPixel;
-    size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
-    uint8_t header[headerLen];
-    setValueLE(&header[0], packetLen);
-    setValueLE(&header[4], width);
-    setValueLE(&header[8], height);
-    setValueLE(&header[12], width * kOutBytesPerPixel);
-    setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
+
+    if (!rawFrames) {
+        // Fill out the header.
+        size_t headerLen = sizeof(uint32_t) * 5;
+        size_t packetLen = headerLen - sizeof(uint32_t) + rgbDataLen;
+        uint8_t header[headerLen];
+        setValueLE(&header[0], packetLen);
+        setValueLE(&header[4], width);
+        setValueLE(&header[8], height);
+        setValueLE(&header[12], width * kOutBytesPerPixel);
+        setValueLE(&header[16], HAL_PIXEL_FORMAT_RGB_888);
+        fwrite(header, 1, headerLen, fp);
+    }
 
     // Currently using buffered I/O rather than writev().  Not expecting it
     // to make much of a difference, but it might be worth a test for larger
@@ -169,7 +173,6 @@
     if (kShowTiming) {
         startWhenNsec = systemTime(CLOCK_MONOTONIC);
     }
-    fwrite(header, 1, headerLen, fp);
     fwrite(mPixelBuf, 1, rgbDataLen, fp);
     fflush(fp);
     if (kShowTiming) {
diff --git a/cmds/screenrecord/FrameOutput.h b/cmds/screenrecord/FrameOutput.h
index c1148d0..c49ec3b 100644
--- a/cmds/screenrecord/FrameOutput.h
+++ b/cmds/screenrecord/FrameOutput.h
@@ -45,7 +45,7 @@
     // specified number of microseconds.
     //
     // Returns ETIMEDOUT if the timeout expired before we found a frame.
-    status_t copyFrame(FILE* fp, long timeoutUsec);
+    status_t copyFrame(FILE* fp, long timeoutUsec, bool rawFrames);
 
     // Prepare to copy frames.  Makes the EGL context used by this object current.
     void prepareToCopy() {
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index a17fc51..02ed53a 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -65,7 +65,7 @@
 static bool gVerbose = false;           // chatty on stdout
 static bool gRotate = false;            // rotate 90 degrees
 static enum {
-    FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES
+    FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
 } gOutputFormat = FORMAT_MP4;           // data format for output
 static bool gSizeSpecified = false;     // was size explicitly requested?
 static bool gWantInfoScreen = false;    // do we want initial info screen?
@@ -563,7 +563,7 @@
     sp<MediaCodec> encoder;
     sp<FrameOutput> frameOutput;
     sp<IGraphicBufferProducer> encoderInputSurface;
-    if (gOutputFormat != FORMAT_FRAMES) {
+    if (gOutputFormat != FORMAT_FRAMES && gOutputFormat != FORMAT_RAW_FRAMES) {
         err = prepareEncoder(mainDpyInfo.fps, &encoder, &encoderInputSurface);
 
         if (err != NO_ERROR && !gSizeSpecified) {
@@ -643,7 +643,8 @@
             break;
         }
         case FORMAT_H264:
-        case FORMAT_FRAMES: {
+        case FORMAT_FRAMES:
+        case FORMAT_RAW_FRAMES: {
             rawFp = prepareRawOutput(fileName);
             if (rawFp == NULL) {
                 if (encoder != NULL) encoder->release();
@@ -656,7 +657,7 @@
             abort();
     }
 
-    if (gOutputFormat == FORMAT_FRAMES) {
+    if (gOutputFormat == FORMAT_FRAMES || gOutputFormat == FORMAT_RAW_FRAMES) {
         // TODO: if we want to make this a proper feature, we should output
         //       an outer header with version info.  Right now we never change
         //       the frame size or format, so we could conceivably just send
@@ -676,7 +677,8 @@
             // stop was requested, but this will do for now.  (It almost
             // works because wait() wakes when a signal hits, but we
             // need to handle the edge cases.)
-            err = frameOutput->copyFrame(rawFp, 250000);
+            bool rawFrames = gOutputFormat == FORMAT_RAW_FRAMES;
+            err = frameOutput->copyFrame(rawFp, 250000, rawFrames);
             if (err == ETIMEDOUT) {
                 err = NO_ERROR;
             } else if (err != NO_ERROR) {
@@ -950,6 +952,8 @@
                 gOutputFormat = FORMAT_H264;
             } else if (strcmp(optarg, "frames") == 0) {
                 gOutputFormat = FORMAT_FRAMES;
+            } else if (strcmp(optarg, "raw-frames") == 0) {
+                gOutputFormat = FORMAT_RAW_FRAMES;
             } else {
                 fprintf(stderr, "Unknown format '%s'\n", optarg);
                 return 2;
diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h
index c35c6b3..28d121c 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -29,6 +29,7 @@
 
 #include <android/native_window.h>
 
+#include "NdkMediaCrypto.h"
 #include "NdkMediaFormat.h"
 
 #ifdef __cplusplus
@@ -46,6 +47,7 @@
     uint32_t flags;
 };
 typedef struct AMediaCodecBufferInfo AMediaCodecBufferInfo;
+typedef struct AMediaCodecCryptoInfo AMediaCodecCryptoInfo;
 
 enum {
     AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM = 4,
@@ -81,8 +83,12 @@
 /**
  * Configure the codec. For decoding you would typically get the format from an extractor.
  */
-int AMediaCodec_configure(AMediaCodec*, const AMediaFormat* format,
-        ANativeWindow* surface, uint32_t flags);  // TODO: other args
+int AMediaCodec_configure(
+        AMediaCodec*,
+        const AMediaFormat* format,
+        ANativeWindow* surface,
+        AMediaCrypto *crypto,
+        uint32_t flags);
 
 /**
  * Start the codec. A codec must be configured before it can be started, and must be started
@@ -127,6 +133,12 @@
         size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags);
 
 /**
+ * Send the specified buffer to the codec for processing.
+ */
+int AMediaCodec_queueSecureInputBuffer(AMediaCodec*,
+        size_t idx, off_t offset, AMediaCodecCryptoInfo*, uint64_t time, uint32_t flags);
+
+/**
  * Get the index of the next available buffer of processed data.
  */
 ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec*, AMediaCodecBufferInfo *info, int64_t timeoutUs);
@@ -138,7 +150,6 @@
 int AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
 
 
-
 typedef void (*OnCodecEvent)(AMediaCodec *codec, void *userdata);
 
 /**
@@ -150,6 +161,36 @@
 int AMediaCodec_setNotificationCallback(AMediaCodec*, OnCodecEvent callback, void *userdata);
 
 
+enum {
+    AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
+    AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1
+};
+
+/**
+ * create an AMediaCodecCryptoInfo from scratch. Use this if you need to use custom
+ * crypto info, rather than one obtained from AMediaExtractor.
+ */
+AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
+        int numsubsamples,
+        uint8_t key[16],
+        uint8_t iv[16],
+        uint32_t mode,
+        size_t *clearbytes,
+        size_t *encryptedbytes);
+
+/**
+ * delete an AMediaCodecCryptoInfo created previously with AMediaCodecCryptoInfo_new, or
+ * obtained from AMediaExtractor
+ */
+int AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo*);
+
+size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo*);
+int AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo*, uint8_t *dst);
+int AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo*, uint8_t *dst);
+uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo*);
+int AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo*, size_t *dst);
+int AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo*, size_t *dst);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/include/ndk/NdkMediaCrypto.h b/include/ndk/NdkMediaCrypto.h
new file mode 100644
index 0000000..83eaad2
--- /dev/null
+++ b/include/ndk/NdkMediaCrypto.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_CRYPTO_H
+#define _NDK_MEDIA_CRYPTO_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AMediaCrypto;
+typedef struct AMediaCrypto AMediaCrypto;
+
+typedef uint8_t AMediaUUID[16];
+
+bool AMediaCrypto_isCryptoSchemeSupported(const AMediaUUID uuid);
+
+bool AMediaCrypto_requiresSecureDecoderComponent(const char *mime);
+
+AMediaCrypto* AMediaCrypto_new(const AMediaUUID uuid, const void *initData, size_t initDataSize);
+
+void AMediaCrypto_delete(AMediaCrypto* crypto);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _NDK_MEDIA_CRYPTO_H
diff --git a/include/ndk/NdkMediaDrm.h b/include/ndk/NdkMediaDrm.h
new file mode 100644
index 0000000..1322a9d
--- /dev/null
+++ b/include/ndk/NdkMediaDrm.h
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_DRM_H
+#define _NDK_MEDIA_DRM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+struct AMediaDrm;
+typedef struct AMediaDrm AMediaDrm;
+
+typedef struct {
+    const uint8_t *ptr;
+    size_t length;
+} AMediaDrmByteArray;
+
+typedef AMediaDrmByteArray AMediaDrmSessionId;
+typedef AMediaDrmByteArray AMediaDrmScope;
+typedef AMediaDrmByteArray AMediaDrmKeySetId;
+typedef AMediaDrmByteArray AMediaDrmSecureStop;
+
+#define MEDIADRM_ERROR_BASE -2000
+
+typedef enum {
+    MEDIADRM_OK = 0,
+    MEDIADRM_NOT_PROVISIONED_ERROR    = MEDIADRM_ERROR_BASE - 1,
+    MEDIADRM_RESOURCE_BUSY_ERROR      = MEDIADRM_ERROR_BASE - 2,
+    MEDIADRM_DEVICE_REVOKED_ERROR     = MEDIADRM_ERROR_BASE - 3,
+    MEDIADRM_SHORT_BUFFER             = MEDIADRM_ERROR_BASE - 4,
+    MEDIADRM_INVALID_OBJECT_ERROR     = MEDIADRM_ERROR_BASE - 5,
+    MEDIADRM_INVALID_PARAMETER_ERROR  = MEDIADRM_ERROR_BASE - 6,
+    MEDIADRM_SESSION_NOT_OPENED_ERROR = MEDIADRM_ERROR_BASE - 7,
+    MEDIADRM_TAMPER_DETECTED_ERROR    = MEDIADRM_ERROR_BASE - 8,
+    MEDIADRM_VERIFY_FAILED            = MEDIADRM_ERROR_BASE - 9,
+    MEDIADRM_NEED_KEY_ERROR           = MEDIADRM_ERROR_BASE - 10,
+    MEDIADRM_LICENSE_EXPIRED_ERROR    = MEDIADRM_ERROR_BASE - 11,
+    MEDIADRM_UNKNOWN_ERROR            = MEDIADRM_ERROR_BASE - 12,
+} mediadrm_status_t;
+
+typedef enum AMediaDrmEventType {
+    /**
+     * This event type indicates that the app needs to request a certificate from
+     * the provisioning server.  The request message data is obtained using
+     * AMediaDrm_getProvisionRequest.
+     */
+    EVENT_PROVISION_REQUIRED = 1,
+
+    /**
+     * This event type indicates that the app needs to request keys from a license
+     * server.  The request message data is obtained using AMediaDrm_getKeyRequest.
+     */
+    EVENT_KEY_REQUIRED = 2,
+
+    /**
+     * This event type indicates that the licensed usage duration for keys in a session
+     * has expired.  The keys are no longer valid.
+     */
+    EVENT_KEY_EXPIRED = 3,
+
+    /**
+     * This event may indicate some specific vendor-defined condition, see your
+     * DRM provider documentation for details
+     */
+    EVENT_VENDOR_DEFINED = 4
+} AMediaDrmEventType;
+
+typedef void (*AMediaDrmEventListener)(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+        AMediaDrmEventType eventType, int extra, const uint8_t *data, size_t dataSize);
+
+
+/**
+ * Query if the given scheme identified by its UUID is supported on this device, and
+ * whether the drm plugin is able to handle the media container format specified by mimeType.
+ *
+ * uuid identifies the universal unique ID of the crypto scheme. uuid must be 16 bytes.
+ * mimeType is the MIME type of the media container, e.g. "video/mp4".  If mimeType
+ * is not known or required, it can be provided as NULL.
+ */
+bool AMediaDrm_isCryptoSchemeSupported(const uint8_t *uuid, const char *mimeType);
+
+/**
+ * Create a MediaDrm instance from a UUID
+ * uuid identifies the universal unique ID of the crypto scheme. uuid must be 16 bytes.
+ */
+AMediaDrm* AMediaDrm_createByUUID(const uint8_t *uuid);
+
+/**
+ * Release a MediaDrm object
+ */
+void AMediaDrm_release(AMediaDrm *);
+
+/**
+ * Register a callback to be invoked when an event occurs
+ *
+ * listener is the callback that will be invoked on event
+ */
+void AMediaDrm_setOnEventListener(AMediaDrm *, AMediaDrmEventListener listener);
+
+/**
+ * Open a new session with the MediaDrm object.  A session ID is returned.
+ *
+ * returns MEDIADRM_NOT_PROVISIONED_ERROR if provisioning is needed
+ * returns MEDIADRM_RESOURCE_BUSY_ERROR if required resources are in use
+ */
+mediadrm_status_t AMediaDrm_openSession(AMediaDrm *, AMediaDrmSessionId &sessionId);
+
+/**
+ * Close a session on the MediaDrm object that was previously opened
+ * with AMediaDrm_openSession.
+ */
+mediadrm_status_t AMediaDrm_closeSession(AMediaDrm *, const AMediaDrmSessionId &sessionId);
+
+typedef enum AMediaDrmKeyType {
+    /**
+     * This key request type species that the keys will be for online use, they will
+     * not be saved to the device for subsequent use when the device is not connected
+     * to a network.
+     */
+    KEY_TYPE_STREAMING = 1,
+
+    /**
+     * This key request type specifies that the keys will be for offline use, they
+     * will be saved to the device for use when the device is not connected to a network.
+     */
+    KEY_TYPE_OFFLINE = 2,
+
+    /**
+     * This key request type specifies that previously saved offline keys should be released.
+     */
+    KEY_TYPE_RELEASE = 3
+} AMediaDrmKeyType;
+
+/**
+ *  Data type containing {key, value} pair
+ */
+typedef struct AMediaDrmKeyValuePair {
+    const char *mKey;
+    const char *mValue;
+} AMediaDrmKeyValue;
+
+/**
+ * A key request/response exchange occurs between the app and a license server
+ * to obtain or release keys used to decrypt encrypted content.
+ * AMediaDrm_getKeyRequest is used to obtain an opaque key request byte array that
+ * is delivered to the license server.  The opaque key request byte array is
+ * returned in KeyRequest.data.  The recommended URL to deliver the key request to
+ * is returned in KeyRequest.defaultUrl.
+ *
+ * After the app has received the key request response from the server,
+ * it should deliver to the response to the DRM engine plugin using the method
+ * AMediaDrm_provideKeyResponse.
+ *
+ * scope may be a sessionId or a keySetId, depending on the specified keyType.
+ * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, scope should be set
+ * to the sessionId the keys will be provided to.  When the keyType is
+ * KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys being released.
+ * Releasing keys from a device invalidates them for all sessions.
+ *
+ * init container-specific data, its meaning is interpreted based on the mime type
+ * provided in the mimeType parameter.  It could contain, for example, the content
+ * ID, key ID or other data obtained from the content metadata that is required in
+ * generating the key request. init may be null when keyType is KEY_TYPE_RELEASE.
+ *
+ * initSize is the number of bytes of initData
+ *
+ * mimeType identifies the mime type of the content.
+ *
+ * keyType specifes the type of the request. The request may be to acquire keys for
+ *   streaming or offline content, or to release previously acquired keys, which are
+ *   identified by a keySetId.
+ *
+ * optionalParameters are included in the key request message to allow a client
+ *   application to provide additional message parameters to the server.
+ *
+ * numOptionalParameters indicates the number of optional parameters provided
+ *   by the caller
+ *
+ * On exit:
+ *   1. The keyRequest pointer will reference the opaque key request data.  It
+ *       will reside in memory owned by the AMediaDrm object, and will remain
+ *       accessible until the next call to AMediaDrm_getKeyRequest or until the
+ *       MediaDrm object is released.
+ *   2. keyRequestSize will be set to the size of the request
+ *
+ * returns MEDIADRM_NOT_PROVISIONED_ERROR if reprovisioning is needed, due to a
+ * problem with the device certificate.
+*/
+mediadrm_status_t AMediaDrm_getKeyRequest(AMediaDrm *, const AMediaDrmScope &scope,
+        const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
+        const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
+        const uint8_t *&keyRequest, size_t &keyRequestSize);
+
+/**
+ * A key response is received from the license server by the app, then it is
+ * provided to the DRM engine plugin using provideKeyResponse.  When the
+ * response is for an offline key request, a keySetId is returned that can be
+ * used to later restore the keys to a new session with AMediaDrm_restoreKeys.
+ * When the response is for a streaming or release request, a null keySetId is
+ * returned.
+ *
+ * scope may be a sessionId or keySetId depending on the type of the
+ * response.  Scope should be set to the sessionId when the response is for either
+ * streaming or offline key requests.  Scope should be set to the keySetId when
+ * the response is for a release request.
+ *
+ * response points to the opaque response from the server
+ * responseSize should be set to the size of the response in bytes
+ */
+
+mediadrm_status_t AMediaDrm_provideKeyResponse(AMediaDrm *, const AMediaDrmScope &scope,
+        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId &keySetId);
+
+/**
+ * Restore persisted offline keys into a new session.  keySetId identifies the
+ * keys to load, obtained from a prior call to AMediaDrm_provideKeyResponse.
+ *
+ * sessionId is the session ID for the DRM session
+ * keySetId identifies the saved key set to restore
+ */
+mediadrm_status_t AMediaDrm_restoreKeys(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+        const AMediaDrmKeySetId &keySetId);
+
+/**
+ * Remove the current keys from a session.
+ *
+ * keySetId identifies keys to remove
+ */
+mediadrm_status_t AMediaDrm_removeKeys(AMediaDrm *, const AMediaDrmSessionId &keySetId);
+
+/**
+ * Request an informative description of the key status for the session.  The status is
+ * in the form of {key, value} pairs.  Since DRM license policies vary by vendor,
+ * the specific status field names are determined by each DRM vendor.  Refer to your
+ * DRM provider documentation for definitions of the field names for a particular
+ * DRM engine plugin.
+ *
+ * On entry, numPairs should be set by the caller to the maximum number of pairs
+ * that can be returned (the size of the array).  On exit, numPairs will be set
+ * to the number of entries written to the array.  If the number of {key, value} pairs
+ * to be returned is greater than *numPairs, MEDIADRM_SHORT_BUFFER will be returned
+ * and numPairs will be set to the number of pairs available.
+ */
+mediadrm_status_t AMediaDrm_queryKeyStatus(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+        AMediaDrmKeyValue *keyValuePairs, size_t &numPairs);
+
+
+/**
+ * A provision request/response exchange occurs between the app and a provisioning
+ * server to retrieve a device certificate.  If provisionining is required, the
+ * EVENT_PROVISION_REQUIRED event will be sent to the event handler.
+ * getProvisionRequest is used to obtain the opaque provision request byte array that
+ * should be delivered to the provisioning server.
+ * On exit:
+ *    1. The provision request data will be referenced by provisionRequest, in
+ *        memory owned by the AMediaDrm object.  It will remain accessible until the
+ *        next call to getProvisionRequest.
+ *    2. provisionRequestSize will be set to the size of the request data.
+ *    3. serverUrl will reference a NULL terminated string containing the URL
+ *       the provisioning request should be sent to.  It will remain accessible until
+ *       the next call to getProvisionRequest.
+ */
+mediadrm_status_t AMediaDrm_getProvisionRequest(AMediaDrm *, const uint8_t *&provisionRequest,
+        size_t &provisionRequestSize, const char *&serverUrl);
+
+
+/**
+ * After a provision response is received by the app, it is provided to the DRM
+ * engine plugin using this method.
+ *
+ * response is the opaque provisioning response byte array to provide to the
+ *   DRM engine plugin.
+ * responseSize is the length of the provisioning response in bytes.
+ *
+ * returns MEDIADRM_DEVICE_REVOKED_ERROR if the response indicates that the
+ * server rejected the request
+ */
+mediadrm_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *,
+        const uint8_t *response, size_t responseSize);
+
+
+/**
+ * A means of enforcing limits on the number of concurrent streams per subscriber
+ * across devices is provided via SecureStop. This is achieved by securely
+ * monitoring the lifetime of sessions.
+ *
+ * Information from the server related to the current playback session is written
+ * to persistent storage on the device when each MediaCrypto object is created.
+ *
+ * In the normal case, playback will be completed, the session destroyed and the
+ * Secure Stops will be queried. The app queries secure stops and forwards the
+ * secure stop message to the server which verifies the signature and notifies the
+ * server side database that the session destruction has been confirmed. The persisted
+ * record on the client is only removed after positive confirmation that the server
+ * received the message using releaseSecureStops().
+ *
+ * numSecureStops is set by the caller to the maximum number of secure stops to
+ * return.  On exit, *numSecureStops will be set to the number actually returned.
+ * If *numSecureStops is too small for the number of secure stops available,
+ * MEDIADRM_SHORT_BUFFER will be returned and *numSecureStops will be set to the
+ * number required.
+ */
+mediadrm_status_t AMediaDrm_getSecureStops(AMediaDrm *,
+        AMediaDrmSecureStop *secureStops, size_t &numSecureStops);
+
+/**
+ * Process the SecureStop server response message ssRelease.  After authenticating
+ * the message, remove the SecureStops identified in the response.
+ *
+ * ssRelease is the server response indicating which secure stops to release
+ */
+mediadrm_status_t AMediaDrm_releaseSecureStops(AMediaDrm *,
+        const AMediaDrmSecureStop &ssRelease);
+
+/**
+ * String property name: identifies the maker of the DRM engine plugin
+ */
+const char *PROPERTY_VENDOR = "vendor";
+
+/**
+ * String property name: identifies the version of the DRM engine plugin
+ */
+const char *PROPERTY_VERSION = "version";
+
+/**
+ * String property name: describes the DRM engine plugin
+ */
+const char *PROPERTY_DESCRIPTION = "description";
+
+/**
+ * String property name: a comma-separated list of cipher and mac algorithms
+ * supported by CryptoSession.  The list may be empty if the DRM engine
+ * plugin does not support CryptoSession operations.
+ */
+const char *PROPERTY_ALGORITHMS = "algorithms";
+
+/**
+ * Read a DRM engine plugin String property value, given the property name string.
+ *
+ * propertyName identifies the property to query
+ * On return, propertyValue will be set to point to the property value.  The
+ * memory that the value resides in is owned by the NDK MediaDrm API and
+ * will remain valid until the next call to AMediaDrm_getPropertyString.
+ */
+mediadrm_status_t AMediaDrm_getPropertyString(AMediaDrm *, const char *propertyName,
+        const char *&propertyValue);
+
+/**
+ * Byte array property name: the device unique identifier is established during
+ * device provisioning and provides a means of uniquely identifying each device.
+ */
+const char *PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
+
+/**
+ * Read a DRM engine plugin byte array property value, given the property name string.
+ * On return, *propertyValue will be set to point to the property value.  The
+ * memory that the value resides in is owned by the NDK MediaDrm API and
+ * will remain valid until the next call to AMediaDrm_getPropertyByteArray.
+ */
+mediadrm_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *, const char *propertyName,
+        AMediaDrmByteArray &propertyValue);
+
+/**
+ * Set a DRM engine plugin String property value.
+ */
+mediadrm_status_t AMediaDrm_setPropertyString(AMediaDrm *, const char *propertyName,
+        const char *value);
+
+/**
+ * Set a DRM engine plugin byte array property value.
+ */
+mediadrm_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *, const char *propertyName,
+        const uint8_t *value, size_t valueSize);
+
+/**
+ * In addition to supporting decryption of DASH Common Encrypted Media, the
+ * MediaDrm APIs provide the ability to securely deliver session keys from
+ * an operator's session key server to a client device, based on the factory-installed
+ * root of trust, and then perform encrypt, decrypt, sign and verify operations
+ * with the session key on arbitrary user data.
+ *
+ * Operators create session key servers that receive session key requests and provide
+ * encrypted session keys which can be used for general purpose crypto operations.
+ *
+ * Generic encrypt/decrypt/sign/verify methods are based on the established session
+ * keys.  These keys are exchanged using the getKeyRequest/provideKeyResponse methods.
+ *
+ * Applications of this capability include securing various types of purchased or
+ * private content, such as applications, books and other media, photos or media
+ * delivery protocols.
+ */
+
+/*
+ * Encrypt the data referenced by input of length dataSize using algorithm specified
+ * by cipherAlgorithm, and write the encrypted result into output.  The caller must
+ * ensure that the output buffer is large enough to accept dataSize bytes. The key
+ * to use is identified by the 16 byte keyId.  The key must have been loaded into
+ * the session using provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_encrypt(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+        const uint8_t *input, uint8_t *output, size_t dataSize);
+
+/*
+ * Decrypt the data referenced by input of length dataSize using algorithm specified
+ * by cipherAlgorithm, and write the decrypted result into output.  The caller must
+ * ensure that the output buffer is large enough to accept dataSize bytes.  The key
+ * to use is identified by the 16 byte keyId.  The key must have been loaded into
+ * the session using provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_decrypt(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+        const uint8_t *input, uint8_t *output, size_t dataSize);
+
+/*
+ * Generate a signature using the specified macAlgorithm over the message data
+ * referenced by message of size messageSize and store the signature in the
+ * buffer referenced signature of max size *signatureSize.  If the buffer is not
+ * large enough to hold the signature, MEDIADRM_SHORT_BUFFER is returned and
+ * *signatureSize is set to the buffer size required.  The key to use is identified
+ * by the 16 byte keyId.  The key must have been loaded into the session using
+ * provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_sign(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+        const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
+        uint8_t *signature, size_t *signatureSize);
+
+/*
+ * Perform a signature verification using the specified macAlgorithm over the message
+ * data referenced by the message parameter of size messageSize. Returns MEDIADRM_OK
+ * if the signature matches, otherwise MEDAIDRM_VERIFY_FAILED is returned. The key to
+ * use is identified by the 16 byte keyId.  The key must have been loaded into the
+ * session using provideKeyResponse.
+ */
+mediadrm_status_t AMediaDrm_verify(AMediaDrm *, const AMediaDrmSessionId &sessionId,
+        const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
+        const uint8_t *signature, size_t signatureSize);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //_NDK_MEDIA_DRM_H
diff --git a/include/ndk/NdkMediaError.h b/include/ndk/NdkMediaError.h
new file mode 100644
index 0000000..b89a10e
--- /dev/null
+++ b/include/ndk/NdkMediaError.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_ERROR_H
+#define _NDK_MEDIA_ERROR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+    AMEDIAERROR_BASE = -10000,
+
+    AMEDIAERROR_GENERIC     = AMEDIAERROR_BASE,
+    AMEDIAERROR_MALFORMED   = AMEDIAERROR_BASE - 1,
+    AMEDIAERROR_UNSUPPORTED = AMEDIAERROR_BASE - 2
+};
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _NDK_MEDIA_ERROR_H
diff --git a/include/ndk/NdkMediaExtractor.h b/include/ndk/NdkMediaExtractor.h
index a7c32c4..9e50ec0 100644
--- a/include/ndk/NdkMediaExtractor.h
+++ b/include/ndk/NdkMediaExtractor.h
@@ -30,7 +30,9 @@
 
 #include <sys/types.h>
 
+#include "NdkMediaCodec.h"
 #include "NdkMediaFormat.h"
+#include "NdkMediaCrypto.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -112,6 +114,33 @@
  */
 bool AMediaExtractor_advance(AMediaExtractor*);
 
+
+/**
+ * mapping of crypto scheme uuid to the scheme specific data for that scheme
+ */
+typedef struct PsshEntry {
+    AMediaUUID uuid;
+    size_t datalen;
+    void *data;
+} PsshEntry;
+
+/**
+ * list of crypto schemes and their data
+ */
+typedef struct PsshInfo {
+    size_t numentries;
+    PsshEntry entries[0];
+} PsshInfo;
+
+/**
+ * Get the PSSH info if present.
+ */
+PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor*);
+
+
+AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *);
+
+
 enum {
     AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC = 1,
     AMEDIAEXTRACTOR_SAMPLE_FLAG_ENCRYPTED = 2,
diff --git a/media/ndk/Android.mk b/media/ndk/Android.mk
index b8dd19e..8f795cd 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -22,9 +22,11 @@
 
 LOCAL_SRC_FILES:=                                       \
                   NdkMediaCodec.cpp                     \
+                  NdkMediaCrypto.cpp                    \
                   NdkMediaExtractor.cpp                 \
                   NdkMediaFormat.cpp                    \
                   NdkMediaMuxer.cpp                     \
+                  NdkMediaDrm.cpp                       \
 
 LOCAL_MODULE:= libmediandk
 
@@ -33,13 +35,17 @@
     frameworks/base/core/jni \
     frameworks/av/include/ndk
 
+LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
+
 LOCAL_SHARED_LIBRARIES := \
+    libbinder \
     libmedia \
     libstagefright \
     libstagefright_foundation \
     liblog \
     libutils \
     libandroid_runtime \
+    libbinder \
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 1789f75..a7c06d5 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -18,13 +18,14 @@
 #define LOG_TAG "NdkMediaCodec"
 
 #include "NdkMediaCodec.h"
+#include "NdkMediaError.h"
+#include "NdkMediaCryptoPriv.h"
 #include "NdkMediaFormatPriv.h"
 
 #include <utils/Log.h>
 #include <utils/StrongPointer.h>
 #include <gui/Surface.h>
 
-#include <media/ICrypto.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -42,7 +43,7 @@
         return AMEDIACODEC_INFO_TRY_AGAIN_LATER;
     }
     ALOGE("sf error code: %d", err);
-    return -1000;
+    return AMEDIAERROR_GENERIC;
 }
 
 enum {
@@ -158,19 +159,22 @@
     return mData;
 }
 
-
+EXPORT
 AMediaCodec* AMediaCodec_createCodecByName(const char *name) {
     return createAMediaCodec(name, false, false);
 }
 
+EXPORT
 AMediaCodec* AMediaCodec_createDecoderByType(const char *mime_type) {
     return createAMediaCodec(mime_type, true, false);
 }
 
+EXPORT
 AMediaCodec* AMediaCodec_createEncoderByType(const char *name) {
     return createAMediaCodec(name, true, true);
 }
 
+EXPORT
 int AMediaCodec_delete(AMediaCodec *mData) {
     if (mData->mCodec != NULL) {
         mData->mCodec->release();
@@ -186,8 +190,13 @@
     return OK;
 }
 
+EXPORT
 int AMediaCodec_configure(
-        AMediaCodec *mData, const AMediaFormat* format, ANativeWindow* window, uint32_t flags) {
+        AMediaCodec *mData,
+        const AMediaFormat* format,
+        ANativeWindow* window,
+        AMediaCrypto *crypto,
+        uint32_t flags) {
     sp<AMessage> nativeFormat;
     AMediaFormat_getFormat(format, &nativeFormat);
     ALOGV("configure with format: %s", nativeFormat->debugString(0).c_str());
@@ -196,9 +205,11 @@
         surface = (Surface*) window;
     }
 
-    return translate_error(mData->mCodec->configure(nativeFormat, surface, NULL, flags));
+    return translate_error(mData->mCodec->configure(nativeFormat, surface,
+            crypto ? crypto->mCrypto : NULL, flags));
 }
 
+EXPORT
 int AMediaCodec_start(AMediaCodec *mData) {
     status_t ret =  mData->mCodec->start();
     if (ret != OK) {
@@ -210,6 +221,7 @@
     return OK;
 }
 
+EXPORT
 int AMediaCodec_stop(AMediaCodec *mData) {
     int ret = translate_error(mData->mCodec->stop());
 
@@ -221,10 +233,12 @@
     return ret;
 }
 
+EXPORT
 int AMediaCodec_flush(AMediaCodec *mData) {
     return translate_error(mData->mCodec->flush());
 }
 
+EXPORT
 ssize_t AMediaCodec_dequeueInputBuffer(AMediaCodec *mData, int64_t timeoutUs) {
     size_t idx;
     status_t ret = mData->mCodec->dequeueInputBuffer(&idx, timeoutUs);
@@ -235,6 +249,7 @@
     return translate_error(ret);
 }
 
+EXPORT
 uint8_t* AMediaCodec_getInputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
     android::Vector<android::sp<android::ABuffer> > abufs;
     if (mData->mCodec->getInputBuffers(&abufs) == 0) {
@@ -252,6 +267,7 @@
     return NULL;
 }
 
+EXPORT
 uint8_t* AMediaCodec_getOutputBuffer(AMediaCodec *mData, size_t idx, size_t *out_size) {
     android::Vector<android::sp<android::ABuffer> > abufs;
     if (mData->mCodec->getOutputBuffers(&abufs) == 0) {
@@ -269,6 +285,7 @@
     return NULL;
 }
 
+EXPORT
 int AMediaCodec_queueInputBuffer(AMediaCodec *mData,
         size_t idx, off_t offset, size_t size, uint64_t time, uint32_t flags) {
 
@@ -277,6 +294,7 @@
     return translate_error(ret);
 }
 
+EXPORT
 ssize_t AMediaCodec_dequeueOutputBuffer(AMediaCodec *mData,
         AMediaCodecBufferInfo *info, int64_t timeoutUs) {
     size_t idx;
@@ -306,12 +324,14 @@
     return translate_error(ret);
 }
 
+EXPORT
 AMediaFormat* AMediaCodec_getOutputFormat(AMediaCodec *mData) {
     sp<AMessage> format;
     mData->mCodec->getOutputFormat(&format);
     return AMediaFormat_fromMsg(&format);
 }
 
+EXPORT
 int AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
     if (render) {
         return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
@@ -320,12 +340,143 @@
     }
 }
 
+EXPORT
 int AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback, void *userdata) {
     mData->mCallback = callback;
     mData->mCallbackUserData = userdata;
     return OK;
 }
 
+typedef struct AMediaCodecCryptoInfo {
+        int numsubsamples;
+        uint8_t key[16];
+        uint8_t iv[16];
+        uint32_t mode;
+        size_t *clearbytes;
+        size_t *encryptedbytes;
+} AMediaCodecCryptoInfo;
+
+EXPORT
+int AMediaCodec_queueSecureInputBuffer(
+        AMediaCodec* codec,
+        size_t idx,
+        off_t offset,
+        AMediaCodecCryptoInfo* crypto,
+        uint64_t time,
+        uint32_t flags) {
+
+    CryptoPlugin::SubSample *subSamples = new CryptoPlugin::SubSample[crypto->numsubsamples];
+    for (int i = 0; i < crypto->numsubsamples; i++) {
+        subSamples[i].mNumBytesOfClearData = crypto->clearbytes[i];
+        subSamples[i].mNumBytesOfEncryptedData = crypto->encryptedbytes[i];
+    }
+
+    AString errormsg;
+    status_t err  = codec->mCodec->queueSecureInputBuffer(idx,
+            offset,
+            subSamples,
+            crypto->numsubsamples,
+            crypto->key,
+            crypto->iv,
+            (CryptoPlugin::Mode) crypto->mode,
+            time,
+            flags,
+            &errormsg);
+    if (err != 0) {
+        ALOGE("queSecureInputBuffer: %s", errormsg.c_str());
+    }
+    delete [] subSamples;
+    return translate_error(err);
+}
+
+
+
+EXPORT
+AMediaCodecCryptoInfo *AMediaCodecCryptoInfo_new(
+        int numsubsamples,
+        uint8_t key[16],
+        uint8_t iv[16],
+        uint32_t mode,
+        size_t *clearbytes,
+        size_t *encryptedbytes) {
+
+    // size needed to store all the crypto data
+    size_t cryptosize = sizeof(AMediaCodecCryptoInfo) + sizeof(size_t) * numsubsamples * 2;
+    AMediaCodecCryptoInfo *ret = (AMediaCodecCryptoInfo*) malloc(cryptosize);
+    if (!ret) {
+        ALOGE("couldn't allocate %d bytes", cryptosize);
+        return NULL;
+    }
+    ret->numsubsamples = numsubsamples;
+    memcpy(ret->key, key, 16);
+    memcpy(ret->iv, iv, 16);
+    ret->mode = mode;
+
+    // clearbytes and encryptedbytes point at the actual data, which follows
+    ret->clearbytes = (size_t*) (ret + 1); // point immediately after the struct
+    ret->encryptedbytes = ret->clearbytes + numsubsamples; // point after the clear sizes
+
+    memcpy(ret->clearbytes, clearbytes, numsubsamples * sizeof(size_t));
+    memcpy(ret->encryptedbytes, encryptedbytes, numsubsamples * sizeof(size_t));
+
+    return ret;
+}
+
+
+EXPORT
+int AMediaCodecCryptoInfo_delete(AMediaCodecCryptoInfo* info) {
+    free(info);
+    return OK;
+}
+
+EXPORT
+size_t AMediaCodecCryptoInfo_getNumSubSamples(AMediaCodecCryptoInfo* ci) {
+    return ci->numsubsamples;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getKey(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
+    if (!dst || !ci) {
+        return AMEDIAERROR_UNSUPPORTED;
+    }
+    memcpy(dst, ci->key, 16);
+    return OK;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getIV(AMediaCodecCryptoInfo* ci, uint8_t *dst) {
+    if (!dst || !ci) {
+        return AMEDIAERROR_UNSUPPORTED;
+    }
+    memcpy(dst, ci->iv, 16);
+    return OK;
+}
+
+EXPORT
+uint32_t AMediaCodecCryptoInfo_getMode(AMediaCodecCryptoInfo* ci) {
+    if (!ci) {
+        return AMEDIAERROR_UNSUPPORTED;
+    }
+    return ci->mode;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getClearBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
+    if (!dst || !ci) {
+        return AMEDIAERROR_UNSUPPORTED;
+    }
+    memcpy(dst, ci->clearbytes, sizeof(size_t) * ci->numsubsamples);
+    return OK;
+}
+
+EXPORT
+int AMediaCodecCryptoInfo_getEncryptedBytes(AMediaCodecCryptoInfo* ci, size_t *dst) {
+    if (!dst || !ci) {
+        return AMEDIAERROR_UNSUPPORTED;
+    }
+    memcpy(dst, ci->encryptedbytes, sizeof(size_t) * ci->numsubsamples);
+    return OK;
+}
 
 } // extern "C"
 
diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp
new file mode 100644
index 0000000..c686273
--- /dev/null
+++ b/media/ndk/NdkMediaCrypto.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 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 "NdkMediaCrypto"
+
+
+#include "NdkMediaCrypto.h"
+#include "NdkMediaCodec.h"
+#include "NdkMediaFormatPriv.h"
+
+
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <binder/IServiceManager.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_util_Binder.h>
+
+#include <jni.h>
+
+using namespace android;
+
+static int translate_error(status_t err) {
+    if (err == OK) {
+        return OK;
+    }
+    ALOGE("sf error code: %d", err);
+    return -1000;
+}
+
+
+static sp<ICrypto> makeCrypto() {
+    sp<IServiceManager> sm = defaultServiceManager();
+
+    sp<IBinder> binder =
+        sm->getService(String16("media.player"));
+
+    sp<IMediaPlayerService> service =
+        interface_cast<IMediaPlayerService>(binder);
+
+    if (service == NULL) {
+        return NULL;
+    }
+
+    sp<ICrypto> crypto = service->makeCrypto();
+
+    if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) {
+        return NULL;
+    }
+
+    return crypto;
+}
+
+struct AMediaCrypto {
+    sp<ICrypto> mCrypto;
+};
+
+
+extern "C" {
+
+
+EXPORT
+bool AMediaCrypto_isCryptoSchemeSupported(const AMediaUUID uuid) {
+    sp<ICrypto> crypto = makeCrypto();
+    if (crypto == NULL) {
+        return false;
+    }
+    return crypto->isCryptoSchemeSupported(uuid);
+}
+
+EXPORT
+bool AMediaCrypto_requiresSecureDecoderComponent(const char *mime) {
+    sp<ICrypto> crypto = makeCrypto();
+    if (crypto == NULL) {
+        return false;
+    }
+    return crypto->requiresSecureDecoderComponent(mime);
+}
+
+EXPORT
+AMediaCrypto* AMediaCrypto_new(const AMediaUUID uuid, const void *data, size_t datasize) {
+
+    sp<ICrypto> tmp = makeCrypto();
+    if (tmp == NULL) {
+        return NULL;
+    }
+
+    if (tmp->createPlugin(uuid, data, datasize) != 0) {
+        return NULL;
+    }
+
+    AMediaCrypto *crypto = new AMediaCrypto();
+    crypto->mCrypto = tmp;
+
+    return crypto;
+}
+
+EXPORT
+void AMediaCrypto_delete(AMediaCrypto* crypto) {
+    delete crypto;
+}
+
+
+
+} // extern "C"
+
diff --git a/media/ndk/NdkMediaCryptoPriv.h b/media/ndk/NdkMediaCryptoPriv.h
new file mode 100644
index 0000000..14ea928
--- /dev/null
+++ b/media/ndk/NdkMediaCryptoPriv.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+
+/*
+ * This file defines an NDK API.
+ * Do not remove methods.
+ * Do not change method signatures.
+ * Do not change the value of constants.
+ * Do not change the size of any of the classes defined in here.
+ * Do not reference types that are not part of the NDK.
+ * Do not #include files that aren't part of the NDK.
+ */
+
+#ifndef _NDK_MEDIA_CRYPTO_PRIV_H
+#define _NDK_MEDIA_CRYPTO_PRIV_H
+
+#include <sys/types.h>
+#include <utils/StrongPointer.h>
+#include <media/ICrypto.h>
+
+using namespace android;
+
+struct AMediaCrypto {
+    sp<ICrypto> mCrypto;
+};
+
+#endif // _NDK_MEDIA_CRYPTO_PRIV_H
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
new file mode 100644
index 0000000..5e50418
--- /dev/null
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2014 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 "NdkMediaDrm"
+
+#include "NdkMediaDrm.h"
+
+#include <utils/Log.h>
+#include <utils/StrongPointer.h>
+#include <gui/Surface.h>
+
+#include <media/IDrm.h>
+#include <media/IDrmClient.h>
+#include <media/stagefright/MediaErrors.h>
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <ndk/NdkMediaCrypto.h>
+
+
+using namespace android;
+
+typedef Vector<uint8_t> idvec_t;
+
+struct AMediaDrm {
+    sp<IDrm> mDrm;
+    sp<IDrmClient> mDrmClient;
+    AMediaDrmEventListener mListener;
+    List<idvec_t> mIds;
+    KeyedVector<String8, String8> mQueryResults;
+    Vector<uint8_t> mKeyRequest;
+    Vector<uint8_t> mProvisionRequest;
+    String8 mProvisionUrl;
+    String8 mPropertyString;
+    Vector<uint8_t> mPropertyByteArray;
+    List<Vector<uint8_t> > mSecureStops;
+};
+
+extern "C" {
+
+static mediadrm_status_t translateStatus(status_t status) {
+    mediadrm_status_t result = MEDIADRM_UNKNOWN_ERROR;
+    switch (status) {
+        case OK:
+            result = MEDIADRM_OK;
+            break;
+        case android::ERROR_DRM_NOT_PROVISIONED:
+            result = MEDIADRM_NOT_PROVISIONED_ERROR;
+            break;
+        case android::ERROR_DRM_RESOURCE_BUSY:
+            result = MEDIADRM_RESOURCE_BUSY_ERROR;
+            break;
+        case android::ERROR_DRM_DEVICE_REVOKED:
+            result = MEDIADRM_DEVICE_REVOKED_ERROR;
+            break;
+        case android::ERROR_DRM_CANNOT_HANDLE:
+            result = MEDIADRM_INVALID_PARAMETER_ERROR;
+            break;
+        case android::ERROR_DRM_TAMPER_DETECTED:
+            result = MEDIADRM_TAMPER_DETECTED_ERROR;
+            break;
+        case android::ERROR_DRM_SESSION_NOT_OPENED:
+            result = MEDIADRM_SESSION_NOT_OPENED_ERROR;
+            break;
+        case android::ERROR_DRM_NO_LICENSE:
+            result = MEDIADRM_NEED_KEY_ERROR;
+            break;
+        case android::ERROR_DRM_LICENSE_EXPIRED:
+            result = MEDIADRM_LICENSE_EXPIRED_ERROR;
+            break;
+        default:
+            result = MEDIADRM_UNKNOWN_ERROR;
+            break;
+    }
+    return result;
+}
+
+static sp<IDrm> CreateDrm() {
+    sp<IServiceManager> sm = defaultServiceManager();
+
+    sp<IBinder> binder =
+        sm->getService(String16("media.player"));
+
+    sp<IMediaPlayerService> service =
+        interface_cast<IMediaPlayerService>(binder);
+
+    if (service == NULL) {
+        return NULL;
+    }
+
+    sp<IDrm> drm = service->makeDrm();
+
+    if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
+        return NULL;
+    }
+
+    return drm;
+}
+
+
+static sp<IDrm> CreateDrmFromUUID(const AMediaUUID uuid) {
+    sp<IDrm> drm = CreateDrm();
+
+    if (drm == NULL) {
+        return NULL;
+    }
+
+    status_t err = drm->createPlugin(uuid);
+
+    if (err != OK) {
+        return NULL;
+    }
+
+    return drm;
+}
+
+EXPORT
+bool AMediaDrm_isCryptoSchemeSupported(const AMediaUUID uuid, const char *mimeType) {
+    sp<IDrm> drm = CreateDrm();
+
+    if (drm == NULL) {
+        return false;
+    }
+
+    String8 mimeStr = mimeType ? String8(mimeType) : String8("");
+    return drm->isCryptoSchemeSupported(uuid, mimeStr);
+}
+
+EXPORT
+AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) {
+    AMediaDrm *mObj = new AMediaDrm();
+    mObj->mDrm = CreateDrmFromUUID(uuid);
+    return mObj;
+}
+
+EXPORT
+void AMediaDrm_release(AMediaDrm *mObj) {
+    if (mObj->mDrm != NULL) {
+        mObj->mDrm->setListener(NULL);
+        mObj->mDrm->destroyPlugin();
+        mObj->mDrm.clear();
+    }
+    delete mObj;
+}
+
+#if 0
+void AMediaDrm_setOnEventListener(AMediaDrm *mObj, AMediaDrmEventListener listener) {
+    mObj->mListener = listener;
+}
+#endif
+
+
+static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
+    iter = mObj->mIds.begin();
+    while (iter != mObj->mIds.end()) {
+        if (iter->array() == id.ptr && iter->size() == id.length) {
+            return true;
+        }
+    }
+    return false;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId &sessionId) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    Vector<uint8_t> session;
+    status_t status = mObj->mDrm->openSession(session);
+    if (status == OK) {
+        mObj->mIds.push_front(session);
+        List<idvec_t>::iterator iter = mObj->mIds.begin();
+        sessionId.ptr = iter->array();
+        sessionId.length = iter->size();
+    }
+    return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_closeSession(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, sessionId, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+    mObj->mDrm->closeSession(*iter);
+    mObj->mIds.erase(iter);
+    return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getKeyRequest(AMediaDrm *mObj, const AMediaDrmScope &scope,
+        const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
+        const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
+        const uint8_t *&keyRequest, size_t &keyRequestSize) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    if (!mimeType) {
+        return MEDIADRM_INVALID_PARAMETER_ERROR;
+    }
+
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, scope, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+
+    Vector<uint8_t> mdInit;
+    mdInit.appendArray(init, initSize);
+    DrmPlugin::KeyType mdKeyType;
+    switch (keyType) {
+        case KEY_TYPE_STREAMING:
+            mdKeyType = DrmPlugin::kKeyType_Streaming;
+            break;
+        case KEY_TYPE_OFFLINE:
+            mdKeyType = DrmPlugin::kKeyType_Offline;
+            break;
+        case KEY_TYPE_RELEASE:
+            mdKeyType = DrmPlugin::kKeyType_Release;
+            break;
+        default:
+            return MEDIADRM_INVALID_PARAMETER_ERROR;
+    }
+    KeyedVector<String8, String8> mdOptionalParameters;
+    for (size_t i = 0; i < numOptionalParameters; i++) {
+        mdOptionalParameters.add(String8(optionalParameters[i].mKey),
+                String8(optionalParameters[i].mValue));
+    }
+    String8 defaultUrl;
+    status_t status = mObj->mDrm->getKeyRequest(*iter, mdInit, String8(mimeType),
+            mdKeyType, mdOptionalParameters, mObj->mKeyRequest, defaultUrl);
+    if (status != OK) {
+        return translateStatus(status);
+    } else {
+        keyRequest = mObj->mKeyRequest.array();
+        keyRequestSize = mObj->mKeyRequest.size();
+    }
+    return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_provideKeyResponse(AMediaDrm *mObj, const AMediaDrmScope &scope,
+        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId &keySetId) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    if (!response || !responseSize) {
+        return MEDIADRM_INVALID_PARAMETER_ERROR;
+    }
+
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, scope, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+    Vector<uint8_t> mdResponse;
+    mdResponse.appendArray(response, responseSize);
+
+    Vector<uint8_t> mdKeySetId;
+    status_t status = mObj->mDrm->provideKeyResponse(*iter, mdResponse, mdKeySetId);
+    if (status == OK) {
+        mObj->mIds.push_front(mdKeySetId);
+        List<idvec_t>::iterator iter = mObj->mIds.begin();
+        keySetId.ptr = iter->array();
+        keySetId.length = iter->size();
+    } else {
+        keySetId.ptr = NULL;
+        keySetId.length = 0;
+    }
+    return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_restoreKeys(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+        const AMediaDrmKeySetId &keySetId) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, sessionId, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+    Vector<uint8_t> keySet;
+    keySet.appendArray(keySetId.ptr, keySetId.length);
+    return translateStatus(mObj->mDrm->restoreKeys(*iter, keySet));
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_removeKeys(AMediaDrm *mObj, const AMediaDrmSessionId &keySetId) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    List<idvec_t>::iterator iter;
+    status_t status;
+    if (!findId(mObj, keySetId, iter)) {
+        Vector<uint8_t> keySet;
+        keySet.appendArray(keySetId.ptr, keySetId.length);
+        status = mObj->mDrm->removeKeys(keySet);
+    } else {
+        status = mObj->mDrm->removeKeys(*iter);
+        mObj->mIds.erase(iter);
+    }
+    return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_queryKeyStatus(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+        AMediaDrmKeyValue *keyValuePairs, size_t &numPairs) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, sessionId, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+
+    status_t status = mObj->mDrm->queryKeyStatus(*iter, mObj->mQueryResults);
+    if (status != OK) {
+        numPairs = 0;
+        return translateStatus(status);
+    }
+
+    if (mObj->mQueryResults.size() > numPairs) {
+        numPairs = mObj->mQueryResults.size();
+        return MEDIADRM_SHORT_BUFFER;
+    }
+
+    for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
+        keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
+        keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
+    }
+    numPairs = mObj->mQueryResults.size();
+    return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getProvisionRequest(AMediaDrm *mObj, const uint8_t *&provisionRequest,
+        size_t &provisionRequestSize, const char *&serverUrl) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    if (!provisionRequestSize || !serverUrl) {
+        return MEDIADRM_INVALID_PARAMETER_ERROR;
+    }
+
+    status_t status = mObj->mDrm->getProvisionRequest(String8(""), String8(""),
+            mObj->mProvisionRequest, mObj->mProvisionUrl);
+    if (status != OK) {
+        return translateStatus(status);
+    } else {
+        provisionRequest = mObj->mProvisionRequest.array();
+        provisionRequestSize = mObj->mProvisionRequest.size();
+        serverUrl = mObj->mProvisionUrl.string();
+    }
+    return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *mObj,
+        const uint8_t *response, size_t responseSize) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    if (!response || !responseSize) {
+        return MEDIADRM_INVALID_PARAMETER_ERROR;
+    }
+
+    Vector<uint8_t> mdResponse;
+    mdResponse.appendArray(response, responseSize);
+
+    Vector<uint8_t> unused;
+    return translateStatus(mObj->mDrm->provideProvisionResponse(mdResponse, unused, unused));
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getSecureStops(AMediaDrm *mObj,
+        AMediaDrmSecureStop *secureStops, size_t &numSecureStops) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    status_t status = mObj->mDrm->getSecureStops(mObj->mSecureStops);
+    if (status != OK) {
+        numSecureStops = 0;
+        return translateStatus(status);
+    }
+    if (numSecureStops < mObj->mSecureStops.size()) {
+        return MEDIADRM_SHORT_BUFFER;
+    }
+    List<Vector<uint8_t> >::iterator iter = mObj->mSecureStops.begin();
+    size_t i = 0;
+    while (iter != mObj->mSecureStops.end()) {
+        secureStops[i].ptr = iter->array();
+        secureStops[i].length = iter->size();
+        ++iter;
+        ++i;
+    }
+    numSecureStops = mObj->mSecureStops.size();
+    return MEDIADRM_OK;
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_releaseSecureStops(AMediaDrm *mObj,
+        const AMediaDrmSecureStop &ssRelease) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+
+    Vector<uint8_t> release;
+    release.appendArray(ssRelease.ptr, ssRelease.length);
+    return translateStatus(mObj->mDrm->releaseSecureStops(release));
+}
+
+
+EXPORT
+mediadrm_status_t AMediaDrm_getPropertyString(AMediaDrm *mObj, const char *propertyName,
+        const char *&propertyValue) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+
+    status_t status = mObj->mDrm->getPropertyString(String8(propertyName),
+            mObj->mPropertyString);
+
+    if (status == OK) {
+        propertyValue = mObj->mPropertyString.string();
+    } else {
+        propertyValue = NULL;
+    }
+    return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *mObj,
+        const char *propertyName, AMediaDrmByteArray &propertyValue) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+
+    status_t status = mObj->mDrm->getPropertyByteArray(String8(propertyName),
+            mObj->mPropertyByteArray);
+
+    if (status == OK) {
+        propertyValue.ptr = mObj->mPropertyByteArray.array();
+        propertyValue.length = mObj->mPropertyByteArray.size();
+    } else {
+        propertyValue.ptr = NULL;
+        propertyValue.length = 0;
+    }
+    return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_setPropertyString(AMediaDrm *mObj,
+        const char *propertyName, const char *value) {
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+
+    return translateStatus(mObj->mDrm->setPropertyString(String8(propertyName),
+                    String8(value)));
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *mObj,
+        const char *propertyName, const uint8_t *value, size_t valueSize) {
+
+    Vector<uint8_t> byteArray;
+    byteArray.appendArray(value, valueSize);
+
+    return translateStatus(mObj->mDrm->getPropertyByteArray(String8(propertyName),
+                    byteArray));
+}
+
+
+static mediadrm_status_t encrypt_decrypt_common(AMediaDrm *mObj,
+        const AMediaDrmSessionId &sessionId,
+        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+        const uint8_t *input, uint8_t *output, size_t dataSize, bool encrypt) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, sessionId, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+
+    status_t status = mObj->mDrm->setCipherAlgorithm(*iter, String8(cipherAlgorithm));
+    if (status != OK) {
+        return translateStatus(status);
+    }
+
+    Vector<uint8_t> keyIdVec;
+    const size_t kKeyIdSize = 16;
+    keyIdVec.appendArray(keyId, kKeyIdSize);
+
+    Vector<uint8_t> inputVec;
+    inputVec.appendArray(input, dataSize);
+
+    Vector<uint8_t> ivVec;
+    const size_t kIvSize = 16;
+    ivVec.appendArray(iv, kIvSize);
+
+    Vector<uint8_t> outputVec;
+    if (encrypt) {
+        status_t status = mObj->mDrm->encrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
+    } else {
+        status_t status = mObj->mDrm->decrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
+    }
+    if (status == OK) {
+        memcpy(output, outputVec.array(), outputVec.size());
+    }
+    return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_encrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+        const uint8_t *input, uint8_t *output, size_t dataSize) {
+    return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
+            input, output, dataSize, true);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_decrypt(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
+        const uint8_t *input, uint8_t *output, size_t dataSize) {
+    return encrypt_decrypt_common(mObj, sessionId, cipherAlgorithm, keyId, iv,
+            input, output, dataSize, false);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_sign(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+        const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
+        uint8_t *signature, size_t *signatureSize) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, sessionId, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+
+    status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
+    if (status != OK) {
+        return translateStatus(status);
+    }
+
+    Vector<uint8_t> keyIdVec;
+    const size_t kKeyIdSize = 16;
+    keyIdVec.appendArray(keyId, kKeyIdSize);
+
+    Vector<uint8_t> messageVec;
+    messageVec.appendArray(message, messageSize);
+
+    Vector<uint8_t> signatureVec;
+    status = mObj->mDrm->sign(*iter, keyIdVec, messageVec, signatureVec);
+    if (signatureVec.size() > *signatureSize) {
+        return MEDIADRM_SHORT_BUFFER;
+    }
+    if (status == OK) {
+        memcpy(signature, signatureVec.array(), signatureVec.size());
+    }
+    return translateStatus(status);
+}
+
+EXPORT
+mediadrm_status_t AMediaDrm_verify(AMediaDrm *mObj, const AMediaDrmSessionId &sessionId,
+        const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
+        const uint8_t *signature, size_t signatureSize) {
+
+    if (!mObj || mObj->mDrm == NULL) {
+        return MEDIADRM_INVALID_OBJECT_ERROR;
+    }
+    List<idvec_t>::iterator iter;
+    if (!findId(mObj, sessionId, iter)) {
+        return MEDIADRM_SESSION_NOT_OPENED_ERROR;
+    }
+
+    status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
+    if (status != OK) {
+        return translateStatus(status);
+    }
+
+    Vector<uint8_t> keyIdVec;
+    const size_t kKeyIdSize = 16;
+    keyIdVec.appendArray(keyId, kKeyIdSize);
+
+    Vector<uint8_t> messageVec;
+    messageVec.appendArray(message, messageSize);
+
+    Vector<uint8_t> signatureVec;
+    signatureVec.appendArray(signature, signatureSize);
+
+    bool match;
+    status = mObj->mDrm->verify(*iter, keyIdVec, messageVec, signatureVec, match);
+    if (status == OK) {
+        return match ? MEDIADRM_OK : MEDIADRM_VERIFY_FAILED;
+    }
+    return translateStatus(status);
+}
+
+} // extern "C"
+
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index 681633a..e23adf3 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -18,12 +18,14 @@
 #define LOG_TAG "NdkMediaExtractor"
 
 
+#include "NdkMediaError.h"
 #include "NdkMediaExtractor.h"
 #include "NdkMediaFormatPriv.h"
 
 
 #include <utils/Log.h>
 #include <utils/StrongPointer.h>
+#include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MetaData.h>
@@ -41,16 +43,18 @@
         return OK;
     }
     ALOGE("sf error code: %d", err);
-    return -1000;
+    return AMEDIAERROR_GENERIC;
 }
 
 struct AMediaExtractor {
     sp<NuMediaExtractor> mImpl;
+    sp<ABuffer> mPsshBuf;
 
 };
 
 extern "C" {
 
+EXPORT
 AMediaExtractor* AMediaExtractor_new() {
     ALOGV("ctor");
     AMediaExtractor *mData = new AMediaExtractor();
@@ -58,18 +62,21 @@
     return mData;
 }
 
+EXPORT
 int AMediaExtractor_delete(AMediaExtractor *mData) {
     ALOGV("dtor");
     delete mData;
     return OK;
 }
 
+EXPORT
 int AMediaExtractor_setDataSourceFd(AMediaExtractor *mData, int fd, off64_t offset, off64_t length) {
     ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
     mData->mImpl->setDataSource(fd, offset, length);
     return 0;
 }
 
+EXPORT
 int AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
     ALOGV("setDataSource(%s)", location);
     // TODO: add header support
@@ -79,14 +86,14 @@
     if (env == NULL) {
         ALOGE("setDataSource(path) must be called from Java thread");
         env->ExceptionClear();
-        return -1;
+        return AMEDIAERROR_UNSUPPORTED;
     }
 
     jclass mediahttpclass = env->FindClass("android/media/MediaHTTPService");
     if (mediahttpclass == NULL) {
         ALOGE("can't find MediaHttpService");
         env->ExceptionClear();
-        return -1;
+        return AMEDIAERROR_UNSUPPORTED;
     }
 
     jmethodID mediaHttpCreateMethod = env->GetStaticMethodID(mediahttpclass,
@@ -94,7 +101,7 @@
     if (mediaHttpCreateMethod == NULL) {
         ALOGE("can't find method");
         env->ExceptionClear();
-        return -1;
+        return AMEDIAERROR_UNSUPPORTED;
     }
 
     jstring jloc = env->NewStringUTF(location);
@@ -110,34 +117,40 @@
 
     mData->mImpl->setDataSource(httpService, location, NULL);
     env->ExceptionClear();
-    return 0;
+    return OK;
 }
 
+EXPORT
 int AMediaExtractor_getTrackCount(AMediaExtractor *mData) {
     return mData->mImpl->countTracks();
 }
 
+EXPORT
 AMediaFormat* AMediaExtractor_getTrackFormat(AMediaExtractor *mData, size_t idx) {
     sp<AMessage> format;
     mData->mImpl->getTrackFormat(idx, &format);
     return AMediaFormat_fromMsg(&format);
 }
 
+EXPORT
 int AMediaExtractor_selectTrack(AMediaExtractor *mData, size_t idx) {
     ALOGV("selectTrack(%z)", idx);
     return translate_error(mData->mImpl->selectTrack(idx));
 }
 
+EXPORT
 int AMediaExtractor_unselectTrack(AMediaExtractor *mData, size_t idx) {
     ALOGV("unselectTrack(%z)", idx);
     return translate_error(mData->mImpl->unselectTrack(idx));
 }
 
+EXPORT
 bool AMediaExtractor_advance(AMediaExtractor *mData) {
     //ALOGV("advance");
     return mData->mImpl->advance();
 }
 
+EXPORT
 int AMediaExtractor_readSampleData(AMediaExtractor *mData, uint8_t *buffer, size_t capacity) {
     //ALOGV("readSampleData");
     sp<ABuffer> tmp = new ABuffer(buffer, capacity);
@@ -147,6 +160,7 @@
     return -1;
 }
 
+EXPORT
 int AMediaExtractor_getSampleFlags(AMediaExtractor *mData) {
     int sampleFlags = 0;
     sp<MetaData> meta;
@@ -168,6 +182,7 @@
     return sampleFlags;
 }
 
+EXPORT
 int AMediaExtractor_getSampleTrackIndex(AMediaExtractor *mData) {
     size_t idx;
     if (mData->mImpl->getSampleTrackIndex(&idx) != OK) {
@@ -176,6 +191,7 @@
     return idx;
 }
 
+EXPORT
 int64_t AMediaExtractor_getSampletime(AMediaExtractor *mData) {
     int64_t time;
     if (mData->mImpl->getSampleTime(&time) != OK) {
@@ -184,6 +200,143 @@
     return time;
 }
 
+EXPORT
+PsshInfo* AMediaExtractor_getPsshInfo(AMediaExtractor *ex) {
+
+    if (ex->mPsshBuf != NULL) {
+        return (PsshInfo*) ex->mPsshBuf->data();
+    }
+
+    sp<AMessage> format;
+    ex->mImpl->getFileFormat(&format);
+    sp<ABuffer> buffer;
+    if(!format->findBuffer("pssh", &buffer)) {
+        return NULL;
+    }
+
+    // the format of the buffer is 1 or more of:
+    //    {
+    //        16 byte uuid
+    //        4 byte data length N
+    //        N bytes of data
+    //    }
+
+    // Determine the number of entries in the source data.
+    // Since we got the data from stagefright, we trust it is valid and properly formatted.
+    const uint8_t* data = buffer->data();
+    size_t len = buffer->size();
+    size_t numentries = 0;
+    while (len > 0) {
+        numentries++;
+
+        // skip uuid
+        data += 16;
+        len -= 16;
+
+        // get data length
+        uint32_t datalen = *((uint32_t*)data);
+        data += 4;
+        len -= 4;
+
+        // skip the data
+        data += datalen;
+        len -= datalen;
+    }
+
+    // there are <numentries> in the buffer, we need
+    // (source buffer size) + 4 + (4 * numentries) bytes for the PsshInfo structure
+    size_t newsize = buffer->size() + 4 + (4 * numentries);
+    ex->mPsshBuf = new ABuffer(newsize);
+    ex->mPsshBuf->setRange(0, newsize);
+
+    // copy data
+    const uint8_t* src = buffer->data();
+    uint8_t* dst = ex->mPsshBuf->data();
+    uint8_t* dstdata = dst + 4 + numentries * sizeof(PsshEntry);
+    *((uint32_t*)dst) = numentries;
+    dst += 4;
+    for (size_t i = 0; i < numentries; i++) {
+        // copy uuid
+        memcpy(dst, src, 16);
+        src += 16;
+        dst += 16;
+
+        // get/copy data length
+        uint32_t datalen = *((uint32_t*)src);
+        memcpy(dst, src, 4);
+        src += 4;
+        dst += 4;
+
+        // the next entry in the destination is a pointer to the actual data, which we store
+        // after the array of PsshEntry
+        memcpy(dst, &dstdata, sizeof(dstdata));
+        dst += 4;
+
+        // copy the actual data
+        memcpy(dstdata, src, datalen);
+        dstdata += datalen;
+        src += datalen;
+    }
+
+    return (PsshInfo*) ex->mPsshBuf->data();
+}
+
+EXPORT
+AMediaCodecCryptoInfo *AMediaExtractor_getSampleCryptoInfo(AMediaExtractor *ex) {
+    sp<MetaData> meta;
+    if(ex->mImpl->getSampleMeta(&meta) != 0) {
+        return NULL;
+    }
+
+    uint32_t type;
+    const void *crypteddata;
+    size_t cryptedsize;
+    if (!meta->findData(kKeyEncryptedSizes, &type, &crypteddata, &cryptedsize)) {
+        return NULL;
+    }
+    size_t numSubSamples = cryptedsize / sizeof(size_t);
+
+    const void *cleardata;
+    size_t clearsize;
+    if (meta->findData(kKeyPlainSizes, &type, &cleardata, &clearsize)) {
+        if (clearsize != cryptedsize) {
+            // The two must be of the same length.
+            return NULL;
+        }
+    }
+
+    const void *key;
+    size_t keysize;
+    if (meta->findData(kKeyCryptoIV, &type, &key, &keysize)) {
+        if (keysize != 16) {
+            // IVs must be 16 bytes in length.
+            return NULL;
+        }
+    }
+
+    const void *iv;
+    size_t ivsize;
+    if (meta->findData(kKeyCryptoIV, &type, &iv, &ivsize)) {
+        if (ivsize != 16) {
+            // IVs must be 16 bytes in length.
+            return NULL;
+        }
+    }
+
+    int32_t mode;
+    if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+        mode = CryptoPlugin::kMode_AES_CTR;
+    }
+
+    return AMediaCodecCryptoInfo_new(
+            numSubSamples,
+            (uint8_t*) key,
+            (uint8_t*) iv,
+            mode,
+            (size_t*) cleardata,
+            (size_t*) crypteddata);
+}
+
 
 } // extern "C"
 
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index c08814f..e1d8c95 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -56,12 +56,14 @@
 /*
  * public function follow
  */
+EXPORT
 AMediaFormat *AMediaFormat_new() {
     ALOGV("ctor");
     sp<AMessage> msg = new AMessage();
     return AMediaFormat_fromMsg(&msg);
 }
 
+EXPORT
 int AMediaFormat_delete(AMediaFormat *mData) {
     ALOGV("dtor");
     delete mData;
@@ -69,6 +71,7 @@
 }
 
 
+EXPORT
 const char* AMediaFormat_toString(AMediaFormat *mData) {
     sp<AMessage> f = mData->mFormat;
     String8 ret;
@@ -141,22 +144,27 @@
     return mData->mDebug.string();
 }
 
+EXPORT
 bool AMediaFormat_getInt32(AMediaFormat* format, const char *name, int32_t *out) {
     return format->mFormat->findInt32(name, out);
 }
 
+EXPORT
 bool AMediaFormat_getInt64(AMediaFormat* format, const char *name, int64_t *out) {
     return format->mFormat->findInt64(name, out);
 }
 
+EXPORT
 bool AMediaFormat_getFloat(AMediaFormat* format, const char *name, float *out) {
     return format->mFormat->findFloat(name, out);
 }
 
+EXPORT
 bool AMediaFormat_getSize(AMediaFormat* format, const char *name, size_t *out) {
     return format->mFormat->findSize(name, out);
 }
 
+EXPORT
 bool AMediaFormat_getBuffer(AMediaFormat* format, const char *name, void** data, size_t *outsize) {
     sp<ABuffer> buf;
     if (format->mFormat->findBuffer(name, &buf)) {
@@ -167,6 +175,7 @@
     return false;
 }
 
+EXPORT
 bool AMediaFormat_getString(AMediaFormat* mData, const char *name, const char **out) {
 
     for (size_t i = 0; i < mData->mStringCache.size(); i++) {
@@ -186,23 +195,28 @@
     return false;
 }
 
+EXPORT
 void AMediaFormat_setInt32(AMediaFormat* format, const char *name, int32_t value) {
     format->mFormat->setInt32(name, value);
 }
 
+EXPORT
 void AMediaFormat_setInt64(AMediaFormat* format, const char *name, int64_t value) {
     format->mFormat->setInt64(name, value);
 }
 
+EXPORT
 void AMediaFormat_setFloat(AMediaFormat* format, const char* name, float value) {
     format->mFormat->setFloat(name, value);
 }
 
+EXPORT
 void AMediaFormat_setString(AMediaFormat* format, const char* name, const char* value) {
     // AMessage::setString() makes a copy of the string
     format->mFormat->setString(name, value, strlen(value));
 }
 
+EXPORT
 void AMediaFormat_setBuffer(AMediaFormat* format, const char* name, void* data, size_t size) {
     // the ABuffer(void*, size_t) constructor doesn't take ownership of the data, so create
     // a new buffer and copy the data into it
@@ -214,30 +228,30 @@
 }
 
 
-const char* AMEDIAFORMAT_KEY_AAC_PROFILE = "aac-profile";
-const char* AMEDIAFORMAT_KEY_BIT_RATE = "bitrate";
-const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT = "channel-count";
-const char* AMEDIAFORMAT_KEY_CHANNEL_MASK = "channel-mask";
-const char* AMEDIAFORMAT_KEY_COLOR_FORMAT = "color-format";
-const char* AMEDIAFORMAT_KEY_DURATION = "durationUs";
-const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
-const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate";
-const char* AMEDIAFORMAT_KEY_HEIGHT = "height";
-const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts";
-const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect";
-const char* AMEDIAFORMAT_KEY_IS_DEFAULT = "is-default";
-const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle";
-const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval";
-const char* AMEDIAFORMAT_KEY_LANGUAGE = "language";
-const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height";
-const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
-const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
-const char* AMEDIAFORMAT_KEY_MIME = "mime";
-const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
-const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
-const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
-const char* AMEDIAFORMAT_KEY_WIDTH = "width";
-const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
+EXPORT const char* AMEDIAFORMAT_KEY_AAC_PROFILE = "aac-profile";
+EXPORT const char* AMEDIAFORMAT_KEY_BIT_RATE = "bitrate";
+EXPORT const char* AMEDIAFORMAT_KEY_CHANNEL_COUNT = "channel-count";
+EXPORT const char* AMEDIAFORMAT_KEY_CHANNEL_MASK = "channel-mask";
+EXPORT const char* AMEDIAFORMAT_KEY_COLOR_FORMAT = "color-format";
+EXPORT const char* AMEDIAFORMAT_KEY_DURATION = "durationUs";
+EXPORT const char* AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
+EXPORT const char* AMEDIAFORMAT_KEY_FRAME_RATE = "frame-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_HEIGHT = "height";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_ADTS = "is-adts";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_AUTOSELECT = "is-autoselect";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_DEFAULT = "is-default";
+EXPORT const char* AMEDIAFORMAT_KEY_IS_FORCED_SUBTITLE = "is-forced-subtitle";
+EXPORT const char* AMEDIAFORMAT_KEY_I_FRAME_INTERVAL = "i-frame-interval";
+EXPORT const char* AMEDIAFORMAT_KEY_LANGUAGE = "language";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_HEIGHT = "max-height";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_INPUT_SIZE = "max-input-size";
+EXPORT const char* AMEDIAFORMAT_KEY_MAX_WIDTH = "max-width";
+EXPORT const char* AMEDIAFORMAT_KEY_MIME = "mime";
+EXPORT const char* AMEDIAFORMAT_KEY_PUSH_BLANK_BUFFERS_ON_STOP = "push-blank-buffers-on-shutdown";
+EXPORT const char* AMEDIAFORMAT_KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
+EXPORT const char* AMEDIAFORMAT_KEY_SAMPLE_RATE = "sample-rate";
+EXPORT const char* AMEDIAFORMAT_KEY_WIDTH = "width";
+EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
 
 
 } // extern "C"
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index 98129cb..aa78740 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -52,6 +52,7 @@
 
 extern "C" {
 
+EXPORT
 AMediaMuxer* AMediaMuxer_new(int fd, OutputFormat format) {
     ALOGV("ctor");
     AMediaMuxer *mData = new AMediaMuxer();
@@ -59,34 +60,41 @@
     return mData;
 }
 
+EXPORT
 int AMediaMuxer_delete(AMediaMuxer *muxer) {
     ALOGV("dtor");
     delete muxer;
     return OK;
 }
 
+EXPORT
 int AMediaMuxer_setLocation(AMediaMuxer *muxer, float latitude, float longtitude) {
     return translate_error(muxer->mImpl->setLocation(latitude * 10000, longtitude * 10000));
 }
 
+EXPORT
 int AMediaMuxer_setOrientationHint(AMediaMuxer *muxer, int degrees) {
     return translate_error(muxer->mImpl->setOrientationHint(degrees));
 }
 
+EXPORT
 ssize_t AMediaMuxer_addTrack(AMediaMuxer *muxer, const AMediaFormat *format) {
     sp<AMessage> msg;
     AMediaFormat_getFormat(format, &msg);
     return translate_error(muxer->mImpl->addTrack(msg));
 }
 
+EXPORT
 int AMediaMuxer_start(AMediaMuxer *muxer) {
     return translate_error(muxer->mImpl->start());
 }
 
+EXPORT
 int AMediaMuxer_stop(AMediaMuxer *muxer) {
     return translate_error(muxer->mImpl->stop());
 }
 
+EXPORT
 int AMediaMuxer_writeSampleData(AMediaMuxer *muxer,
         size_t trackIdx, const uint8_t *data, const AMediaCodecBufferInfo &info) {
     sp<ABuffer> buf = new ABuffer((void*)(data + info.offset), info.size);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c1d4c08..d69d6a2 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -18,6 +18,7 @@
 #ifndef ANDROID_AUDIO_FLINGER_H
 #define ANDROID_AUDIO_FLINGER_H
 
+#include "Configuration.h"
 #include <stdint.h>
 #include <sys/types.h>
 #include <limits.h>
diff --git a/services/audioflinger/FastThreadState.cpp b/services/audioflinger/FastThreadState.cpp
index d4d6255..6994872 100644
--- a/services/audioflinger/FastThreadState.cpp
+++ b/services/audioflinger/FastThreadState.cpp
@@ -35,7 +35,7 @@
     /* mMeasuredWarmupTs({0, 0}), */
     mWarmupCycles(0)
 #ifdef FAST_MIXER_STATISTICS
-    , mSamplingN(0), mBounds(0)
+    , mSamplingN(1), mBounds(0)
 #endif
 {
     mMeasuredWarmupTs.tv_sec = 0;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index e9c6834..08b1728 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -115,7 +115,6 @@
     void invalidate();
     bool isInvalid() const { return mIsInvalid; }
     virtual bool isTimedTrack() const { return false; }
-    bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
     int fastIndex() const { return mFastIndex; }
 
 protected:
@@ -142,8 +141,6 @@
                                     // audio HAL when this track will be fully rendered
                                     // zero means not monitoring
 private:
-    IAudioFlinger::track_flags_t mFlags;
-
     // The following fields are only for fast tracks, and should be in a subclass
     int                 mFastIndex; // index within FastMixerState::mFastTracks[];
                                     // either mFastIndex == -1 if not isFastTrack()
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 4ca2ad4..fe15571 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -30,7 +30,7 @@
                                 size_t frameCount,
                                 int sessionId,
                                 int uid,
-                                bool isFast);
+                                IAudioFlinger::track_flags_t flags);
     virtual             ~RecordTrack();
 
     virtual status_t    start(AudioSystem::sync_event_t event, int triggerSession);
diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h
index 9cde642..9e176c4 100644
--- a/services/audioflinger/StateQueue.h
+++ b/services/audioflinger/StateQueue.h
@@ -89,6 +89,8 @@
 //  arithmetic on the state pointers.  However to the mutator, the state pointers
 //  are in a definite circular order.
 
+#include "Configuration.h"
+
 namespace android {
 
 #ifdef STATE_QUEUE_DUMP
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8243a8b..2d4e025 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5142,7 +5142,7 @@
 
         track = new RecordTrack(this, client, sampleRate,
                       format, channelMask, frameCount, sessionId, uid,
-                      (*flags & IAudioFlinger::TRACK_FAST) != 0);
+                      *flags);
 
         lStatus = track->initCheck();
         if (lStatus != NO_ERROR) {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 06023fd..5f13be3 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -48,6 +48,7 @@
                                 const sp<IMemory>& sharedBuffer,
                                 int sessionId,
                                 int uid,
+                                IAudioFlinger::track_flags_t flags,
                                 bool isOut,
                                 bool useReadOnlyHeap = false);
     virtual             ~TrackBase();
@@ -63,6 +64,7 @@
     virtual status_t    setSyncEvent(const sp<SyncEvent>& event);
 
             sp<IMemory> getBuffers() const { return mBufferMemory; }
+            bool        isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
 
 protected:
                         TrackBase(const TrackBase&);
@@ -134,6 +136,7 @@
     const int           mSessionId;
     int                 mUid;
     Vector < sp<SyncEvent> >mSyncEvents;
+    const IAudioFlinger::track_flags_t mFlags;
     const bool          mIsOut;
     ServerProxy*        mServerProxy;
     const int           mId;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 88ead74..6dc7f30 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -69,6 +69,7 @@
             const sp<IMemory>& sharedBuffer,
             int sessionId,
             int clientUid,
+            IAudioFlinger::track_flags_t flags,
             bool isOut,
             bool useReadOnlyHeap)
     :   RefBase(),
@@ -85,6 +86,7 @@
                 mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
         mFrameCount(frameCount),
         mSessionId(sessionId),
+        mFlags(flags),
         mIsOut(isOut),
         mServerProxy(NULL),
         mId(android_atomic_inc(&nextTrackId)),
@@ -349,7 +351,7 @@
             int uid,
             IAudioFlinger::track_flags_t flags)
     :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
-            sessionId, uid, true /*isOut*/),
+            sessionId, uid, flags, true /*isOut*/),
     mFillingUpStatus(FS_INVALID),
     // mRetryCount initialized later when needed
     mSharedBuffer(sharedBuffer),
@@ -359,7 +361,6 @@
     mAuxBuffer(NULL),
     mAuxEffectId(0), mHasVolumeController(false),
     mPresentationCompleteFrames(0),
-    mFlags(flags),
     mFastIndex(-1),
     mCachedVolume(1.0),
     mIsInvalid(false),
@@ -1833,10 +1834,11 @@
             size_t frameCount,
             int sessionId,
             int uid,
-            bool isFast)
+            IAudioFlinger::track_flags_t flags)
     :   TrackBase(thread, client, sampleRate, format,
-                  channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/,
-                  isFast /*useReadOnlyHeap*/),
+                  channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid,
+                  flags, false /*isOut*/,
+                  (flags & IAudioFlinger::TRACK_FAST) != 0 /*useReadOnlyHeap*/),
         mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
         // See real initialization of mRsmpInFront at RecordThread::start()
         mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)