Merge "Camera3: Be busy as soon as work arrives." into klp-dev
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 3f8567c..d027ba9 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -21,6 +21,7 @@
 #include <binder/IPCThreadState.h>
 #include <utils/Errors.h>
 #include <utils/Thread.h>
+#include <utils/Timers.h>
 
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -35,6 +36,8 @@
 #include <media/stagefright/MediaMuxer.h>
 #include <media/ICrypto.h>
 
+#include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <signal.h>
@@ -42,6 +45,12 @@
 
 using namespace android;
 
+static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
+static const uint32_t kMaxBitRate = 100 * 1000000;  // 100Mbps
+static const uint32_t kMaxTimeLimitSec = 180;       // 3 minutes
+static const uint32_t kFallbackWidth = 1280;        // 720p
+static const uint32_t kFallbackHeight = 720;
+
 // Command-line parameters.
 static bool gVerbose = false;               // chatty on stdout
 static bool gRotate = false;                // rotate 90 degrees
@@ -49,6 +58,7 @@
 static uint32_t gVideoWidth = 0;            // default width+height
 static uint32_t gVideoHeight = 0;
 static uint32_t gBitRate = 4000000;         // 4Mbps
+static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
 
 // Set by signal handler to stop recording.
 static bool gStopRequested;
@@ -57,8 +67,6 @@
 static struct sigaction gOrigSigactionINT;
 static struct sigaction gOrigSigactionHUP;
 
