Merge "NuPlayerRenderer: Do not deliver audio too soon after stop" into mnc-dr-dev
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index a3f014b..18a933f 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -1104,8 +1104,10 @@
         case OPEN_OUTPUT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             audio_module_handle_t module = (audio_module_handle_t)data.readInt32();
-            audio_config_t config;
-            data.read(&config, sizeof(audio_config_t));
+            audio_config_t config = {};
+            if (data.read(&config, sizeof(audio_config_t)) != NO_ERROR) {
+                ALOGE("b/23905951");
+            }
             audio_devices_t devices = (audio_devices_t)data.readInt32();
             String8 address(data.readString8());
             audio_output_flags_t flags = (audio_output_flags_t) data.readInt32();
@@ -1149,8 +1151,10 @@
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             audio_module_handle_t module = (audio_module_handle_t)data.readInt32();
             audio_io_handle_t input = (audio_io_handle_t)data.readInt32();
-            audio_config_t config;
-            data.read(&config, sizeof(audio_config_t));
+            audio_config_t config = {};
+            if (data.read(&config, sizeof(audio_config_t)) != NO_ERROR) {
+                ALOGE("b/23905951");
+            }
             audio_devices_t device = (audio_devices_t)data.readInt32();
             String8 address(data.readString8());
             audio_source_t source = (audio_source_t)data.readInt32();
@@ -1255,8 +1259,10 @@
         }
         case CREATE_EFFECT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            effect_descriptor_t desc;
-            data.read(&desc, sizeof(effect_descriptor_t));
+            effect_descriptor_t desc = {};
+            if (data.read(&desc, sizeof(effect_descriptor_t)) != NO_ERROR) {
+                ALOGE("b/23905951");
+            }
             sp<IEffectClient> client = interface_cast<IEffectClient>(data.readStrongBinder());
             int32_t priority = data.readInt32();
             audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
@@ -1333,8 +1339,10 @@
         } break;
         case GET_AUDIO_PORT: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