-static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
-static const uint32_t kMaxBitRate = 100 * 1000000;  // 100Mbps
 
 /*
  * Catch keyboard interrupt signals.  On receipt, the "stop requested"
@@ -70,9 +78,8 @@
     gStopRequested = true;
     switch (signum) {
     case SIGINT:
-        sigaction(SIGINT, &gOrigSigactionINT, NULL);
-        break;
     case SIGHUP:
+        sigaction(SIGINT, &gOrigSigactionINT, NULL);
         sigaction(SIGHUP, &gOrigSigactionHUP, NULL);
         break;
     default:
@@ -138,7 +145,6 @@
     format->setFloat("frame-rate", displayFps);
     format->setInt32("i-frame-interval", 10);
 
-    /// MediaCodec
     sp<ALooper> looper = new ALooper;
     looper->setName("screenrecord_looper");
     looper->start();
@@ -279,7 +285,8 @@
     status_t err;
     ssize_t trackIdx = -1;
     uint32_t debugNumFrames = 0;
-    time_t debugStartWhen = time(NULL);
+    int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
+    int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
 
     Vector<sp<ABuffer> > buffers;
     err = encoder->getOutputBuffers(&buffers);
@@ -296,6 +303,14 @@
         size_t bufIndex, offset, size;
         int64_t ptsUsec;
         uint32_t flags;
+
+        if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {
+            if (gVerbose) {
+                printf("Time limit reached\n");
+            }
+            break;
+        }
+
         ALOGV("Calling dequeueOutputBuffer");
         err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,
                 &flags, kTimeout);
@@ -345,7 +360,6 @@
             }
             break;
         case -EAGAIN:                       // INFO_TRY_AGAIN_LATER
-            // not expected with infinite timeout
             ALOGV("Got -EAGAIN, looping");
             break;
         case INFO_FORMAT_CHANGED:           // INFO_OUTPUT_FORMAT_CHANGED
@@ -370,18 +384,24 @@
             if (err != NO_ERROR) {
                 fprintf(stderr,
                         "Unable to get new output buffers (err=%d)\n", err);
+                return err;
             }
             break;
+        case INVALID_OPERATION:
+            fprintf(stderr, "Request for encoder buffer failed\n");
+            return err;
         default:
-            ALOGW("Got weird result %d from dequeueOutputBuffer", err);
+            fprintf(stderr,
+                    "Got weird result %d from dequeueOutputBuffer\n", err);
             return err;
         }
     }
 
     ALOGV("Encoder stopping (req=%d)", gStopRequested);
     if (gVerbose) {
-        printf("Encoder stopping; recorded %u frames in %ld seconds\n",
-                debugNumFrames, time(NULL) - debugStartWhen);
+        printf("Encoder stopping; recorded %u frames in %lld seconds\n",
+                debugNumFrames,
+                nanoseconds_to_seconds(systemTime(CLOCK_MONOTONIC) - startWhenNsec));
     }
     return NO_ERROR;
 }
@@ -432,12 +452,12 @@
     sp<IGraphicBufferProducer> bufferProducer;
     err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
     if (err != NO_ERROR && !gSizeSpecified) {
-        ALOGV("Retrying with 720p");
-        if (gVideoWidth != 1280 && gVideoHeight != 720) {
+        if (gVideoWidth != kFallbackWidth && gVideoHeight != kFallbackHeight) {
+            ALOGV("Retrying with 720p");
             fprintf(stderr, "WARNING: failed at %dx%d, retrying at 720p\n",
                     gVideoWidth, gVideoHeight);
-            gVideoWidth = 1280;
-            gVideoHeight = 720;
+            gVideoWidth = kFallbackWidth;
+            gVideoHeight = kFallbackHeight;
             err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
         }
     }
@@ -477,6 +497,29 @@
 }
 
 /*
+ * Sends a broadcast to the media scanner to tell it about the new video.
+ */
+static status_t notifyMediaScanner(const char* fileName) {
+    String8 command("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://");
+    command.append(fileName);
+    if (gVerbose) {
+        printf("Shell: %s\n", command.string());
+    }
+
+    // TODO: for non-verbose mode we should suppress stdout
+    int status = system(command.string());
+    if (status < 0) {
+        fprintf(stderr, "Unable to fork shell for media scanner broadcast\n");
+        return UNKNOWN_ERROR;
+    } else if (status != 0) {
+        fprintf(stderr, "am command failed (status=%d): '%s'\n",
+                status, command.string());
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
+/*
  * Parses a string of the form "1280x720".
  *
  * Returns true on success.
@@ -514,10 +557,13 @@
         "\n"
         "Options:\n"
         "--size WIDTHxHEIGHT\n"
-        "    Set the video size, e.g. \"1280x720\".  For best results, use\n"
-        "    a size supported by the AVC encoder.\n"
+        "    Set the video size, e.g. \"1280x720\".  Default is the device's main\n"
+        "    display resolution (if supported), 1280x720 if not.  For best results,\n"
+        "    use a size supported by the AVC encoder.\n"
         "--bit-rate RATE\n"
-        "    Set the video bit rate, in megabits per second.  Default 4Mbps.\n"
+        "    Set the video bit rate, in megabits per second.  Default %dMbps.\n"
+        "--time-limit TIME\n"
+        "    Set the maximum recording time, in seconds.  Default / maximum is %d.\n"
         "--rotate\n"
         "    Rotate the output 90 degrees.\n"
         "--verbose\n"
@@ -525,8 +571,9 @@
         "--help\n"
         "    Show this message.\n"
         "\n"
-        "Recording continues until Ctrl-C is hit.\n"
-        "\n"
+        "Recording continues until Ctrl-C is hit or the time limit is reached.\n"
+        "\n",
+        gBitRate / 1000000, gTimeLimitSec
         );
 }
 
@@ -539,6 +586,7 @@
         { "verbose",    no_argument,        NULL, 'v' },
         { "size",       required_argument,  NULL, 's' },
         { "bit-rate",   required_argument,  NULL, 'b' },
+        { "time-limit", required_argument,  NULL, 't' },
         { "rotate",     no_argument,        NULL, 'r' },
         { NULL,         0,                  NULL, 0 }
     };
@@ -580,6 +628,15 @@
                 return 2;
             }
             break;
+        case 't':
+            gTimeLimitSec = atoi(optarg);
+            if (gTimeLimitSec == 0 || gTimeLimitSec > kMaxTimeLimitSec) {
+                fprintf(stderr,
+                        "Time limit %ds outside acceptable range [1,%d]\n",
+                        gTimeLimitSec, kMaxTimeLimitSec);
+                return 2;
+            }
+            break;
         case 'r':
             gRotate = true;
             break;
@@ -609,6 +666,10 @@
     close(fd);
 
     status_t err = recordScreen(fileName);
+    if (err == NO_ERROR) {
+        // Try to notify the media scanner.  Not fatal if this fails.
+        notifyMediaScanner(fileName);
+    }
     ALOGD(err == NO_ERROR ? "success" : "failed");
     return (int) err;
 }
diff --git a/drm/mediadrm/plugins/mock/Android.mk b/drm/mediadrm/plugins/mock/Android.mk
deleted file mode 100644
index ada23a2..0000000
--- a/drm/mediadrm/plugins/mock/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-    MockDrmCryptoPlugin.cpp
-
-LOCAL_MODULE := libmockdrmcryptoplugin
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/mediadrm
-
-LOCAL_SHARED_LIBRARIES := \
-    libutils liblog
-
-LOCAL_C_INCLUDES += \
-    $(TOP)/frameworks/av/include \
-    $(TOP)/frameworks/native/include/media
-
-# Set the following flag to enable the decryption passthru flow
-#LOCAL_CFLAGS += -DENABLE_PASSTHRU_DECRYPTION
-
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
deleted file mode 100644
index 4770db0..0000000
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ /dev/null
@@ -1,705 +0,0 @@
-/*
- * Copyright (C) 2013 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 "MockDrmCryptoPlugin"
-#include <utils/Log.h>
-
-
-#include "drm/DrmAPI.h"
-#include "MockDrmCryptoPlugin.h"
-#include "media/stagefright/MediaErrors.h"
-
-using namespace android;
-
-// Shared library entry point
-DrmFactory *createDrmFactory()
-{
-    return new MockDrmFactory();
-}
-
-// Shared library entry point
-CryptoFactory *createCryptoFactory()
-{
-    return new MockCryptoFactory();
-}
-
-const uint8_t mock_uuid[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                               0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
-
-namespace android {
-
-    // MockDrmFactory
-    bool MockDrmFactory::isCryptoSchemeSupported(const uint8_t uuid[16])
-    {
-        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
-    }
-
-    bool MockDrmFactory::isContentTypeSupported(const String8 &mimeType)
-    {
-        if (mimeType != "" && mimeType != "video/mp4") {
-            return false;
-        }
-        return true;
-    }
-
-    status_t MockDrmFactory::createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin)
-    {
-        *plugin = new MockDrmPlugin();
-        return OK;
-    }
-
-    // MockCryptoFactory
-    bool MockCryptoFactory::isCryptoSchemeSupported(const uint8_t uuid[16]) const
-    {
-        return (!memcmp(uuid, mock_uuid, sizeof(uuid)));
-    }
-
-    status_t MockCryptoFactory::createPlugin(const uint8_t uuid[16], const void *data,
-                                             size_t size, CryptoPlugin **plugin)
-    {
-        *plugin = new MockCryptoPlugin();
-        return OK;
-    }
-
-
-    // MockDrmPlugin methods
-
-    status_t MockDrmPlugin::openSession(Vector<uint8_t> &sessionId)
-    {
-        const size_t kSessionIdSize = 8;
-
-        Mutex::Autolock lock(mLock);
-        for (size_t i = 0; i < kSessionIdSize / sizeof(long); i++) {
-            long r = random();
-            sessionId.appendArray((uint8_t *)&r, sizeof(long));
-        }
-        mSessions.add(sessionId);
-
-        ALOGD("MockDrmPlugin::openSession() -> %s", vectorToString(sessionId).string());
-        return OK;
-    }
-
-    status_t MockDrmPlugin::closeSession(Vector<uint8_t> const &sessionId)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::closeSession(%s)", vectorToString(sessionId).string());
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-        mSessions.removeAt(index);
-        return OK;
-    }
-
-
-    status_t MockDrmPlugin::getKeyRequest(Vector<uint8_t> const &sessionId,
-                                          Vector<uint8_t> const &initData,
-                                          String8 const &mimeType, KeyType keyType,
-                                          KeyedVector<String8, String8> const &optionalParameters,
-                                          Vector<uint8_t> &request, String8 &defaultUrl)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::getKeyRequest(sessionId=%s, initData=%s, mimeType=%s"
-              ", keyType=%d, optionalParameters=%s))",
-              vectorToString(sessionId).string(), vectorToString(initData).string(), mimeType.string(),
-              keyType, stringMapToString(optionalParameters).string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] initData           -> mock-initdata
-        //   string mimeType           -> mock-mimetype
-        //   string keyType            -> mock-keytype
-        //   string optionalParameters -> mock-optparams formatted as {key1,value1},{key2,value2}
-
-        mByteArrayProperties.add(String8("mock-initdata"), initData);
-        mStringProperties.add(String8("mock-mimetype"), mimeType);
-
-        String8 keyTypeStr;
-        keyTypeStr.appendFormat("%d", (int)keyType);
-        mStringProperties.add(String8("mock-keytype"), keyTypeStr);
-
-        String8 params;
-        for (size_t i = 0; i < optionalParameters.size(); i++) {
-            params.appendFormat("%s{%s,%s}", i ? "," : "",
-                                optionalParameters.keyAt(i).string(),
-                                optionalParameters.valueAt(i).string());
-        }
-        mStringProperties.add(String8("mock-optparams"), params);
-
-        // Properties used in mock test, set by cts test app returned from mock plugin
-        //   byte[] mock-request       -> request
-        //   string mock-default-url   -> defaultUrl
-
-        index = mByteArrayProperties.indexOfKey(String8("mock-request"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-request' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            request = mByteArrayProperties.valueAt(index);
-        }
-
-        index = mStringProperties.indexOfKey(String8("mock-defaultUrl"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-defaultUrl' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            defaultUrl = mStringProperties.valueAt(index);
-        }
-        return OK;
-    }
-
-    status_t MockDrmPlugin::provideKeyResponse(Vector<uint8_t> const &sessionId,
-                                               Vector<uint8_t> const &response,
-                                               Vector<uint8_t> &keySetId)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::provideKeyResponse(sessionId=%s, response=%s)",
-              vectorToString(sessionId).string(), vectorToString(response).string());
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-        if (response.size() == 0) {
-            return BAD_VALUE;
-        }
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] response            -> mock-response
-        mByteArrayProperties.add(String8("mock-response"), response);
-
-        const size_t kKeySetIdSize = 8;
-
-        for (size_t i = 0; i < kKeySetIdSize / sizeof(long); i++) {
-            long r = random();
-            keySetId.appendArray((uint8_t *)&r, sizeof(long));
-        }
-        mKeySets.add(keySetId);
-
-        return OK;
-    }
-
-    status_t MockDrmPlugin::removeKeys(Vector<uint8_t> const &keySetId)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::removeKeys(keySetId=%s)",
-              vectorToString(keySetId).string());
-
-        ssize_t index = findKeySet(keySetId);
-        if (index == kNotFound) {
-            ALOGD("Invalid keySetId");
-            return BAD_VALUE;
-        }
-        mKeySets.removeAt(index);
-
-        return OK;
-    }
-
-    status_t MockDrmPlugin::restoreKeys(Vector<uint8_t> const &sessionId,
-                                        Vector<uint8_t> const &keySetId)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::restoreKeys(sessionId=%s, keySetId=%s)",
-              vectorToString(sessionId).string(),
-              vectorToString(keySetId).string());
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        index = findKeySet(keySetId);
-        if (index == kNotFound) {
-            ALOGD("Invalid keySetId");
-            return BAD_VALUE;
-        }
-
-        return OK;
-    }
-
-    status_t MockDrmPlugin::queryKeyStatus(Vector<uint8_t> const &sessionId,
-                                               KeyedVector<String8, String8> &infoMap) const
-    {
-        ALOGD("MockDrmPlugin::queryKeyStatus(sessionId=%s)",
-              vectorToString(sessionId).string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        infoMap.add(String8("purchaseDuration"), String8("1000"));
-        infoMap.add(String8("licenseDuration"), String8("100"));
-        return OK;
-    }
-
-    status_t MockDrmPlugin::getProvisionRequest(Vector<uint8_t> &request,
-                                                String8 &defaultUrl)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::getProvisionRequest()");
-
-        // Properties used in mock test, set by cts test app returned from mock plugin
-        //   byte[] mock-request       -> request
-        //   string mock-default-url   -> defaultUrl
-
-        ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-request"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-request' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            request = mByteArrayProperties.valueAt(index);
-        }
-
-        index = mStringProperties.indexOfKey(String8("mock-defaultUrl"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-defaultUrl' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            defaultUrl = mStringProperties.valueAt(index);
-        }
-        return OK;
-    }
-
-    status_t MockDrmPlugin::provideProvisionResponse(Vector<uint8_t> const &response)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::provideProvisionResponse(%s)",
-              vectorToString(response).string());
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] response            -> mock-response
-
-        mByteArrayProperties.add(String8("mock-response"), response);
-        return OK;
-    }
-
-    status_t MockDrmPlugin::getSecureStops(List<Vector<uint8_t> > &secureStops)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::getSecureStops()");
-
-        // Properties used in mock test, set by cts test app returned from mock plugin
-        //   byte[] mock-secure-stop1  -> first secure stop in list
-        //   byte[] mock-secure-stop2  -> second secure stop in list
-
-        Vector<uint8_t> ss1, ss2;
-        ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-secure-stop1"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-secure-stop1' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            ss1 = mByteArrayProperties.valueAt(index);
-        }
-
-        index = mByteArrayProperties.indexOfKey(String8("mock-secure-stop2"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-secure-stop2' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            ss2 = mByteArrayProperties.valueAt(index);
-        }
-
-        secureStops.push_back(ss1);
-        secureStops.push_back(ss2);
-        return OK;
-    }
-
-    status_t MockDrmPlugin::releaseSecureStops(Vector<uint8_t> const &ssRelease)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::releaseSecureStops(%s)",
-              vectorToString(ssRelease).string());
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] secure-stop-release  -> mock-ssrelease
-        mByteArrayProperties.add(String8("mock-ssrelease"), ssRelease);
-
-        return OK;
-    }
-
-    status_t MockDrmPlugin::getPropertyString(String8 const &name, String8 &value) const
-    {
-        ALOGD("MockDrmPlugin::getPropertyString(name=%s)", name.string());
-        ssize_t index = mStringProperties.indexOfKey(name);
-        if (index < 0) {
-            ALOGD("no property for '%s'", name.string());
-            return BAD_VALUE;
-        }
-        value = mStringProperties.valueAt(index);
-        return OK;
-    }
-
-    status_t MockDrmPlugin::getPropertyByteArray(String8 const &name,
-                                                 Vector<uint8_t> &value) const
-    {
-        ALOGD("MockDrmPlugin::getPropertyByteArray(name=%s)", name.string());
-        ssize_t index = mByteArrayProperties.indexOfKey(name);
-        if (index < 0) {
-            ALOGD("no property for '%s'", name.string());
-            return BAD_VALUE;
-        }
-        value = mByteArrayProperties.valueAt(index);
-        return OK;
-    }
-
-    status_t MockDrmPlugin::setPropertyString(String8 const &name,
-                                              String8 const &value)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::setPropertyString(name=%s, value=%s)",
-              name.string(), value.string());
-
-        if (name == "mock-send-event") {
-            unsigned code, extra;
-            sscanf(value.string(), "%d %d", &code, &extra);
-            DrmPlugin::EventType eventType = (DrmPlugin::EventType)code;
-
-            Vector<uint8_t> const *pSessionId = NULL;
-            ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-event-session-id"));
-            if (index >= 0) {
-                pSessionId = &mByteArrayProperties[index];
-            }
-
-            Vector<uint8_t> const *pData = NULL;
-            index = mByteArrayProperties.indexOfKey(String8("mock-event-data"));
-            if (index >= 0) {
-                pData = &mByteArrayProperties[index];
-            }
-            ALOGD("sending event from mock drm plugin: %d %d %s %s",
-                  (int)code, extra, pSessionId ? vectorToString(*pSessionId) : "{}",
-                  pData ? vectorToString(*pData) : "{}");
-
-            sendEvent(eventType, extra, pSessionId, pData);
-        } else {
-            mStringProperties.add(name, value);
-        }
-        return OK;
-    }
-
-    status_t MockDrmPlugin::setPropertyByteArray(String8 const &name,
-                                                 Vector<uint8_t> const &value)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::setPropertyByteArray(name=%s, value=%s)",
-              name.string(), vectorToString(value).string());
-        mByteArrayProperties.add(name, value);
-        return OK;
-    }
-
-    status_t MockDrmPlugin::setCipherAlgorithm(Vector<uint8_t> const &sessionId,
-                                               String8 const &algorithm)
-    {
-        Mutex::Autolock lock(mLock);
-
-        ALOGD("MockDrmPlugin::setCipherAlgorithm(sessionId=%s, algorithm=%s)",
-              vectorToString(sessionId).string(), algorithm.string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        if (algorithm == "AES/CBC/NoPadding") {
-            return OK;
-        }
-        return BAD_VALUE;
-    }
-
-    status_t MockDrmPlugin::setMacAlgorithm(Vector<uint8_t> const &sessionId,
-                                            String8 const &algorithm)
-    {
-        Mutex::Autolock lock(mLock);
-
-        ALOGD("MockDrmPlugin::setMacAlgorithm(sessionId=%s, algorithm=%s)",
-              vectorToString(sessionId).string(), algorithm.string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        if (algorithm == "HmacSHA256") {
-            return OK;
-        }
-        return BAD_VALUE;
-    }
-
-    status_t MockDrmPlugin::encrypt(Vector<uint8_t> const &sessionId,
-                                    Vector<uint8_t> const &keyId,
-                                    Vector<uint8_t> const &input,
-                                    Vector<uint8_t> const &iv,
-                                    Vector<uint8_t> &output)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::encrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
-              vectorToString(sessionId).string(),
-              vectorToString(keyId).string(),
-              vectorToString(input).string(),
-              vectorToString(iv).string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] keyId              -> mock-keyid
-        //   byte[] input              -> mock-input
-        //   byte[] iv                 -> mock-iv
-        mByteArrayProperties.add(String8("mock-keyid"), keyId);
-        mByteArrayProperties.add(String8("mock-input"), input);
-        mByteArrayProperties.add(String8("mock-iv"), iv);
-
-        // Properties used in mock test, set by cts test app returned from mock plugin
-        //   byte[] mock-output        -> output
-        index = mByteArrayProperties.indexOfKey(String8("mock-output"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-request' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            output = mByteArrayProperties.valueAt(index);
-        }
-        return OK;
-    }
-
-    status_t MockDrmPlugin::decrypt(Vector<uint8_t> const &sessionId,
-                                    Vector<uint8_t> const &keyId,
-                                    Vector<uint8_t> const &input,
-                                    Vector<uint8_t> const &iv,
-                                    Vector<uint8_t> &output)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::decrypt(sessionId=%s, keyId=%s, input=%s, iv=%s)",
-              vectorToString(sessionId).string(),
-              vectorToString(keyId).string(),
-              vectorToString(input).string(),
-              vectorToString(iv).string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] keyId              -> mock-keyid
-        //   byte[] input              -> mock-input
-        //   byte[] iv                 -> mock-iv
-        mByteArrayProperties.add(String8("mock-keyid"), keyId);
-        mByteArrayProperties.add(String8("mock-input"), input);
-        mByteArrayProperties.add(String8("mock-iv"), iv);
-
-        // Properties used in mock test, set by cts test app returned from mock plugin
-        //   byte[] mock-output        -> output
-        index = mByteArrayProperties.indexOfKey(String8("mock-output"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-request' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            output = mByteArrayProperties.valueAt(index);
-        }
-        return OK;
-    }
-
-    status_t MockDrmPlugin::sign(Vector<uint8_t> const &sessionId,
-                                 Vector<uint8_t> const &keyId,
-                                 Vector<uint8_t> const &message,
-                                 Vector<uint8_t> &signature)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::sign(sessionId=%s, keyId=%s, message=%s)",
-              vectorToString(sessionId).string(),
-              vectorToString(keyId).string(),
-              vectorToString(message).string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] keyId              -> mock-keyid
-        //   byte[] message            -> mock-message
-        mByteArrayProperties.add(String8("mock-keyid"), keyId);
-        mByteArrayProperties.add(String8("mock-message"), message);
-
-        // Properties used in mock test, set by cts test app returned from mock plugin
-        //   byte[] mock-signature        -> signature
-        index = mByteArrayProperties.indexOfKey(String8("mock-signature"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-request' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            signature = mByteArrayProperties.valueAt(index);
-        }
-        return OK;
-    }
-
-    status_t MockDrmPlugin::verify(Vector<uint8_t> const &sessionId,
-                                   Vector<uint8_t> const &keyId,
-                                   Vector<uint8_t> const &message,
-                                   Vector<uint8_t> const &signature,
-                                   bool &match)
-    {
-        Mutex::Autolock lock(mLock);
-        ALOGD("MockDrmPlugin::verify(sessionId=%s, keyId=%s, message=%s, signature=%s)",
-              vectorToString(sessionId).string(),
-              vectorToString(keyId).string(),
-              vectorToString(message).string(),
-              vectorToString(signature).string());
-
-        ssize_t index = findSession(sessionId);
-        if (index == kNotFound) {
-            ALOGD("Invalid sessionId");
-            return BAD_VALUE;
-        }
-
-        // Properties used in mock test, set by mock plugin and verifed cts test app
-        //   byte[] keyId              -> mock-keyid
-        //   byte[] message            -> mock-message
-        //   byte[] signature          -> mock-signature
-        mByteArrayProperties.add(String8("mock-keyid"), keyId);
-        mByteArrayProperties.add(String8("mock-message"), message);
-        mByteArrayProperties.add(String8("mock-signature"), signature);
-
-        // Properties used in mock test, set by cts test app returned from mock plugin
-        //   String mock-match "1" or "0"         -> match
-        index = mStringProperties.indexOfKey(String8("mock-match"));
-        if (index < 0) {
-            ALOGD("Missing 'mock-request' parameter for mock");
-            return BAD_VALUE;
-        } else {
-            match = atol(mStringProperties.valueAt(index).string());
-        }
-        return OK;
-    }
-
-    ssize_t MockDrmPlugin::findSession(Vector<uint8_t> const &sessionId) const
-    {
-        ALOGD("findSession: nsessions=%d, size=%d", mSessions.size(), sessionId.size());
-        for (size_t i = 0; i < mSessions.size(); ++i) {
-            if (memcmp(mSessions[i].array(), sessionId.array(), sessionId.size()) == 0) {
-                return i;
-            }
-        }
-        return kNotFound;
-    }
-
-    ssize_t MockDrmPlugin::findKeySet(Vector<uint8_t> const &keySetId) const
-    {
-        ALOGD("findKeySet: nkeySets=%d, size=%d", mKeySets.size(), keySetId.size());
-        for (size_t i = 0; i < mKeySets.size(); ++i) {
-            if (memcmp(mKeySets[i].array(), keySetId.array(), keySetId.size()) == 0) {
-                return i;
-            }
-        }
-        return kNotFound;
-    }
-
-
-    // Conversion utilities
-    String8 MockDrmPlugin::vectorToString(Vector<uint8_t> const &vector) const
-    {
-        return arrayToString(vector.array(), vector.size());
-    }
-
-    String8 MockDrmPlugin::arrayToString(uint8_t const *array, size_t len) const
-    {
-        String8 result("{ ");
-        for (size_t i = 0; i < len; i++) {
-            result.appendFormat("0x%02x ", array[i]);
-        }
-        result += "}";
-        return result;
-    }
-
-    String8 MockDrmPlugin::stringMapToString(KeyedVector<String8, String8> map) const
-    {
-        String8 result("{ ");
-        for (size_t i = 0; i < map.size(); i++) {
-            result.appendFormat("%s{name=%s, value=%s}", i > 0 ? ", " : "",
-                                map.keyAt(i).string(), map.valueAt(i).string());
-        }
-        return result + " }";
-    }
-
-    bool operator<(Vector<uint8_t> const &lhs, Vector<uint8_t> const &rhs) {
-        return lhs.size() < rhs.size() || (memcmp(lhs.array(), rhs.array(), lhs.size()) < 0);
-    }
-
-    //
-    // Crypto Plugin
-    //
-
-    bool MockCryptoPlugin::requiresSecureDecoderComponent(const char *mime) const
-    {
-        ALOGD("MockCryptoPlugin::requiresSecureDecoderComponent(mime=%s)", mime);
-        return false;
-    }
-
-    ssize_t
-    MockCryptoPlugin::decrypt(bool secure, const uint8_t key[16], const uint8_t iv[16],
-                              Mode mode, const void *srcPtr, const SubSample *subSamples,
-                              size_t numSubSamples, void *dstPtr, AString *errorDetailMsg)
-    {
-        ALOGD("MockCryptoPlugin::decrypt(secure=%d, key=%s, iv=%s, mode=%d, src=%p, "
-              "subSamples=%s, dst=%p)",
-              (int)secure,
-              arrayToString(key, sizeof(key)).string(),
-              arrayToString(iv, sizeof(iv)).string(),
-              (int)mode, srcPtr,
-              subSamplesToString(subSamples, numSubSamples).string(),
-              dstPtr);
-        return OK;
-    }
-
-    // Conversion utilities
-    String8 MockCryptoPlugin::arrayToString(uint8_t const *array, size_t len) const
-    {
-        String8 result("{ ");
-        for (size_t i = 0; i < len; i++) {
-            result.appendFormat("0x%02x ", array[i]);
-        }
-        result += "}";
-        return result;
-    }
-
-    String8 MockCryptoPlugin::subSamplesToString(SubSample const *subSamples,
-                                                 size_t numSubSamples) const
-    {
-        String8 result;
-        for (size_t i = 0; i < numSubSamples; i++) {
-            result.appendFormat("[%d] {clear:%d, encrypted:%d} ", i,
-                                subSamples[i].mNumBytesOfClearData,
-                                subSamples[i].mNumBytesOfEncryptedData);
-        }
-        return result;
-    }
-
-};
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
deleted file mode 100644
index 2297f9b..0000000
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2013 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 <utils/Mutex.h>
-
-#include "drm/DrmAPI.h"
-#include "hardware/CryptoAPI.h"
-
-extern "C" {
-      android::DrmFactory *createDrmFactory();
-      android::CryptoFactory *createCryptoFactory();
-}
-
-namespace android {
-
-    class MockDrmFactory : public DrmFactory {
-    public:
-        MockDrmFactory() {}
-        virtual ~MockDrmFactory() {}
-
-        bool isCryptoSchemeSupported(const uint8_t uuid[16]);
-        bool isContentTypeSupported(const String8 &mimeType);
-        status_t createDrmPlugin(const uint8_t uuid[16], DrmPlugin **plugin);
-    };
-
-    class MockCryptoFactory : public CryptoFactory {
-    public:
-        MockCryptoFactory() {}
-        virtual ~MockCryptoFactory() {}
-
-        bool isCryptoSchemeSupported(const uint8_t uuid[16]) const;
-        status_t createPlugin(
-            const uint8_t uuid[16], const void *data, size_t size,
-            CryptoPlugin **plugin);
-    };
-
-
-
-    class MockDrmPlugin : public DrmPlugin {
-    public:
-        MockDrmPlugin() {}
-        virtual ~MockDrmPlugin() {}
-
-        // from DrmPlugin
-        status_t openSession(Vector<uint8_t> &sessionId);
-        status_t closeSession(Vector<uint8_t> const &sessionId);
-
-        status_t getKeyRequest(Vector<uint8_t> const &sessionId,
-                               Vector<uint8_t> const &initData,
-                               String8 const &mimeType, KeyType keyType,
-                               KeyedVector<String8, String8> const &optionalParameters,
-                               Vector<uint8_t> &request, String8 &defaultUrl);
-
-        status_t provideKeyResponse(Vector<uint8_t> const &sessionId,
-                                    Vector<uint8_t> const &response,
-                                    Vector<uint8_t> &keySetId);
-
-        status_t removeKeys(Vector<uint8_t> const &keySetId);
-
-        status_t restoreKeys(Vector<uint8_t> const &sessionId,
-                             Vector<uint8_t> const &keySetId);
-
-        status_t queryKeyStatus(Vector<uint8_t> const &sessionId,
-                                KeyedVector<String8, String8> &infoMap) const;
-
-        status_t getProvisionRequest(Vector<uint8_t> &request,
-                                             String8 &defaultUrl);
-
-        status_t provideProvisionResponse(Vector<uint8_t> const &response);
-
-        status_t getSecureStops(List<Vector<uint8_t> > &secureStops);
-        status_t releaseSecureStops(Vector<uint8_t> const &ssRelease);
-
-        status_t getPropertyString(String8 const &name, String8 &value ) const;
-        status_t getPropertyByteArray(String8 const &name,
-                                              Vector<uint8_t> &value ) const;
-
-        status_t setPropertyString(String8 const &name,
-                                   String8 const &value );
-        status_t setPropertyByteArray(String8 const &name,
-                                      Vector<uint8_t> const &value );
-
-        status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
-                                    String8 const &algorithm);
-
-        status_t setMacAlgorithm(Vector<uint8_t> const &sessionId,
-                                 String8 const &algorithm);
-
-        status_t encrypt(Vector<uint8_t> const &sessionId,
-                         Vector<uint8_t> const &keyId,
-                         Vector<uint8_t> const &input,
-                         Vector<uint8_t> const &iv,
-                         Vector<uint8_t> &output);
-
-        status_t decrypt(Vector<uint8_t> const &sessionId,
-                         Vector<uint8_t> const &keyId,
-                         Vector<uint8_t> const &input,
-                         Vector<uint8_t> const &iv,
-                         Vector<uint8_t> &output);
-
-        status_t sign(Vector<uint8_t> const &sessionId,
-                      Vector<uint8_t> const &keyId,
-                      Vector<uint8_t> const &message,
-                      Vector<uint8_t> &signature);
-
-        status_t verify(Vector<uint8_t> const &sessionId,
-                        Vector<uint8_t> const &keyId,
-                        Vector<uint8_t> const &message,
-                        Vector<uint8_t> const &signature,
-                        bool &match);
-
-    private:
-        String8 vectorToString(Vector<uint8_t> const &vector) const;
-        String8 arrayToString(uint8_t const *array, size_t len) const;
-        String8 stringMapToString(KeyedVector<String8, String8> map) const;
-
-        SortedVector<Vector<uint8_t> > mSessions;
-        SortedVector<Vector<uint8_t> > mKeySets;
-
-        static const ssize_t kNotFound = -1;
-        ssize_t findSession(Vector<uint8_t> const &sessionId) const;
-        ssize_t findKeySet(Vector<uint8_t> const &keySetId) const;
-
-        Mutex mLock;
-        KeyedVector<String8, String8> mStringProperties;
-        KeyedVector<String8, Vector<uint8_t> > mByteArrayProperties;
-    };
-
-
-    class MockCryptoPlugin : public CryptoPlugin {
-
-        bool requiresSecureDecoderComponent(const char *mime) const;
-
-        ssize_t decrypt(bool secure,
-            const uint8_t key[16], const uint8_t iv[16],
-            Mode mode, const void *srcPtr,
-            const SubSample *subSamples, size_t numSubSamples,
-            void *dstPtr, AString *errorDetailMsg);
-    private:
-        String8 subSamplesToString(CryptoPlugin::SubSample const *subSamples, size_t numSubSamples) const;
-        String8 arrayToString(uint8_t const *array, size_t len) const;
-    };
-};
diff --git a/include/media/AudioTimestamp.h b/include/media/AudioTimestamp.h
new file mode 100644
index 0000000..c29c7e5
--- /dev/null
+++ b/include/media/AudioTimestamp.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 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 ANDROID_AUDIO_TIMESTAMP_H
+#define ANDROID_AUDIO_TIMESTAMP_H
+
+#include <time.h>
+
+class AudioTimestamp {
+public:
+    AudioTimestamp() : mPosition(0) {
+        mTime.tv_sec = 0;
+        mTime.tv_nsec = 0;
+    }
+    // FIXME change type to match android.media.AudioTrack
+    uint32_t        mPosition; // a frame position in AudioTrack::getPosition() units
+    struct timespec mTime;     // corresponding CLOCK_MONOTONIC when frame is expected to present
+};
+
+#endif  // ANDROID_AUDIO_TIMESTAMP_H
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index ae92cdd..453c106 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -19,6 +19,7 @@
 
 #include <cutils/sched_policy.h>
 #include <media/AudioSystem.h>
+#include <media/AudioTimestamp.h>
 #include <media/IAudioTrack.h>
 #include <utils/threads.h>
 
@@ -62,6 +63,9 @@
                                     // voluntary invalidation by mediaserver, or mediaserver crash.
         EVENT_STREAM_END = 7,       // Sent after all the buffers queued in AF and HW are played
                                     // back (after stop is called)
+        EVENT_NEW_TIMESTAMP = 8,    // Delivered periodically and when there's a significant change
+                                    // in the mapping from frame position to presentation time.
+                                    // See AudioTimestamp for the information included with event.
     };
 
     /* Client should declare Buffer on the stack and pass address to obtainBuffer()
@@ -107,6 +111,8 @@
      *          - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames.
      *          - EVENT_BUFFER_END: unused.
      *          - EVENT_NEW_IAUDIOTRACK: unused.
+     *          - EVENT_STREAM_END: unused.
+     *          - EVENT_NEW_TIMESTAMP: pointer to const AudioTimestamp.
      */
 
     typedef void (*callback_t)(int event, void* user, void *info);
@@ -221,6 +227,7 @@
      *  - INVALID_OPERATION: AudioTrack is already initialized
      *  - BAD_VALUE: invalid parameter (channelMask, format, sampleRate...)
      *  - NO_INIT: audio server or audio hardware not initialized
+     * If status is not equal to NO_ERROR, don't call any other APIs on this AudioTrack.
      * If sharedBuffer is non-0, the frameCount parameter is ignored and
      * replaced by the shared buffer's total allocated size in frame units.
      *
@@ -243,7 +250,7 @@
                             transfer_type transferType = TRANSFER_DEFAULT,
                             const audio_offload_info_t *offloadInfo = NULL);
 
-    /* Result of constructing the AudioTrack. This must be checked
+    /* Result of constructing the AudioTrack. This must be checked for successful initialization
      * before using any AudioTrack API (except for set()), because using
      * an uninitialized AudioTrack produces undefined results.
      * See set() method above for possible return codes.
@@ -564,6 +571,16 @@
     /* Get parameters */
             String8     getParameters(const String8& keys);
 
+    /* Poll for a timestamp on demand.
+     * Use if EVENT_NEW_TIMESTAMP is not delivered often enough for your needs,
+     * or if you need to get the most recent timestamp outside of the event callback handler.
+     * Caution: calling this method too often may be inefficient;
+     * if you need a high resolution mapping between frame position and presentation time,
+     * consider implementing that at application level, based on the low resolution timestamps.
+     * Returns NO_ERROR if timestamp is valid.
+     */
+            status_t    getTimestamp(AudioTimestamp& timestamp);
+
 protected:
     /* copying audio tracks is not allowed */
                         AudioTrack(const AudioTrack& other);
@@ -630,7 +647,7 @@
             bool     isOffloaded() const
                 { return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
 
-    // may be changed if IAudioTrack is re-created
+    // Next 3 fields may be changed if IAudioTrack is re-created, but always != 0
     sp<IAudioTrack>         mAudioTrack;
     sp<IMemory>             mCblkMemory;
     audio_track_cblk_t*     mCblk;                  // re-load after mLock.unlock()
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index 1014403..afac4ae 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -26,6 +26,7 @@
 #include <binder/IMemory.h>
 #include <utils/LinearTransform.h>
 #include <utils/String8.h>
+#include <media/AudioTimestamp.h>
 
 namespace android {
 
@@ -86,6 +87,9 @@
 
     /* Send parameters to the audio hardware */
     virtual status_t    setParameters(const String8& keyValuePairs) = 0;
+
+    /* Return NO_ERROR if timestamp is valid */
+    virtual status_t    getTimestamp(AudioTimestamp& timestamp) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 1379379..ad7409d 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -422,6 +422,9 @@
     // Return the total number of frames which AudioFlinger desired but were unavailable,
     // and thus which resulted in an underrun.
     virtual uint32_t    getUnderrunFrames() const { return mCblk->u.mStreaming.mUnderrunFrames; }
+
+    // Return the total number of frames that AudioFlinger has obtained and released
+    virtual size_t      framesReleased() const { return mCblk->mServer; }
 };
 
 class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 214e789..176197c 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -148,10 +148,8 @@
             mAudioTrackThread->requestExitAndWait();
             mAudioTrackThread.clear();
         }
-        if (mAudioTrack != 0) {
-            mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
-            mAudioTrack.clear();
-        }
+        mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
+        mAudioTrack.clear();
         IPCThreadState::self()->flushCommands();
         AudioSystem::releaseAudioSessionId(mSessionId);
     }
@@ -222,6 +220,7 @@
 
     AutoMutex lock(mLock);
 
+    // invariant that mAudioTrack != 0 is true only after set() returns successfully
     if (mAudioTrack != 0) {
         ALOGE("Track already in use");
         return INVALID_OPERATION;
@@ -968,6 +967,7 @@
         ALOGE("Could not get control block");
         return NO_INIT;
     }
+    // invariant that mAudioTrack != 0 is true only after set() returns successfully
     if (mAudioTrack != 0) {
         mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
         mDeathNotifier.clear();
@@ -1550,7 +1550,7 @@
             return NS_NEVER;
         }
 
-        if (mRetryOnPartialBuffer) {
+        if (mRetryOnPartialBuffer && !isOffloaded()) {
             mRetryOnPartialBuffer = false;
             if (avail < mRemainingFrames) {
                 int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
@@ -1708,11 +1708,13 @@
 status_t AudioTrack::setParameters(const String8& keyValuePairs)
 {
     AutoMutex lock(mLock);
-    if (mAudioTrack != 0) {
-        return mAudioTrack->setParameters(keyValuePairs);
-    } else {
-        return NO_INIT;
-    }
+    return mAudioTrack->setParameters(keyValuePairs);
+}
+
+status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
+{
+    AutoMutex lock(mLock);
+    return mAudioTrack->getTimestamp(timestamp);
 }
 
 String8 AudioTrack::getParameters(const String8& keys)
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index a2b49a3..f0d75ba 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -39,7 +39,8 @@
     ALLOCATE_TIMED_BUFFER,
     QUEUE_TIMED_BUFFER,
     SET_MEDIA_TIME_TRANSFORM,
-    SET_PARAMETERS
+    SET_PARAMETERS,
+    GET_TIMESTAMP,
 };
 
 class BpAudioTrack : public BpInterface<IAudioTrack>
@@ -166,6 +167,21 @@
         }
         return status;
     }