-            struct audio_port port;
-            data.read(&port, sizeof(struct audio_port));
+            struct audio_port port = {};
+            if (data.read(&port, sizeof(struct audio_port)) != NO_ERROR) {
+                ALOGE("b/23905951");
+            }
             status_t status = getAudioPort(&port);
             reply->writeInt32(status);
             if (status == NO_ERROR) {
@@ -1346,8 +1354,10 @@
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             struct audio_patch patch;
             data.read(&patch, sizeof(struct audio_patch));
-            audio_patch_handle_t handle;
-            data.read(&handle, sizeof(audio_patch_handle_t));
+            audio_patch_handle_t handle = {};
+            if (data.read(&handle, sizeof(audio_patch_handle_t)) != NO_ERROR) {
+                ALOGE("b/23905951");
+            }
             status_t status = createAudioPatch(&patch, &handle);
             reply->writeInt32(status);
             if (status == NO_ERROR) {
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 3348441..0cc954f 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -877,7 +877,7 @@
             if (hasOffloadInfo) {
                 data.read(&offloadInfo, sizeof(audio_offload_info_t));
             }
-            audio_io_handle_t output;
+            audio_io_handle_t output = 0;
             status_t status = getOutputForAttr(hasAttributes ? &attr : NULL,
                     &output, session, &stream, uid,
                     samplingRate, format, channelMask,
@@ -994,7 +994,7 @@
             audio_stream_type_t stream =
                     static_cast <audio_stream_type_t>(data.readInt32());
             audio_devices_t device = static_cast <audio_devices_t>(data.readInt32());
-            int index;
+            int index = 0;
             status_t status = getStreamVolumeIndex(stream, &index, device);
             reply->writeInt32(index);
             reply->writeInt32(static_cast <uint32_t>(status));
@@ -1148,8 +1148,10 @@
 
         case GET_AUDIO_PORT: {
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
-            struct audio_port port;
-            data.read(&port, sizeof(struct audio_port));
+            struct audio_port port = {};
+            if (data.read(&port, sizeof(struct audio_port)) != NO_ERROR) {
+                ALOGE("b/23912202");
+            }
             status_t status = getAudioPort(&port);
             reply->writeInt32(status);
             if (status == NO_ERROR) {
@@ -1162,8 +1164,10 @@
             CHECK_INTERFACE(IAudioPolicyService, data, reply);
             struct audio_patch patch;
             data.read(&patch, sizeof(struct audio_patch));
-            audio_patch_handle_t handle;
-            data.read(&handle, sizeof(audio_patch_handle_t));
+            audio_patch_handle_t handle = {};
+            if (data.read(&handle, sizeof(audio_patch_handle_t)) != NO_ERROR) {
+                ALOGE("b/23912202");
+            }
             status_t status = createAudioPatch(&patch, &handle);
             reply->writeInt32(status);
             if (status == NO_ERROR) {
@@ -1292,7 +1296,7 @@
             data.read(&source, sizeof(struct audio_port_config));
             audio_attributes_t attributes;
             data.read(&attributes, sizeof(audio_attributes_t));
-            audio_io_handle_t handle;
+            audio_io_handle_t handle = {};
             status_t status = startAudioSource(&source, &attributes, &handle);
             reply->writeInt32(status);
             reply->writeInt32(handle);
diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp
index 761b243..faf5795 100644
--- a/media/libmedia/IEffect.cpp
+++ b/media/libmedia/IEffect.cpp
@@ -85,13 +85,15 @@
         data.writeInt32(size);
 
         status_t status = remote()->transact(COMMAND, data, &reply);
+        if (status == NO_ERROR) {
+            status = reply.readInt32();
+        }
         if (status != NO_ERROR) {
             if (pReplySize != NULL)
                 *pReplySize = 0;
             return status;
         }
 
-        status = reply.readInt32();
         size = reply.readInt32();
         if (size != 0 && pReplyData != NULL && pReplySize != NULL) {
             reply.read(pReplyData, size);
@@ -155,6 +157,10 @@
             char *cmd = NULL;
             if (cmdSize) {
                 cmd = (char *)calloc(cmdSize, 1);
+                if (cmd == NULL) {
+                    reply->writeInt32(NO_MEMORY);
+                    return NO_ERROR;
+                }
                 data.read(cmd, cmdSize);
             }
             uint32_t replySize = data.readInt32();
@@ -162,15 +168,22 @@
             char *resp = NULL;
             if (replySize) {
                 resp = (char *)calloc(replySize, 1);
+                if (resp == NULL) {
+                    free(cmd);
+                    reply->writeInt32(NO_MEMORY);
+                    return NO_ERROR;
+                }
             }
             status_t status = command(cmdCode, cmdSize, cmd, &replySz, resp);
             reply->writeInt32(status);
-            if (replySz < replySize) {
-                replySize = replySz;
-            }
-            reply->writeInt32(replySize);
-            if (replySize) {
-                reply->write(resp, replySize);
+            if (status == NO_ERROR) {
+                if (replySz < replySize) {
+                    replySize = replySz;
+                }
+                reply->writeInt32(replySize);
+                if (replySize) {
+                    reply->write(resp, replySize);
+                }
             }
             if (cmd) {
                 free(cmd);
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 7387a79..942aec3 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -566,7 +566,7 @@
         } break;
         case GET_CURRENT_POSITION: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
-            int msec;
+            int msec = 0;
             status_t ret = getCurrentPosition(&msec);
             reply->writeInt32(msec);
             reply->writeInt32(ret);
@@ -574,7 +574,7 @@
         } break;
         case GET_DURATION: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
-            int msec;
+            int msec = 0;
             status_t ret = getDuration(&msec);
             reply->writeInt32(msec);
             reply->writeInt32(ret);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 7dc9be7..b3eb5fd 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -467,9 +467,17 @@
 
 void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
     if (err != OK) {
-        mDataSource.clear();
-        mCachedSource.clear();
-        mHttpSource.clear();
+        {
+            sp<DataSource> dataSource = mDataSource;
+            sp<NuCachedSource2> cachedSource = mCachedSource;
+            sp<DataSource> httpSource = mHttpSource;
+            {
+                Mutex::Autolock _l(mDisconnectLock);
+                mDataSource.clear();
+                mCachedSource.clear();
+                mHttpSource.clear();
+            }
+        }
         mBitrate = -1;
 
         cancelPollBuffering();
@@ -522,13 +530,20 @@
 }
 
 void NuPlayer::GenericSource::disconnect() {
-    if (mDataSource != NULL) {
+    sp<DataSource> dataSource, httpSource;
+    {
+        Mutex::Autolock _l(mDisconnectLock);
+        dataSource = mDataSource;
+        httpSource = mHttpSource;
+    }
+
+    if (dataSource != NULL) {
         // disconnect data source
-        if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
-            static_cast<NuCachedSource2 *>(mDataSource.get())->disconnect();
+        if (dataSource->flags() & DataSource::kIsCachingDataSource) {
+            static_cast<NuCachedSource2 *>(dataSource.get())->disconnect();
         }
-    } else if (mHttpSource != NULL) {
-        static_cast<HTTPBase *>(mHttpSource.get())->disconnect();
+    } else if (httpSource != NULL) {
+        static_cast<HTTPBase *>(httpSource.get())->disconnect();
     }
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index dc85d2d..ac980ef 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -153,6 +153,7 @@
     int32_t mPrevBufferPercentage;
 
     mutable Mutex mReadBufferLock;
+    mutable Mutex mDisconnectLock;
 
     sp<ALooper> mLooper;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index de7f5e7..bca2888 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -886,6 +886,8 @@
                 ALOGV("AudioSink write would block when writing %zu bytes", copy);
             } else {
                 ALOGE("AudioSink write error(%zd) when writing %zu bytes", written, copy);
+                // This can only happen when AudioSink was opened with doNotReconnect flag set to
+                // true, in which case the NuPlayer will handle the reconnect.
                 notifyAudioTearDown();
             }
             break;
@@ -952,6 +954,10 @@
 int64_t NuPlayer::Renderer::getDurationUsIfPlayedAtSampleRate(uint32_t numFrames) {
     int32_t sampleRate = offloadingAudio() ?
             mCurrentOffloadInfo.sample_rate : mCurrentPcmInfo.mSampleRate;
+    if (sampleRate == 0) {
+        ALOGE("sampleRate is 0 in %s mode", offloadingAudio() ? "offload" : "non-offload");
+        return 0;
+    }
     // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
     return (int64_t)((int32_t)numFrames * 1000000LL / sampleRate);
 }
@@ -1781,6 +1787,12 @@
         const uint32_t frameCount =
                 (unsigned long long)sampleRate * getAudioSinkPcmMsSetting() / 1000;
 
+        // The doNotReconnect means AudioSink will signal back and let NuPlayer to re-construct
+        // AudioSink. We don't want this when there's video because it will cause a video seek to
+        // the previous I frame. But we do want this when there's only audio because it will give
+        // NuPlayer a chance to switch from non-offload mode to offload mode.
+        // So we only set doNotReconnect when there's no video.
+        const bool doNotReconnect = !hasVideo;
         status_t err = mAudioSink->open(
                     sampleRate,
                     numChannels,
@@ -1791,13 +1803,14 @@
                     mUseAudioCallback ? this : NULL,
                     (audio_output_flags_t)pcmFlags,
                     NULL,
-                    true /* doNotReconnect */,
+                    doNotReconnect,
                     frameCount);
         if (err == OK) {
             err = mAudioSink->setPlaybackRate(mPlaybackSettings);
         }
         if (err != OK) {
             ALOGW("openAudioSink: non offloaded open failed status: %d", err);
+            mAudioSink->close();
             mCurrentPcmInfo = AUDIO_PCMINFO_INITIALIZER;
             return err;
         }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 4e6c2a6..3cd0b0e 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -2290,11 +2290,11 @@
             // The widevine extractor does its own caching.
 
 #if 0
-            mCachedSource = new NuCachedSource2(
+            mCachedSource = NuCachedSource2::Create(
                     new ThrottledSource(
                         mConnectingDataSource, 50 * 1024 /* bytes/sec */));
 #else
-            mCachedSource = new NuCachedSource2(
+            mCachedSource = NuCachedSource2::Create(
                     mConnectingDataSource,
                     cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
                     disconnectAtHighwatermark);
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 75ef288..5020c6c 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -246,7 +246,7 @@
                 *contentType = httpSource->getMIMEType();
             }
 
-            source = new NuCachedSource2(
+            source = NuCachedSource2::Create(
                     httpSource,
                     cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
                     disconnectAtHighwatermark);
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index a76334f..38ae6f3 100755
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1952,15 +1952,14 @@
                 return ERROR_IO;
             }
 
-            if (mLastTrack == NULL)
-                return ERROR_MALFORMED;
-
             uint32_t type = ntohl(buffer);
             // For the 3GPP file format, the handler-type within the 'hdlr' box
             // shall be 'text'. We also want to support 'sbtl' handler type
             // for a practical reason as various MPEG4 containers use it.
             if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) {
-                mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+                if (mLastTrack != NULL) {
+                    mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP);
+                }
             }
 
             break;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index cd59709..7019537 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -572,6 +572,7 @@
 }
 
 status_t MediaCodec::reclaim() {
+    ALOGD("MediaCodec::reclaim(%p) %s", this, mInitName.c_str());
     sp<AMessage> msg = new AMessage(kWhatRelease, this);
     msg->setInt32("reclaimed", 1);
 
@@ -1154,8 +1155,10 @@
                         resourceType = String8(kResourceNonSecureCodec);
                     }
 
-                    const char *subtype = mIsVideo ? kResourceVideoCodec : kResourceAudioCodec;
-                    addResource(resourceType, String8(subtype), 1);
+                    if (mIsVideo) {
+                        // audio codec is currently ignored.
+                        addResource(resourceType, String8(kResourceVideoCodec), 1);
+                    }
 
                     (new AMessage)->postReply(mReplyID);
                     break;
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index f82636b..d6255d6 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -224,9 +224,6 @@
     // So whenever we call DataSource::readAt it may end up in a call to
     // IMediaHTTPConnection::readAt and therefore call back into JAVA.
     mLooper->start(false /* runOnCallingThread */, true /* canCallJava */);
-
-    Mutex::Autolock autoLock(mLock);
-    (new AMessage(kWhatFetchMore, mReflector))->post();
 }
 
 NuCachedSource2::~NuCachedSource2() {
@@ -237,6 +234,18 @@
     mCache = NULL;
 }
 
+// static
+sp<NuCachedSource2> NuCachedSource2::Create(
+        const sp<DataSource> &source,
+        const char *cacheConfig,
+        bool disconnectAtHighwatermark) {
+    sp<NuCachedSource2> instance = new NuCachedSource2(
+            source, cacheConfig, disconnectAtHighwatermark);
+    Mutex::Autolock autoLock(instance->mLock);
+    (new AMessage(kWhatFetchMore, instance->mReflector))->post();
+    return instance;
+}
+
 status_t NuCachedSource2::getEstimatedBandwidthKbps(int32_t *kbps) {
     if (mSource->flags() & kIsHTTPBasedSource) {
         HTTPBase* source = static_cast<HTTPBase *>(mSource.get());
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 6fba8e1..578171f 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -23,6 +23,7 @@
 #include <cutils/properties.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/base64.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
@@ -1202,83 +1203,18 @@
 
 }
 
-// The returned buffer should be free()d.
-static uint8_t *DecodeBase64(const char *s, size_t size, size_t *outSize) {
-    *outSize = 0;
-
-    if ((size % 4) != 0) {
-        return NULL;
-    }
-
-    size_t n = size;
-    size_t padding = 0;
-    if (n >= 1 && s[n - 1] == '=') {
-        padding = 1;
-
-        if (n >= 2 && s[n - 2] == '=') {
-            padding = 2;
-        }
-    }
-
-    size_t outLen = 3 * size / 4 - padding;
-
-    *outSize = outLen;
-
-    void *buffer = malloc(outLen);
-
-    uint8_t *out = (uint8_t *)buffer;
-    size_t j = 0;
-    uint32_t accum = 0;
-    for (size_t i = 0; i < n; ++i) {
-        char c = s[i];
-        unsigned value;
-        if (c >= 'A' && c <= 'Z') {
-            value = c - 'A';
-        } else if (c >= 'a' && c <= 'z') {
-            value = 26 + c - 'a';
-        } else if (c >= '0' && c <= '9') {
-            value = 52 + c - '0';
-        } else if (c == '+') {
-            value = 62;
-        } else if (c == '/') {
-            value = 63;
-        } else if (c != '=') {
-            return NULL;
-        } else {
-            if (i < n - padding) {
-                return NULL;
-            }
-
-            value = 0;
-        }
-
-        accum = (accum << 6) | value;
-
-        if (((i + 1) % 4) == 0) {
-            out[j++] = (accum >> 16);
-
-            if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
-            if (j < outLen) { out[j++] = accum & 0xff; }
-
-            accum = 0;
-        }
-    }
-
-    return (uint8_t *)buffer;
-}
-
 static void extractAlbumArt(
         const sp<MetaData> &fileMeta, const void *data, size_t size) {
     ALOGV("extractAlbumArt from '%s'", (const char *)data);
 
-    size_t flacSize;
-    uint8_t *flac = DecodeBase64((const char *)data, size, &flacSize);
-
-    if (flac == NULL) {
+    sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size));
+    if (flacBuffer == NULL) {
         ALOGE("malformed base64 encoded data.");
         return;
     }
 
+    size_t flacSize = flacBuffer->size();
+    uint8_t *flac = flacBuffer->data();
     ALOGV("got flac of size %zu", flacSize);
 
     uint32_t picType;
@@ -1288,24 +1224,24 @@
     char type[128];
 
     if (flacSize < 8) {
-        goto exit;
+        return;
     }
 
     picType = U32_AT(flac);
 
     if (picType != 3) {
         // This is not a front cover.
-        goto exit;
+        return;
     }
 
     typeLen = U32_AT(&flac[4]);
     if (typeLen > sizeof(type) - 1) {
-        goto exit;
+        return;
     }
 
     // we've already checked above that flacSize >= 8
     if (flacSize - 8 < typeLen) {
-        goto exit;
+        return;
     }
 
     memcpy(type, &flac[8], typeLen);
@@ -1315,7 +1251,7 @@
 
     if (!strcmp(type, "-->")) {
         // This is not inline cover art, but an external url instead.
-        goto exit;
+        return;
     }
 
     descLen = U32_AT(&flac[8 + typeLen]);
@@ -1323,7 +1259,7 @@
     if (flacSize < 32 ||
         flacSize - 32 < typeLen ||
         flacSize - 32 - typeLen < descLen) {
-        goto exit;
+        return;
     }
 
     dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);
@@ -1331,7 +1267,7 @@
 
     // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
     if (flacSize - 32 - typeLen - descLen < dataLen) {
-        goto exit;
+        return;
     }
 
     ALOGV("got image data, %zu trailing bytes",
@@ -1341,10 +1277,6 @@
             kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);
 
     fileMeta->setCString(kKeyAlbumArtMIME, type);
-
-exit:
-    free(flac);
-    flac = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index f0a7277..7c8d441 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -211,8 +211,10 @@
 
         const uint8_t *ptr = (const uint8_t *)data;
 
-        CHECK(size >= 7);
-        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+        if (size < 7 || ptr[0] != 1) {  // configurationVersion == 1
+            ALOGE("b/23680780");
+            return BAD_VALUE;
+        }
         uint8_t profile __unused = ptr[1];
         uint8_t level __unused = ptr[3];
 
@@ -238,7 +240,10 @@
         buffer->setRange(0, 0);
 
         for (size_t i = 0; i < numSeqParameterSets; ++i) {
-            CHECK(size >= 2);
+            if (size < 2) {
+                ALOGE("b/23680780");
+                return BAD_VALUE;
+            }
             size_t length = U16_AT(ptr);
 
             ptr += 2;
@@ -267,13 +272,19 @@
         }
         buffer->setRange(0, 0);
 
-        CHECK(size >= 1);
+        if (size < 1) {
+            ALOGE("b/23680780");
+            return BAD_VALUE;
+        }
         size_t numPictureParameterSets = *ptr;
         ++ptr;
         --size;
 
         for (size_t i = 0; i < numPictureParameterSets; ++i) {
-            CHECK(size >= 2);
+            if (size < 2) {
+                ALOGE("b/23680780");
+                return BAD_VALUE;
+            }
             size_t length = U16_AT(ptr);
 
             ptr += 2;
@@ -297,8 +308,10 @@
     } else if (meta->findData(kKeyHVCC, &type, &data, &size)) {
         const uint8_t *ptr = (const uint8_t *)data;
 
-        CHECK(size >= 7);
-        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
+        if (size < 23 || ptr[0] != 1) {  // configurationVersion == 1
+            ALOGE("b/23680780");
+            return BAD_VALUE;
+        }
         uint8_t profile __unused = ptr[1] & 31;
         uint8_t level __unused = ptr[12];
         ptr += 22;
@@ -317,6 +330,10 @@
         buffer->setRange(0, 0);
 
         for (i = 0; i < numofArrays; i++) {
+            if (size < 3) {
+                ALOGE("b/23680780");
+                return BAD_VALUE;
+            }
             ptr += 1;
             size -= 1;
 
@@ -327,7 +344,10 @@
             size -= 2;
 
             for (j = 0; j < numofNals; j++) {
-                CHECK(size >= 2);
+                if (size < 2) {
+                    ALOGE("b/23680780");
+                    return BAD_VALUE;
+                }
                 size_t length = U16_AT(ptr);
 
                 ptr += 2;
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.mk b/media/libstagefright/codecs/amrnb/dec/Android.mk
index 3750e2e..76a7f40 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.mk
+++ b/media/libstagefright/codecs/amrnb/dec/Android.mk
@@ -98,7 +98,7 @@
         libstagefright_amrnbdec libsndfile
 
 LOCAL_SHARED_LIBRARIES := \
-        libstagefright_amrnb_common libaudioutils
+        libstagefright_amrnb_common libaudioutils liblog
 
 LOCAL_MODULE := libstagefright_amrnbdec_test
 LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/codecs/amrnb/dec/src/a_refl.cpp b/media/libstagefright/codecs/amrnb/dec/src/a_refl.cpp
index fb7cff3..696d2da 100644
--- a/media/libstagefright/codecs/amrnb/dec/src/a_refl.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/src/a_refl.cpp
@@ -59,6 +59,8 @@
 /*----------------------------------------------------------------------------
 ; INCLUDES
 ----------------------------------------------------------------------------*/
+#include <log/log.h>
+
 #include "a_refl.h"
 #include "typedef.h"
 #include "cnst.h"
@@ -291,7 +293,8 @@
                 {
                     refl[i] = 0;
                 }
-                break;
+                ALOGE("b/23609206");
+                return;
             }
 
             bState[j] = extract_l(L_temp);
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
index dcf5bef..7da7db9 100644
--- a/media/libstagefright/foundation/base64.cpp
+++ b/media/libstagefright/foundation/base64.cpp
@@ -22,11 +22,11 @@
 namespace android {
 
 sp<ABuffer> decodeBase64(const AString &s) {
-    if ((s.size() % 4) != 0) {
+    size_t n = s.size();
+    if ((n % 4) != 0) {
         return NULL;
     }
 
-    size_t n = s.size();
     size_t padding = 0;
     if (n >= 1 && s.c_str()[n - 1] == '=') {
         padding = 1;
@@ -40,11 +40,16 @@
         }
     }
 
-    size_t outLen = 3 * s.size() / 4 - padding;
+    // We divide first to avoid overflow. It's OK to do this because we
+    // already made sure that n % 4 == 0.
+    size_t outLen = (n / 4) * 3 - padding;
 
     sp<ABuffer> buffer = new ABuffer(outLen);
 
     uint8_t *out = buffer->data();
+    if (out == NULL || buffer->size() < outLen) {
+        return NULL;
+    }
     size_t j = 0;
     uint32_t accum = 0;
     for (size_t i = 0; i < n; ++i) {
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 4252706..a29bdf9 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -28,7 +28,7 @@
 struct PageCache;
 
 struct NuCachedSource2 : public DataSource {
-    NuCachedSource2(
+    static sp<NuCachedSource2> Create(
             const sp<DataSource> &source,
             const char *cacheConfig = NULL,
             bool disconnectAtHighwatermark = false);
@@ -72,6 +72,11 @@
 private:
     friend struct AHandlerReflector<NuCachedSource2>;
 
+    NuCachedSource2(
+            const sp<DataSource> &source,
+            const char *cacheConfig,
+            bool disconnectAtHighwatermark);
+
     enum {
         kPageSize                       = 65536,
         kDefaultHighWaterThreshold      = 20 * 1024 * 1024,
diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp
index f9c1fe0..c762a74 100644
--- a/media/libstagefright/timedtext/TextDescriptions.cpp
+++ b/media/libstagefright/timedtext/TextDescriptions.cpp
@@ -30,9 +30,9 @@
 
     if (flags & IN_BAND_TEXT_3GPP) {
         if (flags & GLOBAL_DESCRIPTIONS) {
-            return extract3GPPGlobalDescriptions(data, size, parcel, 0);
+            return extract3GPPGlobalDescriptions(data, size, parcel);
         } else if (flags & LOCAL_DESCRIPTIONS) {
-            return extract3GPPLocalDescriptions(data, size, timeMs, parcel, 0);
+            return extract3GPPLocalDescriptions(data, size, timeMs, parcel);
         }
     } else if (flags & OUT_OF_BAND_TEXT_SRT) {
         if (flags & LOCAL_DESCRIPTIONS) {
@@ -69,49 +69,361 @@
 // styles, and 'krok' box contains karaoke timing and positions.
 status_t TextDescriptions::extract3GPPLocalDescriptions(
         const uint8_t *data, ssize_t size,
-        int timeMs, Parcel *parcel, int depth) {
-    if (depth == 0) {
-        parcel->writeInt32(KEY_LOCAL_SETTING);
+        int timeMs, Parcel *parcel) {
 
-        // write start time to display this text sample
-        parcel->writeInt32(KEY_START_TIME);
-        parcel->writeInt32(timeMs);
+    parcel->writeInt32(KEY_LOCAL_SETTING);
 
-        ssize_t textLen = (*data) << 8 | (*(data + 1));
+    // write start time to display this text sample
+    parcel->writeInt32(KEY_START_TIME);
+    parcel->writeInt32(timeMs);
 
-        // write text sample length and text sample itself
-        parcel->writeInt32(KEY_STRUCT_TEXT);
-        parcel->writeInt32(textLen);
-        parcel->writeInt32(textLen);
-        parcel->write(data + 2, textLen);
-
-        if (size > textLen) {
-            data += (textLen + 2);
-            size -= (textLen + 2);
-        } else {
-            return OK;
-        }
+    if (size < 2) {
+        return OK;
     }
+    ssize_t textLen = (*data) << 8 | (*(data + 1));
 
-    const uint8_t *tmpData = data;
-    ssize_t chunkSize = U32_AT(tmpData);
-    uint32_t chunkType = U32_AT(tmpData + 4);
-
-    if (chunkSize <= 0) {
+    if (size < textLen + 2) {
         return OK;
     }
 
-    tmpData += 8;
+    // write text sample length and text sample itself
+    parcel->writeInt32(KEY_STRUCT_TEXT);
+    parcel->writeInt32(textLen);
+    parcel->writeInt32(textLen);
+    parcel->write(data + 2, textLen);
 
-    switch(chunkType) {
-        // 'styl' box specifies the style of the text.
-        case FOURCC('s', 't', 'y', 'l'):
-        {
-            uint16_t count = U16_AT(tmpData);
+    if (size > textLen + 2) {
+        data += (textLen + 2);
+        size -= (textLen + 2);
+    } else {
+        return OK;
+    }
 
-            tmpData += 2;
+    while (size >= 8) {
+        const uint8_t *tmpData = data;
+        ssize_t chunkSize = U32_AT(tmpData);      // size includes size and type
+        uint32_t chunkType = U32_AT(tmpData + 4);
 
-            for (int i = 0; i < count; i++) {
+        if (chunkSize <= 8 || chunkSize > size) {
+            return OK;
+        }
+
+        size_t remaining = chunkSize - 8;
+
+        tmpData += 8;
+
+        switch(chunkType) {
+            // 'styl' box specifies the style of the text.
+            case FOURCC('s', 't', 'y', 'l'):
+            {
+                if (remaining < 2) {
+                    return OK;
+                }
+                size_t dataPos = parcel->dataPosition();
+                uint16_t count = U16_AT(tmpData);
+
+                tmpData += 2;
+                remaining -= 2;
+
+                for (int i = 0; i < count; i++) {
+                    if (remaining < 12) {
+                        // roll back
+                        parcel->setDataPosition(dataPos);
+                        return OK;
+                    }
+                    parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
+                    parcel->writeInt32(KEY_START_CHAR);
+                    parcel->writeInt32(U16_AT(tmpData));
+
+                    parcel->writeInt32(KEY_END_CHAR);
+                    parcel->writeInt32(U16_AT(tmpData + 2));
+
+                    parcel->writeInt32(KEY_FONT_ID);
+                    parcel->writeInt32(U16_AT(tmpData + 4));
+
+                    parcel->writeInt32(KEY_STYLE_FLAGS);
+                    parcel->writeInt32(*(tmpData + 6));
+
+                    parcel->writeInt32(KEY_FONT_SIZE);
+                    parcel->writeInt32(*(tmpData + 7));
+
+                    parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
+                    uint32_t rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
+                        | *(tmpData + 10) << 8 | *(tmpData + 11);
+                    parcel->writeInt32(rgba);
+
+                    tmpData += 12;
+                    remaining -= 12;
+                }
+
+                break;
+            }
+            // 'krok' box. The number of highlight events is specified, and each
+            // event is specified by a starting and ending char offset and an end
+            // time for the event.
+            case FOURCC('k', 'r', 'o', 'k'):
+            {
+                if (remaining < 6) {
+                    return OK;
+                }
+                size_t dataPos = parcel->dataPosition();
+
+                parcel->writeInt32(KEY_STRUCT_KARAOKE_LIST);
+
+                int startTime = U32_AT(tmpData);
+                uint16_t count = U16_AT(tmpData + 4);
+                parcel->writeInt32(count);
+
+                tmpData += 6;
+                remaining -= 6;
+                int lastEndTime = 0;
+
+                for (int i = 0; i < count; i++) {
+                    if (remaining < 8) {
+                        // roll back
+                        parcel->setDataPosition(dataPos);
+                        return OK;
+                    }
+                    parcel->writeInt32(startTime + lastEndTime);
+
+                    lastEndTime = U32_AT(tmpData);
+                    parcel->writeInt32(lastEndTime);
+
+                    parcel->writeInt32(U16_AT(tmpData + 4));
+                    parcel->writeInt32(U16_AT(tmpData + 6));
+
+                    tmpData += 8;
+                    remaining -= 8;
+                }
+
+                break;
+            }
+            // 'hlit' box specifies highlighted text
+            case FOURCC('h', 'l', 'i', 't'):
+            {
+                if (remaining < 4) {
+                    return OK;
+                }
+
+                parcel->writeInt32(KEY_STRUCT_HIGHLIGHT_LIST);
+
+                // the start char offset to highlight
+                parcel->writeInt32(U16_AT(tmpData));
+                // the last char offset to highlight
+                parcel->writeInt32(U16_AT(tmpData + 2));
+
+                tmpData += 4;
+                remaining -= 4;
+                break;
+            }
+            // 'hclr' box specifies the RGBA color: 8 bits each of
+            // red, green, blue, and an alpha(transparency) value
+            case FOURCC('h', 'c', 'l', 'r'):
+            {
+                if (remaining < 4) {
+                    return OK;
+                }
+                parcel->writeInt32(KEY_HIGHLIGHT_COLOR_RGBA);
+
+                uint32_t rgba = *(tmpData) << 24 | *(tmpData + 1) << 16
+                    | *(tmpData + 2) << 8 | *(tmpData + 3);
+                parcel->writeInt32(rgba);
+
+                tmpData += 4;
+                remaining -= 4;
+                break;
+            }
+            // 'dlay' box specifies a delay after a scroll in and/or
+            // before scroll out.
+            case FOURCC('d', 'l', 'a', 'y'):
+            {
+                if (remaining < 4) {
+                    return OK;
+                }
+                parcel->writeInt32(KEY_SCROLL_DELAY);
+
+                uint32_t delay = *(tmpData) << 24 | *(tmpData + 1) << 16
+                    | *(tmpData + 2) << 8 | *(tmpData + 3);
+                parcel->writeInt32(delay);
+
+                tmpData += 4;
+                remaining -= 4;
+                break;
+            }
+            // 'href' box for hyper text link
+            case FOURCC('h', 'r', 'e', 'f'):
+            {
+                if (remaining < 5) {
+                    return OK;
+                }
+
+                size_t dataPos = parcel->dataPosition();
+
+                parcel->writeInt32(KEY_STRUCT_HYPER_TEXT_LIST);
+
+                // the start offset of the text to be linked
+                parcel->writeInt32(U16_AT(tmpData));
+                // the end offset of the text
+                parcel->writeInt32(U16_AT(tmpData + 2));
+
+                // the number of bytes in the following URL
+                size_t len = *(tmpData + 4);
+                parcel->writeInt32(len);
+
+                remaining -= 5;
+
+                if (remaining  < len) {
+                    parcel->setDataPosition(dataPos);
+                    return OK;
+                }
+                // the linked-to URL
+                parcel->writeInt32(len);
+                parcel->write(tmpData + 5, len);
+
+                tmpData += (5 + len);
+                remaining -= len;
+
+                if (remaining  < 1) {
+                    parcel->setDataPosition(dataPos);
+                    return OK;
+                }
+
+                // the number of bytes in the following "alt" string
+                len = *tmpData;
+                parcel->writeInt32(len);
+
+                tmpData += 1;
+                remaining -= 1;
+                if (remaining  < len) {
+                    parcel->setDataPosition(dataPos);
+                    return OK;
+                }
+
+                // an "alt" string for user display
+                parcel->writeInt32(len);
+                parcel->write(tmpData, len);
+
+                tmpData += 1;
+                remaining -= 1;
+                break;
+            }
+            // 'tbox' box to indicate the position of the text with values
+            // of top, left, bottom and right
+            case FOURCC('t', 'b', 'o', 'x'):
+            {
+                if (remaining < 8) {
+                    return OK;
+                }
+                parcel->writeInt32(KEY_STRUCT_TEXT_POS);
+                parcel->writeInt32(U16_AT(tmpData));
+                parcel->writeInt32(U16_AT(tmpData + 2));
+                parcel->writeInt32(U16_AT(tmpData + 4));
+                parcel->writeInt32(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 8;
+                break;
+            }
+            // 'blnk' to specify the char range to be blinked
+            case FOURCC('b', 'l', 'n', 'k'):
+            {
+                if (remaining < 4) {
+                    return OK;
+                }
+
+                parcel->writeInt32(KEY_STRUCT_BLINKING_TEXT_LIST);
+
+                // start char offset
+                parcel->writeInt32(U16_AT(tmpData));
+                // end char offset
+                parcel->writeInt32(U16_AT(tmpData + 2));
+
+                tmpData += 4;
+                remaining -= 4;
+                break;
+            }
+            // 'twrp' box specifies text wrap behavior. If the value if 0x00,
+            // then no wrap. If it's 0x01, then automatic 'soft' wrap is enabled.
+            // 0x02-0xff are reserved.
+            case FOURCC('t', 'w', 'r', 'p'):
+            {
+                if (remaining < 1) {
+                    return OK;
+                }
+                parcel->writeInt32(KEY_WRAP_TEXT);
+                parcel->writeInt32(*tmpData);
+
+                tmpData += 1;
+                remaining -= 1;
+                break;
+            }
+            default:
+            {
+                break;
+            }
+        }
+
+        data += chunkSize;
+        size -= chunkSize;
+    }
+
+    return OK;
+}
+
+// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a Parcel
+status_t TextDescriptions::extract3GPPGlobalDescriptions(
+        const uint8_t *data, ssize_t size, Parcel *parcel) {
+
+    parcel->writeInt32(KEY_GLOBAL_SETTING);
+
+    while (size >= 8) {
+        ssize_t chunkSize = U32_AT(data);
+        uint32_t chunkType = U32_AT(data + 4);
+        const uint8_t *tmpData = data;
+        tmpData += 8;
+        size_t remaining = size - 8;
+
+        if (size < chunkSize) {
+            return OK;
+        }
+        switch(chunkType) {
+            case FOURCC('t', 'x', '3', 'g'):
+            {
+                if (remaining < 18) { // 8 just below, and another 10 a little further down
+                    return OK;
+                }
+                tmpData += 8; // skip the first 8 bytes
+                remaining -=8;
+                parcel->writeInt32(KEY_DISPLAY_FLAGS);
+                parcel->writeInt32(U32_AT(tmpData));
+
+                parcel->writeInt32(KEY_STRUCT_JUSTIFICATION);
+                parcel->writeInt32(tmpData[4]);
+                parcel->writeInt32(tmpData[5]);
+
+                parcel->writeInt32(KEY_BACKGROUND_COLOR_RGBA);
+                uint32_t rgba = *(tmpData + 6) << 24 | *(tmpData + 7) << 16
+                    | *(tmpData + 8) << 8 | *(tmpData + 9);
+                parcel->writeInt32(rgba);
+
+                tmpData += 10;
+                remaining -= 10;
+
+                if (remaining < 8) {
+                    return OK;
+                }
+                parcel->writeInt32(KEY_STRUCT_TEXT_POS);
+                parcel->writeInt32(U16_AT(tmpData));
+                parcel->writeInt32(U16_AT(tmpData + 2));
+                parcel->writeInt32(U16_AT(tmpData + 4));
+                parcel->writeInt32(U16_AT(tmpData + 6));
+
+                tmpData += 8;
+                remaining -= 8;
+
+                if (remaining < 12) {
+                    return OK;
+                }
                 parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
                 parcel->writeInt32(KEY_START_CHAR);
                 parcel->writeInt32(U16_AT(tmpData));
@@ -129,254 +441,65 @@
                 parcel->writeInt32(*(tmpData + 7));
 
                 parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
-                uint32_t rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
+                rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
                     | *(tmpData + 10) << 8 | *(tmpData + 11);
                 parcel->writeInt32(rgba);
 
                 tmpData += 12;
+                remaining -= 12;
+
+                if (remaining < 2) {
+                    return OK;
+                }
+
+                size_t dataPos = parcel->dataPosition();
+
+                parcel->writeInt32(KEY_STRUCT_FONT_LIST);
+                uint16_t count = U16_AT(tmpData);
+                parcel->writeInt32(count);
+
+                tmpData += 2;
+                remaining -= 2;
+
+                for (int i = 0; i < count; i++) {
+                    if (remaining < 3) {
+                        // roll back
+                        parcel->setDataPosition(dataPos);
+                        return OK;
+                    }
+                    // font ID
+                    parcel->writeInt32(U16_AT(tmpData));
+
+                    // font name length
+                    parcel->writeInt32(*(tmpData + 2));
+
+                    size_t len = *(tmpData + 2);
+
+                    tmpData += 3;
+                    remaining -= 3;
+
+                    if (remaining < len) {
+                        // roll back
+                        parcel->setDataPosition(dataPos);
+                        return OK;
+                    }
+
+                    parcel->write(tmpData, len);
+                    tmpData += len;
+                    remaining -= len;
+                }
+
+                // there is a "DisparityBox" after this according to the spec, but we ignore it
+                break;
             }
-
-            break;
-        }
-        // 'krok' box. The number of highlight events is specified, and each
-        // event is specified by a starting and ending char offset and an end
-        // time for the event.
-        case FOURCC('k', 'r', 'o', 'k'):
-        {
-
-            parcel->writeInt32(KEY_STRUCT_KARAOKE_LIST);
-
-            int startTime = U32_AT(tmpData);
-            uint16_t count = U16_AT(tmpData + 4);
-            parcel->writeInt32(count);
-
-            tmpData += 6;
-            int lastEndTime = 0;
-
-            for (int i = 0; i < count; i++) {
-                parcel->writeInt32(startTime + lastEndTime);
-
-                lastEndTime = U32_AT(tmpData);
-                parcel->writeInt32(lastEndTime);
-
-                parcel->writeInt32(U16_AT(tmpData + 4));
-                parcel->writeInt32(U16_AT(tmpData + 6));
-
-                tmpData += 8;
+            default:
+            {
+                break;
             }
-
-            break;
         }
-        // 'hlit' box specifies highlighted text
-        case FOURCC('h', 'l', 'i', 't'):
-        {
-            parcel->writeInt32(KEY_STRUCT_HIGHLIGHT_LIST);
 
-            // the start char offset to highlight
-            parcel->writeInt32(U16_AT(tmpData));
-            // the last char offset to highlight
-            parcel->writeInt32(U16_AT(tmpData + 2));
-
-            break;
-        }
-        // 'hclr' box specifies the RGBA color: 8 bits each of
-        // red, green, blue, and an alpha(transparency) value
-        case FOURCC('h', 'c', 'l', 'r'):
-        {
-            parcel->writeInt32(KEY_HIGHLIGHT_COLOR_RGBA);
-
-            uint32_t rgba = *(tmpData) << 24 | *(tmpData + 1) << 16
-                | *(tmpData + 2) << 8 | *(tmpData + 3);
-            parcel->writeInt32(rgba);
-
-            break;
-        }
-        // 'dlay' box specifies a delay after a scroll in and/or
-        // before scroll out.
-        case FOURCC('d', 'l', 'a', 'y'):
-        {
-            parcel->writeInt32(KEY_SCROLL_DELAY);
-
-            uint32_t delay = *(tmpData) << 24 | *(tmpData + 1) << 16
-                | *(tmpData + 2) << 8 | *(tmpData + 3);
-            parcel->writeInt32(delay);
-
-            break;
-        }
-        // 'href' box for hyper text link
-        case FOURCC('h', 'r', 'e', 'f'):
-        {
-            parcel->writeInt32(KEY_STRUCT_HYPER_TEXT_LIST);
-
-            // the start offset of the text to be linked
-            parcel->writeInt32(U16_AT(tmpData));
-            // the end offset of the text
-            parcel->writeInt32(U16_AT(tmpData + 2));
-
-            // the number of bytes in the following URL
-            int len = *(tmpData + 4);
-            parcel->writeInt32(len);
-
-            // the linked-to URL
-            parcel->writeInt32(len);
-            parcel->write(tmpData + 5, len);
-
-            tmpData += (5 + len);
-
-            // the number of bytes in the following "alt" string
-            len = *tmpData;
-            parcel->writeInt32(len);
-
-            // an "alt" string for user display
-            parcel->writeInt32(len);
-            parcel->write(tmpData + 1, len);
-
-            break;
-        }
-        // 'tbox' box to indicate the position of the text with values
-        // of top, left, bottom and right
-        case FOURCC('t', 'b', 'o', 'x'):
-        {
-            parcel->writeInt32(KEY_STRUCT_TEXT_POS);
-            parcel->writeInt32(U16_AT(tmpData));
-            parcel->writeInt32(U16_AT(tmpData + 2));
-            parcel->writeInt32(U16_AT(tmpData + 4));
-            parcel->writeInt32(U16_AT(tmpData + 6));
-
-            break;
-        }
-        // 'blnk' to specify the char range to be blinked
-        case FOURCC('b', 'l', 'n', 'k'):
-        {
-            parcel->writeInt32(KEY_STRUCT_BLINKING_TEXT_LIST);
-
-            // start char offset
-            parcel->writeInt32(U16_AT(tmpData));
-            // end char offset
-            parcel->writeInt32(U16_AT(tmpData + 2));
-
-            break;
-        }
-        // 'twrp' box specifies text wrap behavior. If the value if 0x00,
-        // then no wrap. If it's 0x01, then automatic 'soft' wrap is enabled.
-        // 0x02-0xff are reserved.
-        case FOURCC('t', 'w', 'r', 'p'):
-        {
-            parcel->writeInt32(KEY_WRAP_TEXT);
-            parcel->writeInt32(*tmpData);
-
-            break;
-        }
-        default:
-        {
-            break;
-        }
-    }
-
-    if (size > chunkSize) {
         data += chunkSize;
         size -= chunkSize;
-        // continue to parse next box
-        return extract3GPPLocalDescriptions(data, size, 0, parcel, 1);
-    }
-
-    return OK;
-}
-
-// To extract box 'tx3g' defined in 3GPP TS 26.245, and store it in a Parcel
-status_t TextDescriptions::extract3GPPGlobalDescriptions(
-        const uint8_t *data, ssize_t size, Parcel *parcel, int depth) {
-
-    ssize_t chunkSize = U32_AT(data);
-    uint32_t chunkType = U32_AT(data + 4);
-    const uint8_t *tmpData = data;
-    tmpData += 8;
-
-    if (size < chunkSize) {
-        return OK;
-    }
-
-    if (depth == 0) {
-        parcel->writeInt32(KEY_GLOBAL_SETTING);
-    }
-    switch(chunkType) {
-        case FOURCC('t', 'x', '3', 'g'):
-        {
-            tmpData += 8; // skip the first 8 bytes
-            parcel->writeInt32(KEY_DISPLAY_FLAGS);
-            parcel->writeInt32(U32_AT(tmpData));
-
-            parcel->writeInt32(KEY_STRUCT_JUSTIFICATION);
-            parcel->writeInt32(tmpData[4]);
-            parcel->writeInt32(tmpData[5]);
-
-            parcel->writeInt32(KEY_BACKGROUND_COLOR_RGBA);
-            uint32_t rgba = *(tmpData + 6) << 24 | *(tmpData + 7) << 16
-                | *(tmpData + 8) << 8 | *(tmpData + 9);
-            parcel->writeInt32(rgba);
-
-            tmpData += 10;
-            parcel->writeInt32(KEY_STRUCT_TEXT_POS);
-            parcel->writeInt32(U16_AT(tmpData));
-            parcel->writeInt32(U16_AT(tmpData + 2));
-            parcel->writeInt32(U16_AT(tmpData + 4));
-            parcel->writeInt32(U16_AT(tmpData + 6));
-
-            tmpData += 8;
-            parcel->writeInt32(KEY_STRUCT_STYLE_LIST);
-            parcel->writeInt32(KEY_START_CHAR);
-            parcel->writeInt32(U16_AT(tmpData));
-
-            parcel->writeInt32(KEY_END_CHAR);
-            parcel->writeInt32(U16_AT(tmpData + 2));
-
-            parcel->writeInt32(KEY_FONT_ID);
-            parcel->writeInt32(U16_AT(tmpData + 4));
-
-            parcel->writeInt32(KEY_STYLE_FLAGS);
-            parcel->writeInt32(*(tmpData + 6));
-
-            parcel->writeInt32(KEY_FONT_SIZE);
-            parcel->writeInt32(*(tmpData + 7));
-
-            parcel->writeInt32(KEY_TEXT_COLOR_RGBA);
-            rgba = *(tmpData + 8) << 24 | *(tmpData + 9) << 16
-                | *(tmpData + 10) << 8 | *(tmpData + 11);
-            parcel->writeInt32(rgba);
-
-            tmpData += 12;
-            parcel->writeInt32(KEY_STRUCT_FONT_LIST);
-            uint16_t count = U16_AT(tmpData);
-            parcel->writeInt32(count);
-
-            tmpData += 2;
-            for (int i = 0; i < count; i++) {
-                // font ID
-                parcel->writeInt32(U16_AT(tmpData));
-
-                // font name length
-                parcel->writeInt32(*(tmpData + 2));
-
-                int len = *(tmpData + 2);
-
-                parcel->write(tmpData + 3, len);
-                tmpData += 3 + len;
-            }
-
-            break;
-        }
-        default:
-        {
-            break;
-        }
-    }
-
-    data += chunkSize;
-    size -= chunkSize;
-
-    if (size > 0) {
-        // continue to extract next 'tx3g'
-        return extract3GPPGlobalDescriptions(data, size, parcel, 1);
     }
 
     return OK;
diff --git a/media/libstagefright/timedtext/TextDescriptions.h b/media/libstagefright/timedtext/TextDescriptions.h
index 0144917..bf67f3f 100644
--- a/media/libstagefright/timedtext/TextDescriptions.h
+++ b/media/libstagefright/timedtext/TextDescriptions.h
@@ -72,10 +72,10 @@
             int timeMs, Parcel *parcel);
     static status_t extract3GPPGlobalDescriptions(
             const uint8_t *data, ssize_t size,
-            Parcel *parcel, int depth);
+            Parcel *parcel);
     static status_t extract3GPPLocalDescriptions(
             const uint8_t *data, ssize_t size,
-            int timeMs, Parcel *parcel, int depth);
+            int timeMs, Parcel *parcel);
 
     DISALLOW_EVIL_CONSTRUCTORS(TextDescriptions);
 };
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 0a7d4a2..5bd9149 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3479,6 +3479,12 @@
         if (state->mCommand != FastMixerState::MIX_WRITE &&
                 (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)) {
             if (state->mCommand == FastMixerState::COLD_IDLE) {
+
+                // FIXME workaround for first HAL write being CPU bound on some devices
+                ATRACE_BEGIN("write");
+                mOutput->write((char *)mSinkBuffer, 0);
+                ATRACE_END();
+
                 int32_t old = android_atomic_inc(&mFastMixerFutex);
                 if (old == -1) {
                     (void) syscall(__NR_futex, &mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index f7da209..b3fac0b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -432,7 +432,10 @@
     }
     // only allocate a fast track index if we were able to allocate a normal track name
     if (flags & IAudioFlinger::TRACK_FAST) {
-        mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
+        // FIXME: Not calling framesReadyIsCalledByMultipleThreads() exposes a potential
+        // race with setSyncEvent(). However, if we call it, we cannot properly start
+        // static fast tracks (SoundPool) immediately after stopping.
+        //mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
         ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
         int i = __builtin_ctz(thread->mFastTrackAvailMask);
         ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 7128df7..2aaefe9 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -15,6 +15,7 @@
  */
 
 #define LOG_TAG "CameraService"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
 //#define LOG_NDEBUG 0
 
 #include <algorithm>
@@ -156,7 +157,6 @@
     }
 
     mModule = new CameraModule(rawModule);
-    ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
     err = mModule->init();
     if (err != OK) {
         ALOGE("Could not initialize camera HAL module: %d (%s)", err,
@@ -168,6 +168,7 @@
         mModule = nullptr;
         return;
     }
+    ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
 
     mNumberOfCameras = mModule->getNumberOfCameras();
     mNumberOfNormalCameras = mNumberOfCameras;
@@ -368,7 +369,7 @@
 
     {
         // Update battery life logging for flashlight
-        Mutex::Autolock al(mTorchClientMapMutex);
+        Mutex::Autolock al(mTorchUidMapMutex);
         auto iter = mTorchUidMap.find(cameraId);
         if (iter != mTorchUidMap.end()) {
             int oldUid = iter->second.second;
@@ -403,10 +404,12 @@
 }
 
 int32_t CameraService::getNumberOfCameras() {
+    ATRACE_CALL();
     return getNumberOfCameras(CAMERA_TYPE_BACKWARD_COMPATIBLE);
 }
 
 int32_t CameraService::getNumberOfCameras(int type) {
+    ATRACE_CALL();
     switch (type) {
         case CAMERA_TYPE_BACKWARD_COMPATIBLE:
             return mNumberOfNormalCameras;
@@ -421,6 +424,7 @@
 
 status_t CameraService::getCameraInfo(int cameraId,
                                       struct CameraInfo* cameraInfo) {
+    ATRACE_CALL();
     if (!mModule) {
         return -ENODEV;
     }
@@ -448,6 +452,7 @@
 }
 
 status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) {
+    ATRACE_CALL();
     status_t ret = OK;
     struct CameraInfo info;
     if ((ret = getCameraInfo(cameraId, &info)) != OK) {
@@ -534,6 +539,7 @@
 
 status_t CameraService::getCameraCharacteristics(int cameraId,
                                                 CameraMetadata* cameraInfo) {
+    ATRACE_CALL();
     if (!cameraInfo) {
         ALOGE("%s: cameraInfo is NULL", __FUNCTION__);
         return BAD_VALUE;
@@ -602,10 +608,16 @@
                 procState);
         return -1;
     }
+    // Treat sleeping TOP processes the same as regular TOP processes, for
+    // access priority.  This is important for lock-screen camera launch scenarios
+    if (procState == PROCESS_STATE_TOP_SLEEPING) {
+        procState = PROCESS_STATE_TOP;
+    }
     return INT_MAX - procState;
 }
 
 status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) {
+    ATRACE_CALL();
     if (!mModule) {
         ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__);
         return -ENODEV;
@@ -616,6 +628,7 @@
 }
 
 int CameraService::getDeviceVersion(int cameraId, int* facing) {
+    ATRACE_CALL();
     struct camera_info info;
     if (mModule->getCameraInfo(cameraId, &info) != OK) {
         return -1;
@@ -647,6 +660,7 @@
 }
 
 bool CameraService::setUpVendorTags() {
+    ATRACE_CALL();
     vendor_tag_ops_t vOps = vendor_tag_ops_t();
 
     // Check if vendor operations have been implemented
@@ -655,9 +669,7 @@
         return false;
     }
 
-    ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops");
     mModule->getVendorTagOps(&vOps);
-    ATRACE_END();
 
     // Ensure all vendor operations are present
     if (vOps.get_tag_count == NULL || vOps.get_all_tags == NULL ||
@@ -940,6 +952,16 @@
         LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, clients not evicted properly",
                 __FUNCTION__);
     }
+
+    // And register a death notification for the client callback. Do
+    // this last to avoid Binder policy where a nested Binder
+    // transaction might be pre-empted to service the client death
+    // notification if the client process dies before linkToDeath is
+    // invoked.
+    sp<IBinder> remoteCallback = client->getRemote();
+    if (remoteCallback != nullptr) {
+        remoteCallback->linkToDeath(this);
+    }
 }
 
 status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,
@@ -947,7 +969,7 @@
         /*out*/
         sp<BasicClient>* client,
         std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
-
+    ATRACE_CALL();
     status_t ret = NO_ERROR;
     std::vector<DescriptorPtr> evictedClients;
     DescriptorPtr clientDescriptor;
@@ -1136,6 +1158,7 @@
         /*out*/
         sp<ICamera>& device) {
 
+    ATRACE_CALL();
     status_t ret = NO_ERROR;
     String8 id = String8::format("%d", cameraId);
     sp<Client> client = nullptr;
@@ -1160,6 +1183,7 @@
         /*out*/
         sp<ICamera>& device) {
 
+    ATRACE_CALL();
     String8 id = String8::format("%d", cameraId);
     int apiVersion = mModule->getModuleApiVersion();
     if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
@@ -1200,6 +1224,7 @@
         /*out*/
         sp<ICameraDeviceUser>& device) {
 
+    ATRACE_CALL();
     status_t ret = NO_ERROR;
     String8 id = String8::format("%d", cameraId);
     sp<CameraDeviceClient> client = nullptr;
@@ -1219,6 +1244,8 @@
 
 status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,
         const sp<IBinder>& clientBinder) {
+
+    ATRACE_CALL();
     if (enabled && clientBinder == nullptr) {
         ALOGE("%s: torch client binder is NULL", __FUNCTION__);
         return -EINVAL;
@@ -1267,7 +1294,7 @@
     {
         // Update UID map - this is used in the torch status changed callbacks, so must be done
         // before setTorchMode
-        Mutex::Autolock al(mTorchClientMapMutex);
+        Mutex::Autolock al(mTorchUidMapMutex);
         if (mTorchUidMap.find(id) == mTorchUidMap.end()) {
             mTorchUidMap[id].first = uid;
             mTorchUidMap[id].second = uid;
@@ -1307,6 +1334,8 @@
 }
 
 void CameraService::notifySystemEvent(int32_t eventId, const int32_t* args, size_t length) {
+    ATRACE_CALL();
+
     switch(eventId) {
         case ICameraService::USER_SWITCHED: {
             doUserSwitch(/*newUserIds*/args, /*length*/length);
@@ -1322,6 +1351,8 @@
 }
 
 status_t CameraService::addListener(const sp<ICameraServiceListener>& listener) {
+    ATRACE_CALL();
+
     ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
 
     if (listener == nullptr) {
@@ -1370,6 +1401,8 @@
 }
 
 status_t CameraService::removeListener(const sp<ICameraServiceListener>& listener) {
+    ATRACE_CALL();
+
     ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get());
 
     if (listener == 0) {
@@ -1396,6 +1429,8 @@
 }
 
 status_t CameraService::getLegacyParameters(int cameraId, /*out*/String16* parameters) {
+
+    ATRACE_CALL();
     ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId);
 
     if (parameters == NULL) {
@@ -1420,6 +1455,8 @@
 }
 
 status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) {
+    ATRACE_CALL();
+
     ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId);
 
     switch (apiVersion) {
@@ -1787,6 +1824,8 @@
 }
 
 void CameraService::loadSound() {
+    ATRACE_CALL();
+
     Mutex::Autolock lock(mSoundLock);
     LOG1("CameraService::loadSound ref=%d", mSoundRef);
     if (mSoundRef++) return;
@@ -1809,6 +1848,8 @@
 }
 
 void CameraService::playSound(sound_kind kind) {
+    ATRACE_CALL();
+
     LOG1("playSound(%d)", kind);
     Mutex::Autolock lock(mSoundLock);
     sp<MediaPlayer> player = mSoundPlayer[kind];
@@ -1879,11 +1920,9 @@
 
 void CameraService::BasicClient::disconnect() {
     if (mDisconnected) {
-        ALOGE("%s: Disconnect called on already disconnected client for device %d", __FUNCTION__,
-                mCameraId);
         return;
     }
-    mDisconnected = true;;
+    mDisconnected = true;
 
     mCameraService->removeByClient(this);
     mCameraService->logDisconnected(String8::format("%d", mCameraId), mClientPid,
@@ -1920,6 +1959,8 @@
 }
 
 status_t CameraService::BasicClient::startCameraOps() {
+    ATRACE_CALL();
+
     int32_t res;
     // Notify app ops that the camera is not available
     mOpsCallback = new OpsCallback(this);
@@ -1961,6 +2002,8 @@
 }
 
 status_t CameraService::BasicClient::finishCameraOps() {
+    ATRACE_CALL();
+
     // Check if startCameraOps succeeded, and if so, finish the camera op
     if (mOpsActive) {
         // Notify app ops that the camera is available again
@@ -1993,6 +2036,8 @@
 }
 
 void CameraService::BasicClient::opChanged(int32_t op, const String16& packageName) {
+    ATRACE_CALL();
+
     String8 name(packageName);
     String8 myName(mClientPackageName);
 
@@ -2216,9 +2261,11 @@
 }
 
 status_t CameraService::dump(int fd, const Vector<String16>& args) {
+    ATRACE_CALL();
+
     String8 result("Dump of the Camera Service:\n");
     if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
-        result.appendFormat("Permission Denial: "
+        result = result.format("Permission Denial: "
                 "can't dump CameraService from pid=%d, uid=%d\n",
                 getCallingPid(),
                 getCallingUid());
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 894767a..cd97b08 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -75,6 +75,8 @@
 
     // Process state (mirrors frameworks/base/core/java/android/app/ActivityManager.java)
     static const int PROCESS_STATE_NONEXISTENT = -1;
+    static const int PROCESS_STATE_TOP = 2;
+    static const int PROCESS_STATE_TOP_SLEEPING = 5;
 
     // 3 second busy timeout when other clients are connecting
     static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000;
@@ -657,8 +659,10 @@
     sp<CameraFlashlight> mFlashlight;
     // guard mTorchStatusMap
     Mutex                mTorchStatusMutex;
-    // guard mTorchClientMap, mTorchUidMap
+    // guard mTorchClientMap
     Mutex                mTorchClientMapMutex;
+    // guard mTorchUidMap
+    Mutex                mTorchUidMapMutex;
     // camera id -> torch status
     KeyedVector<String8, ICameraServiceListener::TorchStatus> mTorchStatusMap;
     // camera id -> torch client binder
@@ -869,11 +873,6 @@
             return ret;
         }
 
-        sp<IBinder> remoteCallback = client->getRemote();
-        if (remoteCallback != nullptr) {
-            remoteCallback->linkToDeath(this);
-        }
-
         // Update shim paremeters for legacy clients
         if (effectiveApiLevel == API_1) {
             // Assume we have always received a Client subclass for API1
diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp
index 6a4dfe0..16b8aba 100644
--- a/services/camera/libcameraservice/common/CameraModule.cpp
+++ b/services/camera/libcameraservice/common/CameraModule.cpp
@@ -15,14 +15,18 @@
  */
 
 #define LOG_TAG "CameraModule"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
 //#define LOG_NDEBUG 0
 
+#include <utils/Trace.h>
+
 #include "CameraModule.h"
 
 namespace android {
 
 void CameraModule::deriveCameraCharacteristicsKeys(
         uint32_t deviceVersion, CameraMetadata &chars) {
+    ATRACE_CALL();
     // HAL1 devices should not reach here
     if (deviceVersion < CAMERA_DEVICE_API_VERSION_2_0) {
         ALOGV("%s: Cannot derive keys for HAL version < 2.0");
@@ -150,9 +154,7 @@
         ALOGE("%s: camera hardware module must not be null", __FUNCTION__);
         assert(0);
     }
-
     mModule = module;
-    mCameraInfoMap.setCapacity(getNumberOfCameras());
 }
 
 CameraModule::~CameraModule()
@@ -168,14 +170,20 @@
 }
 
 int CameraModule::init() {
+    ATRACE_CALL();
+    int res = OK;
     if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 &&
             mModule->init != NULL) {
-        return mModule->init();
+        ATRACE_BEGIN("camera_module->init");
+        res = mModule->init();
+        ATRACE_END();
     }
-    return OK;
+    mCameraInfoMap.setCapacity(getNumberOfCameras());
+    return res;
 }
 
 int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) {
+    ATRACE_CALL();
     Mutex::Autolock lock(mCameraInfoLock);
     if (cameraId < 0) {
         ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
@@ -185,14 +193,20 @@
     // Only override static_camera_characteristics for API2 devices
     int apiVersion = mModule->common.module_api_version;
     if (apiVersion < CAMERA_MODULE_API_VERSION_2_0) {
-        return mModule->get_camera_info(cameraId, info);
+        int ret;
+        ATRACE_BEGIN("camera_module->get_camera_info");
+        ret = mModule->get_camera_info(cameraId, info);
+        ATRACE_END();
+        return ret;
     }
 
     ssize_t index = mCameraInfoMap.indexOfKey(cameraId);
     if (index == NAME_NOT_FOUND) {
         // Get camera info from raw module and cache it
         camera_info rawInfo, cameraInfo;
+        ATRACE_BEGIN("camera_module->get_camera_info");
         int ret = mModule->get_camera_info(cameraId, &rawInfo);
+        ATRACE_END();
         if (ret != 0) {
             return ret;
         }
@@ -217,20 +231,36 @@
 }
 
 int CameraModule::open(const char* id, struct hw_device_t** device) {
-    return filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
+    int res;
+    ATRACE_BEGIN("camera_module->open");
+    res = filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
+    ATRACE_END();
+    return res;
 }
 
 int CameraModule::openLegacy(
         const char* id, uint32_t halVersion, struct hw_device_t** device) {
-    return mModule->open_legacy(&mModule->common, id, halVersion, device);
+    int res;
+    ATRACE_BEGIN("camera_module->open_legacy");
+    res = mModule->open_legacy(&mModule->common, id, halVersion, device);
+    ATRACE_END();
+    return res;
 }
 
 int CameraModule::getNumberOfCameras() {
-    return mModule->get_number_of_cameras();
+    int numCameras;
+    ATRACE_BEGIN("camera_module->get_number_of_cameras");
+    numCameras = mModule->get_number_of_cameras();
+    ATRACE_END();
+    return numCameras;
 }
 
 int CameraModule::setCallbacks(const camera_module_callbacks_t *callbacks) {
-    return mModule->set_callbacks(callbacks);
+    int res;
+    ATRACE_BEGIN("camera_module->set_callbacks");
+    res = mModule->set_callbacks(callbacks);
+    ATRACE_END();
+    return res;
 }
 
 bool CameraModule::isVendorTagDefined() {
@@ -239,12 +269,18 @@
 
 void CameraModule::getVendorTagOps(vendor_tag_ops_t* ops) {
     if (mModule->get_vendor_tag_ops) {
+        ATRACE_BEGIN("camera_module->get_vendor_tag_ops");
         mModule->get_vendor_tag_ops(ops);
+        ATRACE_END();
     }
 }
 
 int CameraModule::setTorchMode(const char* camera_id, bool enable) {
-    return mModule->set_torch_mode(camera_id, enable);
+    int res;
+    ATRACE_BEGIN("camera_module->set_torch_mode");
+    res = mModule->set_torch_mode(camera_id, enable);
+    ATRACE_END();
+    return res;
 }
 
 status_t CameraModule::filterOpenErrorCode(status_t err) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ec3591f..433a745 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -565,6 +565,18 @@
 
         ALOGV("%s: requestId = %" PRId32, __FUNCTION__, newRequest->mResultExtras.requestId);
     }
+
+    // Setup batch size if this is a high speed video recording request.
+    if (mIsConstrainedHighSpeedConfiguration && requestList->size() > 0) {
+        auto firstRequest = requestList->begin();
+        for (auto& outputStream : (*firstRequest)->mOutputStreams) {
+            if (outputStream->isVideoStream()) {
+                (*firstRequest)->mBatchSize = requestList->size();
+                break;
+            }
+        }
+    }
+
     return OK;
 }
 
@@ -1406,7 +1418,7 @@
 
     status_t res;
     if (mHal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_1) {
-        res = mHal3Device->ops->flush(mHal3Device);
+        res = mRequestThread->flush();
     } else {
         Mutex::Autolock l(mLock);
         res = waitUntilDrainedLocked();
@@ -1595,6 +1607,7 @@
         newRequest->mOutputStreams.push(stream);
     }
     newRequest->mSettings.erase(ANDROID_REQUEST_OUTPUT_STREAMS);
+    newRequest->mBatchSize = 1;
 
     return newRequest;
 }
@@ -2766,6 +2779,17 @@
     return OK;
 }
 
+status_t Camera3Device::RequestThread::flush() {
+    ATRACE_CALL();
+    Mutex::Autolock l(mFlushLock);
+
+    if (mHal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_1) {
+        return mHal3Device->ops->flush(mHal3Device);
+    }
+
+    return -ENOTSUP;
+}
+
 void Camera3Device::RequestThread::setPaused(bool paused) {
     Mutex::Autolock l(mPauseLock);
     mDoPause = paused;
@@ -2856,7 +2880,7 @@
 }
 
 bool Camera3Device::RequestThread::threadLoop() {
-
+    ATRACE_CALL();
     status_t res;
 
     // Handle paused state.
@@ -2864,147 +2888,31 @@
         return true;
     }
 
-    // Get work to do
-
-    sp<CaptureRequest> nextRequest = waitForNextRequest();
-    if (nextRequest == NULL) {
+    // Wait for the next batch of requests.
+    waitForNextRequestBatch();
+    if (mNextRequests.size() == 0) {
         return true;
     }
 
-    // Create request to HAL
-    camera3_capture_request_t request = camera3_capture_request_t();
-    request.frame_number = nextRequest->mResultExtras.frameNumber;
-    Vector<camera3_stream_buffer_t> outputBuffers;
-
-    // Get the request ID, if any
-    int requestId;
-    camera_metadata_entry_t requestIdEntry =
-            nextRequest->mSettings.find(ANDROID_REQUEST_ID);
+    // Get the latest request ID, if any
+    int latestRequestId;
+    camera_metadata_entry_t requestIdEntry = mNextRequests[mNextRequests.size() - 1].
+            captureRequest->mSettings.find(ANDROID_REQUEST_ID);
     if (requestIdEntry.count > 0) {
-        requestId = requestIdEntry.data.i32[0];
+        latestRequestId = requestIdEntry.data.i32[0];
     } else {
-        ALOGW("%s: Did not have android.request.id set in the request",
-                __FUNCTION__);
-        requestId = NAME_NOT_FOUND;
+        ALOGW("%s: Did not have android.request.id set in the request.", __FUNCTION__);
+        latestRequestId = NAME_NOT_FOUND;
     }
 
-    // Insert any queued triggers (before metadata is locked)
-    int32_t triggerCount;
-    res = insertTriggers(nextRequest);
-    if (res < 0) {
-        SET_ERR("RequestThread: Unable to insert triggers "
-                "(capture request %d, HAL device: %s (%d)",
-                request.frame_number, strerror(-res), res);
-        cleanUpFailedRequest(request, nextRequest, outputBuffers);
-        return false;
-    }
-    triggerCount = res;
-
-    bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
-
-    // If the request is the same as last, or we had triggers last time
-    if (mPrevRequest != nextRequest || triggersMixedIn) {
-        /**
-         * HAL workaround:
-         * Insert a dummy trigger ID if a trigger is set but no trigger ID is
-         */
-        res = addDummyTriggerIds(nextRequest);
-        if (res != OK) {
-            SET_ERR("RequestThread: Unable to insert dummy trigger IDs "
-                    "(capture request %d, HAL device: %s (%d)",
-                    request.frame_number, strerror(-res), res);
-            cleanUpFailedRequest(request, nextRequest, outputBuffers);
-            return false;
-        }
-
-        /**
-         * The request should be presorted so accesses in HAL
-         *   are O(logn). Sidenote, sorting a sorted metadata is nop.
-         */
-        nextRequest->mSettings.sort();
-        request.settings = nextRequest->mSettings.getAndLock();
-        mPrevRequest = nextRequest;
-        ALOGVV("%s: Request settings are NEW", __FUNCTION__);
-
-        IF_ALOGV() {
-            camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
-            find_camera_metadata_ro_entry(
-                    request.settings,
-                    ANDROID_CONTROL_AF_TRIGGER,
-                    &e
-            );
-            if (e.count > 0) {
-                ALOGV("%s: Request (frame num %d) had AF trigger 0x%x",
-                      __FUNCTION__,
-                      request.frame_number,
-                      e.data.u8[0]);
-            }
-        }
-    } else {
-        // leave request.settings NULL to indicate 'reuse latest given'
-        ALOGVV("%s: Request settings are REUSED",
-               __FUNCTION__);
-    }
-
-    uint32_t totalNumBuffers = 0;
-
-    // Fill in buffers
-    if (nextRequest->mInputStream != NULL) {
-        request.input_buffer = &nextRequest->mInputBuffer;
-        totalNumBuffers += 1;
-    } else {
-        request.input_buffer = NULL;
-    }
-
-    outputBuffers.insertAt(camera3_stream_buffer_t(), 0,
-            nextRequest->mOutputStreams.size());
-    request.output_buffers = outputBuffers.array();
-    for (size_t i = 0; i < nextRequest->mOutputStreams.size(); i++) {
-        res = nextRequest->mOutputStreams.editItemAt(i)->
-                getBuffer(&outputBuffers.editItemAt(i));
-        if (res != OK) {
-            // Can't get output buffer from gralloc queue - this could be due to
-            // abandoned queue or other consumer misbehavior, so not a fatal
-            // error
-            ALOGE("RequestThread: Can't get output buffer, skipping request:"
-                    " %s (%d)", strerror(-res), res);
-            {
-                Mutex::Autolock l(mRequestLock);
-                if (mListener != NULL) {
-                    mListener->notifyError(
-                            ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
-                            nextRequest->mResultExtras);
-                }
-            }
-            cleanUpFailedRequest(request, nextRequest, outputBuffers);
-            return true;
-        }
-        request.num_output_buffers++;
-    }
-    totalNumBuffers += request.num_output_buffers;
-
-    // Log request in the in-flight queue
-    sp<Camera3Device> parent = mParent.promote();
-    if (parent == NULL) {
-        // Should not happen, and nowhere to send errors to, so just log it
-        CLOGE("RequestThread: Parent is gone");
-        cleanUpFailedRequest(request, nextRequest, outputBuffers);
-        return false;
-    }
-
-    res = parent->registerInFlight(request.frame_number,
-            totalNumBuffers, nextRequest->mResultExtras,
-            /*hasInput*/request.input_buffer != NULL,
-            nextRequest->mAeTriggerCancelOverride);
-    ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
-           ", burstId = %" PRId32 ".",
-            __FUNCTION__,
-            nextRequest->mResultExtras.requestId, nextRequest->mResultExtras.frameNumber,
-            nextRequest->mResultExtras.burstId);
-    if (res != OK) {
-        SET_ERR("RequestThread: Unable to register new in-flight request:"
-                " %s (%d)", strerror(-res), res);
-        cleanUpFailedRequest(request, nextRequest, outputBuffers);
+    // Prepare a batch of HAL requests and output buffers.
+    res = prepareHalRequests();
+    if (res == TIMED_OUT) {
+        // Not a fatal error if getting output buffers time out.
+        cleanUpFailedRequests(/*sendRequestError*/ true);
+        return true;
+    } else if (res != OK) {
+        cleanUpFailedRequests(/*sendRequestError*/ false);
         return false;
     }
 
@@ -3012,57 +2920,210 @@
     {
         Mutex::Autolock al(mLatestRequestMutex);
 
-        mLatestRequestId = requestId;
+        mLatestRequestId = latestRequestId;
         mLatestRequestSignal.signal();
     }
 
-    // Submit request and block until ready for next one
-    ATRACE_ASYNC_BEGIN("frame capture", request.frame_number);
-    ATRACE_BEGIN("camera3->process_capture_request");
-    res = mHal3Device->ops->process_capture_request(mHal3Device, &request);
-    ATRACE_END();
+    // Submit a batch of requests to HAL.
+    // Use flush lock only when submitting multilple requests in a batch.
+    // TODO: The problem with flush lock is flush() will be blocked by process_capture_request()
+    // which may take a long time to finish so synchronizing flush() and
+    // process_capture_request() defeats the purpose of cancelling requests ASAP with flush().
+    // For now, only synchronize for high speed recording and we should figure something out for
+    // removing the synchronization.
+    bool useFlushLock = mNextRequests.size() > 1;
 
-    if (res != OK) {
-        // Should only get a failure here for malformed requests or device-level
-        // errors, so consider all errors fatal.  Bad metadata failures should
-        // come through notify.
-        SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
-                " device: %s (%d)", request.frame_number, strerror(-res), res);
-        cleanUpFailedRequest(request, nextRequest, outputBuffers);
-        return false;
+    if (useFlushLock) {
+        mFlushLock.lock();
     }
 
-    // Update the latest request sent to HAL
-    if (request.settings != NULL) { // Don't update them if they were unchanged
-        Mutex::Autolock al(mLatestRequestMutex);
+    ALOGVV("%s: %d: submitting %d requests in a batch.", __FUNCTION__, __LINE__,
+            mNextRequests.size());
+    for (auto& nextRequest : mNextRequests) {
+        // Submit request and block until ready for next one
+        ATRACE_ASYNC_BEGIN("frame capture", nextRequest.halRequest.frame_number);
+        ATRACE_BEGIN("camera3->process_capture_request");
+        res = mHal3Device->ops->process_capture_request(mHal3Device, &nextRequest.halRequest);
+        ATRACE_END();
 
-        camera_metadata_t* cloned = clone_camera_metadata(request.settings);
-        mLatestRequest.acquire(cloned);
+        if (res != OK) {
+            // Should only get a failure here for malformed requests or device-level
+            // errors, so consider all errors fatal.  Bad metadata failures should
+            // come through notify.
+            SET_ERR("RequestThread: Unable to submit capture request %d to HAL"
+                    " device: %s (%d)", nextRequest.halRequest.frame_number, strerror(-res),
+                    res);
+            cleanUpFailedRequests(/*sendRequestError*/ false);
+            if (useFlushLock) {
+                mFlushLock.unlock();
+            }
+            return false;
+        }
+
+        // Mark that the request has be submitted successfully.
+        nextRequest.submitted = true;
+
+        // Update the latest request sent to HAL
+        if (nextRequest.halRequest.settings != NULL) { // Don't update if they were unchanged
+            Mutex::Autolock al(mLatestRequestMutex);
+
+            camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
+            mLatestRequest.acquire(cloned);
+        }
+
+        if (nextRequest.halRequest.settings != NULL) {
+            nextRequest.captureRequest->mSettings.unlock(nextRequest.halRequest.settings);
+        }
+
+        // Remove any previously queued triggers (after unlock)
+        res = removeTriggers(mPrevRequest);
+        if (res != OK) {
+            SET_ERR("RequestThread: Unable to remove triggers "
+                  "(capture request %d, HAL device: %s (%d)",
+                  nextRequest.halRequest.frame_number, strerror(-res), res);
+            cleanUpFailedRequests(/*sendRequestError*/ false);
+            if (useFlushLock) {
+                mFlushLock.unlock();
+            }
+            return false;
+        }
     }
 
-    if (request.settings != NULL) {
-        nextRequest->mSettings.unlock(request.settings);
+    if (useFlushLock) {
+        mFlushLock.unlock();
     }
 
     // Unset as current request
     {
         Mutex::Autolock l(mRequestLock);
-        mNextRequest.clear();
+        mNextRequests.clear();
     }
 
-    // Remove any previously queued triggers (after unlock)
-    res = removeTriggers(mPrevRequest);
-    if (res != OK) {
-        SET_ERR("RequestThread: Unable to remove triggers "
-              "(capture request %d, HAL device: %s (%d)",
-              request.frame_number, strerror(-res), res);
-        return false;
-    }
-    mPrevTriggers = triggerCount;
-
     return true;
 }
 
+status_t Camera3Device::RequestThread::prepareHalRequests() {
+    ATRACE_CALL();
+
+    for (auto& nextRequest : mNextRequests) {
+        sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
+        camera3_capture_request_t* halRequest = &nextRequest.halRequest;
+        Vector<camera3_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
+
+        // Prepare a request to HAL
+        halRequest->frame_number = captureRequest->mResultExtras.frameNumber;
+
+        // Insert any queued triggers (before metadata is locked)
+        status_t res = insertTriggers(captureRequest);
+
+        if (res < 0) {
+            SET_ERR("RequestThread: Unable to insert triggers "
+                    "(capture request %d, HAL device: %s (%d)",
+                    halRequest->frame_number, strerror(-res), res);
+            return INVALID_OPERATION;
+        }
+        int triggerCount = res;
+        bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
+        mPrevTriggers = triggerCount;
+
+        // If the request is the same as last, or we had triggers last time
+        if (mPrevRequest != captureRequest || triggersMixedIn) {
+            /**
+             * HAL workaround:
+             * Insert a dummy trigger ID if a trigger is set but no trigger ID is
+             */
+            res = addDummyTriggerIds(captureRequest);
+            if (res != OK) {
+                SET_ERR("RequestThread: Unable to insert dummy trigger IDs "
+                        "(capture request %d, HAL device: %s (%d)",
+                        halRequest->frame_number, strerror(-res), res);
+                return INVALID_OPERATION;
+            }
+
+            /**
+             * The request should be presorted so accesses in HAL
+             *   are O(logn). Sidenote, sorting a sorted metadata is nop.
+             */
+            captureRequest->mSettings.sort();
+            halRequest->settings = captureRequest->mSettings.getAndLock();
+            mPrevRequest = captureRequest;
+            ALOGVV("%s: Request settings are NEW", __FUNCTION__);
+
+            IF_ALOGV() {
+                camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
+                find_camera_metadata_ro_entry(
+                        halRequest->settings,
+                        ANDROID_CONTROL_AF_TRIGGER,
+                        &e
+                );
+                if (e.count > 0) {
+                    ALOGV("%s: Request (frame num %d) had AF trigger 0x%x",
+                          __FUNCTION__,
+                          halRequest->frame_number,
+                          e.data.u8[0]);
+                }
+            }
+        } else {
+            // leave request.settings NULL to indicate 'reuse latest given'
+            ALOGVV("%s: Request settings are REUSED",
+                   __FUNCTION__);
+        }
+
+        uint32_t totalNumBuffers = 0;
+
+        // Fill in buffers
+        if (captureRequest->mInputStream != NULL) {
+            halRequest->input_buffer = &captureRequest->mInputBuffer;
+            totalNumBuffers += 1;
+        } else {
+            halRequest->input_buffer = NULL;
+        }
+
+        outputBuffers->insertAt(camera3_stream_buffer_t(), 0,
+                captureRequest->mOutputStreams.size());
+        halRequest->output_buffers = outputBuffers->array();
+        for (size_t i = 0; i < captureRequest->mOutputStreams.size(); i++) {
+            res = captureRequest->mOutputStreams.editItemAt(i)->
+                    getBuffer(&outputBuffers->editItemAt(i));
+            if (res != OK) {
+                // Can't get output buffer from gralloc queue - this could be due to
+                // abandoned queue or other consumer misbehavior, so not a fatal
+                // error
+                ALOGE("RequestThread: Can't get output buffer, skipping request:"
+                        " %s (%d)", strerror(-res), res);
+
+                return TIMED_OUT;
+            }
+            halRequest->num_output_buffers++;
+        }
+        totalNumBuffers += halRequest->num_output_buffers;
+
+        // Log request in the in-flight queue
+        sp<Camera3Device> parent = mParent.promote();
+        if (parent == NULL) {
+            // Should not happen, and nowhere to send errors to, so just log it
+            CLOGE("RequestThread: Parent is gone");
+            return INVALID_OPERATION;
+        }
+        res = parent->registerInFlight(halRequest->frame_number,
+                totalNumBuffers, captureRequest->mResultExtras,
+                /*hasInput*/halRequest->input_buffer != NULL,
+                captureRequest->mAeTriggerCancelOverride);
+        ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
+               ", burstId = %" PRId32 ".",
+                __FUNCTION__,
+                captureRequest->mResultExtras.requestId, captureRequest->mResultExtras.frameNumber,
+                captureRequest->mResultExtras.burstId);
+        if (res != OK) {
+            SET_ERR("RequestThread: Unable to register new in-flight request:"
+                    " %s (%d)", strerror(-res), res);
+            return INVALID_OPERATION;
+        }
+    }
+
+    return OK;
+}
+
 CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {
     Mutex::Autolock al(mLatestRequestMutex);
 
@@ -3075,11 +3136,13 @@
         sp<Camera3StreamInterface>& stream) {
     Mutex::Autolock l(mRequestLock);
 
-    if (mNextRequest != nullptr) {
-        for (const auto& s : mNextRequest->mOutputStreams) {
-            if (stream == s) return true;
+    for (const auto& nextRequest : mNextRequests) {
+        if (!nextRequest.submitted) {
+            for (const auto& s : nextRequest.captureRequest->mOutputStreams) {
+                if (stream == s) return true;
+            }
+            if (stream == nextRequest.captureRequest->mInputStream) return true;
         }
-        if (stream == mNextRequest->mInputStream) return true;
     }
 
     for (const auto& request : mRequestQueue) {
@@ -3099,37 +3162,95 @@
     return false;
 }
 
-void Camera3Device::RequestThread::cleanUpFailedRequest(
-        camera3_capture_request_t &request,
-        sp<CaptureRequest> &nextRequest,
-        Vector<camera3_stream_buffer_t> &outputBuffers) {
+void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) {
+    if (mNextRequests.empty()) {
+        return;
+    }
 
-    if (request.settings != NULL) {
-        nextRequest->mSettings.unlock(request.settings);
-    }
-    if (nextRequest->mInputStream != NULL) {
-        nextRequest->mInputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
-        nextRequest->mInputStream->returnInputBuffer(nextRequest->mInputBuffer);
-    }
-    for (size_t i = 0; i < request.num_output_buffers; i++) {
-        outputBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
-        nextRequest->mOutputStreams.editItemAt(i)->returnBuffer(
-            outputBuffers[i], 0);
+    for (auto& nextRequest : mNextRequests) {
+        // Skip the ones that have been submitted successfully.
+        if (nextRequest.submitted) {
+            continue;
+        }
+
+        sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
+        camera3_capture_request_t* halRequest = &nextRequest.halRequest;
+        Vector<camera3_stream_buffer_t>* outputBuffers = &nextRequest.outputBuffers;
+
+        if (halRequest->settings != NULL) {
+            captureRequest->mSettings.unlock(halRequest->settings);
+        }
+
+        if (captureRequest->mInputStream != NULL) {
+            captureRequest->mInputBuffer.status = CAMERA3_BUFFER_STATUS_ERROR;
+            captureRequest->mInputStream->returnInputBuffer(captureRequest->mInputBuffer);
+        }
+
+        for (size_t i = 0; i < halRequest->num_output_buffers; i++) {
+            outputBuffers->editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
+            captureRequest->mOutputStreams.editItemAt(i)->returnBuffer((*outputBuffers)[i], 0);
+        }
+
+        if (sendRequestError) {
+            Mutex::Autolock l(mRequestLock);
+            if (mListener != NULL) {
+                mListener->notifyError(
+                        ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST,
+                        captureRequest->mResultExtras);
+            }
+        }
     }
 
     Mutex::Autolock l(mRequestLock);
-    mNextRequest.clear();
+    mNextRequests.clear();
 }
 
-sp<Camera3Device::CaptureRequest>
-        Camera3Device::RequestThread::waitForNextRequest() {
-    status_t res;
-    sp<CaptureRequest> nextRequest;
-
+void Camera3Device::RequestThread::waitForNextRequestBatch() {
     // Optimized a bit for the simple steady-state case (single repeating
     // request), to avoid putting that request in the queue temporarily.
     Mutex::Autolock l(mRequestLock);
 
+    assert(mNextRequests.empty());
+
+    NextRequest nextRequest;
+    nextRequest.captureRequest = waitForNextRequestLocked();
+    if (nextRequest.captureRequest == nullptr) {
+        return;
+    }
+
+    nextRequest.halRequest = camera3_capture_request_t();
+    nextRequest.submitted = false;
+    mNextRequests.add(nextRequest);
+
+    // Wait for additional requests
+    const size_t batchSize = nextRequest.captureRequest->mBatchSize;
+
+    for (size_t i = 1; i < batchSize; i++) {
+        NextRequest additionalRequest;
+        additionalRequest.captureRequest = waitForNextRequestLocked();
+        if (additionalRequest.captureRequest == nullptr) {
+            break;
+        }
+
+        additionalRequest.halRequest = camera3_capture_request_t();
+        additionalRequest.submitted = false;
+        mNextRequests.add(additionalRequest);
+    }
+
+    if (mNextRequests.size() < batchSize) {
+        ALOGE("RequestThread: only get %d out of %d requests. Skipping requests.",
+                mNextRequests.size(), batchSize);
+        cleanUpFailedRequests(/*sendRequestError*/true);
+    }
+
+    return;
+}
+
+sp<Camera3Device::CaptureRequest>
+        Camera3Device::RequestThread::waitForNextRequestLocked() {
+    status_t res;
+    sp<CaptureRequest> nextRequest;
+
     while (mRequestQueue.empty()) {
         if (!mRepeatingRequests.empty()) {
             // Always atomically enqueue all requests in a repeating request
@@ -3224,8 +3345,6 @@
 
     handleAePrecaptureCancelRequest(nextRequest);
 
-    mNextRequest = nextRequest;
-
     return nextRequest;
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 1f39940..9d3c533 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -264,6 +264,11 @@
         // Used to cancel AE precapture trigger for devices doesn't support
         // CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
         AeTriggerCancelOverride_t           mAeTriggerCancelOverride;
+        // The number of requests that should be submitted to HAL at a time.
+        // For example, if batch size is 8, this request and the following 7
+        // requests will be submitted to HAL at a time. The batch size for
+        // the following 7 requests will be ignored by the request thread.
+        int                                 mBatchSize;
     };
     typedef List<sp<CaptureRequest> > RequestList;
 
@@ -441,6 +446,11 @@
                        int64_t *lastFrameNumber = NULL);
 
         /**
+         * Flush all pending requests in HAL.
+         */
+        status_t flush();
+
+        /**
          * Queue a trigger to be dispatched with the next outgoing
          * process_capture_request. The settings for that request only
          * will be temporarily rewritten to add the trigger tag/value.
@@ -501,16 +511,30 @@
 
         static const nsecs_t kRequestTimeout = 50e6; // 50 ms
 
-        // Waits for a request, or returns NULL if times out.
-        sp<CaptureRequest> waitForNextRequest();
+        // Used to prepare a batch of requests.
+        struct NextRequest {
+            sp<CaptureRequest>              captureRequest;
+            camera3_capture_request_t       halRequest;
+            Vector<camera3_stream_buffer_t> outputBuffers;
+            bool                            submitted;
+        };
 
-        // Return buffers, etc, for a request that couldn't be fully
-        // constructed. The buffers will be returned in the ERROR state
-        // to mark them as not having valid data.
-        // All arguments will be modified.
-        void cleanUpFailedRequest(camera3_capture_request_t &request,
-                sp<CaptureRequest> &nextRequest,
-                Vector<camera3_stream_buffer_t> &outputBuffers);
+        // Wait for the next batch of requests and put them in mNextRequests. mNextRequests will
+        // be empty if it times out.
+        void waitForNextRequestBatch();
+
+        // Waits for a request, or returns NULL if times out. Must be called with mRequestLock hold.
+        sp<CaptureRequest> waitForNextRequestLocked();
+
+        // Prepare HAL requests and output buffers in mNextRequests. Return TIMED_OUT if getting any
+        // output buffer timed out. If an error is returned, the caller should clean up the pending
+        // request batch.
+        status_t prepareHalRequests();
+
+        // Return buffers, etc, for requests in mNextRequests that couldn't be fully constructed and
+        // send request errors if sendRequestError is true. The buffers will be returned in the
+        // ERROR state to mark them as not having valid data. mNextRequests will be cleared.
+        void cleanUpFailedRequests(bool sendRequestError);
 
         // Pause handling
         bool               waitIfPaused();
@@ -539,10 +563,13 @@
         Condition          mRequestSignal;
         RequestList        mRequestQueue;
         RequestList        mRepeatingRequests;
-        // The next request being prepped for submission to the HAL, no longer
+        // The next batch of requests being prepped for submission to the HAL, no longer
         // on the request queue. Read-only even with mRequestLock held, outside
         // of threadLoop
-        sp<const CaptureRequest> mNextRequest;
+        Vector<NextRequest> mNextRequests;
+
+        // To protect flush() and sending a request batch to HAL.
+        Mutex              mFlushLock;
 
         bool               mReconfigured;
 
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index ecb8ac8..1d9d04f 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -92,6 +92,10 @@
     return OK;
 }
 
+bool Camera3DummyStream::isVideoStream() const {
+    return false;
+}
+
 }; // namespace camera3
 
 }; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 3a3dbf4..97c0c96 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -54,6 +54,11 @@
 
     status_t         setTransform(int transform);
 
+    /**
+     * Return if this output stream is for video encoding.
+     */
+    bool isVideoStream() const;
+
   protected:
 
     /**
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 8c611d5..3f0a736 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -426,6 +426,17 @@
     return res;
 }
 
+bool Camera3OutputStream::isVideoStream() const {
+    uint32_t usage = 0;
+    status_t res = getEndpointUsage(&usage);
+    if (res != OK) {
+        ALOGE("%s: getting end point usage failed: %s (%d).", __FUNCTION__, strerror(-res), res);
+        return false;
+    }
+
+    return (usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) != 0;
+}
+
 }; // namespace camera3
 
 }; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 941d693..3c083ec 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -64,6 +64,11 @@
      */
     status_t         setTransform(int transform);
 
+    /**
+     * Return if this output stream is for video encoding.
+     */
+    bool isVideoStream() const;
+
   protected:
     Camera3OutputStream(int id, camera3_stream_type_t type,
             uint32_t width, uint32_t height, int format,
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index aae72cf..df89b34 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -34,6 +34,11 @@
      * HAL_TRANSFORM_* / NATIVE_WINDOW_TRANSFORM_* constants.
      */
     virtual status_t setTransform(int transform) = 0;
+
+    /**
+     * Return if this output stream is for video encoding.
+     */
+    virtual bool isVideoStream() const = 0;
 };
 
 } // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 7163d62..96299b3 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -455,8 +455,9 @@
         res = mOutputBufferReturnedSignal.waitRelative(mLock, kWaitForBufferDuration);
         if (res != OK) {
             if (res == TIMED_OUT) {
-                ALOGE("%s: wait for output buffer return timed out after %lldms", __FUNCTION__,
-                        kWaitForBufferDuration / 1000000LL);
+                ALOGE("%s: wait for output buffer return timed out after %lldms (max_buffers %d)",
+                        __FUNCTION__, kWaitForBufferDuration / 1000000LL,
+                        camera3_stream::max_buffers);
             }
             return res;
         }
@@ -486,9 +487,12 @@
     status_t res = returnBufferLocked(buffer, timestamp);
     if (res == OK) {
         fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/true);
-        mOutputBufferReturnedSignal.signal();
     }
 
+    // Even if returning the buffer failed, we still want to signal whoever is waiting for the
+    // buffer to be returned.
+    mOutputBufferReturnedSignal.signal();
+
     return res;
 }
 
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index e54cc5a..4790754 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -96,6 +96,15 @@
     const size_t SIZE = 256;
     char buffer[SIZE];
 
+    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+        result.format("Permission Denial: "
+                "can't dump ResourceManagerService from pid=%d, uid=%d\n",
+                IPCThreadState::self()->getCallingPid(),
+                IPCThreadState::self()->getCallingUid());
+        write(fd, result.string(), result.size());
+        return PERMISSION_DENIED;
+    }
+
     snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this);
     result.append(buffer);
     result.append("  Policies:\n");