+
+    virtual status_t getTimestamp(AudioTimestamp& timestamp) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+        status_t status = remote()->transact(GET_TIMESTAMP, data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+            if (status == NO_ERROR) {
+                timestamp.mPosition = reply.readInt32();
+                timestamp.mTime.tv_sec = reply.readInt32();
+                timestamp.mTime.tv_nsec = reply.readInt32();
+            }
+        }
+        return status;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
@@ -241,6 +257,18 @@
             reply->writeInt32(setParameters(keyValuePairs));
             return NO_ERROR;
         } break;
+        case GET_TIMESTAMP: {
+            CHECK_INTERFACE(IAudioTrack, data, reply);
+            AudioTimestamp timestamp;
+            status_t status = getTimestamp(timestamp);
+            reply->writeInt32(status);
+            if (status == NO_ERROR) {
+                reply->writeInt32(timestamp.mPosition);
+                reply->writeInt32(timestamp.mTime.tv_sec);
+                reply->writeInt32(timestamp.mTime.tv_nsec);
+            }
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 4b527d0..eebcb79 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -222,7 +222,11 @@
         }
     }
 
-    return mFactory->isContentTypeSupported(mimeType);
+    if (mimeType != "") {
+        return mFactory->isContentTypeSupported(mimeType);
+    }
+
+    return true;
 }
 
 status_t Drm::createPlugin(const uint8_t uuid[16]) {
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 2418aab..e38e261 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -680,6 +680,14 @@
 
 int64_t AudioPlayer::getRealTimeUs() {
     Mutex::Autolock autoLock(mLock);
+    if (useOffload()) {
+        if (mSeeking) {
+            return mSeekTimeUs;
+        }
+        mPositionTimeRealUs = getOutputPlayPositionUs_l();
+        return mPositionTimeRealUs;
+    }
+
     return getRealTimeUsLocked();
 }
 
@@ -741,11 +749,6 @@
         return 0;
     }
 
-    if (useOffload()) {
-        mPositionTimeRealUs = getOutputPlayPositionUs_l();
-        return mPositionTimeRealUs;
-    }
-
     int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs;
     if (realTimeOffset < 0) {
         realTimeOffset = 0;
@@ -758,8 +761,14 @@
         int64_t *realtime_us, int64_t *mediatime_us) {
     Mutex::Autolock autoLock(mLock);
 
-    *realtime_us = mPositionTimeRealUs;
-    *mediatime_us = mPositionTimeMediaUs;
+    if (useOffload()) {
+        mPositionTimeRealUs = getOutputPlayPositionUs_l();
+        *realtime_us = mPositionTimeRealUs;
+        *mediatime_us = mPositionTimeRealUs;
+    } else {
+        *realtime_us = mPositionTimeRealUs;
+        *mediatime_us = mPositionTimeMediaUs;
+    }
 
     return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1;
 }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 52e178e..5fbee7e 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -2805,7 +2805,6 @@
 
     // Reset and recreate
     reset_l();
-    mFlags |= PREPARING;
 
     status_t err;
 
@@ -2816,6 +2815,7 @@
         err = setDataSource_l(uri, &uriHeaders);
     }
 
+    mFlags |= PREPARING;
     if ( err != OK ) {
         // This will force beingPrepareAsync_l() to notify
         // a MEDIA_ERROR to the client and abort the prepare
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e5e4113..5df04f4 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -411,6 +411,7 @@
         virtual status_t    setMediaTimeTransform(const LinearTransform& xform,
                                                   int target);
         virtual status_t    setParameters(const String8& keyValuePairs);
+        virtual status_t    getTimestamp(AudioTimestamp& timestamp);
 
         virtual status_t onTransact(
             uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 5600411c..d34833f 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -59,6 +59,7 @@
             void        setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
             int16_t     *mainBuffer() const { return mMainBuffer; }
             int         auxEffectId() const { return mAuxEffectId; }
+    virtual status_t    getTimestamp(AudioTimestamp& timestamp);
 
 // implement FastMixerState::VolumeProvider interface
     virtual uint32_t    getVolumeLR();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 2c2931f..bc01ede 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -943,7 +943,9 @@
         mDraining(false),
         mScreenState(AudioFlinger::mScreenState),
         // index 0 is reserved for normal mixer's submix
-        mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1)
+        mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+        // mLatchD, mLatchQ,
+        mLatchDValid(false), mLatchQValid(false)
 {
     snprintf(mName, kNameLength, "AudioOut_%X", id);
     mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
@@ -1818,6 +1820,14 @@
         } else {
             bytesWritten = framesWritten;
         }
+        status_t status = INVALID_OPERATION;    // mLatchD.mTimestamp is invalid
+        if (status == NO_ERROR) {
+            size_t totalFramesWritten = mNormalSink->framesWritten();
+            if (totalFramesWritten >= mLatchD.mTimestamp.mPosition) {
+                mLatchD.mUnpresentedFrames = totalFramesWritten - mLatchD.mTimestamp.mPosition;
+                mLatchDValid = true;
+            }
+        }
     // otherwise use the HAL / AudioStreamOut directly
     } else {
         // Direct output and offload threads
@@ -2096,6 +2106,12 @@
                 logString = NULL;
             }
 
+            if (mLatchDValid) {
+                mLatchQ = mLatchD;
+                mLatchDValid = false;
+                mLatchQValid = true;
+            }
+
             if (checkForNewParameters_l()) {
                 cacheParameters_l();
             }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 31d5323..1333de2 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -606,6 +606,17 @@
                 // accessed by both binder threads and within threadLoop(), lock on mutex needed
                 unsigned    mFastTrackAvailMask;    // bit i set if fast track [i] is available
     virtual     void        flushOutput_l();
+
+private:
+    // timestamp latch:
+    //  D input is written by threadLoop_write while mutex is unlocked, and read while locked
+    //  Q output is written while locked, and read while locked
+    struct {
+        AudioTimestamp  mTimestamp;
+        uint32_t        mUnpresentedFrames;
+    } mLatchD, mLatchQ;
+    bool mLatchDValid;  // true means mLatchD is valid, and clock it into latch at next opportunity
+    bool mLatchQValid;  // true means mLatchQ is valid
 };
 
 class MixerThread : public PlaybackThread {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index e676365..9622709 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -283,6 +283,11 @@
     return mTrack->setParameters(keyValuePairs);
 }
 
+status_t AudioFlinger::TrackHandle::getTimestamp(AudioTimestamp& timestamp)
+{
+    return mTrack->getTimestamp(timestamp);
+}
+
 status_t AudioFlinger::TrackHandle::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
@@ -711,6 +716,29 @@
     }
 }
 
+status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
+{
+    sp<ThreadBase> thread = mThread.promote();
+    if (thread == 0) {
+        return false;
+    }
+    Mutex::Autolock _l(thread->mLock);
+    PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+    if (!playbackThread->mLatchQValid) {
+        return INVALID_OPERATION;
+    }
+    uint32_t unpresentedFrames =
+            ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
+            playbackThread->mSampleRate;
+    uint32_t framesWritten = mAudioTrackServerProxy->framesReleased();
+    if (framesWritten < unpresentedFrames) {
+        return INVALID_OPERATION;
+    }
+    timestamp.mPosition = framesWritten - unpresentedFrames;
+    timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime;
+    return NO_ERROR;
+}
+
 status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId)
 {
     status_t status = DEAD_OBJECT;